diff --git a/ROADMAP.md b/ROADMAP.md
index 1d58fef72..ee003268c 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -29,11 +29,11 @@
## Work in progress
- Examples & tests
-- Add/improve GLTFScenesManager features
+- glTF IBL shader
## TODO / possible improvements
-- Sort transparent objects by distance from the camera to the object bounding box center + radius distance before drawing them
+- Add/improve GLTFScenesManager features
- Option to chose between sphere and OBB frustum culling
- Mesh raycasting
- Lights management?
diff --git a/dist/esm/core/bindings/BufferBinding.mjs b/dist/esm/core/bindings/BufferBinding.mjs
index 5f76d89b8..aa3095005 100644
--- a/dist/esm/core/bindings/BufferBinding.mjs
+++ b/dist/esm/core/bindings/BufferBinding.mjs
@@ -136,7 +136,13 @@ class BufferBinding extends Binding {
});
binding.value = bindings[bindingKey].value;
if (binding.value instanceof Vec2 || binding.value instanceof Vec3) {
- binding.value.onChange(() => binding.shouldUpdate = true);
+ const _onChangeCallback = binding.value._onChangeCallback;
+ binding.value._onChangeCallback = () => {
+ if (_onChangeCallback) {
+ _onChangeCallback();
+ }
+ binding.shouldUpdate = true;
+ };
}
this.inputs[bindingKey] = binding;
this.cacheKey += `${bindingKey},${bindings[bindingKey].type},`;
diff --git a/dist/esm/core/camera/Camera.mjs b/dist/esm/core/camera/Camera.mjs
index 1ae9c8088..961c074a4 100644
--- a/dist/esm/core/camera/Camera.mjs
+++ b/dist/esm/core/camera/Camera.mjs
@@ -107,7 +107,7 @@ class Camera extends Object3D {
*/
updateModelMatrix() {
super.updateModelMatrix();
- this.setScreenRatios();
+ this.setVisibleSize();
this.matrices.view.shouldUpdate = true;
}
/**
@@ -142,7 +142,7 @@ class Camera extends Object3D {
__privateSet(this, _fov, fov);
this.shouldUpdateProjectionMatrix();
}
- this.setScreenRatios();
+ this.setVisibleSize();
this.setCSSPerspective();
}
/**
@@ -203,7 +203,7 @@ class Camera extends Object3D {
}
this.size.width = width;
this.size.height = height;
- this.setScreenRatios();
+ this.setVisibleSize();
this.setCSSPerspective();
}
/**
@@ -241,7 +241,7 @@ class Camera extends Object3D {
* @param depth - depth to use for calculations
* @returns - visible width and height at given depth
*/
- getScreenRatiosAtDepth(depth = 0) {
+ getVisibleSizeAtDepth(depth = 0) {
const cameraOffset = this.position.z;
if (depth < cameraOffset) {
depth -= cameraOffset;
@@ -258,8 +258,8 @@ class Camera extends Object3D {
/**
* Sets visible width / height at a depth of 0.
*/
- setScreenRatios() {
- this.screenRatio = this.getScreenRatiosAtDepth();
+ setVisibleSize() {
+ this.visibleSize = this.getVisibleSizeAtDepth();
}
/**
* Rotate this {@link Camera} so it looks at the {@link Vec3 | target}
diff --git a/dist/esm/core/renderers/GPUCameraRenderer.mjs b/dist/esm/core/renderers/GPUCameraRenderer.mjs
index a86d2a94d..8af508b7c 100644
--- a/dist/esm/core/renderers/GPUCameraRenderer.mjs
+++ b/dist/esm/core/renderers/GPUCameraRenderer.mjs
@@ -180,8 +180,8 @@ class GPUCameraRenderer extends GPURenderer {
* Call our {@link GPURenderer#onResize | GPURenderer onResize method} and resize our {@link camera} as well
*/
onResize() {
- super.onResize();
this.setPerspective();
+ super.onResize();
}
/* RENDER */
/**
diff --git a/dist/esm/curtains/objects3D/DOMObject3D.mjs b/dist/esm/curtains/objects3D/DOMObject3D.mjs
index c2b2a140e..5a3dfdf87 100644
--- a/dist/esm/curtains/objects3D/DOMObject3D.mjs
+++ b/dist/esm/curtains/objects3D/DOMObject3D.mjs
@@ -31,7 +31,7 @@ class DOMObject3D extends ProjectedObject3D {
* @param element - {@link HTMLElement} or string representing an {@link HTMLElement} selector used to scale and position the {@link DOMObject3D}
* @param parameters - {@link DOMObject3DParams | parameters} used to create this {@link DOMObject3D}
*/
- constructor(renderer, element, parameters) {
+ constructor(renderer, element, parameters = {}) {
super(renderer);
/** Private {@link Vec3 | vector} used to keep track of the actual {@link DOMObject3DTransforms#position.world | world position} accounting the {@link DOMObject3DTransforms#position.document | additional document translation} converted into world space */
__privateAdd(this, _DOMObjectWorldPosition, new Vec3());
@@ -59,8 +59,7 @@ class DOMObject3D extends ProjectedObject3D {
position: new Vec2()
},
cameraWorld: {
- size: new Vec2(1),
- position: new Vec2()
+ size: new Vec2(1)
},
scaledWorld: {
size: new Vec3(1),
@@ -255,8 +254,8 @@ class DOMObject3D extends ProjectedObject3D {
*/
documentToWorldSpace(vector = new Vec3()) {
return new Vec3(
- vector.x * this.renderer.pixelRatio / this.renderer.boundingRect.width * this.camera.screenRatio.width,
- -(vector.y * this.renderer.pixelRatio / this.renderer.boundingRect.height) * this.camera.screenRatio.height,
+ vector.x * this.renderer.pixelRatio / this.renderer.boundingRect.width * this.camera.visibleSize.width,
+ -(vector.y * this.renderer.pixelRatio / this.renderer.boundingRect.height) * this.camera.visibleSize.height,
vector.z
);
}
@@ -286,19 +285,15 @@ class DOMObject3D extends ProjectedObject3D {
(containerCenter.y - planeCenter.y) / containerBoundingRect.height
);
this.size.cameraWorld.size.set(
- this.size.normalizedWorld.size.x * this.camera.screenRatio.width,
- this.size.normalizedWorld.size.y * this.camera.screenRatio.height
- );
- this.size.cameraWorld.position.set(
- this.size.normalizedWorld.position.x * this.camera.screenRatio.width,
- this.size.normalizedWorld.position.y * this.camera.screenRatio.height
+ this.size.normalizedWorld.size.x * this.camera.visibleSize.width,
+ this.size.normalizedWorld.size.y * this.camera.visibleSize.height
);
this.size.scaledWorld.size.set(this.size.cameraWorld.size.x / size.x, this.size.cameraWorld.size.y / size.y, 1);
this.size.scaledWorld.size.z = this.size.scaledWorld.size.y * (size.x / size.y / (this.size.document.width / this.size.document.height));
this.size.scaledWorld.position.set(
- this.size.cameraWorld.position.x - center.x * this.size.scaledWorld.size.x * size.x,
- this.size.cameraWorld.position.y - center.y * this.size.scaledWorld.size.y * size.y,
- -center.z
+ this.size.normalizedWorld.position.x * this.camera.visibleSize.width,
+ this.size.normalizedWorld.position.y * this.camera.visibleSize.height,
+ 0
);
}
/**
@@ -334,10 +329,10 @@ class DOMObject3D extends ProjectedObject3D {
setWorldTransformOrigin() {
this.transforms.origin.world = new Vec3(
(this.transformOrigin.x * 2 - 1) * // between -1 and 1
- this.size.scaledWorld.size.x,
+ __privateGet(this, _DOMObjectWorldScale).x,
-(this.transformOrigin.y * 2 - 1) * // between -1 and 1
- this.size.scaledWorld.size.y,
- this.transformOrigin.z * this.size.scaledWorld.size.z
+ __privateGet(this, _DOMObjectWorldScale).y,
+ this.transformOrigin.z * __privateGet(this, _DOMObjectWorldScale).z
);
this.shouldUpdateMatrixStack();
}
@@ -353,7 +348,7 @@ class DOMObject3D extends ProjectedObject3D {
/**
* Callback to execute just after the {@link domElement} has been resized.
* @param callback - callback to run just after {@link domElement} has been resized
- * @returns - our Mesh
+ * @returns - our {@link DOMObject3D}
*/
onAfterDOMElementResize(callback) {
if (callback) {
diff --git a/dist/esm/curtains/renderers/GPUCurtainsRenderer.mjs b/dist/esm/curtains/renderers/GPUCurtainsRenderer.mjs
index 1c3ab1fda..2d16d88dd 100644
--- a/dist/esm/curtains/renderers/GPUCurtainsRenderer.mjs
+++ b/dist/esm/curtains/renderers/GPUCurtainsRenderer.mjs
@@ -56,14 +56,9 @@ class GPUCurtainsRenderer extends GPUCameraRenderer {
}
});
this.domObjects.forEach((domObject) => {
- this.onBeforeCommandEncoderCreation.add(
- () => {
- if (!domObject.domElement.isResizing) {
- domObject.domElement.setSize();
- }
- },
- { once: true }
- );
+ if (!domObject.domElement.isResizing) {
+ domObject.domElement.setSize();
+ }
});
}
}
diff --git a/dist/esm/curtains/meshes/PingPongPlane.mjs b/dist/esm/extras/meshes/PingPongPlane.mjs
similarity index 100%
rename from dist/esm/curtains/meshes/PingPongPlane.mjs
rename to dist/esm/extras/meshes/PingPongPlane.mjs
diff --git a/dist/esm/index.mjs b/dist/esm/index.mjs
index 32ba97905..48dcb8353 100644
--- a/dist/esm/index.mjs
+++ b/dist/esm/index.mjs
@@ -35,7 +35,6 @@ export { Texture } from './core/textures/Texture.mjs';
export { DOMTexture } from './core/textures/DOMTexture.mjs';
export { DOMElement } from './core/DOM/DOMElement.mjs';
export { DOMMesh } from './curtains/meshes/DOMMesh.mjs';
-export { PingPongPlane } from './curtains/meshes/PingPongPlane.mjs';
export { Plane } from './curtains/meshes/Plane.mjs';
export { DOMObject3D } from './curtains/objects3D/DOMObject3D.mjs';
export { GPUCurtainsRenderer } from './curtains/renderers/GPUCurtainsRenderer.mjs';
@@ -49,6 +48,7 @@ export { Vec3 } from './math/Vec3.mjs';
export { OrbitControls } from './extras/controls/OrbitControls.mjs';
export { BoxGeometry } from './extras/geometries/BoxGeometry.mjs';
export { SphereGeometry } from './extras/geometries/SphereGeometry.mjs';
+export { PingPongPlane } from './extras/meshes/PingPongPlane.mjs';
export { GLTFLoader } from './extras/gltf/GLTFLoader.mjs';
export { GLTFScenesManager } from './extras/gltf/GLTFScenesManager.mjs';
export { buildIBLShaders, buildPBRShaders, buildShaders } from './extras/gltf/utils.mjs';
diff --git a/dist/gpu-curtains.umd.js b/dist/gpu-curtains.umd.js
index 4a68f9b4a..c394b4863 100644
--- a/dist/gpu-curtains.umd.js
+++ b/dist/gpu-curtains.umd.js
@@ -1747,7 +1747,13 @@
});
binding.value = bindings[bindingKey].value;
if (binding.value instanceof Vec2 || binding.value instanceof Vec3) {
- binding.value.onChange(() => binding.shouldUpdate = true);
+ const _onChangeCallback = binding.value._onChangeCallback;
+ binding.value._onChangeCallback = () => {
+ if (_onChangeCallback) {
+ _onChangeCallback();
+ }
+ binding.shouldUpdate = true;
+ };
}
this.inputs[bindingKey] = binding;
this.cacheKey += `${bindingKey},${bindings[bindingKey].type},`;
@@ -4272,7 +4278,7 @@
*/
updateModelMatrix() {
super.updateModelMatrix();
- this.setScreenRatios();
+ this.setVisibleSize();
this.matrices.view.shouldUpdate = true;
}
/**
@@ -4307,7 +4313,7 @@
__privateSet$8(this, _fov, fov);
this.shouldUpdateProjectionMatrix();
}
- this.setScreenRatios();
+ this.setVisibleSize();
this.setCSSPerspective();
}
/**
@@ -4368,7 +4374,7 @@
}
this.size.width = width;
this.size.height = height;
- this.setScreenRatios();
+ this.setVisibleSize();
this.setCSSPerspective();
}
/**
@@ -4406,7 +4412,7 @@
* @param depth - depth to use for calculations
* @returns - visible width and height at given depth
*/
- getScreenRatiosAtDepth(depth = 0) {
+ getVisibleSizeAtDepth(depth = 0) {
const cameraOffset = this.position.z;
if (depth < cameraOffset) {
depth -= cameraOffset;
@@ -4423,8 +4429,8 @@
/**
* Sets visible width / height at a depth of 0.
*/
- setScreenRatios() {
- this.screenRatio = this.getScreenRatiosAtDepth();
+ setVisibleSize() {
+ this.visibleSize = this.getVisibleSizeAtDepth();
}
/**
* Rotate this {@link Camera} so it looks at the {@link Vec3 | target}
@@ -10835,8 +10841,8 @@ ${this.shaders.compute.head}`;
* Call our {@link GPURenderer#onResize | GPURenderer onResize method} and resize our {@link camera} as well
*/
onResize() {
- super.onResize();
this.setPerspective();
+ super.onResize();
}
/* RENDER */
/**
@@ -11445,7 +11451,7 @@ struct VSOutput {
* @param element - {@link HTMLElement} or string representing an {@link HTMLElement} selector used to scale and position the {@link DOMObject3D}
* @param parameters - {@link DOMObject3DParams | parameters} used to create this {@link DOMObject3D}
*/
- constructor(renderer, element, parameters) {
+ constructor(renderer, element, parameters = {}) {
super(renderer);
/** Private {@link Vec3 | vector} used to keep track of the actual {@link DOMObject3DTransforms#position.world | world position} accounting the {@link DOMObject3DTransforms#position.document | additional document translation} converted into world space */
__privateAdd$2(this, _DOMObjectWorldPosition, new Vec3());
@@ -11473,8 +11479,7 @@ struct VSOutput {
position: new Vec2()
},
cameraWorld: {
- size: new Vec2(1),
- position: new Vec2()
+ size: new Vec2(1)
},
scaledWorld: {
size: new Vec3(1),
@@ -11669,8 +11674,8 @@ struct VSOutput {
*/
documentToWorldSpace(vector = new Vec3()) {
return new Vec3(
- vector.x * this.renderer.pixelRatio / this.renderer.boundingRect.width * this.camera.screenRatio.width,
- -(vector.y * this.renderer.pixelRatio / this.renderer.boundingRect.height) * this.camera.screenRatio.height,
+ vector.x * this.renderer.pixelRatio / this.renderer.boundingRect.width * this.camera.visibleSize.width,
+ -(vector.y * this.renderer.pixelRatio / this.renderer.boundingRect.height) * this.camera.visibleSize.height,
vector.z
);
}
@@ -11700,19 +11705,15 @@ struct VSOutput {
(containerCenter.y - planeCenter.y) / containerBoundingRect.height
);
this.size.cameraWorld.size.set(
- this.size.normalizedWorld.size.x * this.camera.screenRatio.width,
- this.size.normalizedWorld.size.y * this.camera.screenRatio.height
- );
- this.size.cameraWorld.position.set(
- this.size.normalizedWorld.position.x * this.camera.screenRatio.width,
- this.size.normalizedWorld.position.y * this.camera.screenRatio.height
+ this.size.normalizedWorld.size.x * this.camera.visibleSize.width,
+ this.size.normalizedWorld.size.y * this.camera.visibleSize.height
);
this.size.scaledWorld.size.set(this.size.cameraWorld.size.x / size.x, this.size.cameraWorld.size.y / size.y, 1);
this.size.scaledWorld.size.z = this.size.scaledWorld.size.y * (size.x / size.y / (this.size.document.width / this.size.document.height));
this.size.scaledWorld.position.set(
- this.size.cameraWorld.position.x - center.x * this.size.scaledWorld.size.x * size.x,
- this.size.cameraWorld.position.y - center.y * this.size.scaledWorld.size.y * size.y,
- -center.z
+ this.size.normalizedWorld.position.x * this.camera.visibleSize.width,
+ this.size.normalizedWorld.position.y * this.camera.visibleSize.height,
+ 0
);
}
/**
@@ -11748,10 +11749,10 @@ struct VSOutput {
setWorldTransformOrigin() {
this.transforms.origin.world = new Vec3(
(this.transformOrigin.x * 2 - 1) * // between -1 and 1
- this.size.scaledWorld.size.x,
+ __privateGet$2(this, _DOMObjectWorldScale).x,
-(this.transformOrigin.y * 2 - 1) * // between -1 and 1
- this.size.scaledWorld.size.y,
- this.transformOrigin.z * this.size.scaledWorld.size.z
+ __privateGet$2(this, _DOMObjectWorldScale).y,
+ this.transformOrigin.z * __privateGet$2(this, _DOMObjectWorldScale).z
);
this.shouldUpdateMatrixStack();
}
@@ -11767,7 +11768,7 @@ struct VSOutput {
/**
* Callback to execute just after the {@link domElement} has been resized.
* @param callback - callback to run just after {@link domElement} has been resized
- * @returns - our Mesh
+ * @returns - our {@link DOMObject3D}
*/
onAfterDOMElementResize(callback) {
if (callback) {
@@ -11964,73 +11965,6 @@ struct VSOutput {
}
}
- class PingPongPlane extends FullscreenPlane {
- /**
- * PingPongPlane constructor
- * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link PingPongPlane}
- * @param parameters - {@link MeshBaseRenderParams | parameters} use to create this {@link PingPongPlane}
- */
- constructor(renderer, parameters = {}) {
- renderer = renderer && renderer.renderer || renderer;
- isRenderer(renderer, parameters.label ? parameters.label + " PingPongPlane" : "PingPongPlane");
- const colorAttachments = parameters.targets && parameters.targets.length && parameters.targets.map((target) => {
- return {
- targetFormat: target.format
- };
- });
- parameters.outputTarget = new RenderTarget(renderer, {
- label: parameters.label ? parameters.label + " render target" : "Ping Pong render target",
- useDepth: false,
- ...colorAttachments && { colorAttachments }
- });
- parameters.transparent = false;
- parameters.depth = false;
- parameters.label = parameters.label ?? "PingPongPlane " + renderer.pingPongPlanes?.length;
- super(renderer, parameters);
- this.type = "PingPongPlane";
- this.createTexture({
- label: parameters.label ? `${parameters.label} render texture` : "PingPongPlane render texture",
- name: "renderTexture",
- ...parameters.targets && parameters.targets.length && { format: parameters.targets[0].format },
- usage: ["copyDst", "textureBinding"]
- });
- }
- /**
- * Get our main {@link Texture}, the one that contains our ping pong content
- * @readonly
- */
- get renderTexture() {
- return this.textures.find((texture) => texture.options.name === "renderTexture");
- }
- /**
- * Add the {@link PingPongPlane} to the {@link core/scenes/Scene.Scene | Scene} and optionally to the renderer.
- * @param addToRenderer - whether to add this {@link PingPongPlane} to the {@link Renderer#pingPongPlanes | Renderer pingPongPlanes array}
- */
- addToScene(addToRenderer = false) {
- if (addToRenderer) {
- this.renderer.pingPongPlanes.push(this);
- }
- if (this.autoRender) {
- this.renderer.scene.addPingPongPlane(this);
- }
- }
- /**
- * Remove the {@link PingPongPlane} from the {@link core/scenes/Scene.Scene | Scene} and optionally from the renderer as well.
- * @param removeFromRenderer - whether to remove this {@link PingPongPlane} from the {@link Renderer#pingPongPlanes | Renderer pingPongPlanes array}
- */
- removeFromScene(removeFromRenderer = false) {
- if (this.outputTarget) {
- this.outputTarget.destroy();
- }
- if (this.autoRender) {
- this.renderer.scene.removePingPongPlane(this);
- }
- if (removeFromRenderer) {
- this.renderer.pingPongPlanes = this.renderer.pingPongPlanes.filter((pPP) => pPP.uuid !== this.uuid);
- }
- }
- }
-
const defaultPlaneParams = {
label: "Plane",
// geometry
@@ -12162,14 +12096,9 @@ struct VSOutput {
}
});
this.domObjects.forEach((domObject) => {
- this.onBeforeCommandEncoderCreation.add(
- () => {
- if (!domObject.domElement.isResizing) {
- domObject.domElement.setSize();
- }
- },
- { once: true }
- );
+ if (!domObject.domElement.isResizing) {
+ domObject.domElement.setSize();
+ }
});
}
}
@@ -13022,6 +12951,73 @@ struct VSOutput {
}
}
+ class PingPongPlane extends FullscreenPlane {
+ /**
+ * PingPongPlane constructor
+ * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link PingPongPlane}
+ * @param parameters - {@link MeshBaseRenderParams | parameters} use to create this {@link PingPongPlane}
+ */
+ constructor(renderer, parameters = {}) {
+ renderer = renderer && renderer.renderer || renderer;
+ isRenderer(renderer, parameters.label ? parameters.label + " PingPongPlane" : "PingPongPlane");
+ const colorAttachments = parameters.targets && parameters.targets.length && parameters.targets.map((target) => {
+ return {
+ targetFormat: target.format
+ };
+ });
+ parameters.outputTarget = new RenderTarget(renderer, {
+ label: parameters.label ? parameters.label + " render target" : "Ping Pong render target",
+ useDepth: false,
+ ...colorAttachments && { colorAttachments }
+ });
+ parameters.transparent = false;
+ parameters.depth = false;
+ parameters.label = parameters.label ?? "PingPongPlane " + renderer.pingPongPlanes?.length;
+ super(renderer, parameters);
+ this.type = "PingPongPlane";
+ this.createTexture({
+ label: parameters.label ? `${parameters.label} render texture` : "PingPongPlane render texture",
+ name: "renderTexture",
+ ...parameters.targets && parameters.targets.length && { format: parameters.targets[0].format },
+ usage: ["copyDst", "textureBinding"]
+ });
+ }
+ /**
+ * Get our main {@link Texture}, the one that contains our ping pong content
+ * @readonly
+ */
+ get renderTexture() {
+ return this.textures.find((texture) => texture.options.name === "renderTexture");
+ }
+ /**
+ * Add the {@link PingPongPlane} to the {@link core/scenes/Scene.Scene | Scene} and optionally to the renderer.
+ * @param addToRenderer - whether to add this {@link PingPongPlane} to the {@link Renderer#pingPongPlanes | Renderer pingPongPlanes array}
+ */
+ addToScene(addToRenderer = false) {
+ if (addToRenderer) {
+ this.renderer.pingPongPlanes.push(this);
+ }
+ if (this.autoRender) {
+ this.renderer.scene.addPingPongPlane(this);
+ }
+ }
+ /**
+ * Remove the {@link PingPongPlane} from the {@link core/scenes/Scene.Scene | Scene} and optionally from the renderer as well.
+ * @param removeFromRenderer - whether to remove this {@link PingPongPlane} from the {@link Renderer#pingPongPlanes | Renderer pingPongPlanes array}
+ */
+ removeFromScene(removeFromRenderer = false) {
+ if (this.outputTarget) {
+ this.outputTarget.destroy();
+ }
+ if (this.autoRender) {
+ this.renderer.scene.removePingPongPlane(this);
+ }
+ if (removeFromRenderer) {
+ this.renderer.pingPongPlanes = this.renderer.pingPongPlanes.filter((pPP) => pPP.uuid !== this.uuid);
+ }
+ }
+ }
+
const GL$1 = WebGLRenderingContext;
const GLB_MAGIC = 1179937895;
const CHUNK_TYPE = {
diff --git a/dist/gpu-curtains.umd.min.js b/dist/gpu-curtains.umd.min.js
index d97128cd4..0b3e854f4 100644
--- a/dist/gpu-curtains.umd.min.js
+++ b/dist/gpu-curtains.umd.min.js
@@ -1,4 +1,4 @@
-(function(w,k){typeof exports=="object"&&typeof module<"u"?k(exports):typeof define=="function"&&define.amd?define(["exports"],k):(w=typeof globalThis<"u"?globalThis:w||self,k(w.window=w.window||{}))})(this,function(w){"use strict";const k=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,o=>{const e=Math.random()*16|0;return(o==="x"?e:e&3|8).toString(16).toUpperCase()}),ye=o=>o.replace(/(?:^\w|[A-Z]|\b\w)/g,(e,t)=>t===0?e.toLowerCase():e.toUpperCase()).replace(/\s+/g,""),Xe=o=>{const e=ye(o);return e.charAt(0).toUpperCase()+e.slice(1)};let Ke=0;const D=o=>{Ke>100||(console.warn(Ke===100?"GPUCurtains: too many warnings thrown, stop logging.":o),Ke++)},q=o=>{throw new Error(o)},Ze=(o,e="GPURenderer",t)=>{const s=t?`Unable to create ${t} because the ${e} is not defined: ${o}`:`The ${e} is not defined: ${o}`;q(s)},$=(o,e)=>{const t=o&&(o.type==="GPURenderer"||o.type==="GPUCameraRenderer"||o.type==="GPUCurtainsRenderer");return t||Ze(o,"GPURenderer",e),t},Be=(o,e)=>{const t=o&&(o.type==="GPUCameraRenderer"||o.type==="GPUCurtainsRenderer");return t||Ze(o,"GPUCameraRenderer",e),t},Qe=(o,e)=>{const t=o&&o.type==="GPUCurtainsRenderer";return t||Ze(o,"GPUCurtainsRenderer",e),t},Pt=(()=>{let o,e;const t={};return function(i,r){e||(e=i.createShaderModule({label:"textured quad shaders for mip level generation",code:`
+(function(w,k){typeof exports=="object"&&typeof module<"u"?k(exports):typeof define=="function"&&define.amd?define(["exports"],k):(w=typeof globalThis<"u"?globalThis:w||self,k(w.window=w.window||{}))})(this,function(w){"use strict";const k=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,o=>{const e=Math.random()*16|0;return(o==="x"?e:e&3|8).toString(16).toUpperCase()}),xe=o=>o.replace(/(?:^\w|[A-Z]|\b\w)/g,(e,t)=>t===0?e.toLowerCase():e.toUpperCase()).replace(/\s+/g,""),Xe=o=>{const e=xe(o);return e.charAt(0).toUpperCase()+e.slice(1)};let Ke=0;const D=o=>{Ke>100||(console.warn(Ke===100?"GPUCurtains: too many warnings thrown, stop logging.":o),Ke++)},q=o=>{throw new Error(o)},Ze=(o,e="GPURenderer",t)=>{const s=t?`Unable to create ${t} because the ${e} is not defined: ${o}`:`The ${e} is not defined: ${o}`;q(s)},$=(o,e)=>{const t=o&&(o.type==="GPURenderer"||o.type==="GPUCameraRenderer"||o.type==="GPUCurtainsRenderer");return t||Ze(o,"GPURenderer",e),t},Ce=(o,e)=>{const t=o&&(o.type==="GPUCameraRenderer"||o.type==="GPUCurtainsRenderer");return t||Ze(o,"GPUCameraRenderer",e),t},Qe=(o,e)=>{const t=o&&o.type==="GPUCurtainsRenderer";return t||Ze(o,"GPUCurtainsRenderer",e),t},Pt=(()=>{let o,e;const t={};return function(i,r){e||(e=i.createShaderModule({label:"textured quad shaders for mip level generation",code:`
struct VSOutput {
@builtin(position) position: vec4f,
@location(0) texcoord: vec2f,
@@ -32,7 +32,7 @@
@fragment fn fs(fsInput: VSOutput) -> @location(0) vec4f {
return textureSample(ourTexture, ourSampler, fsInput.texcoord);
}
- `}),o=i.createSampler({minFilter:"linear"})),t[r.format]||(t[r.format]=i.createRenderPipeline({label:"mip level generator pipeline",layout:"auto",vertex:{module:e,entryPoint:"vs"},fragment:{module:e,entryPoint:"fs",targets:[{format:r.format}]}}));const n=t[r.format],a=i.createCommandEncoder({label:"mip gen encoder"});let h=r.width,u=r.height,l=0;for(;h>1||u>1;){h=Math.max(1,h/2|0),u=Math.max(1,u/2|0);const c=i.createBindGroup({layout:n.getBindGroupLayout(0),entries:[{binding:0,resource:o},{binding:1,resource:r.createView({baseMipLevel:l,mipLevelCount:1})}]});++l;const p={label:"our basic canvas renderPass",colorAttachments:[{view:r.createView({baseMipLevel:l,mipLevelCount:1}),loadOp:"clear",storeOp:"store"}]},g=a.beginRenderPass(p);g.setPipeline(n),g.setBindGroup(0,c),g.draw(6),g.end()}const d=a.finish();i.queue.submit([d])}})(),bs=new Map([["vertex",GPUShaderStage.VERTEX],["fragment",GPUShaderStage.FRAGMENT],["compute",GPUShaderStage.COMPUTE]]),vs=(o=[])=>o.reduce((e,t)=>e|bs.get(t),0),ws={i32:{numElements:1,align:4,size:4,type:"i32",View:Int32Array},u32:{numElements:1,align:4,size:4,type:"u32",View:Uint32Array},f32:{numElements:1,align:4,size:4,type:"f32",View:Float32Array},f16:{numElements:1,align:2,size:2,type:"u16",View:Uint16Array},vec2f:{numElements:2,align:8,size:8,type:"f32",View:Float32Array},vec2i:{numElements:2,align:8,size:8,type:"i32",View:Int32Array},vec2u:{numElements:2,align:8,size:8,type:"u32",View:Uint32Array},vec2h:{numElements:2,align:4,size:4,type:"u16",View:Uint16Array},vec3i:{numElements:3,align:16,size:12,type:"i32",View:Int32Array},vec3u:{numElements:3,align:16,size:12,type:"u32",View:Uint32Array},vec3f:{numElements:3,align:16,size:12,type:"f32",View:Float32Array},vec3h:{numElements:3,align:8,size:6,type:"u16",View:Uint16Array},vec4i:{numElements:4,align:16,size:16,type:"i32",View:Int32Array},vec4u:{numElements:4,align:16,size:16,type:"u32",View:Uint32Array},vec4f:{numElements:4,align:16,size:16,type:"f32",View:Float32Array},vec4h:{numElements:4,align:8,size:8,type:"u16",View:Uint16Array},mat2x2f:{numElements:4,align:8,size:16,type:"f32",View:Float32Array},mat2x2h:{numElements:4,align:4,size:8,type:"u16",View:Uint16Array},mat3x2f:{numElements:6,align:8,size:24,type:"f32",View:Float32Array},mat3x2h:{numElements:6,align:4,size:12,type:"u16",View:Uint16Array},mat4x2f:{numElements:8,align:8,size:32,type:"f32",View:Float32Array},mat4x2h:{numElements:8,align:4,size:16,type:"u16",View:Uint16Array},mat2x3f:{numElements:8,align:16,size:32,pad:[3,1],type:"f32",View:Float32Array},mat2x3h:{numElements:8,align:8,size:16,pad:[3,1],type:"u16",View:Uint16Array},mat3x3f:{numElements:12,align:16,size:48,pad:[3,1],type:"f32",View:Float32Array},mat3x3h:{numElements:12,align:8,size:24,pad:[3,1],type:"u16",View:Uint16Array},mat4x3f:{numElements:16,align:16,size:64,pad:[3,1],type:"f32",View:Float32Array},mat4x3h:{numElements:16,align:8,size:32,pad:[3,1],type:"u16",View:Uint16Array},mat2x4f:{numElements:8,align:16,size:32,type:"f32",View:Float32Array},mat2x4h:{numElements:8,align:8,size:16,type:"u16",View:Uint16Array},mat3x4f:{numElements:12,align:16,size:48,pad:[3,1],type:"f32",View:Float32Array},mat3x4h:{numElements:12,align:8,size:24,pad:[3,1],type:"u16",View:Uint16Array},mat4x4f:{numElements:16,align:16,size:64,type:"f32",View:Float32Array},mat4x4h:{numElements:16,align:8,size:32,type:"u16",View:Uint16Array}},Rt=o=>ws[o],Ce=o=>(()=>{switch(o.bindingType){case"storage":return`var<${o.bindingType}, ${o.options.access}>`;case"uniform":default:return"var"}})(),Ms=o=>o.bindingType==="externalTexture"?`var ${o.name}: texture_external;`:o.bindingType==="storage"?`var ${o.name}: texture_storage_${o.options.viewDimension}<${o.options.format}, ${o.options.access}>;`:o.bindingType==="depth"?`var ${o.name}: texture_depth${o.options.multisampled?"_multisampled":""}_${o.options.viewDimension};`:`var ${o.name}: texture${o.options.multisampled?"_multisampled":""}_${o.options.viewDimension};`,St=o=>o.bindingType==="storage"&&o.options.access==="read_write"?"storage":o.bindingType==="storage"?"read-only-storage":"uniform",Bs=o=>(()=>{switch(o.bindingType){case"externalTexture":return{externalTexture:{}};case"storage":return{storageTexture:{format:o.options.format,viewDimension:o.options.viewDimension}};case"texture":return{texture:{multisampled:o.options.multisampled,viewDimension:o.options.viewDimension,sampleType:o.options.multisampled?"unfilterable-float":"float"}};case"depth":return{texture:{multisampled:o.options.multisampled,viewDimension:o.options.viewDimension,sampleType:"depth"}};default:return null}})(),Cs=o=>(()=>{switch(o.bindingType){case"externalTexture":return`externalTexture,${o.visibility},`;case"storage":return`storageTexture,${o.options.format},${o.options.viewDimension},${o.visibility},`;case"texture":return`texture,${o.options.multisampled},${o.options.viewDimension},${o.options.multisampled?"unfilterable-float":"float"},${o.visibility},`;case"depth":return`depthTexture,${o.options.format},${o.options.viewDimension},${o.visibility},`;default:return`${o.visibility},`}})();class Te{constructor({label:e="Uniform",name:t="uniform",bindingType:s="uniform",visibility:i=["vertex","fragment","compute"]}){this.label=e,this.name=ye(t),this.bindingType=s,this.visibility=vs(i),this.options={label:e,name:t,bindingType:s,visibility:i},this.shouldResetBindGroup=!1,this.shouldResetBindGroupLayout=!1,this.cacheKey=`${s},${this.visibility},`}}class E{constructor(e=0,t=e){this.type="Vec2",this._x=e,this._y=t}get x(){return this._x}set x(e){const t=e!==this._x;this._x=e,t&&this._onChangeCallback&&this._onChangeCallback()}get y(){return this._y}set y(e){const t=e!==this._y;this._y=e,t&&this._onChangeCallback&&this._onChangeCallback()}onChange(e){return e&&(this._onChangeCallback=e),this}set(e=0,t=e){return this.x=e,this.y=t,this}add(e=new E){return this.x+=e.x,this.y+=e.y,this}addScalar(e=0){return this.x+=e,this.y+=e,this}sub(e=new E){return this.x-=e.x,this.y-=e.y,this}subScalar(e=0){return this.x-=e,this.y-=e,this}multiply(e=new E(1)){return this.x*=e.x,this.y*=e.y,this}multiplyScalar(e=1){return this.x*=e,this.y*=e,this}divide(e=new E(1)){return this.x/=e.x,this.y/=e.y,this}divideScalar(e=1){return this.x/=e,this.y/=e,this}copy(e=new E){return this.x=e.x,this.y=e.y,this}clone(){return new E(this.x,this.y)}max(e=new E){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this}min(e=new E){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this}clamp(e=new E,t=new E){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this}equals(e=new E){return this.x===e.x&&this.y===e.y}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.lengthSq())}normalize(){let e=this.x*this.x+this.y*this.y;return e>0&&(e=1/Math.sqrt(e)),this.x*=e,this.y*=e,this}dot(e=new E){return this.x*e.x+this.y*e.y}lerp(e=new E,t=1){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this}}class J{constructor(e=new Float32Array([0,0,0,1]),t="XYZ"){this.type="Quat",this.elements=e,this.axisOrder=t}setFromArray(e=new Float32Array([0,0,0,1])){return this.elements[0]=e[0],this.elements[1]=e[1],this.elements[2]=e[2],this.elements[3]=e[3],this}setAxisOrder(e="XYZ"){switch(e=e.toUpperCase(),e){case"XYZ":case"YXZ":case"ZXY":case"ZYX":case"YZX":case"XZY":this.axisOrder=e;break;default:this.axisOrder="XYZ"}return this}copy(e=new J){return this.elements=e.elements,this.axisOrder=e.axisOrder,this}clone(){return new J().copy(this)}equals(e=new J){return this.elements[0]===e.elements[0]&&this.elements[1]===e.elements[1]&&this.elements[2]===e.elements[2]&&this.elements[3]===e.elements[3]&&this.axisOrder===e.axisOrder}setFromVec3(e){const t=e.x*.5,s=e.y*.5,i=e.z*.5,r=Math.cos(t),n=Math.cos(s),a=Math.cos(i),h=Math.sin(t),u=Math.sin(s),l=Math.sin(i);return this.axisOrder==="XYZ"?(this.elements[0]=h*n*a+r*u*l,this.elements[1]=r*u*a-h*n*l,this.elements[2]=r*n*l+h*u*a,this.elements[3]=r*n*a-h*u*l):this.axisOrder==="YXZ"?(this.elements[0]=h*n*a+r*u*l,this.elements[1]=r*u*a-h*n*l,this.elements[2]=r*n*l-h*u*a,this.elements[3]=r*n*a+h*u*l):this.axisOrder==="ZXY"?(this.elements[0]=h*n*a-r*u*l,this.elements[1]=r*u*a+h*n*l,this.elements[2]=r*n*l+h*u*a,this.elements[3]=r*n*a-h*u*l):this.axisOrder==="ZYX"?(this.elements[0]=h*n*a-r*u*l,this.elements[1]=r*u*a+h*n*l,this.elements[2]=r*n*l-h*u*a,this.elements[3]=r*n*a+h*u*l):this.axisOrder==="YZX"?(this.elements[0]=h*n*a+r*u*l,this.elements[1]=r*u*a+h*n*l,this.elements[2]=r*n*l-h*u*a,this.elements[3]=r*n*a-h*u*l):this.axisOrder==="XZY"&&(this.elements[0]=h*n*a-r*u*l,this.elements[1]=r*u*a-h*n*l,this.elements[2]=r*n*l+h*u*a,this.elements[3]=r*n*a+h*u*l),this}setFromAxisAngle(e,t=0){const s=t/2,i=Math.sin(s);return this.elements[0]=e.x*i,this.elements[1]=e.y*i,this.elements[2]=e.z*i,this.elements[3]=Math.cos(s),this}setFromRotationMatrix(e){const t=e.elements,s=t[0],i=t[4],r=t[8],n=t[1],a=t[5],h=t[9],u=t[2],l=t[6],d=t[10],c=s+a+d;if(c>0){const p=.5/Math.sqrt(c+1);this.elements[3]=.25/p,this.elements[0]=(l-h)*p,this.elements[1]=(r-u)*p,this.elements[2]=(n-i)*p}else if(s>a&&s>d){const p=2*Math.sqrt(1+s-a-d);this.elements[3]=(l-h)/p,this.elements[0]=.25*p,this.elements[1]=(i+n)/p,this.elements[2]=(r+u)/p}else if(a>d){const p=2*Math.sqrt(1+a-s-d);this.elements[3]=(r-u)/p,this.elements[0]=(i+n)/p,this.elements[1]=.25*p,this.elements[2]=(h+l)/p}else{const p=2*Math.sqrt(1+d-s-a);this.elements[3]=(n-i)/p,this.elements[0]=(r+u)/p,this.elements[1]=(h+l)/p,this.elements[2]=.25*p}return this}}class f{constructor(e=0,t=e,s=e){this.type="Vec3",this._x=e,this._y=t,this._z=s}get x(){return this._x}set x(e){const t=e!==this._x;this._x=e,t&&this._onChangeCallback&&this._onChangeCallback()}get y(){return this._y}set y(e){const t=e!==this._y;this._y=e,t&&this._onChangeCallback&&this._onChangeCallback()}get z(){return this._z}set z(e){const t=e!==this._z;this._z=e,t&&this._onChangeCallback&&this._onChangeCallback()}onChange(e){return e&&(this._onChangeCallback=e),this}set(e=0,t=e,s=e){return this.x=e,this.y=t,this.z=s,this}add(e=new f){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}addScalar(e=0){return this.x+=e,this.y+=e,this.z+=e,this}sub(e=new f){return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}subScalar(e=0){return this.x-=e,this.y-=e,this.z-=e,this}multiply(e=new f(1)){return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}multiplyScalar(e=1){return this.x*=e,this.y*=e,this.z*=e,this}divide(e=new f(1)){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this}divideScalar(e=1){return this.x/=e,this.y/=e,this.z/=e,this}copy(e=new f){return this.x=e.x,this.y=e.y,this.z=e.z,this}clone(){return new f(this.x,this.y,this.z)}max(e=new f){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this}min(e=new f){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this}clamp(e=new f,t=new f){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this.z=Math.max(e.z,Math.min(t.z,this.z)),this}equals(e=new f){return this.x===e.x&&this.y===e.y&&this.z===e.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.lengthSq())}distance(e=new f){return Math.hypot(e.x-this.x,e.y-this.y,e.z-this.z)}normalize(){let e=this.lengthSq();return e>0&&(e=1/Math.sqrt(e)),this.x*=e,this.y*=e,this.z*=e,this}dot(e=new f){return this.x*e.x+this.y*e.y+this.z*e.z}cross(e=new f){return this.crossVectors(this,e)}crossVectors(e=new f,t=new f){const s=e.x,i=e.y,r=e.z,n=t.x,a=t.y,h=t.z;return this.x=i*h-r*a,this.y=r*n-s*h,this.z=s*a-i*n,this}lerp(e=new f,t=1){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this.z+=(e.z-this.z)*t,this}applyMat4(e){const t=this._x,s=this._y,i=this._z,r=e.elements;let n=r[3]*t+r[7]*s+r[11]*i+r[15];return n=n||1,this.x=(r[0]*t+r[4]*s+r[8]*i+r[12])/n,this.y=(r[1]*t+r[5]*s+r[9]*i+r[13])/n,this.z=(r[2]*t+r[6]*s+r[10]*i+r[14])/n,this}setFromMatrixPosition(e){const t=e.elements;return this.x=t[12],this.y=t[13],this.z=t[14],this}applyQuat(e=new J){const t=this.x,s=this.y,i=this.z,r=e.elements[0],n=e.elements[1],a=e.elements[2],h=e.elements[3],u=h*t+n*i-a*s,l=h*s+a*t-r*i,d=h*i+r*s-n*t,c=-r*t-n*s-a*i;return this.x=u*h+c*-r+l*-a-d*-n,this.y=l*h+c*-n+d*-r-u*-a,this.z=d*h+c*-a+u*-n-l*-r,this}applyAxisAngle(e=new f,t=0,s=new J){return this.applyQuat(s.setFromAxisAngle(e,t))}project(e){return this.applyMat4(e.viewMatrix).applyMat4(e.projectionMatrix),this}unproject(e){return this.applyMat4(e.projectionMatrix.getInverse()).applyMat4(e.modelMatrix),this}}const Ts=4,Pe=4,j=Ts*Pe;class Je{constructor({name:e,key:t,type:s="f32"}){this.name=e,this.key=t,this.type=s,this.bufferLayout=Rt(this.type.replace("array","").replace("<","").replace(">","")),this.alignment={start:{row:0,byte:0},end:{row:0,byte:0}},this.setValue=null}get rowCount(){return this.alignment.end.row-this.alignment.start.row+1}get byteCount(){return Math.abs(this.endOffset-this.startOffset)+1}get paddedByteCount(){return(this.alignment.end.row+1)*j}get startOffset(){return this.getByteCountAtPosition(this.alignment.start)}get startOffsetToIndex(){return this.startOffset/Pe}get endOffset(){return this.getByteCountAtPosition(this.alignment.end)}get endOffsetToIndex(){return Math.floor(this.endOffset/Pe)}getPositionAtOffset(e=0){return{row:Math.floor(e/j),byte:e%j}}getByteCountAtPosition(e={row:0,byte:0}){return e.row*j+e.byte}applyOverflowToPosition(e={row:0,byte:0}){if(e.byte>j-1){const t=e.byte%j;e.row+=Math.floor(e.byte/j),e.byte=t}return e}getByteCountBetweenPositions(e={row:0,byte:0},t={row:0,byte:0}){return Math.abs(this.getByteCountAtPosition(t)-this.getByteCountAtPosition(e))}getElementAlignment(e={row:0,byte:0}){const t={start:e,end:e},{size:s,align:i}=this.bufferLayout;return e.byte%i!==0&&(e.byte+=e.byte%i),(s<=j&&e.byte+s>j||s>j&&e.byte>j)&&(e.row+=1,e.byte=0),t.end={row:e.row+Math.ceil(s/j)-1,byte:e.byte+(s%j===0?j-1:s%j-1)},t.end=this.applyOverflowToPosition(t.end),t}setAlignmentFromPosition(e={row:0,byte:0}){this.alignment=this.getElementAlignment(e)}setAlignment(e=0){this.setAlignmentFromPosition(this.getPositionAtOffset(e))}setView(e,t){this.view=new this.bufferLayout.View(e,this.startOffset,this.byteCount/this.bufferLayout.View.BYTES_PER_ELEMENT)}setValueFromFloat(e){this.view[0]=e}setValueFromVec2(e){this.view[0]=e.x??e[0]??0,this.view[1]=e.y??e[1]??0}setValueFromVec3(e){this.view[0]=e.x??e[0]??0,this.view[1]=e.y??e[1]??0,this.view[2]=e.z??e[2]??0}setValueFromMat4OrQuat(e){this.view.set(e.elements)}setValueFromMat3(e){this.setValueFromArrayWithPad(e.elements)}setValueFromArray(e){this.view.set(e)}setValueFromArrayWithPad(e){for(let t=0,s=0;t{if(this.type==="f32"||this.type==="u32"||this.type==="i32")return this.setValueFromFloat;if(this.type==="vec2f")return this.setValueFromVec2;if(this.type==="vec3f")return this.setValueFromVec3;if(this.type==="mat3x3f")return t.elements?this.setValueFromMat3:this.setValueFromArrayWithPad;if(t.elements)return this.setValueFromMat4OrQuat;if(ArrayBuffer.isView(t)||Array.isArray(t))return this.bufferLayout.pad?this.setValueFromArrayWithPad:this.setValueFromArray;D(`${this.constructor.name}: value passed to ${this.name} cannot be used: ${t}`)})(e)),this.setValue(e)}extractDataFromBufferResult(e){return e.slice(this.startOffsetToIndex,this.endOffsetToIndex)}}class zt extends Je{constructor({name:e,key:t,type:s="f32",arrayLength:i=1}){super({name:e,key:t,type:s}),this.arrayLength=i,this.numElements=Math.ceil(this.arrayLength/this.bufferLayout.numElements)}get arrayStrideToIndex(){return this.arrayStride/Pe}setAlignment(e=0){super.setAlignment(e);const t=this.getElementAlignment(this.getPositionAtOffset(this.endOffset+1));this.arrayStride=this.getByteCountBetweenPositions(this.alignment.end,t.end),this.alignment.end=this.getPositionAtOffset(this.endOffset+this.arrayStride*(this.numElements-1))}setValueFromArray(e){let t=0;const s=this.byteCount/this.bufferLayout.View.BYTES_PER_ELEMENT,i=Math.ceil(s/this.numElements);for(let r=0;r{switch(this.bufferLayout.View){case Int32Array:return s.setInt32.bind(s);case Uint16Array:return s.setUint16.bind(s);case Uint32Array:return s.setUint32.bind(s);case Float32Array:default:return s.setFloat32.bind(s)}})(t)}update(e){super.update(e);for(let t=0;t{this.viewSetFunction(i+n*this.bufferLayout.View.BYTES_PER_ELEMENT,r,!0)})}}extractDataFromBufferResult(e){const t=new Float32Array(this.arrayLength);for(let s=0;so.reduce((e,t)=>e|Ps.get(t),0);class de{constructor({label:e="Buffer",size:t=0,usage:s=["copySrc","copyDst"],mappedAtCreation:i=!1}={}){this.type="Buffer",this.reset(),this.uuid=k(),this.consumers=new Set,this.options={label:e,size:t,usage:Et(s),mappedAtCreation:i}}reset(){this.GPUBuffer=null}set size(e){this.options.size=e}createBuffer(e,t={}){const{usage:s,...i}=t;this.options={...this.options,...i,...s!==void 0&&{usage:Et(s)}},this.setBuffer(e.createBuffer(this))}setBuffer(e){this.GPUBuffer=e}copy(e,t=!1){t&&this.destroy(),this.options=e.options,this.GPUBuffer=e.GPUBuffer,this.consumers=new Set([...this.consumers,...e.consumers])}async mapBufferAsync(){if(!this.GPUBuffer||this.GPUBuffer.mapState!=="unmapped")return new Float32Array(0);await this.GPUBuffer.mapAsync(GPUMapMode.READ);const e=new Float32Array(this.GPUBuffer.getMappedRange().slice(0));return this.GPUBuffer.unmap(),e}destroy(){this.GPUBuffer?.destroy(),this.reset(),this.consumers.clear()}}class ce extends Te{constructor({label:e="Uniform",name:t="uniform",bindingType:s,visibility:i,useStruct:r=!0,access:n="read",usage:a=[],struct:h={}}){s=s??"uniform",super({label:e,name:t,bindingType:s,visibility:i}),this.options={...this.options,useStruct:r,access:n,usage:a,struct:h},this.cacheKey+=`${r},${n},`,this.arrayBufferSize=0,this.shouldUpdate=!1,this.useStruct=r,this.bufferElements=[],this.inputs={},this.buffer=new de,Object.keys(h).length&&(this.setBindings(h),this.setBufferAttributes(),this.setWGSLFragment())}get resourceLayout(){return{buffer:{type:St(this)}}}get resourceLayoutCacheKey(){return`buffer,${St(this)},${this.visibility},`}get resource(){return{buffer:this.buffer.GPUBuffer}}clone(e){const{struct:t,...s}=e,i=new this.constructor(s);return i.setBindings(t),i.options.struct=t,i.arrayBufferSize=this.arrayBufferSize,i.arrayBuffer=new ArrayBuffer(i.arrayBufferSize),i.arrayView=new DataView(i.arrayBuffer,0,i.arrayBuffer.byteLength),i.buffer.size=i.arrayBuffer.byteLength,this.bufferElements.forEach(r=>{const n=new r.constructor({name:r.name,key:r.key,type:r.type,...r.arrayLength&&{arrayLength:r.arrayLength}});n.alignment=r.alignment,r.arrayStride&&(n.arrayStride=r.arrayStride),n.setView(i.arrayBuffer,i.arrayView),i.bufferElements.push(n)}),this.name===i.name&&this.label===i.label?(i.wgslStructFragment=this.wgslStructFragment,i.wgslGroupFragment=this.wgslGroupFragment):i.setWGSLFragment(),i.shouldUpdate=i.arrayBufferSize>0,i}setBindings(e){for(const t of Object.keys(e)){const s={};for(const i in e[t])i!=="value"&&(s[i]=e[t][i]);s.name=t,Object.defineProperty(s,"value",{get(){return s._value},set(i){s._value=i,s.shouldUpdate=!0}}),s.value=e[t].value,(s.value instanceof E||s.value instanceof f)&&s.value.onChange(()=>s.shouldUpdate=!0),this.inputs[t]=s,this.cacheKey+=`${t},${e[t].type},`}}setBufferAttributes(){let e=Object.keys(this.inputs);const t=e.filter(s=>this.inputs[s].type.includes("array"));t.length&&(e.sort((s,i)=>{const r=Math.min(0,this.inputs[s].type.indexOf("array")),n=Math.min(0,this.inputs[i].type.indexOf("array"));return r-n}),t.length>1&&(e=e.filter(s=>!t.includes(s))));for(const s of e){const i=this.inputs[s],r={name:ye(i.name??s),key:s,type:i.type},n=i.type.includes("array")&&(Array.isArray(i.value)||ArrayBuffer.isView(i.value));this.bufferElements.push(n?new zt({...r,arrayLength:i.value.length}):new Je(r))}if(this.bufferElements.forEach((s,i)=>{const r=i===0?0:this.bufferElements[i-1].endOffset+1;s.setAlignment(r)}),t.length>1)if(t.map(r=>{const n=this.inputs[r],a=Rt(n.type.replace("array","").replace("<","").replace(">",""));return Math.ceil(n.value.length/a.numElements)}).every((r,n,a)=>r===a[0])){const r=t.map(h=>{const u=this.inputs[h];return new et({name:ye(u.name??h),key:h,type:u.type,arrayLength:u.value.length})}),n=t.map(h=>{const u=this.inputs[h];return new Je({name:ye(u.name??h),key:h,type:u.type.replace("array","").replace("<","").replace(">","")})});n.forEach((h,u)=>{u===0?this.bufferElements.length?h.setAlignmentFromPosition({row:this.bufferElements[this.bufferElements.length-1].alignment.end.row+1,byte:0}):h.setAlignment(0):h.setAlignment(n[u-1].endOffset+1)});const a=n[n.length-1].endOffset+1-n[0].startOffset;r.forEach((h,u)=>{h.setAlignment(n[u].startOffset,a)}),this.bufferElements=[...this.bufferElements,...r]}else D(`BufferBinding: "${this.label}" contains multiple array inputs that should use an interleaved array, but their sizes do not match. These inputs cannot be added to the BufferBinding: "${t.join(", ")}"`);this.arrayBufferSize=this.bufferElements.length?this.bufferElements[this.bufferElements.length-1].paddedByteCount:0,this.arrayBuffer=new ArrayBuffer(this.arrayBufferSize),this.arrayView=new DataView(this.arrayBuffer,0,this.arrayBuffer.byteLength),this.buffer.size=this.arrayBuffer.byteLength;for(const s of this.bufferElements)s.setView(this.arrayBuffer,this.arrayView);this.shouldUpdate=this.arrayBufferSize>0}setWGSLFragment(){if(!this.bufferElements.length)return;const e=Xe(this.label);if(this.useStruct){const t=this.bufferElements.filter(i=>!(i instanceof et)),s=this.bufferElements.filter(i=>i instanceof et);if(s.length){const i=this.bindingType==="uniform"?`, ${s[0].numElements}`:"";if(t.length){this.wgslStructFragment=`struct ${e}Element {
+ `}),o=i.createSampler({minFilter:"linear"})),t[r.format]||(t[r.format]=i.createRenderPipeline({label:"mip level generator pipeline",layout:"auto",vertex:{module:e,entryPoint:"vs"},fragment:{module:e,entryPoint:"fs",targets:[{format:r.format}]}}));const n=t[r.format],a=i.createCommandEncoder({label:"mip gen encoder"});let h=r.width,u=r.height,l=0;for(;h>1||u>1;){h=Math.max(1,h/2|0),u=Math.max(1,u/2|0);const c=i.createBindGroup({layout:n.getBindGroupLayout(0),entries:[{binding:0,resource:o},{binding:1,resource:r.createView({baseMipLevel:l,mipLevelCount:1})}]});++l;const p={label:"our basic canvas renderPass",colorAttachments:[{view:r.createView({baseMipLevel:l,mipLevelCount:1}),loadOp:"clear",storeOp:"store"}]},g=a.beginRenderPass(p);g.setPipeline(n),g.setBindGroup(0,c),g.draw(6),g.end()}const d=a.finish();i.queue.submit([d])}})(),bs=new Map([["vertex",GPUShaderStage.VERTEX],["fragment",GPUShaderStage.FRAGMENT],["compute",GPUShaderStage.COMPUTE]]),vs=(o=[])=>o.reduce((e,t)=>e|bs.get(t),0),ws={i32:{numElements:1,align:4,size:4,type:"i32",View:Int32Array},u32:{numElements:1,align:4,size:4,type:"u32",View:Uint32Array},f32:{numElements:1,align:4,size:4,type:"f32",View:Float32Array},f16:{numElements:1,align:2,size:2,type:"u16",View:Uint16Array},vec2f:{numElements:2,align:8,size:8,type:"f32",View:Float32Array},vec2i:{numElements:2,align:8,size:8,type:"i32",View:Int32Array},vec2u:{numElements:2,align:8,size:8,type:"u32",View:Uint32Array},vec2h:{numElements:2,align:4,size:4,type:"u16",View:Uint16Array},vec3i:{numElements:3,align:16,size:12,type:"i32",View:Int32Array},vec3u:{numElements:3,align:16,size:12,type:"u32",View:Uint32Array},vec3f:{numElements:3,align:16,size:12,type:"f32",View:Float32Array},vec3h:{numElements:3,align:8,size:6,type:"u16",View:Uint16Array},vec4i:{numElements:4,align:16,size:16,type:"i32",View:Int32Array},vec4u:{numElements:4,align:16,size:16,type:"u32",View:Uint32Array},vec4f:{numElements:4,align:16,size:16,type:"f32",View:Float32Array},vec4h:{numElements:4,align:8,size:8,type:"u16",View:Uint16Array},mat2x2f:{numElements:4,align:8,size:16,type:"f32",View:Float32Array},mat2x2h:{numElements:4,align:4,size:8,type:"u16",View:Uint16Array},mat3x2f:{numElements:6,align:8,size:24,type:"f32",View:Float32Array},mat3x2h:{numElements:6,align:4,size:12,type:"u16",View:Uint16Array},mat4x2f:{numElements:8,align:8,size:32,type:"f32",View:Float32Array},mat4x2h:{numElements:8,align:4,size:16,type:"u16",View:Uint16Array},mat2x3f:{numElements:8,align:16,size:32,pad:[3,1],type:"f32",View:Float32Array},mat2x3h:{numElements:8,align:8,size:16,pad:[3,1],type:"u16",View:Uint16Array},mat3x3f:{numElements:12,align:16,size:48,pad:[3,1],type:"f32",View:Float32Array},mat3x3h:{numElements:12,align:8,size:24,pad:[3,1],type:"u16",View:Uint16Array},mat4x3f:{numElements:16,align:16,size:64,pad:[3,1],type:"f32",View:Float32Array},mat4x3h:{numElements:16,align:8,size:32,pad:[3,1],type:"u16",View:Uint16Array},mat2x4f:{numElements:8,align:16,size:32,type:"f32",View:Float32Array},mat2x4h:{numElements:8,align:8,size:16,type:"u16",View:Uint16Array},mat3x4f:{numElements:12,align:16,size:48,pad:[3,1],type:"f32",View:Float32Array},mat3x4h:{numElements:12,align:8,size:24,pad:[3,1],type:"u16",View:Uint16Array},mat4x4f:{numElements:16,align:16,size:64,type:"f32",View:Float32Array},mat4x4h:{numElements:16,align:8,size:32,type:"u16",View:Uint16Array}},Rt=o=>ws[o],Te=o=>(()=>{switch(o.bindingType){case"storage":return`var<${o.bindingType}, ${o.options.access}>`;case"uniform":default:return"var"}})(),Ms=o=>o.bindingType==="externalTexture"?`var ${o.name}: texture_external;`:o.bindingType==="storage"?`var ${o.name}: texture_storage_${o.options.viewDimension}<${o.options.format}, ${o.options.access}>;`:o.bindingType==="depth"?`var ${o.name}: texture_depth${o.options.multisampled?"_multisampled":""}_${o.options.viewDimension};`:`var ${o.name}: texture${o.options.multisampled?"_multisampled":""}_${o.options.viewDimension};`,St=o=>o.bindingType==="storage"&&o.options.access==="read_write"?"storage":o.bindingType==="storage"?"read-only-storage":"uniform",Bs=o=>(()=>{switch(o.bindingType){case"externalTexture":return{externalTexture:{}};case"storage":return{storageTexture:{format:o.options.format,viewDimension:o.options.viewDimension}};case"texture":return{texture:{multisampled:o.options.multisampled,viewDimension:o.options.viewDimension,sampleType:o.options.multisampled?"unfilterable-float":"float"}};case"depth":return{texture:{multisampled:o.options.multisampled,viewDimension:o.options.viewDimension,sampleType:"depth"}};default:return null}})(),Cs=o=>(()=>{switch(o.bindingType){case"externalTexture":return`externalTexture,${o.visibility},`;case"storage":return`storageTexture,${o.options.format},${o.options.viewDimension},${o.visibility},`;case"texture":return`texture,${o.options.multisampled},${o.options.viewDimension},${o.options.multisampled?"unfilterable-float":"float"},${o.visibility},`;case"depth":return`depthTexture,${o.options.format},${o.options.viewDimension},${o.visibility},`;default:return`${o.visibility},`}})();class Pe{constructor({label:e="Uniform",name:t="uniform",bindingType:s="uniform",visibility:i=["vertex","fragment","compute"]}){this.label=e,this.name=xe(t),this.bindingType=s,this.visibility=vs(i),this.options={label:e,name:t,bindingType:s,visibility:i},this.shouldResetBindGroup=!1,this.shouldResetBindGroupLayout=!1,this.cacheKey=`${s},${this.visibility},`}}class E{constructor(e=0,t=e){this.type="Vec2",this._x=e,this._y=t}get x(){return this._x}set x(e){const t=e!==this._x;this._x=e,t&&this._onChangeCallback&&this._onChangeCallback()}get y(){return this._y}set y(e){const t=e!==this._y;this._y=e,t&&this._onChangeCallback&&this._onChangeCallback()}onChange(e){return e&&(this._onChangeCallback=e),this}set(e=0,t=e){return this.x=e,this.y=t,this}add(e=new E){return this.x+=e.x,this.y+=e.y,this}addScalar(e=0){return this.x+=e,this.y+=e,this}sub(e=new E){return this.x-=e.x,this.y-=e.y,this}subScalar(e=0){return this.x-=e,this.y-=e,this}multiply(e=new E(1)){return this.x*=e.x,this.y*=e.y,this}multiplyScalar(e=1){return this.x*=e,this.y*=e,this}divide(e=new E(1)){return this.x/=e.x,this.y/=e.y,this}divideScalar(e=1){return this.x/=e,this.y/=e,this}copy(e=new E){return this.x=e.x,this.y=e.y,this}clone(){return new E(this.x,this.y)}max(e=new E){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this}min(e=new E){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this}clamp(e=new E,t=new E){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this}equals(e=new E){return this.x===e.x&&this.y===e.y}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.lengthSq())}normalize(){let e=this.x*this.x+this.y*this.y;return e>0&&(e=1/Math.sqrt(e)),this.x*=e,this.y*=e,this}dot(e=new E){return this.x*e.x+this.y*e.y}lerp(e=new E,t=1){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this}}class J{constructor(e=new Float32Array([0,0,0,1]),t="XYZ"){this.type="Quat",this.elements=e,this.axisOrder=t}setFromArray(e=new Float32Array([0,0,0,1])){return this.elements[0]=e[0],this.elements[1]=e[1],this.elements[2]=e[2],this.elements[3]=e[3],this}setAxisOrder(e="XYZ"){switch(e=e.toUpperCase(),e){case"XYZ":case"YXZ":case"ZXY":case"ZYX":case"YZX":case"XZY":this.axisOrder=e;break;default:this.axisOrder="XYZ"}return this}copy(e=new J){return this.elements=e.elements,this.axisOrder=e.axisOrder,this}clone(){return new J().copy(this)}equals(e=new J){return this.elements[0]===e.elements[0]&&this.elements[1]===e.elements[1]&&this.elements[2]===e.elements[2]&&this.elements[3]===e.elements[3]&&this.axisOrder===e.axisOrder}setFromVec3(e){const t=e.x*.5,s=e.y*.5,i=e.z*.5,r=Math.cos(t),n=Math.cos(s),a=Math.cos(i),h=Math.sin(t),u=Math.sin(s),l=Math.sin(i);return this.axisOrder==="XYZ"?(this.elements[0]=h*n*a+r*u*l,this.elements[1]=r*u*a-h*n*l,this.elements[2]=r*n*l+h*u*a,this.elements[3]=r*n*a-h*u*l):this.axisOrder==="YXZ"?(this.elements[0]=h*n*a+r*u*l,this.elements[1]=r*u*a-h*n*l,this.elements[2]=r*n*l-h*u*a,this.elements[3]=r*n*a+h*u*l):this.axisOrder==="ZXY"?(this.elements[0]=h*n*a-r*u*l,this.elements[1]=r*u*a+h*n*l,this.elements[2]=r*n*l+h*u*a,this.elements[3]=r*n*a-h*u*l):this.axisOrder==="ZYX"?(this.elements[0]=h*n*a-r*u*l,this.elements[1]=r*u*a+h*n*l,this.elements[2]=r*n*l-h*u*a,this.elements[3]=r*n*a+h*u*l):this.axisOrder==="YZX"?(this.elements[0]=h*n*a+r*u*l,this.elements[1]=r*u*a+h*n*l,this.elements[2]=r*n*l-h*u*a,this.elements[3]=r*n*a-h*u*l):this.axisOrder==="XZY"&&(this.elements[0]=h*n*a-r*u*l,this.elements[1]=r*u*a-h*n*l,this.elements[2]=r*n*l+h*u*a,this.elements[3]=r*n*a+h*u*l),this}setFromAxisAngle(e,t=0){const s=t/2,i=Math.sin(s);return this.elements[0]=e.x*i,this.elements[1]=e.y*i,this.elements[2]=e.z*i,this.elements[3]=Math.cos(s),this}setFromRotationMatrix(e){const t=e.elements,s=t[0],i=t[4],r=t[8],n=t[1],a=t[5],h=t[9],u=t[2],l=t[6],d=t[10],c=s+a+d;if(c>0){const p=.5/Math.sqrt(c+1);this.elements[3]=.25/p,this.elements[0]=(l-h)*p,this.elements[1]=(r-u)*p,this.elements[2]=(n-i)*p}else if(s>a&&s>d){const p=2*Math.sqrt(1+s-a-d);this.elements[3]=(l-h)/p,this.elements[0]=.25*p,this.elements[1]=(i+n)/p,this.elements[2]=(r+u)/p}else if(a>d){const p=2*Math.sqrt(1+a-s-d);this.elements[3]=(r-u)/p,this.elements[0]=(i+n)/p,this.elements[1]=.25*p,this.elements[2]=(h+l)/p}else{const p=2*Math.sqrt(1+d-s-a);this.elements[3]=(n-i)/p,this.elements[0]=(r+u)/p,this.elements[1]=(h+l)/p,this.elements[2]=.25*p}return this}}class f{constructor(e=0,t=e,s=e){this.type="Vec3",this._x=e,this._y=t,this._z=s}get x(){return this._x}set x(e){const t=e!==this._x;this._x=e,t&&this._onChangeCallback&&this._onChangeCallback()}get y(){return this._y}set y(e){const t=e!==this._y;this._y=e,t&&this._onChangeCallback&&this._onChangeCallback()}get z(){return this._z}set z(e){const t=e!==this._z;this._z=e,t&&this._onChangeCallback&&this._onChangeCallback()}onChange(e){return e&&(this._onChangeCallback=e),this}set(e=0,t=e,s=e){return this.x=e,this.y=t,this.z=s,this}add(e=new f){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}addScalar(e=0){return this.x+=e,this.y+=e,this.z+=e,this}sub(e=new f){return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}subScalar(e=0){return this.x-=e,this.y-=e,this.z-=e,this}multiply(e=new f(1)){return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}multiplyScalar(e=1){return this.x*=e,this.y*=e,this.z*=e,this}divide(e=new f(1)){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this}divideScalar(e=1){return this.x/=e,this.y/=e,this.z/=e,this}copy(e=new f){return this.x=e.x,this.y=e.y,this.z=e.z,this}clone(){return new f(this.x,this.y,this.z)}max(e=new f){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this}min(e=new f){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this}clamp(e=new f,t=new f){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this.z=Math.max(e.z,Math.min(t.z,this.z)),this}equals(e=new f){return this.x===e.x&&this.y===e.y&&this.z===e.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.lengthSq())}distance(e=new f){return Math.hypot(e.x-this.x,e.y-this.y,e.z-this.z)}normalize(){let e=this.lengthSq();return e>0&&(e=1/Math.sqrt(e)),this.x*=e,this.y*=e,this.z*=e,this}dot(e=new f){return this.x*e.x+this.y*e.y+this.z*e.z}cross(e=new f){return this.crossVectors(this,e)}crossVectors(e=new f,t=new f){const s=e.x,i=e.y,r=e.z,n=t.x,a=t.y,h=t.z;return this.x=i*h-r*a,this.y=r*n-s*h,this.z=s*a-i*n,this}lerp(e=new f,t=1){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this.z+=(e.z-this.z)*t,this}applyMat4(e){const t=this._x,s=this._y,i=this._z,r=e.elements;let n=r[3]*t+r[7]*s+r[11]*i+r[15];return n=n||1,this.x=(r[0]*t+r[4]*s+r[8]*i+r[12])/n,this.y=(r[1]*t+r[5]*s+r[9]*i+r[13])/n,this.z=(r[2]*t+r[6]*s+r[10]*i+r[14])/n,this}setFromMatrixPosition(e){const t=e.elements;return this.x=t[12],this.y=t[13],this.z=t[14],this}applyQuat(e=new J){const t=this.x,s=this.y,i=this.z,r=e.elements[0],n=e.elements[1],a=e.elements[2],h=e.elements[3],u=h*t+n*i-a*s,l=h*s+a*t-r*i,d=h*i+r*s-n*t,c=-r*t-n*s-a*i;return this.x=u*h+c*-r+l*-a-d*-n,this.y=l*h+c*-n+d*-r-u*-a,this.z=d*h+c*-a+u*-n-l*-r,this}applyAxisAngle(e=new f,t=0,s=new J){return this.applyQuat(s.setFromAxisAngle(e,t))}project(e){return this.applyMat4(e.viewMatrix).applyMat4(e.projectionMatrix),this}unproject(e){return this.applyMat4(e.projectionMatrix.getInverse()).applyMat4(e.modelMatrix),this}}const Ts=4,Re=4,j=Ts*Re;class Je{constructor({name:e,key:t,type:s="f32"}){this.name=e,this.key=t,this.type=s,this.bufferLayout=Rt(this.type.replace("array","").replace("<","").replace(">","")),this.alignment={start:{row:0,byte:0},end:{row:0,byte:0}},this.setValue=null}get rowCount(){return this.alignment.end.row-this.alignment.start.row+1}get byteCount(){return Math.abs(this.endOffset-this.startOffset)+1}get paddedByteCount(){return(this.alignment.end.row+1)*j}get startOffset(){return this.getByteCountAtPosition(this.alignment.start)}get startOffsetToIndex(){return this.startOffset/Re}get endOffset(){return this.getByteCountAtPosition(this.alignment.end)}get endOffsetToIndex(){return Math.floor(this.endOffset/Re)}getPositionAtOffset(e=0){return{row:Math.floor(e/j),byte:e%j}}getByteCountAtPosition(e={row:0,byte:0}){return e.row*j+e.byte}applyOverflowToPosition(e={row:0,byte:0}){if(e.byte>j-1){const t=e.byte%j;e.row+=Math.floor(e.byte/j),e.byte=t}return e}getByteCountBetweenPositions(e={row:0,byte:0},t={row:0,byte:0}){return Math.abs(this.getByteCountAtPosition(t)-this.getByteCountAtPosition(e))}getElementAlignment(e={row:0,byte:0}){const t={start:e,end:e},{size:s,align:i}=this.bufferLayout;return e.byte%i!==0&&(e.byte+=e.byte%i),(s<=j&&e.byte+s>j||s>j&&e.byte>j)&&(e.row+=1,e.byte=0),t.end={row:e.row+Math.ceil(s/j)-1,byte:e.byte+(s%j===0?j-1:s%j-1)},t.end=this.applyOverflowToPosition(t.end),t}setAlignmentFromPosition(e={row:0,byte:0}){this.alignment=this.getElementAlignment(e)}setAlignment(e=0){this.setAlignmentFromPosition(this.getPositionAtOffset(e))}setView(e,t){this.view=new this.bufferLayout.View(e,this.startOffset,this.byteCount/this.bufferLayout.View.BYTES_PER_ELEMENT)}setValueFromFloat(e){this.view[0]=e}setValueFromVec2(e){this.view[0]=e.x??e[0]??0,this.view[1]=e.y??e[1]??0}setValueFromVec3(e){this.view[0]=e.x??e[0]??0,this.view[1]=e.y??e[1]??0,this.view[2]=e.z??e[2]??0}setValueFromMat4OrQuat(e){this.view.set(e.elements)}setValueFromMat3(e){this.setValueFromArrayWithPad(e.elements)}setValueFromArray(e){this.view.set(e)}setValueFromArrayWithPad(e){for(let t=0,s=0;t{if(this.type==="f32"||this.type==="u32"||this.type==="i32")return this.setValueFromFloat;if(this.type==="vec2f")return this.setValueFromVec2;if(this.type==="vec3f")return this.setValueFromVec3;if(this.type==="mat3x3f")return t.elements?this.setValueFromMat3:this.setValueFromArrayWithPad;if(t.elements)return this.setValueFromMat4OrQuat;if(ArrayBuffer.isView(t)||Array.isArray(t))return this.bufferLayout.pad?this.setValueFromArrayWithPad:this.setValueFromArray;D(`${this.constructor.name}: value passed to ${this.name} cannot be used: ${t}`)})(e)),this.setValue(e)}extractDataFromBufferResult(e){return e.slice(this.startOffsetToIndex,this.endOffsetToIndex)}}class zt extends Je{constructor({name:e,key:t,type:s="f32",arrayLength:i=1}){super({name:e,key:t,type:s}),this.arrayLength=i,this.numElements=Math.ceil(this.arrayLength/this.bufferLayout.numElements)}get arrayStrideToIndex(){return this.arrayStride/Re}setAlignment(e=0){super.setAlignment(e);const t=this.getElementAlignment(this.getPositionAtOffset(this.endOffset+1));this.arrayStride=this.getByteCountBetweenPositions(this.alignment.end,t.end),this.alignment.end=this.getPositionAtOffset(this.endOffset+this.arrayStride*(this.numElements-1))}setValueFromArray(e){let t=0;const s=this.byteCount/this.bufferLayout.View.BYTES_PER_ELEMENT,i=Math.ceil(s/this.numElements);for(let r=0;r{switch(this.bufferLayout.View){case Int32Array:return s.setInt32.bind(s);case Uint16Array:return s.setUint16.bind(s);case Uint32Array:return s.setUint32.bind(s);case Float32Array:default:return s.setFloat32.bind(s)}})(t)}update(e){super.update(e);for(let t=0;t{this.viewSetFunction(i+n*this.bufferLayout.View.BYTES_PER_ELEMENT,r,!0)})}}extractDataFromBufferResult(e){const t=new Float32Array(this.arrayLength);for(let s=0;so.reduce((e,t)=>e|Ps.get(t),0);class pe{constructor({label:e="Buffer",size:t=0,usage:s=["copySrc","copyDst"],mappedAtCreation:i=!1}={}){this.type="Buffer",this.reset(),this.uuid=k(),this.consumers=new Set,this.options={label:e,size:t,usage:Et(s),mappedAtCreation:i}}reset(){this.GPUBuffer=null}set size(e){this.options.size=e}createBuffer(e,t={}){const{usage:s,...i}=t;this.options={...this.options,...i,...s!==void 0&&{usage:Et(s)}},this.setBuffer(e.createBuffer(this))}setBuffer(e){this.GPUBuffer=e}copy(e,t=!1){t&&this.destroy(),this.options=e.options,this.GPUBuffer=e.GPUBuffer,this.consumers=new Set([...this.consumers,...e.consumers])}async mapBufferAsync(){if(!this.GPUBuffer||this.GPUBuffer.mapState!=="unmapped")return new Float32Array(0);await this.GPUBuffer.mapAsync(GPUMapMode.READ);const e=new Float32Array(this.GPUBuffer.getMappedRange().slice(0));return this.GPUBuffer.unmap(),e}destroy(){this.GPUBuffer?.destroy(),this.reset(),this.consumers.clear()}}class fe extends Pe{constructor({label:e="Uniform",name:t="uniform",bindingType:s,visibility:i,useStruct:r=!0,access:n="read",usage:a=[],struct:h={}}){s=s??"uniform",super({label:e,name:t,bindingType:s,visibility:i}),this.options={...this.options,useStruct:r,access:n,usage:a,struct:h},this.cacheKey+=`${r},${n},`,this.arrayBufferSize=0,this.shouldUpdate=!1,this.useStruct=r,this.bufferElements=[],this.inputs={},this.buffer=new pe,Object.keys(h).length&&(this.setBindings(h),this.setBufferAttributes(),this.setWGSLFragment())}get resourceLayout(){return{buffer:{type:St(this)}}}get resourceLayoutCacheKey(){return`buffer,${St(this)},${this.visibility},`}get resource(){return{buffer:this.buffer.GPUBuffer}}clone(e){const{struct:t,...s}=e,i=new this.constructor(s);return i.setBindings(t),i.options.struct=t,i.arrayBufferSize=this.arrayBufferSize,i.arrayBuffer=new ArrayBuffer(i.arrayBufferSize),i.arrayView=new DataView(i.arrayBuffer,0,i.arrayBuffer.byteLength),i.buffer.size=i.arrayBuffer.byteLength,this.bufferElements.forEach(r=>{const n=new r.constructor({name:r.name,key:r.key,type:r.type,...r.arrayLength&&{arrayLength:r.arrayLength}});n.alignment=r.alignment,r.arrayStride&&(n.arrayStride=r.arrayStride),n.setView(i.arrayBuffer,i.arrayView),i.bufferElements.push(n)}),this.name===i.name&&this.label===i.label?(i.wgslStructFragment=this.wgslStructFragment,i.wgslGroupFragment=this.wgslGroupFragment):i.setWGSLFragment(),i.shouldUpdate=i.arrayBufferSize>0,i}setBindings(e){for(const t of Object.keys(e)){const s={};for(const i in e[t])i!=="value"&&(s[i]=e[t][i]);if(s.name=t,Object.defineProperty(s,"value",{get(){return s._value},set(i){s._value=i,s.shouldUpdate=!0}}),s.value=e[t].value,s.value instanceof E||s.value instanceof f){const i=s.value._onChangeCallback;s.value._onChangeCallback=()=>{i&&i(),s.shouldUpdate=!0}}this.inputs[t]=s,this.cacheKey+=`${t},${e[t].type},`}}setBufferAttributes(){let e=Object.keys(this.inputs);const t=e.filter(s=>this.inputs[s].type.includes("array"));t.length&&(e.sort((s,i)=>{const r=Math.min(0,this.inputs[s].type.indexOf("array")),n=Math.min(0,this.inputs[i].type.indexOf("array"));return r-n}),t.length>1&&(e=e.filter(s=>!t.includes(s))));for(const s of e){const i=this.inputs[s],r={name:xe(i.name??s),key:s,type:i.type},n=i.type.includes("array")&&(Array.isArray(i.value)||ArrayBuffer.isView(i.value));this.bufferElements.push(n?new zt({...r,arrayLength:i.value.length}):new Je(r))}if(this.bufferElements.forEach((s,i)=>{const r=i===0?0:this.bufferElements[i-1].endOffset+1;s.setAlignment(r)}),t.length>1)if(t.map(r=>{const n=this.inputs[r],a=Rt(n.type.replace("array","").replace("<","").replace(">",""));return Math.ceil(n.value.length/a.numElements)}).every((r,n,a)=>r===a[0])){const r=t.map(h=>{const u=this.inputs[h];return new et({name:xe(u.name??h),key:h,type:u.type,arrayLength:u.value.length})}),n=t.map(h=>{const u=this.inputs[h];return new Je({name:xe(u.name??h),key:h,type:u.type.replace("array","").replace("<","").replace(">","")})});n.forEach((h,u)=>{u===0?this.bufferElements.length?h.setAlignmentFromPosition({row:this.bufferElements[this.bufferElements.length-1].alignment.end.row+1,byte:0}):h.setAlignment(0):h.setAlignment(n[u-1].endOffset+1)});const a=n[n.length-1].endOffset+1-n[0].startOffset;r.forEach((h,u)=>{h.setAlignment(n[u].startOffset,a)}),this.bufferElements=[...this.bufferElements,...r]}else D(`BufferBinding: "${this.label}" contains multiple array inputs that should use an interleaved array, but their sizes do not match. These inputs cannot be added to the BufferBinding: "${t.join(", ")}"`);this.arrayBufferSize=this.bufferElements.length?this.bufferElements[this.bufferElements.length-1].paddedByteCount:0,this.arrayBuffer=new ArrayBuffer(this.arrayBufferSize),this.arrayView=new DataView(this.arrayBuffer,0,this.arrayBuffer.byteLength),this.buffer.size=this.arrayBuffer.byteLength;for(const s of this.bufferElements)s.setView(this.arrayBuffer,this.arrayView);this.shouldUpdate=this.arrayBufferSize>0}setWGSLFragment(){if(!this.bufferElements.length)return;const e=Xe(this.label);if(this.useStruct){const t=this.bufferElements.filter(i=>!(i instanceof et)),s=this.bufferElements.filter(i=>i instanceof et);if(s.length){const i=this.bindingType==="uniform"?`, ${s[0].numElements}`:"";if(t.length){this.wgslStructFragment=`struct ${e}Element {
${s.map(a=>a.name+": "+a.type.replace("array","").replace("<","").replace(">","")).join(`,
`)}
};
@@ -41,13 +41,13 @@
${t.map(a=>a.name+": "+a.type).join(`,
`)}
${r}
-};`;const n=Ce(this);this.wgslGroupFragment=[`${n} ${this.name}: ${e};`]}else{this.wgslStructFragment=`struct ${e} {
+};`;const n=Te(this);this.wgslGroupFragment=[`${n} ${this.name}: ${e};`]}else{this.wgslStructFragment=`struct ${e} {
${this.bufferElements.map(n=>n.name+": "+n.type.replace("array","").replace("<","").replace(">","")).join(`,
`)}
-};`;const r=Ce(this);this.wgslGroupFragment=[`${r} ${this.name}: array<${e}${i}>;`]}}else{this.wgslStructFragment=`struct ${e} {
+};`;const r=Te(this);this.wgslGroupFragment=[`${r} ${this.name}: array<${e}${i}>;`]}}else{this.wgslStructFragment=`struct ${e} {
${this.bufferElements.map(r=>{const n=this.bindingType==="uniform"&&"numElements"in r?`array<${r.type.replace("array","").replace("<","").replace(">","")}, ${r.numElements}>`:r.type;return r.name+": "+n}).join(`,
`)}
-};`;const i=Ce(this);this.wgslGroupFragment=[`${i} ${this.name}: ${e};`]}}else this.wgslStructFragment="",this.wgslGroupFragment=this.bufferElements.map(t=>`${Ce(this)} ${t.name}: ${t.type};`)}shouldUpdateBinding(e=""){this.inputs[e]&&(this.inputs[e].shouldUpdate=!0)}update(){const e=Object.values(this.inputs);for(const t of e){const s=this.bufferElements.find(i=>i.key===t.name);t.shouldUpdate&&s&&(t.onBeforeUpdate&&t.onBeforeUpdate(),s.update(t.value),this.shouldUpdate=!0,t.shouldUpdate=!1)}}extractBufferElementDataFromBufferResult({result:e,bufferElementName:t}){const s=this.bufferElements.find(i=>i.name===t);return s?s.extractDataFromBufferResult(e):e}}class tt extends ce{constructor({label:e="Work",name:t="work",bindingType:s,visibility:i,useStruct:r=!0,access:n="read_write",usage:a=[],struct:h={},shouldCopyResult:u=!1}){s="storage",i=["compute"],super({label:e,name:t,bindingType:s,visibility:i,useStruct:r,access:n,usage:a,struct:h}),this.options={...this.options,shouldCopyResult:u},this.shouldCopyResult=u,this.cacheKey+=`${u},`,this.resultBuffer=new de}}class Re{constructor(e,{label:t="BindGroup",index:s=0,bindings:i=[],uniforms:r,storages:n}={}){this.type="BindGroup",e=e&&e.renderer||e,$(e,this.type),this.renderer=e,this.options={label:t,index:s,bindings:i,...r&&{uniforms:r},...n&&{storages:n}},this.index=s,this.uuid=k(),this.bindings=[],i.length&&this.addBindings(i),(this.options.uniforms||this.options.storages)&&this.setInputBindings(),this.layoutCacheKey="",this.pipelineCacheKey="",this.resetEntries(),this.bindGroupLayout=null,this.bindGroup=null,this.needsPipelineFlush=!1,this.consumers=new Set;for(const a of this.bufferBindings)"buffer"in a&&a.buffer.consumers.add(this.uuid),"resultBuffer"in a&&a.resultBuffer.consumers.add(this.uuid);this.renderer.addBindGroup(this)}setIndex(e){this.index=e}addBindings(e=[]){e.forEach(t=>{"buffer"in t&&(this.renderer.deviceManager.bufferBindings.set(t.cacheKey,t),t.buffer.consumers.add(this.uuid))}),this.bindings=[...this.bindings,...e]}addBinding(e){this.bindings.push(e)}createInputBindings(e="uniform",t={}){let s=[...Object.keys(t).map(i=>{const r=t[i];if(!r.struct)return;const n={label:Xe(r.label||i),name:i,bindingType:e,visibility:r.access==="read_write"?["compute"]:r.visibility,useStruct:!0,access:r.access??"read",...r.usage&&{usage:r.usage},struct:r.struct,...r.shouldCopyResult!==void 0&&{shouldCopyResult:r.shouldCopyResult}};if(r.useStruct!==!1){let h=`${e},${r.visibility===void 0?"all":r.access==="read_write"?"compute":r.visibility},true,${r.access??"read"},`;Object.keys(r.struct).forEach(l=>{h+=`${l},${r.struct[l].type},`}),r.shouldCopyResult!==void 0&&(h+=`${r.shouldCopyResult},`);const u=this.renderer.deviceManager.bufferBindings.get(h);if(u)return u.clone(n)}const a=n.access==="read_write"?tt:ce;return r.useStruct!==!1?new a(n):Object.keys(r.struct).map(h=>(n.label=Xe(r.label?r.label+h:i+h),n.name=i+h,n.useStruct=!1,n.struct={[h]:r.struct[h]},new a(n)))})].flat();return s=s.filter(Boolean),s.forEach(i=>{this.renderer.deviceManager.bufferBindings.set(i.cacheKey,i)}),s}setInputBindings(){this.addBindings([...this.createInputBindings("uniform",this.options.uniforms),...this.createInputBindings("storage",this.options.storages)])}get shouldCreateBindGroup(){return!this.bindGroup&&!!this.bindings.length}resetEntries(){this.entries={bindGroupLayout:[],bindGroup:[]}}createBindGroup(){this.fillEntries(),this.setBindGroupLayout(),this.setBindGroup()}resetBindGroup(){this.entries.bindGroup=[],this.pipelineCacheKey="";for(const e of this.bindings)this.addBindGroupEntry(e);this.setBindGroup()}addBindGroupEntry(e){this.entries.bindGroup.push({binding:this.entries.bindGroup.length,resource:e.resource}),this.pipelineCacheKey+=e.cacheKey}resetBindGroupLayout(){this.entries.bindGroupLayout=[],this.layoutCacheKey="";for(const e of this.bindings)this.addBindGroupLayoutEntry(e);this.setBindGroupLayout()}addBindGroupLayoutEntry(e){this.entries.bindGroupLayout.push({binding:this.entries.bindGroupLayout.length,...e.resourceLayout,visibility:e.visibility}),this.layoutCacheKey+=e.resourceLayoutCacheKey}loseContext(){this.resetEntries();for(const e of this.bufferBindings)e.buffer.reset(),"resultBuffer"in e&&e.resultBuffer.reset();this.bindGroup=null,this.bindGroupLayout=null,this.needsPipelineFlush=!0}restoreContext(){this.shouldCreateBindGroup&&this.createBindGroup();for(const e of this.bufferBindings)e.shouldUpdate=!0}get bufferBindings(){return this.bindings.filter(e=>e instanceof ce||e instanceof tt)}createBindingBuffer(e){e.buffer.createBuffer(this.renderer,{label:this.options.label+": "+e.bindingType+" buffer from: "+e.label,usage:["copySrc","copyDst",e.bindingType,...e.options.usage]}),"resultBuffer"in e&&e.resultBuffer.createBuffer(this.renderer,{label:this.options.label+": Result buffer from: "+e.label,size:e.arrayBuffer.byteLength,usage:["copyDst","mapRead"]})}fillEntries(){for(const e of this.bindings)e.visibility||(e.visibility=GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT|GPUShaderStage.COMPUTE),"buffer"in e&&(e.buffer.GPUBuffer||this.createBindingBuffer(e)),this.addBindGroupLayoutEntry(e),this.addBindGroupEntry(e)}getBindingByName(e=""){return this.bindings.find(t=>t.name===e)}setBindGroupLayout(){const e=this.renderer.deviceManager.bindGroupLayouts.get(this.layoutCacheKey);e?this.bindGroupLayout=e:(this.bindGroupLayout=this.renderer.createBindGroupLayout({label:this.options.label+" layout",entries:this.entries.bindGroupLayout}),this.renderer.deviceManager.bindGroupLayouts.set(this.layoutCacheKey,this.bindGroupLayout))}setBindGroup(){this.bindGroup=this.renderer.createBindGroup({label:this.options.label,layout:this.bindGroupLayout,entries:this.entries.bindGroup})}updateBufferBindings(){this.bindings.forEach((e,t)=>{"buffer"in e&&(e.update(),e.shouldUpdate&&(!e.useStruct&&e.bufferElements.length>1?this.renderer.queueWriteBuffer(e.buffer.GPUBuffer,0,e.bufferElements[t].view):this.renderer.queueWriteBuffer(e.buffer.GPUBuffer,0,e.arrayBuffer)),e.shouldUpdate=!1)})}update(){this.updateBufferBindings();const e=this.bindings.some(s=>s.shouldResetBindGroup),t=this.bindings.some(s=>s.shouldResetBindGroupLayout);(e||t)&&this.renderer.onAfterCommandEncoderSubmission.add(()=>{for(const s of this.bindings)s.shouldResetBindGroup=!1,s.shouldResetBindGroupLayout=!1},{once:!0}),t&&(this.resetBindGroupLayout(),this.needsPipelineFlush=!0),e&&this.resetBindGroup()}clone({bindings:e=[],keepLayout:t=!1}={}){const s={...this.options};s.label+=" (copy)";const i=new this.constructor(this.renderer,{label:s.label});i.setIndex(this.index),i.options=s;const r=e.length?e:this.bindings;for(const n of r)i.addBinding(n),"buffer"in n&&(n.buffer.GPUBuffer||this.createBindingBuffer(n),n.buffer.consumers.add(i.uuid),"resultBuffer"in n&&n.resultBuffer.consumers.add(i.uuid)),t||i.addBindGroupLayoutEntry(n),i.addBindGroupEntry(n);return t&&(i.entries.bindGroupLayout=[...this.entries.bindGroupLayout],i.layoutCacheKey=this.layoutCacheKey),i.setBindGroupLayout(),i.setBindGroup(),i}destroy(){this.renderer.removeBindGroup(this);for(const e of this.bufferBindings)"buffer"in e&&(this.renderer.removeBuffer(e.buffer),e.buffer.consumers.delete(this.uuid),e.buffer.consumers.size||e.buffer.destroy()),"resultBuffer"in e&&(this.renderer.removeBuffer(e.resultBuffer),e.resultBuffer.consumers.delete(this.uuid),e.resultBuffer.consumers.size||e.resultBuffer.destroy());this.bindings=[],this.bindGroupLayout=null,this.bindGroup=null,this.resetEntries()}}class st extends Te{constructor({label:e="Texture",name:t="texture",bindingType:s,visibility:i,texture:r,format:n="rgba8unorm",access:a="write",viewDimension:h="2d",multisampled:u=!1}){s=s??"texture",s==="storage"&&(i=["compute"]),super({label:e,name:t,bindingType:s,visibility:i}),this.options={...this.options,texture:r,format:n,access:a,viewDimension:h,multisampled:u},this.cacheKey+=`${n},${a},${h},${u},`,this.resource=r,this.setWGSLFragment()}get resourceLayout(){return Bs(this)}get resourceLayoutCacheKey(){return Cs(this)}get resource(){return this.texture instanceof GPUTexture?this.texture.createView({label:this.options.label+" view",dimension:this.options.viewDimension}):this.texture instanceof GPUExternalTexture?this.texture:null}set resource(e){(e||this.texture)&&(this.shouldResetBindGroup=!0),this.texture=e}setBindingType(e){e!==this.bindingType&&(e&&(this.shouldResetBindGroupLayout=!0),this.bindingType=e,this.cacheKey=`${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`,this.setWGSLFragment())}setFormat(e){const t=e!==this.options.format;this.options.format=e,t&&this.bindingType==="storage"&&(this.setWGSLFragment(),this.shouldResetBindGroupLayout=!0,this.cacheKey=`${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`)}setMultisampled(e){const t=e!==this.options.multisampled;this.options.multisampled=e,t&&this.bindingType!=="storage"&&(this.setWGSLFragment(),this.shouldResetBindGroupLayout=!0,this.cacheKey=`${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`)}setWGSLFragment(){this.wgslGroupFragment=[`${Ms(this)}`]}}const I=new f,ee=new f,O=new f;class A{constructor(e=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])){this.type="Mat4",this.elements=e}set(e,t,s,i,r,n,a,h,u,l,d,c,p,g,m,y){const b=this.elements;return b[0]=e,b[1]=t,b[2]=s,b[3]=i,b[4]=r,b[5]=n,b[6]=a,b[7]=h,b[8]=u,b[9]=l,b[10]=d,b[11]=c,b[12]=p,b[13]=g,b[14]=m,b[15]=y,this}identity(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this}setFromArray(e=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])){for(let t=0;tt.object3DIndex!==this.object3DIndex)),e&&this.shouldUpdateWorldMatrix(),this._parent=e,this._parent?.children.push(this))}setTransforms(){this.transforms={origin:{model:new f},quaternion:new J,rotation:new f,position:{world:new f},scale:new f(1)},this.rotation.onChange(()=>this.applyRotation()),this.position.onChange(()=>this.applyPosition()),this.scale.onChange(()=>this.applyScale()),this.transformOrigin.onChange(()=>this.applyTransformOrigin())}get rotation(){return this.transforms.rotation}set rotation(e){this.transforms.rotation=e,this.applyRotation()}get quaternion(){return this.transforms.quaternion}set quaternion(e){this.transforms.quaternion=e}get position(){return this.transforms.position.world}set position(e){this.transforms.position.world=e}get scale(){return this.transforms.scale}set scale(e){this.transforms.scale=e,this.applyScale()}get transformOrigin(){return this.transforms.origin.model}set transformOrigin(e){this.transforms.origin.model=e}applyRotation(){this.quaternion.setFromVec3(this.rotation),this.shouldUpdateModelMatrix()}applyPosition(){this.shouldUpdateModelMatrix()}applyScale(){this.shouldUpdateModelMatrix()}applyTransformOrigin(){this.shouldUpdateModelMatrix()}setMatrices(){this.matrices={model:{matrix:new A,shouldUpdate:!0,onUpdate:()=>this.updateModelMatrix()},world:{matrix:new A,shouldUpdate:!0,onUpdate:()=>this.updateWorldMatrix()}}}get modelMatrix(){return this.matrices.model.matrix}set modelMatrix(e){this.matrices.model.matrix=e,this.shouldUpdateModelMatrix()}shouldUpdateModelMatrix(){this.matrices.model.shouldUpdate=!0,this.shouldUpdateWorldMatrix()}get worldMatrix(){return this.matrices.world.matrix}set worldMatrix(e){this.matrices.world.matrix=e,this.shouldUpdateWorldMatrix()}shouldUpdateWorldMatrix(){this.matrices.world.shouldUpdate=!0}lookAt(e=new f,t=this.position){const s=Ss.lookAt(e,t);this.quaternion.setFromRotationMatrix(s),this.shouldUpdateModelMatrix()}updateModelMatrix(){this.modelMatrix=this.modelMatrix.composeFromOrigin(this.position,this.quaternion,this.scale,this.transformOrigin),this.shouldUpdateWorldMatrix()}updateWorldMatrix(){this.parent?this.worldMatrix.multiplyMatrices(this.parent.worldMatrix,this.modelMatrix):this.worldMatrix.copy(this.modelMatrix);for(let e=0,t=this.children.length;ee.shouldUpdate)}updateMatrixStack(){if(this.shouldUpdateMatrices(),this.matricesNeedUpdate)for(const e in this.matrices)this.matrices[e].shouldUpdate&&(this.matrices[e].onUpdate(),this.matrices[e].shouldUpdate=!1);for(let e=0,t=this.children.length;eo.reduce((e,t)=>e|zs.get(t),0),Gs=(o=[],e)=>o.length?Es(o):e!=="storage"?GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_SRC|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT:GPUTextureUsage.STORAGE_BINDING|GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST,Gt=(...o)=>{const e=Math.max(...o);return 1+Math.log2(e)|0};var Os=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},Y=(o,e,t)=>(Os(o,e,"read from private field"),t?t.call(o):e.get(o)),Se=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},oe,ae,ze,Ee;const Ot={name:"texture",generateMips:!1,flipY:!1,format:"rgba8unorm",premultipliedAlpha:!1,placeholderColor:[0,0,0,255],useExternalTextures:!0,fromTexture:null,viewDimension:"2d",visibility:["fragment"],cache:!0};class he extends se{constructor(e,t=Ot){super(),Se(this,oe,new f(1)),Se(this,ae,new f(1)),Se(this,ze,new f(1)),Se(this,Ee,new A),this._onSourceLoadedCallback=()=>{},this._onSourceUploadedCallback=()=>{},this.type="Texture",e=e&&e.renderer||e,$(e,t.label?t.label+" "+this.type:this.type),this.renderer=e,this.uuid=k();const s={...Ot,source:t.fromTexture?t.fromTexture.options.source:null,sourceType:t.fromTexture?t.fromTexture.options.sourceType:null};this.options={...s,...t},this.options.label=this.options.label??this.options.name,this.texture=null,this.externalTexture=null,this.source=null,this.size={width:1,height:1,depth:1},this.textureMatrix=new ce({label:this.options.label+": model matrix",name:this.options.name+"Matrix",useStruct:!1,struct:{[this.options.name+"Matrix"]:{type:"mat4x4f",value:this.modelMatrix}}}),this.renderer.deviceManager.bufferBindings.set(this.textureMatrix.cacheKey,this.textureMatrix),this.setBindings(),this._parentMesh=null,this.sourceLoaded=!1,this.sourceUploaded=!1,this.shouldUpdate=!1,this.renderer.addDOMTexture(this),this.createTexture()}setBindings(){this.bindings=[new st({label:this.options.label+": texture",name:this.options.name,bindingType:this.options.sourceType==="externalVideo"?"externalTexture":"texture",visibility:this.options.visibility,texture:this.options.sourceType==="externalVideo"?this.externalTexture:this.texture,viewDimension:this.options.viewDimension}),this.textureMatrix]}get textureBinding(){return this.bindings[0]}get parentMesh(){return this._parentMesh}set parentMesh(e){this._parentMesh=e,this.resize()}get sourceLoaded(){return this._sourceLoaded}set sourceLoaded(e){e&&!this.sourceLoaded&&this._onSourceLoadedCallback&&this._onSourceLoadedCallback(),this._sourceLoaded=e}get sourceUploaded(){return this._sourceUploaded}set sourceUploaded(e){e&&!this.sourceUploaded&&this._onSourceUploadedCallback&&this._onSourceUploadedCallback(),this._sourceUploaded=e}setTransforms(){super.setTransforms(),this.transforms.quaternion.setAxisOrder("ZXY"),this.transforms.origin.model.set(.5,.5,0)}updateModelMatrix(){if(!this.parentMesh)return;const e=this.parentMesh.scale?this.parentMesh.scale:new f(1,1,1),t=this.parentMesh.boundingRect?this.parentMesh.boundingRect.width*e.x:this.size.width,s=this.parentMesh.boundingRect?this.parentMesh.boundingRect.height*e.y:this.size.height,i=t/s,r=this.size.width/this.size.height;t>s?(Y(this,oe).set(i,1,1),Y(this,ae).set(1/r,1,1)):(Y(this,oe).set(1,1/i,1),Y(this,ae).set(1,r,1));const n=i>r!=t>s?1:t>s?Y(this,oe).x*Y(this,ae).x:Y(this,ae).y*Y(this,oe).y;Y(this,ze).set(1/(n*this.scale.x),1/(n*this.scale.y),1),Y(this,Ee).rotateFromQuaternion(this.quaternion),this.modelMatrix.identity().premultiplyTranslate(this.transformOrigin.clone().multiplyScalar(-1)).premultiplyScale(Y(this,ze)).premultiplyScale(Y(this,oe)).premultiply(Y(this,Ee)).premultiplyScale(Y(this,ae)).premultiplyTranslate(this.transformOrigin).translate(this.position)}updateMatrixStack(){super.updateMatrixStack(),this.matricesNeedUpdate&&this.textureMatrix.shouldUpdateBinding(this.options.name+"Matrix")}resize(){this.source&&this.source instanceof HTMLCanvasElement&&(this.source.width!==this.size.width||this.source.height!==this.size.height)&&(this.setSourceSize(),this.createTexture()),this.shouldUpdateModelMatrix()}uploadTexture(){this.renderer.uploadTexture(this),this.shouldUpdate=!1}uploadVideoTexture(){this.externalTexture=this.renderer.importExternalTexture(this.source),this.textureBinding.resource=this.externalTexture,this.textureBinding.setBindingType("externalTexture"),this.shouldUpdate=!1,this.sourceUploaded=!0}copy(e){if(this.options.sourceType==="externalVideo"&&e.options.sourceType!=="externalVideo"){D(`${this.options.label}: cannot copy a GPUTexture to a GPUExternalTexture`);return}else if(this.options.sourceType!=="externalVideo"&&e.options.sourceType==="externalVideo"){D(`${this.options.label}: cannot copy a GPUExternalTexture to a GPUTexture`);return}this.options.fromTexture=e,this.options.sourceType=e.options.sourceType,this.options.generateMips=e.options.generateMips,this.options.flipY=e.options.flipY,this.options.format=e.options.format,this.options.premultipliedAlpha=e.options.premultipliedAlpha,this.options.placeholderColor=e.options.placeholderColor,this.options.useExternalTextures=e.options.useExternalTextures,this.sourceLoaded=e.sourceLoaded,this.sourceUploaded=e.sourceUploaded,e.texture&&(e.sourceLoaded&&(this.size=e.size,this.source=e.source,this.resize()),e.sourceUploaded?(this.texture=e.texture,this.textureBinding.resource=this.texture):this.createTexture())}createTexture(){const e={label:this.options.label,format:this.options.format,size:[this.size.width,this.size.height,this.size.depth],dimensions:this.options.viewDimension==="1d"?"1d":this.options.viewDimension==="3d"?"3d":"2d",usage:this.source?GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST};this.options.sourceType!=="externalVideo"&&(e.mipLevelCount=this.options.generateMips?Gt(this.size.width,this.size.height):1,this.texture?.destroy(),this.texture=this.renderer.createTexture(e),this.textureBinding.resource=this.texture),this.shouldUpdate=!0}setSourceSize(){this.size={width:this.source.naturalWidth||this.source.width||this.source.videoWidth,height:this.source.naturalHeight||this.source.height||this.source.videoHeight,depth:1}}async loadImageBitmap(e){const s=await(await fetch(e)).blob();return await createImageBitmap(s,{colorSpaceConversion:"none"})}async loadImage(e){const t=typeof e=="string"?e:e.getAttribute("src");this.options.source=t,this.options.sourceType="image";const s=this.renderer.domTextures.find(i=>i.options.source===t);if(s&&s.texture&&s.sourceUploaded){this.copy(s);return}this.sourceLoaded=!1,this.sourceUploaded=!1,this.source=await this.loadImageBitmap(this.options.source),this.setSourceSize(),this.resize(),this.sourceLoaded=!0,this.createTexture()}onVideoFrameCallback(){this.videoFrameCallbackId&&(this.shouldUpdate=!0,this.source.requestVideoFrameCallback(this.onVideoFrameCallback.bind(this)))}onVideoLoaded(e){this.sourceLoaded||(this.source=e,this.setSourceSize(),this.resize(),this.options.useExternalTextures?(this.options.sourceType="externalVideo",this.texture?.destroy()):(this.options.sourceType="video",this.createTexture()),"requestVideoFrameCallback"in HTMLVideoElement.prototype&&(this.videoFrameCallbackId=this.source.requestVideoFrameCallback(this.onVideoFrameCallback.bind(this))),this.sourceLoaded=!0)}get isVideoSource(){return this.source&&(this.options.sourceType==="video"||this.options.sourceType==="externalVideo")}loadVideo(e){let t;typeof e=="string"?(t=document.createElement("video"),t.src=e):t=e,t.preload="auto",t.muted=!0,t.loop=!0,t.crossOrigin="anonymous",t.setAttribute("playsinline",""),this.options.source=t.src,this.sourceLoaded=!1,this.sourceUploaded=!1,t.readyState>=t.HAVE_ENOUGH_DATA?this.onVideoLoaded(t):t.addEventListener("canplaythrough",this.onVideoLoaded.bind(this,t),{once:!0}),isNaN(t.duration)&&t.load()}loadCanvas(e){this.options.source=e,this.options.sourceType="canvas",this.sourceLoaded=!1,this.sourceUploaded=!1,this.source=e,this.setSourceSize(),this.resize(),this.sourceLoaded=!0,this.createTexture()}onSourceLoaded(e){return e&&(this._onSourceLoadedCallback=e),this}onSourceUploaded(e){return e&&(this._onSourceUploadedCallback=e),this}render(){this.updateMatrixStack(),this.textureMatrix.update(),this.options.sourceType==="externalVideo"&&(this.shouldUpdate=!0),this.isVideoSource&&!this.videoFrameCallbackId&&this.source.readyState>=this.source.HAVE_CURRENT_DATA&&!this.source.paused&&(this.shouldUpdate=!0),this.shouldUpdate&&this.options.sourceType&&this.options.sourceType!=="externalVideo"&&this.uploadTexture()}destroy(){this.videoFrameCallbackId&&this.source.cancelVideoFrameCallback(this.videoFrameCallbackId),this.isVideoSource&&this.source.removeEventListener("canplaythrough",this.onVideoLoaded.bind(this,this.source),{once:!0}),this.renderer.removeDOMTexture(this),this.texture?.destroy(),this.texture=null}}oe=new WeakMap,ae=new WeakMap,ze=new WeakMap,Ee=new WeakMap;class it extends Re{constructor(e,{label:t,index:s=0,bindings:i=[],uniforms:r,storages:n,textures:a=[],samplers:h=[]}={}){const u="TextureBindGroup";if(e=e&&e.renderer||e,$(e,u),super(e,{label:t,index:s,bindings:i,uniforms:r,storages:n}),this.options={...this.options,textures:[],samplers:[]},a.length)for(const l of a)this.addTexture(l);if(h.length)for(const l of h)this.addSampler(l);this.type=u}addTexture(e){this.textures.push(e),this.addBindings([...e.bindings])}get textures(){return this.options.textures}addSampler(e){this.samplers.push(e),this.addBindings([e.binding])}get samplers(){return this.options.samplers}get shouldCreateBindGroup(){return!this.bindGroup&&!!this.bindings.length&&!this.textures.find(e=>!(e.texture||e.externalTexture))&&!this.samplers.find(e=>!e.sampler)}updateTextures(){for(const e of this.textures)e instanceof he&&(e.options.fromTexture&&e.options.fromTexture.sourceUploaded&&!e.sourceUploaded&&e.copy(e.options.fromTexture),e.shouldUpdate&&e.options.sourceType&&e.options.sourceType==="externalVideo"&&e.uploadVideoTexture())}update(){this.updateTextures(),super.update()}destroy(){super.destroy(),this.options.textures=[],this.options.samplers=[]}}class At extends Te{constructor({label:e="Sampler",name:t="sampler",bindingType:s,visibility:i,sampler:r,type:n="filtering"}){s=s??"sampler",super({label:e,name:t,bindingType:s,visibility:i}),this.cacheKey+=`${n},`,this.options={...this.options,sampler:r,type:n},this.resource=r,this.setWGSLFragment()}get resourceLayout(){return{sampler:{type:this.options.type}}}get resourceLayoutCacheKey(){return`sampler,${this.options.type},${this.visibility},`}get resource(){return this.sampler}set resource(e){e&&this.sampler&&(this.shouldResetBindGroup=!0),this.sampler=e}setWGSLFragment(){this.wgslGroupFragment=[`var ${this.name}: ${this.options.type==="comparison"?`${this.bindingType}_comparison`:this.bindingType};`]}}var Ut=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},Ge=(o,e,t)=>(Ut(o,e,"read from private field"),t?t.call(o):e.get(o)),Oe=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},Ae=(o,e,t,s)=>(Ut(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),Ue,Fe,_e,$e;class Ft extends se{constructor({fov:e=50,near:t=.1,far:s=150,width:i=1,height:r=1,pixelRatio:n=1,onMatricesChanged:a=()=>{}}={}){super(),Oe(this,Ue,void 0),Oe(this,Fe,void 0),Oe(this,_e,void 0),Oe(this,$e,void 0),this.position.set(0,0,10),this.onMatricesChanged=a,this.size={width:1,height:1},this.setPerspective({fov:e,near:t,far:s,width:i,height:r,pixelRatio:n})}setMatrices(){super.setMatrices(),this.matrices={...this.matrices,view:{matrix:new A,shouldUpdate:!0,onUpdate:()=>{this.viewMatrix.copy(this.worldMatrix).invert()}},projection:{matrix:new A,shouldUpdate:!0,onUpdate:()=>this.updateProjectionMatrix()}}}get viewMatrix(){return this.matrices.view.matrix}set viewMatrix(e){this.matrices.view.matrix=e,this.matrices.view.shouldUpdate=!0}get projectionMatrix(){return this.matrices.projection.matrix}set projectionMatrix(e){this.matrices.projection.matrix=e,this.shouldUpdateProjectionMatrix()}shouldUpdateProjectionMatrix(){this.matrices.projection.shouldUpdate=!0}updateModelMatrix(){super.updateModelMatrix(),this.setScreenRatios(),this.matrices.view.shouldUpdate=!0}updateWorldMatrix(){super.updateWorldMatrix(),this.matrices.view.shouldUpdate=!0}updateMatrixStack(){super.updateMatrixStack(),this.matricesNeedUpdate&&this.onMatricesChanged()}get fov(){return Ge(this,Ue)}set fov(e){e=Math.max(1,Math.min(e??this.fov,179)),e!==this.fov&&(Ae(this,Ue,e),this.shouldUpdateProjectionMatrix()),this.setScreenRatios(),this.setCSSPerspective()}get near(){return Ge(this,Fe)}set near(e){e=Math.max(e??this.near,.01),e!==this.near&&(Ae(this,Fe,e),this.shouldUpdateProjectionMatrix())}get far(){return Ge(this,_e)}set far(e){e=Math.max(e??this.far,this.near+1),e!==this.far&&(Ae(this,_e,e),this.shouldUpdateProjectionMatrix())}get pixelRatio(){return Ge(this,$e)}set pixelRatio(e){Ae(this,$e,e??this.pixelRatio),this.setCSSPerspective()}setSize({width:e,height:t}){(e!==this.size.width||t!==this.size.height)&&this.shouldUpdateProjectionMatrix(),this.size.width=e,this.size.height=t,this.setScreenRatios(),this.setCSSPerspective()}setPerspective({fov:e=this.fov,near:t=this.near,far:s=this.far,width:i=this.size.width,height:r=this.size.height,pixelRatio:n=this.pixelRatio}={}){this.setSize({width:i,height:r}),this.pixelRatio=n,this.fov=e,this.near=t,this.far=s}setCSSPerspective(){this.CSSPerspective=Math.pow(Math.pow(this.size.width/(2*this.pixelRatio),2)+Math.pow(this.size.height/(2*this.pixelRatio),2),.5)/Math.tan(this.fov*.5*Math.PI/180)}getScreenRatiosAtDepth(e=0){const t=this.position.z;e{if(!e.has(o))throw TypeError("Cannot "+t)},As=(o,e,t)=>(_t(o,e,"read from private field"),t?t.call(o):e.get(o)),Us=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},Fs=(o,e,t,s)=>(_t(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),De;const $t={label:"Texture",name:"renderTexture",type:"texture",access:"write",fromTexture:null,viewDimension:"2d",sampleCount:1,qualityRatio:1,generateMips:!1,flipY:!1,premultipliedAlpha:!1};class te{constructor(e,t=$t){Us(this,De,!0),e=e&&e.renderer||e,$(e,t.label?t.label+" Texture":"Texture"),this.type="Texture",this.renderer=e,this.uuid=k(),this.options={...$t,...t},t.fromTexture&&(this.options.format=t.fromTexture.texture.format,this.options.sampleCount=t.fromTexture.texture.sampleCount,this.options.viewDimension=t.fromTexture.options.viewDimension),this.options.format||(this.options.format=this.renderer.options.preferredFormat),this.size=this.options.fixedSize?{width:this.options.fixedSize.width*this.options.qualityRatio,height:this.options.fixedSize.height*this.options.qualityRatio,depth:this.options.fixedSize.depth??this.options.viewDimension.indexOf("cube")!==-1?6:1}:{width:Math.floor(this.renderer.canvas.width*this.options.qualityRatio),height:Math.floor(this.renderer.canvas.height*this.options.qualityRatio),depth:this.options.viewDimension.indexOf("cube")!==-1?6:1},this.options.fixedSize&&Fs(this,De,!1),this.setBindings(),this.renderer.addTexture(this),this.createTexture()}copy(e){this.options.fromTexture=e,this.createTexture()}copyGPUTexture(e){this.size={width:e.width,height:e.height,depth:e.depthOrArrayLayers},this.options.format=e.format,this.options.sampleCount=e.sampleCount,this.texture=e,this.textureBinding.setFormat(this.options.format),this.textureBinding.setMultisampled(this.options.sampleCount>1),this.textureBinding.resource=this.texture}createTexture(){if(!(!this.size.width||!this.size.height)){if(this.options.fromTexture){this.copyGPUTexture(this.options.fromTexture.texture);return}this.texture?.destroy(),this.texture=this.renderer.createTexture({label:this.options.label,format:this.options.format,size:[this.size.width,this.size.height,this.size.depth??1],dimensions:this.options.viewDimension,sampleCount:this.options.sampleCount,mipLevelCount:this.options.generateMips?Gt(this.size.width,this.size.height,this.size.depth??1):1,usage:Gs(this.options.usage,this.options.type)}),this.textureBinding.resource=this.texture}}uploadSource({source:e,width:t=this.size.width,height:s=this.size.height,depth:i=this.size.depth,origin:r=[0,0,0]}){this.renderer.device.queue.copyExternalImageToTexture({source:e,flipY:this.options.flipY},{texture:this.texture,premultipliedAlpha:this.options.premultipliedAlpha,origin:r},[t,s,i]),this.texture.mipLevelCount>1&&Pt(this.renderer.device,this.texture)}setBindings(){this.bindings=[new st({label:this.options.label+": "+this.options.name+" texture",name:this.options.name,bindingType:this.options.type,visibility:this.options.visibility,texture:this.texture,format:this.options.format,viewDimension:this.options.viewDimension,multisampled:this.options.sampleCount>1})]}get textureBinding(){return this.bindings[0]}resize(e=null){As(this,De)&&(e||(e={width:Math.floor(this.renderer.canvas.width*this.options.qualityRatio),height:Math.floor(this.renderer.canvas.height*this.options.qualityRatio),depth:1}),!(e.width===this.size.width&&e.height===this.size.height&&e.depth===this.size.depth)&&(this.size=e,this.createTexture()))}destroy(){this.renderer.removeTexture(this),this.options.fromTexture||this.texture?.destroy(),this.texture=null}}De=new WeakMap;class rt{constructor(e,t){this.type="Material",e=e&&e.renderer||e,$(e,this.type),this.renderer=e,this.uuid=k();const{shaders:s,label:i,useAsyncPipeline:r,uniforms:n,storages:a,bindings:h,bindGroups:u,samplers:l,textures:d,domTextures:c}=t;this.options={shaders:s,label:i,useAsyncPipeline:r===void 0?!0:r,...n!==void 0&&{uniforms:n},...a!==void 0&&{storages:a},...h!==void 0&&{bindings:h},...u!==void 0&&{bindGroups:u},...l!==void 0&&{samplers:l},...d!==void 0&&{textures:d},...c!==void 0&&{domTextures:c}},this.bindGroups=[],this.texturesBindGroups=[],this.clonedBindGroups=[],this.setBindGroups(),this.setTextures(),this.setSamplers()}compileMaterial(){const e=this.texturesBindGroup.bindings.length?1:0;this.bindGroups.length>=this.inputsBindGroups.length+e||this.createBindGroups()}get ready(){return!!(this.renderer.ready&&this.pipelineEntry&&this.pipelineEntry.pipeline&&this.pipelineEntry.ready)}loseContext(){for(const e of this.domTextures)e.texture=null,e.sourceUploaded=!1;for(const e of this.textures)e.texture=null;[...this.bindGroups,...this.clonedBindGroups,...this.inputsBindGroups].forEach(e=>e.loseContext()),this.pipelineEntry.pipeline=null}restoreContext(){for(const e of this.samplers)e.createSampler(),e.binding.resource=e.sampler;for(const e of this.domTextures)e.createTexture(),e.resize();for(const e of this.textures)e.resize(e.size);[...this.bindGroups,...this.clonedBindGroups,...this.inputsBindGroups].forEach(e=>{e.restoreContext()})}getShaderCode(e="full"){return this.pipelineEntry?(e=(()=>{switch(e){case"vertex":case"fragment":case"compute":case"full":return e;default:return"full"}})(),this.pipelineEntry.shaders[e].code):""}getAddedShaderCode(e="vertex"){return this.pipelineEntry?(e=(()=>{switch(e){case"vertex":case"fragment":case"compute":return e;default:return"vertex"}})(),this.pipelineEntry.shaders[e].head):""}setBindGroups(){if(this.uniforms={},this.storages={},this.inputsBindGroups=[],this.inputsBindings=new Map,this.options.uniforms||this.options.storages||this.options.bindings){const e=new Re(this.renderer,{label:this.options.label+": Bindings bind group",uniforms:this.options.uniforms,storages:this.options.storages,bindings:this.options.bindings});this.processBindGroupBindings(e),this.inputsBindGroups.push(e),e.consumers.add(this.uuid)}this.options.bindGroups?.forEach(e=>{this.processBindGroupBindings(e),this.inputsBindGroups.push(e),e.consumers.add(this.uuid)})}get texturesBindGroup(){return this.texturesBindGroups[0]}processBindGroupBindings(e){for(const t of e.bindings)t.bindingType==="uniform"&&(this.uniforms={...this.uniforms,[t.name]:t.inputs}),t.bindingType==="storage"&&(this.storages={...this.storages,[t.name]:t.inputs}),this.inputsBindings.set(t.name,t)}createBindGroups(){this.texturesBindGroup.shouldCreateBindGroup&&(this.texturesBindGroup.setIndex(this.bindGroups.length),this.texturesBindGroup.createBindGroup(),this.bindGroups.push(this.texturesBindGroup));for(const e of this.inputsBindGroups)e.shouldCreateBindGroup&&(e.setIndex(this.bindGroups.length),e.createBindGroup(),this.bindGroups.push(e));this.options.bindGroups?.forEach(e=>{if(!e.shouldCreateBindGroup&&!this.bindGroups.find(t=>t.uuid===e.uuid)&&(e.setIndex(this.bindGroups.length),this.bindGroups.push(e)),e instanceof it&&!this.texturesBindGroups.find(t=>t.uuid===e.uuid)){this.texturesBindGroups.push(e);for(const t of e.textures)t instanceof he&&!this.domTextures.find(s=>s.uuid===t.uuid)?this.domTextures.push(t):t instanceof te&&!this.textures.find(s=>s.uuid===t.uuid)&&this.textures.push(t)}})}cloneBindGroup({bindGroup:e,bindings:t=[],keepLayout:s=!0}){if(!e)return null;const i=e.clone({bindings:t,keepLayout:s});return this.clonedBindGroups.push(i),i}getBindGroupByBindingName(e=""){return(this.ready?this.bindGroups:this.inputsBindGroups).find(t=>t.bindings.find(s=>s.name===e))}destroyBindGroup(e){e.consumers.delete(this.uuid),e.consumers.size||e.destroy()}destroyBindGroups(){this.bindGroups.forEach(e=>this.destroyBindGroup(e)),this.clonedBindGroups.forEach(e=>this.destroyBindGroup(e)),this.texturesBindGroups.forEach(e=>this.destroyBindGroup(e)),this.texturesBindGroups=[],this.inputsBindGroups=[],this.bindGroups=[],this.clonedBindGroups=[]}updateBindGroups(){for(const e of this.bindGroups)e.update(),e.needsPipelineFlush&&this.pipelineEntry.ready&&(this.pipelineEntry.flushPipelineEntry(this.bindGroups),e.needsPipelineFlush=!1)}getBindingByName(e=""){return this.inputsBindings.get(e)}getBufferBindingByName(e=""){const t=this.getBindingByName(e);return t&&"buffer"in t?t:void 0}shouldUpdateInputsBindings(e,t){if(!e)return;const s=this.getBindingByName(e);s&&(t?s.shouldUpdateBinding(t):Object.keys(s.inputs).forEach(i=>s.shouldUpdateBinding(i)))}setTextures(){this.domTextures=[],this.textures=[],this.texturesBindGroups.push(new it(this.renderer,{label:this.options.label+": Textures bind group"})),this.texturesBindGroup.consumers.add(this.uuid),this.options.domTextures?.forEach(e=>{this.addTexture(e)}),this.options.textures?.forEach(e=>{this.addTexture(e)})}addTexture(e){e instanceof he?this.domTextures.push(e):e instanceof te&&this.textures.push(e),(this.options.shaders.vertex&&this.options.shaders.vertex.code.indexOf(e.options.name)!==-1||this.options.shaders.fragment&&this.options.shaders.fragment.code.indexOf(e.options.name)!==-1||this.options.shaders.compute&&this.options.shaders.compute.code.indexOf(e.options.name)!==-1)&&this.texturesBindGroup.addTexture(e)}destroyTexture(e){if(e.options.cache)return;const t=this.renderer.getObjectsByTexture(e);(!t||!t.some(i=>i.material.uuid!==this.uuid))&&e.destroy()}destroyTextures(){this.domTextures?.forEach(e=>this.destroyTexture(e)),this.textures?.forEach(e=>this.destroyTexture(e)),this.domTextures=[],this.textures=[]}setSamplers(){if(this.samplers=[],this.options.samplers?.forEach(t=>{this.addSampler(t)}),!this.samplers.find(t=>t.name==="defaultSampler")){const t=new Le(this.renderer,{label:"Default sampler",name:"defaultSampler"});this.addSampler(t)}}addSampler(e){this.samplers.push(e),(this.options.shaders.vertex&&this.options.shaders.vertex.code.indexOf(e.name)!==-1||this.options.shaders.fragment&&this.options.shaders.fragment.code.indexOf(e.name)!==-1||this.options.shaders.compute&&this.options.shaders.compute.code.indexOf(e.name)!==-1)&&this.texturesBindGroup.addSampler(e)}async getBufferResult(e){return await e.mapBufferAsync()}async getBufferBindingResultByBindingName(e=""){const t=this.getBufferBindingByName(e);if(t&&"buffer"in t){const s=this.renderer.copyBufferToBuffer({srcBuffer:t.buffer});return await this.getBufferResult(s)}else return new Float32Array(0)}async getBufferElementResultByNames({bindingName:e,bufferElementName:t}){const s=await this.getBufferBindingResultByBindingName(e);if(!t||s.length)return s;{const i=this.getBufferBindingByName(e);return i?i.extractBufferElementDataFromBufferResult({result:s,bufferElementName:t}):s}}onBeforeRender(){this.compileMaterial();for(const e of this.domTextures)e.render();this.updateBindGroups()}setPipeline(e){this.renderer.pipelineManager.setCurrentPipeline(e,this.pipelineEntry)}setActiveBindGroups(e){this.renderer.pipelineManager.setActiveBindGroups(e,this.bindGroups)}render(e){this.ready&&(this.setPipeline(e),this.setActiveBindGroups(e))}destroy(){this.destroyBindGroups(),this.destroyTextures()}}class Lt extends rt{constructor(e,t){e=e&&e.renderer||e;const s="ComputeMaterial";$(e,s),super(e,t),this.type=s,this.renderer=e;let{shaders:i,dispatchSize:r}=t;(!i||!i.compute)&&(i={compute:{code:"",entryPoint:"main"}}),i.compute.code||(i.compute.code="@compute @workgroup_size(1) fn main(){}"),i.compute.entryPoint||(i.compute.entryPoint="main"),this.options={...this.options,shaders:i,...t.dispatchSize!==void 0&&{dispatchSize:t.dispatchSize}},r||(r=1),Array.isArray(r)?(r[0]=Math.ceil(r[0]??1),r[1]=Math.ceil(r[1]??1),r[2]=Math.ceil(r[2]??1)):isNaN(r)||(r=[Math.ceil(r),1,1]),this.dispatchSize=r,this.pipelineEntry=this.renderer.pipelineManager.createComputePipeline({renderer:this.renderer,label:this.options.label+" compute pipeline",shaders:this.options.shaders,useAsync:this.options.useAsyncPipeline})}setPipelineEntryProperties(){this.pipelineEntry.setPipelineEntryProperties({bindGroups:this.bindGroups})}async compilePipelineEntry(){await this.pipelineEntry.compilePipelineEntry()}async compileMaterial(){super.compileMaterial(),this.pipelineEntry&&this.pipelineEntry.canCompile&&(this.setPipelineEntryProperties(),await this.compilePipelineEntry())}getShaderCode(e="compute"){return super.getShaderCode(e)}getAddedShaderCode(e="compute"){return super.getAddedShaderCode(e)}useCustomRender(e){e&&(this._useCustomRenderCallback=e)}render(e){if(this.ready)if(this.setPipeline(e),this._useCustomRenderCallback!==void 0)this._useCustomRenderCallback(e);else{for(const t of this.bindGroups)e.setBindGroup(t.index,t.bindGroup);e.dispatchWorkgroups(this.dispatchSize[0],this.dispatchSize[1],this.dispatchSize[2])}}copyBufferToResult(e){for(const t of this.bindGroups)t.bufferBindings.forEach(s=>{s.shouldCopyResult&&this.renderer.copyBufferToBuffer({srcBuffer:s.buffer,dstBuffer:s.resultBuffer,commandEncoder:e})})}async getComputeResult({bindingName:e="",bufferElementName:t=""}){const s=this.getBufferBindingByName(e);if(s&&"resultBuffer"in s){const i=await this.getBufferResult(s.resultBuffer);return t&&i.length?s.extractBufferElementDataFromBufferResult({result:i,bufferElementName:t}):i}else return new Float32Array(0)}}var Dt=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},kt=(o,e,t)=>(Dt(o,e,"read from private field"),t?t.call(o):e.get(o)),_s=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},$s=(o,e,t,s)=>(Dt(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),xe;let Ls=0;class Ds{constructor(e,t={}){_s(this,xe,!0),this._onReadyCallback=()=>{},this._onBeforeRenderCallback=()=>{},this._onRenderCallback=()=>{},this._onAfterRenderCallback=()=>{},this._onAfterResizeCallback=()=>{};const s="ComputePass";e=e&&e.renderer||e,$(e,t.label?`${t.label} ${s}`:s),t.label=t.label??"ComputePass "+e.computePasses?.length,this.renderer=e,this.type=s,this.uuid=k(),Object.defineProperty(this,"index",{value:Ls++});const{label:i,shaders:r,renderOrder:n,uniforms:a,storages:h,bindGroups:u,samplers:l,domTextures:d,textures:c,autoRender:p,useAsyncPipeline:g,texturesOptions:m,dispatchSize:y}=t;this.options={label:i,shaders:r,...p!==void 0&&{autoRender:p},...n!==void 0&&{renderOrder:n},...y!==void 0&&{dispatchSize:y},useAsyncPipeline:g===void 0?!0:g,texturesOptions:m},this.renderOrder=n??0,p!==void 0&&$s(this,xe,p),this.userData={},this.ready=!1,this.setMaterial({label:this.options.label,shaders:this.options.shaders,uniforms:a,storages:h,bindGroups:u,samplers:l,textures:c,domTextures:d,useAsyncPipeline:g,dispatchSize:y}),this.addToScene()}get ready(){return this._ready}set ready(e){e&&this._onReadyCallback&&this._onReadyCallback(),this._ready=e}addToScene(){this.renderer.computePasses.push(this),kt(this,xe)&&this.renderer.scene.addComputePass(this)}removeFromScene(){kt(this,xe)&&this.renderer.scene.removeComputePass(this),this.renderer.computePasses=this.renderer.computePasses.filter(e=>e.uuid!==this.uuid)}setMaterial(e){this.useMaterial(new Lt(this.renderer,e))}useMaterial(e){this.material=e}loseContext(){this.material.loseContext()}restoreContext(){this.material.restoreContext()}get domTextures(){return this.material?.domTextures||[]}get textures(){return this.material?.textures||[]}createDOMTexture(e){e.name||(e.name="texture"+(this.textures.length+this.domTextures.length)),e.label||(e.label=this.options.label+" "+e.name);const t=new he(this.renderer,{...e,...this.options.texturesOptions});return this.addTexture(t),t}createTexture(e){e.name||(e.name="texture"+(this.textures.length+this.domTextures.length));const t=new te(this.renderer,e);return this.addTexture(t),t}addTexture(e){this.material.addTexture(e)}get uniforms(){return this.material?.uniforms}get storages(){return this.material?.storages}resize(){this._onAfterResizeCallback&&this._onAfterResizeCallback()}onReady(e){return e&&(this._onReadyCallback=e),this}onBeforeRender(e){return e&&(this._onBeforeRenderCallback=e),this}onRender(e){return e&&(this._onRenderCallback=e),this}onAfterRender(e){return e&&(this._onAfterRenderCallback=e),this}useCustomRender(e){return this.material.useCustomRender(e),this}onAfterResize(e){return e&&(this._onAfterResizeCallback=e),this}onBeforeRenderPass(){this.renderer.ready&&(this.material&&this.material.ready&&!this.ready&&(this.ready=!0),this._onBeforeRenderCallback&&this._onBeforeRenderCallback(),this.material.onBeforeRender())}onRenderPass(e){this.material.ready&&(this._onRenderCallback&&this._onRenderCallback(),this.material.render(e))}onAfterRenderPass(){this._onAfterRenderCallback&&this._onAfterRenderCallback()}render(e){this.onBeforeRenderPass(),this.renderer.ready&&(!this.renderer.production&&e.pushDebugGroup(this.options.label),this.onRenderPass(e),!this.renderer.production&&e.popDebugGroup(),this.onAfterRenderPass())}copyBufferToResult(e){this.material?.copyBufferToResult(e)}async getComputeResult({bindingName:e,bufferElementName:t}){return await this.material?.getComputeResult({bindingName:e,bufferElementName:t})}remove(){this.removeFromScene(),this.destroy()}destroy(){this.material?.destroy()}}xe=new WeakMap;const X=[new f,new f,new f,new f,new f,new f,new f,new f];class ie{constructor(e=new f(1/0),t=new f(-1/0)){this.min=e,this.max=t}set(e=new f(1/0),t=new f(-1/0)){return this.min.copy(e),this.max.copy(t),this}isEmpty(){return this.max.x{},onLeaveView:n=()=>{}}){this.boundingBox=e,this.modelViewProjectionMatrix=t,this.containerBoundingRect=s,this.DOMFrustumMargins={...It,...i},this.projectedBoundingRect={top:0,right:0,bottom:0,left:0,width:0,height:0,x:0,y:0},this.onReEnterView=r,this.onLeaveView=n,this.isIntersecting=!1}setContainerBoundingRect(e){this.containerBoundingRect=e}get DOMFrustumBoundingRect(){return{top:this.projectedBoundingRect.top-this.DOMFrustumMargins.top,right:this.projectedBoundingRect.right+this.DOMFrustumMargins.right,bottom:this.projectedBoundingRect.bottom+this.DOMFrustumMargins.bottom,left:this.projectedBoundingRect.left-this.DOMFrustumMargins.left}}computeProjectedToDocumentCoords(){const e=this.boundingBox.applyMat4(this.modelViewProjectionMatrix);e.min.x=(e.min.x+1)*.5,e.max.x=(e.max.x+1)*.5,e.min.y=1-(e.min.y+1)*.5,e.max.y=1-(e.max.y+1)*.5;const{width:t,height:s,top:i,left:r}=this.containerBoundingRect;this.projectedBoundingRect={left:e.min.x*t+r,x:e.min.x*t+r,top:e.max.y*s+i,y:e.max.y*s+i,right:e.max.x*t+r,bottom:e.min.y*s+i,width:e.max.x*t+r-(e.min.x*t+r),height:e.min.y*s+i-(e.max.y*s+i)},this.intersectsContainer()}intersectsContainer(){Math.round(this.DOMFrustumBoundingRect.right)<=this.containerBoundingRect.left||Math.round(this.DOMFrustumBoundingRect.left)>=this.containerBoundingRect.left+this.containerBoundingRect.width||Math.round(this.DOMFrustumBoundingRect.bottom)<=this.containerBoundingRect.top||Math.round(this.DOMFrustumBoundingRect.top)>=this.containerBoundingRect.top+this.containerBoundingRect.height?(this.isIntersecting&&this.onLeaveView(),this.isIntersecting=!1):(this.isIntersecting||this.onReEnterView(),this.isIntersecting=!0)}}class nt{constructor({verticesOrder:e="ccw",topology:t="triangle-list",instancesCount:s=1,vertexBuffers:i=[],mapBuffersAtCreation:r=!0}={}){this.verticesCount=0,this.verticesOrder=e,this.topology=t,this.instancesCount=s,this.ready=!1,this.boundingBox=new ie,this.type="Geometry",this.uuid=k(),this.vertexBuffers=[],this.consumers=new Set,this.options={verticesOrder:e,topology:t,instancesCount:s,vertexBuffers:i,mapBuffersAtCreation:r};const n=i.find(a=>a.name==="attributes");(!i.length||!n)&&this.addVertexBuffer({name:"attributes"});for(const a of i)this.addVertexBuffer({stepMode:a.stepMode??"vertex",name:a.name,attributes:a.attributes,...a.array&&{array:a.array},...a.buffer&&{buffer:a.buffer},...a.bufferOffset&&{bufferOffset:a.bufferOffset},...a.bufferSize&&{bufferSize:a.bufferSize}});n&&this.setWGSLFragment()}loseContext(){this.ready=!1;for(const e of this.vertexBuffers)e.buffer.destroy()}restoreContext(e){if(!this.ready){for(const t of this.vertexBuffers)!t.buffer.GPUBuffer&&t.buffer.consumers.size===0&&(t.buffer.createBuffer(e),this.uploadBuffer(e,t)),t.buffer.consumers.add(this.uuid);this.ready=!0}}addVertexBuffer({stepMode:e="vertex",name:t,attributes:s=[],buffer:i=null,array:r=null,bufferOffset:n=0,bufferSize:a=null}={}){i=i||new de;const h={name:t??"attributes"+this.vertexBuffers.length,stepMode:e,arrayStride:0,bufferLength:0,attributes:[],buffer:i,array:r,bufferOffset:n,bufferSize:a};return s?.forEach(u=>{this.setAttribute({vertexBuffer:h,...u})}),this.vertexBuffers.push(h),h}getVertexBufferByName(e=""){return this.vertexBuffers.find(t=>t.name===e)}setAttribute({vertexBuffer:e=this.vertexBuffers[0],name:t,type:s="vec3f",bufferFormat:i="float32x3",size:r=3,array:n=new Float32Array(this.verticesCount*r),verticesStride:a=1}){const h=e.attributes,u=h.length;t||(t="geometryAttribute"+u),t==="position"&&(s!=="vec3f"||i!=="float32x3"||r!==3)&&(D(`Geometry 'position' attribute must have this exact properties set:
+};`;const i=Te(this);this.wgslGroupFragment=[`${i} ${this.name}: ${e};`]}}else this.wgslStructFragment="",this.wgslGroupFragment=this.bufferElements.map(t=>`${Te(this)} ${t.name}: ${t.type};`)}shouldUpdateBinding(e=""){this.inputs[e]&&(this.inputs[e].shouldUpdate=!0)}update(){const e=Object.values(this.inputs);for(const t of e){const s=this.bufferElements.find(i=>i.key===t.name);t.shouldUpdate&&s&&(t.onBeforeUpdate&&t.onBeforeUpdate(),s.update(t.value),this.shouldUpdate=!0,t.shouldUpdate=!1)}}extractBufferElementDataFromBufferResult({result:e,bufferElementName:t}){const s=this.bufferElements.find(i=>i.name===t);return s?s.extractDataFromBufferResult(e):e}}class tt extends fe{constructor({label:e="Work",name:t="work",bindingType:s,visibility:i,useStruct:r=!0,access:n="read_write",usage:a=[],struct:h={},shouldCopyResult:u=!1}){s="storage",i=["compute"],super({label:e,name:t,bindingType:s,visibility:i,useStruct:r,access:n,usage:a,struct:h}),this.options={...this.options,shouldCopyResult:u},this.shouldCopyResult=u,this.cacheKey+=`${u},`,this.resultBuffer=new pe}}class Se{constructor(e,{label:t="BindGroup",index:s=0,bindings:i=[],uniforms:r,storages:n}={}){this.type="BindGroup",e=e&&e.renderer||e,$(e,this.type),this.renderer=e,this.options={label:t,index:s,bindings:i,...r&&{uniforms:r},...n&&{storages:n}},this.index=s,this.uuid=k(),this.bindings=[],i.length&&this.addBindings(i),(this.options.uniforms||this.options.storages)&&this.setInputBindings(),this.layoutCacheKey="",this.pipelineCacheKey="",this.resetEntries(),this.bindGroupLayout=null,this.bindGroup=null,this.needsPipelineFlush=!1,this.consumers=new Set;for(const a of this.bufferBindings)"buffer"in a&&a.buffer.consumers.add(this.uuid),"resultBuffer"in a&&a.resultBuffer.consumers.add(this.uuid);this.renderer.addBindGroup(this)}setIndex(e){this.index=e}addBindings(e=[]){e.forEach(t=>{"buffer"in t&&(this.renderer.deviceManager.bufferBindings.set(t.cacheKey,t),t.buffer.consumers.add(this.uuid))}),this.bindings=[...this.bindings,...e]}addBinding(e){this.bindings.push(e)}createInputBindings(e="uniform",t={}){let s=[...Object.keys(t).map(i=>{const r=t[i];if(!r.struct)return;const n={label:Xe(r.label||i),name:i,bindingType:e,visibility:r.access==="read_write"?["compute"]:r.visibility,useStruct:!0,access:r.access??"read",...r.usage&&{usage:r.usage},struct:r.struct,...r.shouldCopyResult!==void 0&&{shouldCopyResult:r.shouldCopyResult}};if(r.useStruct!==!1){let h=`${e},${r.visibility===void 0?"all":r.access==="read_write"?"compute":r.visibility},true,${r.access??"read"},`;Object.keys(r.struct).forEach(l=>{h+=`${l},${r.struct[l].type},`}),r.shouldCopyResult!==void 0&&(h+=`${r.shouldCopyResult},`);const u=this.renderer.deviceManager.bufferBindings.get(h);if(u)return u.clone(n)}const a=n.access==="read_write"?tt:fe;return r.useStruct!==!1?new a(n):Object.keys(r.struct).map(h=>(n.label=Xe(r.label?r.label+h:i+h),n.name=i+h,n.useStruct=!1,n.struct={[h]:r.struct[h]},new a(n)))})].flat();return s=s.filter(Boolean),s.forEach(i=>{this.renderer.deviceManager.bufferBindings.set(i.cacheKey,i)}),s}setInputBindings(){this.addBindings([...this.createInputBindings("uniform",this.options.uniforms),...this.createInputBindings("storage",this.options.storages)])}get shouldCreateBindGroup(){return!this.bindGroup&&!!this.bindings.length}resetEntries(){this.entries={bindGroupLayout:[],bindGroup:[]}}createBindGroup(){this.fillEntries(),this.setBindGroupLayout(),this.setBindGroup()}resetBindGroup(){this.entries.bindGroup=[],this.pipelineCacheKey="";for(const e of this.bindings)this.addBindGroupEntry(e);this.setBindGroup()}addBindGroupEntry(e){this.entries.bindGroup.push({binding:this.entries.bindGroup.length,resource:e.resource}),this.pipelineCacheKey+=e.cacheKey}resetBindGroupLayout(){this.entries.bindGroupLayout=[],this.layoutCacheKey="";for(const e of this.bindings)this.addBindGroupLayoutEntry(e);this.setBindGroupLayout()}addBindGroupLayoutEntry(e){this.entries.bindGroupLayout.push({binding:this.entries.bindGroupLayout.length,...e.resourceLayout,visibility:e.visibility}),this.layoutCacheKey+=e.resourceLayoutCacheKey}loseContext(){this.resetEntries();for(const e of this.bufferBindings)e.buffer.reset(),"resultBuffer"in e&&e.resultBuffer.reset();this.bindGroup=null,this.bindGroupLayout=null,this.needsPipelineFlush=!0}restoreContext(){this.shouldCreateBindGroup&&this.createBindGroup();for(const e of this.bufferBindings)e.shouldUpdate=!0}get bufferBindings(){return this.bindings.filter(e=>e instanceof fe||e instanceof tt)}createBindingBuffer(e){e.buffer.createBuffer(this.renderer,{label:this.options.label+": "+e.bindingType+" buffer from: "+e.label,usage:["copySrc","copyDst",e.bindingType,...e.options.usage]}),"resultBuffer"in e&&e.resultBuffer.createBuffer(this.renderer,{label:this.options.label+": Result buffer from: "+e.label,size:e.arrayBuffer.byteLength,usage:["copyDst","mapRead"]})}fillEntries(){for(const e of this.bindings)e.visibility||(e.visibility=GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT|GPUShaderStage.COMPUTE),"buffer"in e&&(e.buffer.GPUBuffer||this.createBindingBuffer(e)),this.addBindGroupLayoutEntry(e),this.addBindGroupEntry(e)}getBindingByName(e=""){return this.bindings.find(t=>t.name===e)}setBindGroupLayout(){const e=this.renderer.deviceManager.bindGroupLayouts.get(this.layoutCacheKey);e?this.bindGroupLayout=e:(this.bindGroupLayout=this.renderer.createBindGroupLayout({label:this.options.label+" layout",entries:this.entries.bindGroupLayout}),this.renderer.deviceManager.bindGroupLayouts.set(this.layoutCacheKey,this.bindGroupLayout))}setBindGroup(){this.bindGroup=this.renderer.createBindGroup({label:this.options.label,layout:this.bindGroupLayout,entries:this.entries.bindGroup})}updateBufferBindings(){this.bindings.forEach((e,t)=>{"buffer"in e&&(e.update(),e.shouldUpdate&&(!e.useStruct&&e.bufferElements.length>1?this.renderer.queueWriteBuffer(e.buffer.GPUBuffer,0,e.bufferElements[t].view):this.renderer.queueWriteBuffer(e.buffer.GPUBuffer,0,e.arrayBuffer)),e.shouldUpdate=!1)})}update(){this.updateBufferBindings();const e=this.bindings.some(s=>s.shouldResetBindGroup),t=this.bindings.some(s=>s.shouldResetBindGroupLayout);(e||t)&&this.renderer.onAfterCommandEncoderSubmission.add(()=>{for(const s of this.bindings)s.shouldResetBindGroup=!1,s.shouldResetBindGroupLayout=!1},{once:!0}),t&&(this.resetBindGroupLayout(),this.needsPipelineFlush=!0),e&&this.resetBindGroup()}clone({bindings:e=[],keepLayout:t=!1}={}){const s={...this.options};s.label+=" (copy)";const i=new this.constructor(this.renderer,{label:s.label});i.setIndex(this.index),i.options=s;const r=e.length?e:this.bindings;for(const n of r)i.addBinding(n),"buffer"in n&&(n.buffer.GPUBuffer||this.createBindingBuffer(n),n.buffer.consumers.add(i.uuid),"resultBuffer"in n&&n.resultBuffer.consumers.add(i.uuid)),t||i.addBindGroupLayoutEntry(n),i.addBindGroupEntry(n);return t&&(i.entries.bindGroupLayout=[...this.entries.bindGroupLayout],i.layoutCacheKey=this.layoutCacheKey),i.setBindGroupLayout(),i.setBindGroup(),i}destroy(){this.renderer.removeBindGroup(this);for(const e of this.bufferBindings)"buffer"in e&&(this.renderer.removeBuffer(e.buffer),e.buffer.consumers.delete(this.uuid),e.buffer.consumers.size||e.buffer.destroy()),"resultBuffer"in e&&(this.renderer.removeBuffer(e.resultBuffer),e.resultBuffer.consumers.delete(this.uuid),e.resultBuffer.consumers.size||e.resultBuffer.destroy());this.bindings=[],this.bindGroupLayout=null,this.bindGroup=null,this.resetEntries()}}class st extends Pe{constructor({label:e="Texture",name:t="texture",bindingType:s,visibility:i,texture:r,format:n="rgba8unorm",access:a="write",viewDimension:h="2d",multisampled:u=!1}){s=s??"texture",s==="storage"&&(i=["compute"]),super({label:e,name:t,bindingType:s,visibility:i}),this.options={...this.options,texture:r,format:n,access:a,viewDimension:h,multisampled:u},this.cacheKey+=`${n},${a},${h},${u},`,this.resource=r,this.setWGSLFragment()}get resourceLayout(){return Bs(this)}get resourceLayoutCacheKey(){return Cs(this)}get resource(){return this.texture instanceof GPUTexture?this.texture.createView({label:this.options.label+" view",dimension:this.options.viewDimension}):this.texture instanceof GPUExternalTexture?this.texture:null}set resource(e){(e||this.texture)&&(this.shouldResetBindGroup=!0),this.texture=e}setBindingType(e){e!==this.bindingType&&(e&&(this.shouldResetBindGroupLayout=!0),this.bindingType=e,this.cacheKey=`${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`,this.setWGSLFragment())}setFormat(e){const t=e!==this.options.format;this.options.format=e,t&&this.bindingType==="storage"&&(this.setWGSLFragment(),this.shouldResetBindGroupLayout=!0,this.cacheKey=`${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`)}setMultisampled(e){const t=e!==this.options.multisampled;this.options.multisampled=e,t&&this.bindingType!=="storage"&&(this.setWGSLFragment(),this.shouldResetBindGroupLayout=!0,this.cacheKey=`${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`)}setWGSLFragment(){this.wgslGroupFragment=[`${Ms(this)}`]}}const I=new f,ee=new f,O=new f;class A{constructor(e=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])){this.type="Mat4",this.elements=e}set(e,t,s,i,r,n,a,h,u,l,d,c,p,g,m,y){const b=this.elements;return b[0]=e,b[1]=t,b[2]=s,b[3]=i,b[4]=r,b[5]=n,b[6]=a,b[7]=h,b[8]=u,b[9]=l,b[10]=d,b[11]=c,b[12]=p,b[13]=g,b[14]=m,b[15]=y,this}identity(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this}setFromArray(e=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])){for(let t=0;tt.object3DIndex!==this.object3DIndex)),e&&this.shouldUpdateWorldMatrix(),this._parent=e,this._parent?.children.push(this))}setTransforms(){this.transforms={origin:{model:new f},quaternion:new J,rotation:new f,position:{world:new f},scale:new f(1)},this.rotation.onChange(()=>this.applyRotation()),this.position.onChange(()=>this.applyPosition()),this.scale.onChange(()=>this.applyScale()),this.transformOrigin.onChange(()=>this.applyTransformOrigin())}get rotation(){return this.transforms.rotation}set rotation(e){this.transforms.rotation=e,this.applyRotation()}get quaternion(){return this.transforms.quaternion}set quaternion(e){this.transforms.quaternion=e}get position(){return this.transforms.position.world}set position(e){this.transforms.position.world=e}get scale(){return this.transforms.scale}set scale(e){this.transforms.scale=e,this.applyScale()}get transformOrigin(){return this.transforms.origin.model}set transformOrigin(e){this.transforms.origin.model=e}applyRotation(){this.quaternion.setFromVec3(this.rotation),this.shouldUpdateModelMatrix()}applyPosition(){this.shouldUpdateModelMatrix()}applyScale(){this.shouldUpdateModelMatrix()}applyTransformOrigin(){this.shouldUpdateModelMatrix()}setMatrices(){this.matrices={model:{matrix:new A,shouldUpdate:!0,onUpdate:()=>this.updateModelMatrix()},world:{matrix:new A,shouldUpdate:!0,onUpdate:()=>this.updateWorldMatrix()}}}get modelMatrix(){return this.matrices.model.matrix}set modelMatrix(e){this.matrices.model.matrix=e,this.shouldUpdateModelMatrix()}shouldUpdateModelMatrix(){this.matrices.model.shouldUpdate=!0,this.shouldUpdateWorldMatrix()}get worldMatrix(){return this.matrices.world.matrix}set worldMatrix(e){this.matrices.world.matrix=e,this.shouldUpdateWorldMatrix()}shouldUpdateWorldMatrix(){this.matrices.world.shouldUpdate=!0}lookAt(e=new f,t=this.position){const s=Ss.lookAt(e,t);this.quaternion.setFromRotationMatrix(s),this.shouldUpdateModelMatrix()}updateModelMatrix(){this.modelMatrix=this.modelMatrix.composeFromOrigin(this.position,this.quaternion,this.scale,this.transformOrigin),this.shouldUpdateWorldMatrix()}updateWorldMatrix(){this.parent?this.worldMatrix.multiplyMatrices(this.parent.worldMatrix,this.modelMatrix):this.worldMatrix.copy(this.modelMatrix);for(let e=0,t=this.children.length;ee.shouldUpdate)}updateMatrixStack(){if(this.shouldUpdateMatrices(),this.matricesNeedUpdate)for(const e in this.matrices)this.matrices[e].shouldUpdate&&(this.matrices[e].onUpdate(),this.matrices[e].shouldUpdate=!1);for(let e=0,t=this.children.length;eo.reduce((e,t)=>e|zs.get(t),0),Gs=(o=[],e)=>o.length?Es(o):e!=="storage"?GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_SRC|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT:GPUTextureUsage.STORAGE_BINDING|GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST,Gt=(...o)=>{const e=Math.max(...o);return 1+Math.log2(e)|0};var Os=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},Y=(o,e,t)=>(Os(o,e,"read from private field"),t?t.call(o):e.get(o)),ze=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},ae,he,Ee,Ge;const Ot={name:"texture",generateMips:!1,flipY:!1,format:"rgba8unorm",premultipliedAlpha:!1,placeholderColor:[0,0,0,255],useExternalTextures:!0,fromTexture:null,viewDimension:"2d",visibility:["fragment"],cache:!0};class ue extends se{constructor(e,t=Ot){super(),ze(this,ae,new f(1)),ze(this,he,new f(1)),ze(this,Ee,new f(1)),ze(this,Ge,new A),this._onSourceLoadedCallback=()=>{},this._onSourceUploadedCallback=()=>{},this.type="Texture",e=e&&e.renderer||e,$(e,t.label?t.label+" "+this.type:this.type),this.renderer=e,this.uuid=k();const s={...Ot,source:t.fromTexture?t.fromTexture.options.source:null,sourceType:t.fromTexture?t.fromTexture.options.sourceType:null};this.options={...s,...t},this.options.label=this.options.label??this.options.name,this.texture=null,this.externalTexture=null,this.source=null,this.size={width:1,height:1,depth:1},this.textureMatrix=new fe({label:this.options.label+": model matrix",name:this.options.name+"Matrix",useStruct:!1,struct:{[this.options.name+"Matrix"]:{type:"mat4x4f",value:this.modelMatrix}}}),this.renderer.deviceManager.bufferBindings.set(this.textureMatrix.cacheKey,this.textureMatrix),this.setBindings(),this._parentMesh=null,this.sourceLoaded=!1,this.sourceUploaded=!1,this.shouldUpdate=!1,this.renderer.addDOMTexture(this),this.createTexture()}setBindings(){this.bindings=[new st({label:this.options.label+": texture",name:this.options.name,bindingType:this.options.sourceType==="externalVideo"?"externalTexture":"texture",visibility:this.options.visibility,texture:this.options.sourceType==="externalVideo"?this.externalTexture:this.texture,viewDimension:this.options.viewDimension}),this.textureMatrix]}get textureBinding(){return this.bindings[0]}get parentMesh(){return this._parentMesh}set parentMesh(e){this._parentMesh=e,this.resize()}get sourceLoaded(){return this._sourceLoaded}set sourceLoaded(e){e&&!this.sourceLoaded&&this._onSourceLoadedCallback&&this._onSourceLoadedCallback(),this._sourceLoaded=e}get sourceUploaded(){return this._sourceUploaded}set sourceUploaded(e){e&&!this.sourceUploaded&&this._onSourceUploadedCallback&&this._onSourceUploadedCallback(),this._sourceUploaded=e}setTransforms(){super.setTransforms(),this.transforms.quaternion.setAxisOrder("ZXY"),this.transforms.origin.model.set(.5,.5,0)}updateModelMatrix(){if(!this.parentMesh)return;const e=this.parentMesh.scale?this.parentMesh.scale:new f(1,1,1),t=this.parentMesh.boundingRect?this.parentMesh.boundingRect.width*e.x:this.size.width,s=this.parentMesh.boundingRect?this.parentMesh.boundingRect.height*e.y:this.size.height,i=t/s,r=this.size.width/this.size.height;t>s?(Y(this,ae).set(i,1,1),Y(this,he).set(1/r,1,1)):(Y(this,ae).set(1,1/i,1),Y(this,he).set(1,r,1));const n=i>r!=t>s?1:t>s?Y(this,ae).x*Y(this,he).x:Y(this,he).y*Y(this,ae).y;Y(this,Ee).set(1/(n*this.scale.x),1/(n*this.scale.y),1),Y(this,Ge).rotateFromQuaternion(this.quaternion),this.modelMatrix.identity().premultiplyTranslate(this.transformOrigin.clone().multiplyScalar(-1)).premultiplyScale(Y(this,Ee)).premultiplyScale(Y(this,ae)).premultiply(Y(this,Ge)).premultiplyScale(Y(this,he)).premultiplyTranslate(this.transformOrigin).translate(this.position)}updateMatrixStack(){super.updateMatrixStack(),this.matricesNeedUpdate&&this.textureMatrix.shouldUpdateBinding(this.options.name+"Matrix")}resize(){this.source&&this.source instanceof HTMLCanvasElement&&(this.source.width!==this.size.width||this.source.height!==this.size.height)&&(this.setSourceSize(),this.createTexture()),this.shouldUpdateModelMatrix()}uploadTexture(){this.renderer.uploadTexture(this),this.shouldUpdate=!1}uploadVideoTexture(){this.externalTexture=this.renderer.importExternalTexture(this.source),this.textureBinding.resource=this.externalTexture,this.textureBinding.setBindingType("externalTexture"),this.shouldUpdate=!1,this.sourceUploaded=!0}copy(e){if(this.options.sourceType==="externalVideo"&&e.options.sourceType!=="externalVideo"){D(`${this.options.label}: cannot copy a GPUTexture to a GPUExternalTexture`);return}else if(this.options.sourceType!=="externalVideo"&&e.options.sourceType==="externalVideo"){D(`${this.options.label}: cannot copy a GPUExternalTexture to a GPUTexture`);return}this.options.fromTexture=e,this.options.sourceType=e.options.sourceType,this.options.generateMips=e.options.generateMips,this.options.flipY=e.options.flipY,this.options.format=e.options.format,this.options.premultipliedAlpha=e.options.premultipliedAlpha,this.options.placeholderColor=e.options.placeholderColor,this.options.useExternalTextures=e.options.useExternalTextures,this.sourceLoaded=e.sourceLoaded,this.sourceUploaded=e.sourceUploaded,e.texture&&(e.sourceLoaded&&(this.size=e.size,this.source=e.source,this.resize()),e.sourceUploaded?(this.texture=e.texture,this.textureBinding.resource=this.texture):this.createTexture())}createTexture(){const e={label:this.options.label,format:this.options.format,size:[this.size.width,this.size.height,this.size.depth],dimensions:this.options.viewDimension==="1d"?"1d":this.options.viewDimension==="3d"?"3d":"2d",usage:this.source?GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST};this.options.sourceType!=="externalVideo"&&(e.mipLevelCount=this.options.generateMips?Gt(this.size.width,this.size.height):1,this.texture?.destroy(),this.texture=this.renderer.createTexture(e),this.textureBinding.resource=this.texture),this.shouldUpdate=!0}setSourceSize(){this.size={width:this.source.naturalWidth||this.source.width||this.source.videoWidth,height:this.source.naturalHeight||this.source.height||this.source.videoHeight,depth:1}}async loadImageBitmap(e){const s=await(await fetch(e)).blob();return await createImageBitmap(s,{colorSpaceConversion:"none"})}async loadImage(e){const t=typeof e=="string"?e:e.getAttribute("src");this.options.source=t,this.options.sourceType="image";const s=this.renderer.domTextures.find(i=>i.options.source===t);if(s&&s.texture&&s.sourceUploaded){this.copy(s);return}this.sourceLoaded=!1,this.sourceUploaded=!1,this.source=await this.loadImageBitmap(this.options.source),this.setSourceSize(),this.resize(),this.sourceLoaded=!0,this.createTexture()}onVideoFrameCallback(){this.videoFrameCallbackId&&(this.shouldUpdate=!0,this.source.requestVideoFrameCallback(this.onVideoFrameCallback.bind(this)))}onVideoLoaded(e){this.sourceLoaded||(this.source=e,this.setSourceSize(),this.resize(),this.options.useExternalTextures?(this.options.sourceType="externalVideo",this.texture?.destroy()):(this.options.sourceType="video",this.createTexture()),"requestVideoFrameCallback"in HTMLVideoElement.prototype&&(this.videoFrameCallbackId=this.source.requestVideoFrameCallback(this.onVideoFrameCallback.bind(this))),this.sourceLoaded=!0)}get isVideoSource(){return this.source&&(this.options.sourceType==="video"||this.options.sourceType==="externalVideo")}loadVideo(e){let t;typeof e=="string"?(t=document.createElement("video"),t.src=e):t=e,t.preload="auto",t.muted=!0,t.loop=!0,t.crossOrigin="anonymous",t.setAttribute("playsinline",""),this.options.source=t.src,this.sourceLoaded=!1,this.sourceUploaded=!1,t.readyState>=t.HAVE_ENOUGH_DATA?this.onVideoLoaded(t):t.addEventListener("canplaythrough",this.onVideoLoaded.bind(this,t),{once:!0}),isNaN(t.duration)&&t.load()}loadCanvas(e){this.options.source=e,this.options.sourceType="canvas",this.sourceLoaded=!1,this.sourceUploaded=!1,this.source=e,this.setSourceSize(),this.resize(),this.sourceLoaded=!0,this.createTexture()}onSourceLoaded(e){return e&&(this._onSourceLoadedCallback=e),this}onSourceUploaded(e){return e&&(this._onSourceUploadedCallback=e),this}render(){this.updateMatrixStack(),this.textureMatrix.update(),this.options.sourceType==="externalVideo"&&(this.shouldUpdate=!0),this.isVideoSource&&!this.videoFrameCallbackId&&this.source.readyState>=this.source.HAVE_CURRENT_DATA&&!this.source.paused&&(this.shouldUpdate=!0),this.shouldUpdate&&this.options.sourceType&&this.options.sourceType!=="externalVideo"&&this.uploadTexture()}destroy(){this.videoFrameCallbackId&&this.source.cancelVideoFrameCallback(this.videoFrameCallbackId),this.isVideoSource&&this.source.removeEventListener("canplaythrough",this.onVideoLoaded.bind(this,this.source),{once:!0}),this.renderer.removeDOMTexture(this),this.texture?.destroy(),this.texture=null}}ae=new WeakMap,he=new WeakMap,Ee=new WeakMap,Ge=new WeakMap;class it extends Se{constructor(e,{label:t,index:s=0,bindings:i=[],uniforms:r,storages:n,textures:a=[],samplers:h=[]}={}){const u="TextureBindGroup";if(e=e&&e.renderer||e,$(e,u),super(e,{label:t,index:s,bindings:i,uniforms:r,storages:n}),this.options={...this.options,textures:[],samplers:[]},a.length)for(const l of a)this.addTexture(l);if(h.length)for(const l of h)this.addSampler(l);this.type=u}addTexture(e){this.textures.push(e),this.addBindings([...e.bindings])}get textures(){return this.options.textures}addSampler(e){this.samplers.push(e),this.addBindings([e.binding])}get samplers(){return this.options.samplers}get shouldCreateBindGroup(){return!this.bindGroup&&!!this.bindings.length&&!this.textures.find(e=>!(e.texture||e.externalTexture))&&!this.samplers.find(e=>!e.sampler)}updateTextures(){for(const e of this.textures)e instanceof ue&&(e.options.fromTexture&&e.options.fromTexture.sourceUploaded&&!e.sourceUploaded&&e.copy(e.options.fromTexture),e.shouldUpdate&&e.options.sourceType&&e.options.sourceType==="externalVideo"&&e.uploadVideoTexture())}update(){this.updateTextures(),super.update()}destroy(){super.destroy(),this.options.textures=[],this.options.samplers=[]}}class At extends Pe{constructor({label:e="Sampler",name:t="sampler",bindingType:s,visibility:i,sampler:r,type:n="filtering"}){s=s??"sampler",super({label:e,name:t,bindingType:s,visibility:i}),this.cacheKey+=`${n},`,this.options={...this.options,sampler:r,type:n},this.resource=r,this.setWGSLFragment()}get resourceLayout(){return{sampler:{type:this.options.type}}}get resourceLayoutCacheKey(){return`sampler,${this.options.type},${this.visibility},`}get resource(){return this.sampler}set resource(e){e&&this.sampler&&(this.shouldResetBindGroup=!0),this.sampler=e}setWGSLFragment(){this.wgslGroupFragment=[`var ${this.name}: ${this.options.type==="comparison"?`${this.bindingType}_comparison`:this.bindingType};`]}}var Ut=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},Oe=(o,e,t)=>(Ut(o,e,"read from private field"),t?t.call(o):e.get(o)),Ae=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},Ue=(o,e,t,s)=>(Ut(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),Fe,_e,$e,Le;class Ft extends se{constructor({fov:e=50,near:t=.1,far:s=150,width:i=1,height:r=1,pixelRatio:n=1,onMatricesChanged:a=()=>{}}={}){super(),Ae(this,Fe,void 0),Ae(this,_e,void 0),Ae(this,$e,void 0),Ae(this,Le,void 0),this.position.set(0,0,10),this.onMatricesChanged=a,this.size={width:1,height:1},this.setPerspective({fov:e,near:t,far:s,width:i,height:r,pixelRatio:n})}setMatrices(){super.setMatrices(),this.matrices={...this.matrices,view:{matrix:new A,shouldUpdate:!0,onUpdate:()=>{this.viewMatrix.copy(this.worldMatrix).invert()}},projection:{matrix:new A,shouldUpdate:!0,onUpdate:()=>this.updateProjectionMatrix()}}}get viewMatrix(){return this.matrices.view.matrix}set viewMatrix(e){this.matrices.view.matrix=e,this.matrices.view.shouldUpdate=!0}get projectionMatrix(){return this.matrices.projection.matrix}set projectionMatrix(e){this.matrices.projection.matrix=e,this.shouldUpdateProjectionMatrix()}shouldUpdateProjectionMatrix(){this.matrices.projection.shouldUpdate=!0}updateModelMatrix(){super.updateModelMatrix(),this.setVisibleSize(),this.matrices.view.shouldUpdate=!0}updateWorldMatrix(){super.updateWorldMatrix(),this.matrices.view.shouldUpdate=!0}updateMatrixStack(){super.updateMatrixStack(),this.matricesNeedUpdate&&this.onMatricesChanged()}get fov(){return Oe(this,Fe)}set fov(e){e=Math.max(1,Math.min(e??this.fov,179)),e!==this.fov&&(Ue(this,Fe,e),this.shouldUpdateProjectionMatrix()),this.setVisibleSize(),this.setCSSPerspective()}get near(){return Oe(this,_e)}set near(e){e=Math.max(e??this.near,.01),e!==this.near&&(Ue(this,_e,e),this.shouldUpdateProjectionMatrix())}get far(){return Oe(this,$e)}set far(e){e=Math.max(e??this.far,this.near+1),e!==this.far&&(Ue(this,$e,e),this.shouldUpdateProjectionMatrix())}get pixelRatio(){return Oe(this,Le)}set pixelRatio(e){Ue(this,Le,e??this.pixelRatio),this.setCSSPerspective()}setSize({width:e,height:t}){(e!==this.size.width||t!==this.size.height)&&this.shouldUpdateProjectionMatrix(),this.size.width=e,this.size.height=t,this.setVisibleSize(),this.setCSSPerspective()}setPerspective({fov:e=this.fov,near:t=this.near,far:s=this.far,width:i=this.size.width,height:r=this.size.height,pixelRatio:n=this.pixelRatio}={}){this.setSize({width:i,height:r}),this.pixelRatio=n,this.fov=e,this.near=t,this.far=s}setCSSPerspective(){this.CSSPerspective=Math.pow(Math.pow(this.size.width/(2*this.pixelRatio),2)+Math.pow(this.size.height/(2*this.pixelRatio),2),.5)/Math.tan(this.fov*.5*Math.PI/180)}getVisibleSizeAtDepth(e=0){const t=this.position.z;e{if(!e.has(o))throw TypeError("Cannot "+t)},As=(o,e,t)=>(_t(o,e,"read from private field"),t?t.call(o):e.get(o)),Us=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},Fs=(o,e,t,s)=>(_t(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),ke;const $t={label:"Texture",name:"renderTexture",type:"texture",access:"write",fromTexture:null,viewDimension:"2d",sampleCount:1,qualityRatio:1,generateMips:!1,flipY:!1,premultipliedAlpha:!1};class te{constructor(e,t=$t){Us(this,ke,!0),e=e&&e.renderer||e,$(e,t.label?t.label+" Texture":"Texture"),this.type="Texture",this.renderer=e,this.uuid=k(),this.options={...$t,...t},t.fromTexture&&(this.options.format=t.fromTexture.texture.format,this.options.sampleCount=t.fromTexture.texture.sampleCount,this.options.viewDimension=t.fromTexture.options.viewDimension),this.options.format||(this.options.format=this.renderer.options.preferredFormat),this.size=this.options.fixedSize?{width:this.options.fixedSize.width*this.options.qualityRatio,height:this.options.fixedSize.height*this.options.qualityRatio,depth:this.options.fixedSize.depth??this.options.viewDimension.indexOf("cube")!==-1?6:1}:{width:Math.floor(this.renderer.canvas.width*this.options.qualityRatio),height:Math.floor(this.renderer.canvas.height*this.options.qualityRatio),depth:this.options.viewDimension.indexOf("cube")!==-1?6:1},this.options.fixedSize&&Fs(this,ke,!1),this.setBindings(),this.renderer.addTexture(this),this.createTexture()}copy(e){this.options.fromTexture=e,this.createTexture()}copyGPUTexture(e){this.size={width:e.width,height:e.height,depth:e.depthOrArrayLayers},this.options.format=e.format,this.options.sampleCount=e.sampleCount,this.texture=e,this.textureBinding.setFormat(this.options.format),this.textureBinding.setMultisampled(this.options.sampleCount>1),this.textureBinding.resource=this.texture}createTexture(){if(!(!this.size.width||!this.size.height)){if(this.options.fromTexture){this.copyGPUTexture(this.options.fromTexture.texture);return}this.texture?.destroy(),this.texture=this.renderer.createTexture({label:this.options.label,format:this.options.format,size:[this.size.width,this.size.height,this.size.depth??1],dimensions:this.options.viewDimension,sampleCount:this.options.sampleCount,mipLevelCount:this.options.generateMips?Gt(this.size.width,this.size.height,this.size.depth??1):1,usage:Gs(this.options.usage,this.options.type)}),this.textureBinding.resource=this.texture}}uploadSource({source:e,width:t=this.size.width,height:s=this.size.height,depth:i=this.size.depth,origin:r=[0,0,0]}){this.renderer.device.queue.copyExternalImageToTexture({source:e,flipY:this.options.flipY},{texture:this.texture,premultipliedAlpha:this.options.premultipliedAlpha,origin:r},[t,s,i]),this.texture.mipLevelCount>1&&Pt(this.renderer.device,this.texture)}setBindings(){this.bindings=[new st({label:this.options.label+": "+this.options.name+" texture",name:this.options.name,bindingType:this.options.type,visibility:this.options.visibility,texture:this.texture,format:this.options.format,viewDimension:this.options.viewDimension,multisampled:this.options.sampleCount>1})]}get textureBinding(){return this.bindings[0]}resize(e=null){As(this,ke)&&(e||(e={width:Math.floor(this.renderer.canvas.width*this.options.qualityRatio),height:Math.floor(this.renderer.canvas.height*this.options.qualityRatio),depth:1}),!(e.width===this.size.width&&e.height===this.size.height&&e.depth===this.size.depth)&&(this.size=e,this.createTexture()))}destroy(){this.renderer.removeTexture(this),this.options.fromTexture||this.texture?.destroy(),this.texture=null}}ke=new WeakMap;class rt{constructor(e,t){this.type="Material",e=e&&e.renderer||e,$(e,this.type),this.renderer=e,this.uuid=k();const{shaders:s,label:i,useAsyncPipeline:r,uniforms:n,storages:a,bindings:h,bindGroups:u,samplers:l,textures:d,domTextures:c}=t;this.options={shaders:s,label:i,useAsyncPipeline:r===void 0?!0:r,...n!==void 0&&{uniforms:n},...a!==void 0&&{storages:a},...h!==void 0&&{bindings:h},...u!==void 0&&{bindGroups:u},...l!==void 0&&{samplers:l},...d!==void 0&&{textures:d},...c!==void 0&&{domTextures:c}},this.bindGroups=[],this.texturesBindGroups=[],this.clonedBindGroups=[],this.setBindGroups(),this.setTextures(),this.setSamplers()}compileMaterial(){const e=this.texturesBindGroup.bindings.length?1:0;this.bindGroups.length>=this.inputsBindGroups.length+e||this.createBindGroups()}get ready(){return!!(this.renderer.ready&&this.pipelineEntry&&this.pipelineEntry.pipeline&&this.pipelineEntry.ready)}loseContext(){for(const e of this.domTextures)e.texture=null,e.sourceUploaded=!1;for(const e of this.textures)e.texture=null;[...this.bindGroups,...this.clonedBindGroups,...this.inputsBindGroups].forEach(e=>e.loseContext()),this.pipelineEntry.pipeline=null}restoreContext(){for(const e of this.samplers)e.createSampler(),e.binding.resource=e.sampler;for(const e of this.domTextures)e.createTexture(),e.resize();for(const e of this.textures)e.resize(e.size);[...this.bindGroups,...this.clonedBindGroups,...this.inputsBindGroups].forEach(e=>{e.restoreContext()})}getShaderCode(e="full"){return this.pipelineEntry?(e=(()=>{switch(e){case"vertex":case"fragment":case"compute":case"full":return e;default:return"full"}})(),this.pipelineEntry.shaders[e].code):""}getAddedShaderCode(e="vertex"){return this.pipelineEntry?(e=(()=>{switch(e){case"vertex":case"fragment":case"compute":return e;default:return"vertex"}})(),this.pipelineEntry.shaders[e].head):""}setBindGroups(){if(this.uniforms={},this.storages={},this.inputsBindGroups=[],this.inputsBindings=new Map,this.options.uniforms||this.options.storages||this.options.bindings){const e=new Se(this.renderer,{label:this.options.label+": Bindings bind group",uniforms:this.options.uniforms,storages:this.options.storages,bindings:this.options.bindings});this.processBindGroupBindings(e),this.inputsBindGroups.push(e),e.consumers.add(this.uuid)}this.options.bindGroups?.forEach(e=>{this.processBindGroupBindings(e),this.inputsBindGroups.push(e),e.consumers.add(this.uuid)})}get texturesBindGroup(){return this.texturesBindGroups[0]}processBindGroupBindings(e){for(const t of e.bindings)t.bindingType==="uniform"&&(this.uniforms={...this.uniforms,[t.name]:t.inputs}),t.bindingType==="storage"&&(this.storages={...this.storages,[t.name]:t.inputs}),this.inputsBindings.set(t.name,t)}createBindGroups(){this.texturesBindGroup.shouldCreateBindGroup&&(this.texturesBindGroup.setIndex(this.bindGroups.length),this.texturesBindGroup.createBindGroup(),this.bindGroups.push(this.texturesBindGroup));for(const e of this.inputsBindGroups)e.shouldCreateBindGroup&&(e.setIndex(this.bindGroups.length),e.createBindGroup(),this.bindGroups.push(e));this.options.bindGroups?.forEach(e=>{if(!e.shouldCreateBindGroup&&!this.bindGroups.find(t=>t.uuid===e.uuid)&&(e.setIndex(this.bindGroups.length),this.bindGroups.push(e)),e instanceof it&&!this.texturesBindGroups.find(t=>t.uuid===e.uuid)){this.texturesBindGroups.push(e);for(const t of e.textures)t instanceof ue&&!this.domTextures.find(s=>s.uuid===t.uuid)?this.domTextures.push(t):t instanceof te&&!this.textures.find(s=>s.uuid===t.uuid)&&this.textures.push(t)}})}cloneBindGroup({bindGroup:e,bindings:t=[],keepLayout:s=!0}){if(!e)return null;const i=e.clone({bindings:t,keepLayout:s});return this.clonedBindGroups.push(i),i}getBindGroupByBindingName(e=""){return(this.ready?this.bindGroups:this.inputsBindGroups).find(t=>t.bindings.find(s=>s.name===e))}destroyBindGroup(e){e.consumers.delete(this.uuid),e.consumers.size||e.destroy()}destroyBindGroups(){this.bindGroups.forEach(e=>this.destroyBindGroup(e)),this.clonedBindGroups.forEach(e=>this.destroyBindGroup(e)),this.texturesBindGroups.forEach(e=>this.destroyBindGroup(e)),this.texturesBindGroups=[],this.inputsBindGroups=[],this.bindGroups=[],this.clonedBindGroups=[]}updateBindGroups(){for(const e of this.bindGroups)e.update(),e.needsPipelineFlush&&this.pipelineEntry.ready&&(this.pipelineEntry.flushPipelineEntry(this.bindGroups),e.needsPipelineFlush=!1)}getBindingByName(e=""){return this.inputsBindings.get(e)}getBufferBindingByName(e=""){const t=this.getBindingByName(e);return t&&"buffer"in t?t:void 0}shouldUpdateInputsBindings(e,t){if(!e)return;const s=this.getBindingByName(e);s&&(t?s.shouldUpdateBinding(t):Object.keys(s.inputs).forEach(i=>s.shouldUpdateBinding(i)))}setTextures(){this.domTextures=[],this.textures=[],this.texturesBindGroups.push(new it(this.renderer,{label:this.options.label+": Textures bind group"})),this.texturesBindGroup.consumers.add(this.uuid),this.options.domTextures?.forEach(e=>{this.addTexture(e)}),this.options.textures?.forEach(e=>{this.addTexture(e)})}addTexture(e){e instanceof ue?this.domTextures.push(e):e instanceof te&&this.textures.push(e),(this.options.shaders.vertex&&this.options.shaders.vertex.code.indexOf(e.options.name)!==-1||this.options.shaders.fragment&&this.options.shaders.fragment.code.indexOf(e.options.name)!==-1||this.options.shaders.compute&&this.options.shaders.compute.code.indexOf(e.options.name)!==-1)&&this.texturesBindGroup.addTexture(e)}destroyTexture(e){if(e.options.cache)return;const t=this.renderer.getObjectsByTexture(e);(!t||!t.some(i=>i.material.uuid!==this.uuid))&&e.destroy()}destroyTextures(){this.domTextures?.forEach(e=>this.destroyTexture(e)),this.textures?.forEach(e=>this.destroyTexture(e)),this.domTextures=[],this.textures=[]}setSamplers(){if(this.samplers=[],this.options.samplers?.forEach(t=>{this.addSampler(t)}),!this.samplers.find(t=>t.name==="defaultSampler")){const t=new De(this.renderer,{label:"Default sampler",name:"defaultSampler"});this.addSampler(t)}}addSampler(e){this.samplers.push(e),(this.options.shaders.vertex&&this.options.shaders.vertex.code.indexOf(e.name)!==-1||this.options.shaders.fragment&&this.options.shaders.fragment.code.indexOf(e.name)!==-1||this.options.shaders.compute&&this.options.shaders.compute.code.indexOf(e.name)!==-1)&&this.texturesBindGroup.addSampler(e)}async getBufferResult(e){return await e.mapBufferAsync()}async getBufferBindingResultByBindingName(e=""){const t=this.getBufferBindingByName(e);if(t&&"buffer"in t){const s=this.renderer.copyBufferToBuffer({srcBuffer:t.buffer});return await this.getBufferResult(s)}else return new Float32Array(0)}async getBufferElementResultByNames({bindingName:e,bufferElementName:t}){const s=await this.getBufferBindingResultByBindingName(e);if(!t||s.length)return s;{const i=this.getBufferBindingByName(e);return i?i.extractBufferElementDataFromBufferResult({result:s,bufferElementName:t}):s}}onBeforeRender(){this.compileMaterial();for(const e of this.domTextures)e.render();this.updateBindGroups()}setPipeline(e){this.renderer.pipelineManager.setCurrentPipeline(e,this.pipelineEntry)}setActiveBindGroups(e){this.renderer.pipelineManager.setActiveBindGroups(e,this.bindGroups)}render(e){this.ready&&(this.setPipeline(e),this.setActiveBindGroups(e))}destroy(){this.destroyBindGroups(),this.destroyTextures()}}class Lt extends rt{constructor(e,t){e=e&&e.renderer||e;const s="ComputeMaterial";$(e,s),super(e,t),this.type=s,this.renderer=e;let{shaders:i,dispatchSize:r}=t;(!i||!i.compute)&&(i={compute:{code:"",entryPoint:"main"}}),i.compute.code||(i.compute.code="@compute @workgroup_size(1) fn main(){}"),i.compute.entryPoint||(i.compute.entryPoint="main"),this.options={...this.options,shaders:i,...t.dispatchSize!==void 0&&{dispatchSize:t.dispatchSize}},r||(r=1),Array.isArray(r)?(r[0]=Math.ceil(r[0]??1),r[1]=Math.ceil(r[1]??1),r[2]=Math.ceil(r[2]??1)):isNaN(r)||(r=[Math.ceil(r),1,1]),this.dispatchSize=r,this.pipelineEntry=this.renderer.pipelineManager.createComputePipeline({renderer:this.renderer,label:this.options.label+" compute pipeline",shaders:this.options.shaders,useAsync:this.options.useAsyncPipeline})}setPipelineEntryProperties(){this.pipelineEntry.setPipelineEntryProperties({bindGroups:this.bindGroups})}async compilePipelineEntry(){await this.pipelineEntry.compilePipelineEntry()}async compileMaterial(){super.compileMaterial(),this.pipelineEntry&&this.pipelineEntry.canCompile&&(this.setPipelineEntryProperties(),await this.compilePipelineEntry())}getShaderCode(e="compute"){return super.getShaderCode(e)}getAddedShaderCode(e="compute"){return super.getAddedShaderCode(e)}useCustomRender(e){e&&(this._useCustomRenderCallback=e)}render(e){if(this.ready)if(this.setPipeline(e),this._useCustomRenderCallback!==void 0)this._useCustomRenderCallback(e);else{for(const t of this.bindGroups)e.setBindGroup(t.index,t.bindGroup);e.dispatchWorkgroups(this.dispatchSize[0],this.dispatchSize[1],this.dispatchSize[2])}}copyBufferToResult(e){for(const t of this.bindGroups)t.bufferBindings.forEach(s=>{s.shouldCopyResult&&this.renderer.copyBufferToBuffer({srcBuffer:s.buffer,dstBuffer:s.resultBuffer,commandEncoder:e})})}async getComputeResult({bindingName:e="",bufferElementName:t=""}){const s=this.getBufferBindingByName(e);if(s&&"resultBuffer"in s){const i=await this.getBufferResult(s.resultBuffer);return t&&i.length?s.extractBufferElementDataFromBufferResult({result:i,bufferElementName:t}):i}else return new Float32Array(0)}}var Dt=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},kt=(o,e,t)=>(Dt(o,e,"read from private field"),t?t.call(o):e.get(o)),_s=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},$s=(o,e,t,s)=>(Dt(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),be;let Ls=0;class Ds{constructor(e,t={}){_s(this,be,!0),this._onReadyCallback=()=>{},this._onBeforeRenderCallback=()=>{},this._onRenderCallback=()=>{},this._onAfterRenderCallback=()=>{},this._onAfterResizeCallback=()=>{};const s="ComputePass";e=e&&e.renderer||e,$(e,t.label?`${t.label} ${s}`:s),t.label=t.label??"ComputePass "+e.computePasses?.length,this.renderer=e,this.type=s,this.uuid=k(),Object.defineProperty(this,"index",{value:Ls++});const{label:i,shaders:r,renderOrder:n,uniforms:a,storages:h,bindGroups:u,samplers:l,domTextures:d,textures:c,autoRender:p,useAsyncPipeline:g,texturesOptions:m,dispatchSize:y}=t;this.options={label:i,shaders:r,...p!==void 0&&{autoRender:p},...n!==void 0&&{renderOrder:n},...y!==void 0&&{dispatchSize:y},useAsyncPipeline:g===void 0?!0:g,texturesOptions:m},this.renderOrder=n??0,p!==void 0&&$s(this,be,p),this.userData={},this.ready=!1,this.setMaterial({label:this.options.label,shaders:this.options.shaders,uniforms:a,storages:h,bindGroups:u,samplers:l,textures:c,domTextures:d,useAsyncPipeline:g,dispatchSize:y}),this.addToScene()}get ready(){return this._ready}set ready(e){e&&this._onReadyCallback&&this._onReadyCallback(),this._ready=e}addToScene(){this.renderer.computePasses.push(this),kt(this,be)&&this.renderer.scene.addComputePass(this)}removeFromScene(){kt(this,be)&&this.renderer.scene.removeComputePass(this),this.renderer.computePasses=this.renderer.computePasses.filter(e=>e.uuid!==this.uuid)}setMaterial(e){this.useMaterial(new Lt(this.renderer,e))}useMaterial(e){this.material=e}loseContext(){this.material.loseContext()}restoreContext(){this.material.restoreContext()}get domTextures(){return this.material?.domTextures||[]}get textures(){return this.material?.textures||[]}createDOMTexture(e){e.name||(e.name="texture"+(this.textures.length+this.domTextures.length)),e.label||(e.label=this.options.label+" "+e.name);const t=new ue(this.renderer,{...e,...this.options.texturesOptions});return this.addTexture(t),t}createTexture(e){e.name||(e.name="texture"+(this.textures.length+this.domTextures.length));const t=new te(this.renderer,e);return this.addTexture(t),t}addTexture(e){this.material.addTexture(e)}get uniforms(){return this.material?.uniforms}get storages(){return this.material?.storages}resize(){this._onAfterResizeCallback&&this._onAfterResizeCallback()}onReady(e){return e&&(this._onReadyCallback=e),this}onBeforeRender(e){return e&&(this._onBeforeRenderCallback=e),this}onRender(e){return e&&(this._onRenderCallback=e),this}onAfterRender(e){return e&&(this._onAfterRenderCallback=e),this}useCustomRender(e){return this.material.useCustomRender(e),this}onAfterResize(e){return e&&(this._onAfterResizeCallback=e),this}onBeforeRenderPass(){this.renderer.ready&&(this.material&&this.material.ready&&!this.ready&&(this.ready=!0),this._onBeforeRenderCallback&&this._onBeforeRenderCallback(),this.material.onBeforeRender())}onRenderPass(e){this.material.ready&&(this._onRenderCallback&&this._onRenderCallback(),this.material.render(e))}onAfterRenderPass(){this._onAfterRenderCallback&&this._onAfterRenderCallback()}render(e){this.onBeforeRenderPass(),this.renderer.ready&&(!this.renderer.production&&e.pushDebugGroup(this.options.label),this.onRenderPass(e),!this.renderer.production&&e.popDebugGroup(),this.onAfterRenderPass())}copyBufferToResult(e){this.material?.copyBufferToResult(e)}async getComputeResult({bindingName:e,bufferElementName:t}){return await this.material?.getComputeResult({bindingName:e,bufferElementName:t})}remove(){this.removeFromScene(),this.destroy()}destroy(){this.material?.destroy()}}be=new WeakMap;const X=[new f,new f,new f,new f,new f,new f,new f,new f];class ie{constructor(e=new f(1/0),t=new f(-1/0)){this.min=e,this.max=t}set(e=new f(1/0),t=new f(-1/0)){return this.min.copy(e),this.max.copy(t),this}isEmpty(){return this.max.x{},onLeaveView:n=()=>{}}){this.boundingBox=e,this.modelViewProjectionMatrix=t,this.containerBoundingRect=s,this.DOMFrustumMargins={...It,...i},this.projectedBoundingRect={top:0,right:0,bottom:0,left:0,width:0,height:0,x:0,y:0},this.onReEnterView=r,this.onLeaveView=n,this.isIntersecting=!1}setContainerBoundingRect(e){this.containerBoundingRect=e}get DOMFrustumBoundingRect(){return{top:this.projectedBoundingRect.top-this.DOMFrustumMargins.top,right:this.projectedBoundingRect.right+this.DOMFrustumMargins.right,bottom:this.projectedBoundingRect.bottom+this.DOMFrustumMargins.bottom,left:this.projectedBoundingRect.left-this.DOMFrustumMargins.left}}computeProjectedToDocumentCoords(){const e=this.boundingBox.applyMat4(this.modelViewProjectionMatrix);e.min.x=(e.min.x+1)*.5,e.max.x=(e.max.x+1)*.5,e.min.y=1-(e.min.y+1)*.5,e.max.y=1-(e.max.y+1)*.5;const{width:t,height:s,top:i,left:r}=this.containerBoundingRect;this.projectedBoundingRect={left:e.min.x*t+r,x:e.min.x*t+r,top:e.max.y*s+i,y:e.max.y*s+i,right:e.max.x*t+r,bottom:e.min.y*s+i,width:e.max.x*t+r-(e.min.x*t+r),height:e.min.y*s+i-(e.max.y*s+i)},this.intersectsContainer()}intersectsContainer(){Math.round(this.DOMFrustumBoundingRect.right)<=this.containerBoundingRect.left||Math.round(this.DOMFrustumBoundingRect.left)>=this.containerBoundingRect.left+this.containerBoundingRect.width||Math.round(this.DOMFrustumBoundingRect.bottom)<=this.containerBoundingRect.top||Math.round(this.DOMFrustumBoundingRect.top)>=this.containerBoundingRect.top+this.containerBoundingRect.height?(this.isIntersecting&&this.onLeaveView(),this.isIntersecting=!1):(this.isIntersecting||this.onReEnterView(),this.isIntersecting=!0)}}class nt{constructor({verticesOrder:e="ccw",topology:t="triangle-list",instancesCount:s=1,vertexBuffers:i=[],mapBuffersAtCreation:r=!0}={}){this.verticesCount=0,this.verticesOrder=e,this.topology=t,this.instancesCount=s,this.ready=!1,this.boundingBox=new ie,this.type="Geometry",this.uuid=k(),this.vertexBuffers=[],this.consumers=new Set,this.options={verticesOrder:e,topology:t,instancesCount:s,vertexBuffers:i,mapBuffersAtCreation:r};const n=i.find(a=>a.name==="attributes");(!i.length||!n)&&this.addVertexBuffer({name:"attributes"});for(const a of i)this.addVertexBuffer({stepMode:a.stepMode??"vertex",name:a.name,attributes:a.attributes,...a.array&&{array:a.array},...a.buffer&&{buffer:a.buffer},...a.bufferOffset&&{bufferOffset:a.bufferOffset},...a.bufferSize&&{bufferSize:a.bufferSize}});n&&this.setWGSLFragment()}loseContext(){this.ready=!1;for(const e of this.vertexBuffers)e.buffer.destroy()}restoreContext(e){if(!this.ready){for(const t of this.vertexBuffers)!t.buffer.GPUBuffer&&t.buffer.consumers.size===0&&(t.buffer.createBuffer(e),this.uploadBuffer(e,t)),t.buffer.consumers.add(this.uuid);this.ready=!0}}addVertexBuffer({stepMode:e="vertex",name:t,attributes:s=[],buffer:i=null,array:r=null,bufferOffset:n=0,bufferSize:a=null}={}){i=i||new pe;const h={name:t??"attributes"+this.vertexBuffers.length,stepMode:e,arrayStride:0,bufferLength:0,attributes:[],buffer:i,array:r,bufferOffset:n,bufferSize:a};return s?.forEach(u=>{this.setAttribute({vertexBuffer:h,...u})}),this.vertexBuffers.push(h),h}getVertexBufferByName(e=""){return this.vertexBuffers.find(t=>t.name===e)}setAttribute({vertexBuffer:e=this.vertexBuffers[0],name:t,type:s="vec3f",bufferFormat:i="float32x3",size:r=3,array:n=new Float32Array(this.verticesCount*r),verticesStride:a=1}){const h=e.attributes,u=h.length;t||(t="geometryAttribute"+u),t==="position"&&(s!=="vec3f"||i!=="float32x3"||r!==3)&&(D(`Geometry 'position' attribute must have this exact properties set:
type: 'vec3f',
bufferFormat: 'float32x3',
size: 3`),s="vec3f",i="float32x3",r=3);const l=n.length/r;t==="position"&&(this.verticesCount=l),e.stepMode==="vertex"&&this.verticesCount&&this.verticesCount!==l*a?q(`Geometry vertex attribute error. Attribute array of size ${r} must be of length: ${this.verticesCount*r}, current given: ${n.length}. (${this.verticesCount} vertices).`):e.stepMode==="instance"&&l!==this.instancesCount&&q(`Geometry instance attribute error. Attribute array of size ${r} must be of length: ${this.instancesCount*r}, current given: ${n.length}. (${this.instancesCount} instances).`);const d={name:t,type:s,bufferFormat:i,size:r,bufferLength:n.length,offset:u?h.reduce((c,p)=>c+p.bufferLength,0):0,bufferOffset:u?h[u-1].bufferOffset+h[u-1].size*4:0,array:n,verticesStride:a};e.bufferLength+=d.bufferLength*a,e.arrayStride+=d.size,e.attributes.push(d)}get shouldCompute(){return this.vertexBuffers.length&&!this.vertexBuffers[0].array}getAttributeByName(e){let t;return this.vertexBuffers.forEach(s=>{t=s.attributes.find(i=>i.name===e)}),t}computeGeometry(){this.ready||(this.vertexBuffers.forEach((e,t)=>{if(t===0){const r=e.attributes.find(n=>n.name==="position");r||q("Geometry must have a 'position' attribute"),(r.type!=="vec3f"||r.bufferFormat!=="float32x3"||r.size!==3)&&(D(`Geometry 'position' attribute must have this exact properties set:
@@ -57,7 +57,7 @@
@builtin(vertex_index) vertexIndex : u32,
@builtin(instance_index) instanceIndex : u32,${this.vertexBuffers.map(t=>t.attributes.map(s=>(e++,`
@location(${e}) ${s.name}: ${s.type}`))).join(",")}
-};`,this.layoutCacheKey=this.vertexBuffers.map(t=>t.name+","+t.attributes.map(s=>`${s.name},${s.size}`)).join(",")+","}createBuffers({renderer:e,label:t=this.type}){if(!this.ready){for(const s of this.vertexBuffers)s.bufferSize||(s.bufferSize=s.array.length*s.array.constructor.BYTES_PER_ELEMENT),!s.buffer.GPUBuffer&&!s.buffer.consumers.size&&(s.buffer.createBuffer(e,{label:t+": "+s.name+" buffer",size:s.bufferSize,usage:this.options.mapBuffersAtCreation?["vertex"]:["copyDst","vertex"],mappedAtCreation:this.options.mapBuffersAtCreation}),this.uploadBuffer(e,s)),s.buffer.consumers.add(this.uuid);this.ready=!0}}uploadBuffer(e,t){this.options.mapBuffersAtCreation?(new t.array.constructor(t.buffer.GPUBuffer.getMappedRange()).set(t.array),t.buffer.GPUBuffer.unmap()):e.queueWriteBuffer(t.buffer.GPUBuffer,0,t.array)}setGeometryBuffers(e){this.vertexBuffers.forEach((t,s)=>{e.setVertexBuffer(s,t.buffer.GPUBuffer,t.bufferOffset,t.bufferSize)})}drawGeometry(e){e.draw(this.verticesCount,this.instancesCount)}render(e){this.ready&&(this.setGeometryBuffers(e),this.drawGeometry(e))}destroy(e=null){this.ready=!1;for(const t of this.vertexBuffers)t.buffer.consumers.delete(this.uuid),t.buffer.consumers.size||t.buffer.destroy(),t.array=null,e&&e.removeBuffer(t.buffer)}}class be extends nt{constructor({verticesOrder:e="ccw",topology:t="triangle-list",instancesCount:s=1,vertexBuffers:i=[],mapBuffersAtCreation:r=!0}={}){super({verticesOrder:e,topology:t,instancesCount:s,vertexBuffers:i,mapBuffersAtCreation:r}),this.type="IndexedGeometry"}loseContext(){super.loseContext(),this.indexBuffer&&this.indexBuffer.buffer.destroy()}restoreContext(e){this.ready||(this.indexBuffer.buffer.GPUBuffer||(this.indexBuffer.buffer.createBuffer(e),this.uploadBuffer(e,this.indexBuffer),this.indexBuffer.buffer.consumers.add(this.uuid)),super.restoreContext(e))}get useUint16IndexArray(){return this.verticesCount<256*256}setIndexBuffer({bufferFormat:e="uint32",array:t=new Uint32Array(0),buffer:s=new de,bufferOffset:i=0,bufferSize:r=null}){this.indexBuffer={array:t,bufferFormat:e,bufferLength:t.length,buffer:s,bufferOffset:i,bufferSize:r!==null?r:t.length*t.constructor.BYTES_PER_ELEMENT}}createBuffers({renderer:e,label:t=this.type}){this.indexBuffer.buffer.GPUBuffer||(this.indexBuffer.buffer.createBuffer(e,{label:t+": index buffer",size:this.indexBuffer.array.byteLength,usage:this.options.mapBuffersAtCreation?["index"]:["copyDst","index"],mappedAtCreation:this.options.mapBuffersAtCreation}),this.uploadBuffer(e,this.indexBuffer)),this.indexBuffer.buffer.consumers.add(this.uuid),super.createBuffers({renderer:e,label:t})}setGeometryBuffers(e){super.setGeometryBuffers(e),e.setIndexBuffer(this.indexBuffer.buffer.GPUBuffer,this.indexBuffer.bufferFormat,this.indexBuffer.bufferOffset,this.indexBuffer.bufferSize)}drawGeometry(e){e.drawIndexed(this.indexBuffer.bufferLength,this.instancesCount)}destroy(e=null){super.destroy(e),this.indexBuffer&&(this.indexBuffer.buffer.consumers.delete(this.uuid),this.indexBuffer.buffer.destroy(),e&&e.removeBuffer(this.indexBuffer.buffer))}}class ot extends be{constructor({widthSegments:e=1,heightSegments:t=1,instancesCount:s=1,vertexBuffers:i=[],topology:r}={}){super({verticesOrder:"ccw",topology:r,instancesCount:s,vertexBuffers:i,mapBuffersAtCreation:!0}),this.type="PlaneGeometry",e=Math.floor(e),t=Math.floor(t),this.definition={id:e*t+e,width:e,height:t,count:e*t};const n=(this.definition.width+1)*(this.definition.height+1),a=this.getIndexedVerticesAndUVs(n);for(const h of Object.values(a))this.setAttribute(h);this.setIndexArray()}setIndexArray(){const e=this.useUint16IndexArray?new Uint16Array(this.definition.count*6):new Uint32Array(this.definition.count*6);let t=0;for(let s=0;sObject.keys(o).filter(t=>Array.isArray(o[t])?JSON.stringify(o[t])!==JSON.stringify(e[t]):o[t]!==e[t]);var at=`
+};`,this.layoutCacheKey=this.vertexBuffers.map(t=>t.name+","+t.attributes.map(s=>`${s.name},${s.size}`)).join(",")+","}createBuffers({renderer:e,label:t=this.type}){if(!this.ready){for(const s of this.vertexBuffers)s.bufferSize||(s.bufferSize=s.array.length*s.array.constructor.BYTES_PER_ELEMENT),!s.buffer.GPUBuffer&&!s.buffer.consumers.size&&(s.buffer.createBuffer(e,{label:t+": "+s.name+" buffer",size:s.bufferSize,usage:this.options.mapBuffersAtCreation?["vertex"]:["copyDst","vertex"],mappedAtCreation:this.options.mapBuffersAtCreation}),this.uploadBuffer(e,s)),s.buffer.consumers.add(this.uuid);this.ready=!0}}uploadBuffer(e,t){this.options.mapBuffersAtCreation?(new t.array.constructor(t.buffer.GPUBuffer.getMappedRange()).set(t.array),t.buffer.GPUBuffer.unmap()):e.queueWriteBuffer(t.buffer.GPUBuffer,0,t.array)}setGeometryBuffers(e){this.vertexBuffers.forEach((t,s)=>{e.setVertexBuffer(s,t.buffer.GPUBuffer,t.bufferOffset,t.bufferSize)})}drawGeometry(e){e.draw(this.verticesCount,this.instancesCount)}render(e){this.ready&&(this.setGeometryBuffers(e),this.drawGeometry(e))}destroy(e=null){this.ready=!1;for(const t of this.vertexBuffers)t.buffer.consumers.delete(this.uuid),t.buffer.consumers.size||t.buffer.destroy(),t.array=null,e&&e.removeBuffer(t.buffer)}}class ve extends nt{constructor({verticesOrder:e="ccw",topology:t="triangle-list",instancesCount:s=1,vertexBuffers:i=[],mapBuffersAtCreation:r=!0}={}){super({verticesOrder:e,topology:t,instancesCount:s,vertexBuffers:i,mapBuffersAtCreation:r}),this.type="IndexedGeometry"}loseContext(){super.loseContext(),this.indexBuffer&&this.indexBuffer.buffer.destroy()}restoreContext(e){this.ready||(this.indexBuffer.buffer.GPUBuffer||(this.indexBuffer.buffer.createBuffer(e),this.uploadBuffer(e,this.indexBuffer),this.indexBuffer.buffer.consumers.add(this.uuid)),super.restoreContext(e))}get useUint16IndexArray(){return this.verticesCount<256*256}setIndexBuffer({bufferFormat:e="uint32",array:t=new Uint32Array(0),buffer:s=new pe,bufferOffset:i=0,bufferSize:r=null}){this.indexBuffer={array:t,bufferFormat:e,bufferLength:t.length,buffer:s,bufferOffset:i,bufferSize:r!==null?r:t.length*t.constructor.BYTES_PER_ELEMENT}}createBuffers({renderer:e,label:t=this.type}){this.indexBuffer.buffer.GPUBuffer||(this.indexBuffer.buffer.createBuffer(e,{label:t+": index buffer",size:this.indexBuffer.array.byteLength,usage:this.options.mapBuffersAtCreation?["index"]:["copyDst","index"],mappedAtCreation:this.options.mapBuffersAtCreation}),this.uploadBuffer(e,this.indexBuffer)),this.indexBuffer.buffer.consumers.add(this.uuid),super.createBuffers({renderer:e,label:t})}setGeometryBuffers(e){super.setGeometryBuffers(e),e.setIndexBuffer(this.indexBuffer.buffer.GPUBuffer,this.indexBuffer.bufferFormat,this.indexBuffer.bufferOffset,this.indexBuffer.bufferSize)}drawGeometry(e){e.drawIndexed(this.indexBuffer.bufferLength,this.instancesCount)}destroy(e=null){super.destroy(e),this.indexBuffer&&(this.indexBuffer.buffer.consumers.delete(this.uuid),this.indexBuffer.buffer.destroy(),e&&e.removeBuffer(this.indexBuffer.buffer))}}class ot extends ve{constructor({widthSegments:e=1,heightSegments:t=1,instancesCount:s=1,vertexBuffers:i=[],topology:r}={}){super({verticesOrder:"ccw",topology:r,instancesCount:s,vertexBuffers:i,mapBuffersAtCreation:!0}),this.type="PlaneGeometry",e=Math.floor(e),t=Math.floor(t),this.definition={id:e*t+e,width:e,height:t,count:e*t};const n=(this.definition.width+1)*(this.definition.height+1),a=this.getIndexedVerticesAndUVs(n);for(const h of Object.values(a))this.setAttribute(h);this.setIndexArray()}setIndexArray(){const e=this.useUint16IndexArray?new Uint16Array(this.definition.count*6):new Uint32Array(this.definition.count*6);let t=0;for(let s=0;sObject.keys(o).filter(t=>Array.isArray(o[t])?JSON.stringify(o[t])!==JSON.stringify(e[t]):o[t]!==e[t]);var at=`
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) uv: vec2f,
@@ -92,7 +92,7 @@ struct VertexOutput {
}`,ut=`
@fragment fn main() -> @location(0) vec4f {
return vec4(0.0, 0.0, 0.0, 1.0);
-}`;class Nt extends rt{constructor(e,t){e=e&&e.renderer||e;const s="RenderMaterial";$(e,s),t.shaders||(t.shaders={}),t.shaders?.vertex||(t.shaders.vertex={code:t.useProjection?at:ht,entryPoint:"main"}),t.shaders.vertex.entryPoint||(t.shaders.vertex.entryPoint="main"),t.shaders.fragment===void 0&&(t.shaders.fragment={entryPoint:"main",code:ut}),super(e,t),this.type=s,this.renderer=e;const{shaders:i}=t,{useProjection:r,transparent:n,depth:a,depthWriteEnabled:h,depthCompare:u,depthFormat:l,cullMode:d,sampleCount:c,verticesOrder:p,topology:g}=t;let{targets:m}=t;(!m||!m.length)&&(m=[{format:this.renderer.options.preferredFormat}]),m[0].format||(m[0].format=this.renderer.options.preferredFormat),this.options={...this.options,shaders:i,rendering:{useProjection:r,transparent:n,depth:a,depthWriteEnabled:h,depthCompare:u,depthFormat:l,cullMode:d,sampleCount:c,targets:m,verticesOrder:p,topology:g}},this.attributes=null,this.pipelineEntry=null}setPipelineEntry(){this.pipelineEntry=this.renderer.pipelineManager.createRenderPipeline({renderer:this.renderer,label:this.options.label+" render pipeline",shaders:this.options.shaders,useAsync:this.options.useAsyncPipeline,rendering:this.options.rendering,attributes:this.attributes,bindGroups:this.bindGroups})}async compilePipelineEntry(){await this.pipelineEntry.compilePipelineEntry()}async compileMaterial(){super.compileMaterial(),this.attributes&&!this.pipelineEntry&&this.setPipelineEntry(),this.pipelineEntry&&this.pipelineEntry.canCompile&&await this.compilePipelineEntry()}setRenderingOptions(e={}){const t=Wt(e,this.options.rendering),s={...this.options.rendering};if(this.options.rendering={...this.options.rendering,...e},this.pipelineEntry)if(this.pipelineEntry.ready&&t.length){if(!this.renderer.production){const i=t.map(n=>({[n]:Array.isArray(s[n])?s[n].map(a=>a):s[n]})),r=t.map(n=>({[n]:Array.isArray(e[n])?e[n].map(a=>a):e[n]}));D(`${this.options.label}: the change of rendering options is causing this RenderMaterial pipeline to be recompiled. This should be avoided.
+}`;class Wt extends rt{constructor(e,t){e=e&&e.renderer||e;const s="RenderMaterial";$(e,s),t.shaders||(t.shaders={}),t.shaders?.vertex||(t.shaders.vertex={code:t.useProjection?at:ht,entryPoint:"main"}),t.shaders.vertex.entryPoint||(t.shaders.vertex.entryPoint="main"),t.shaders.fragment===void 0&&(t.shaders.fragment={entryPoint:"main",code:ut}),super(e,t),this.type=s,this.renderer=e;const{shaders:i}=t,{useProjection:r,transparent:n,depth:a,depthWriteEnabled:h,depthCompare:u,depthFormat:l,cullMode:d,sampleCount:c,verticesOrder:p,topology:g}=t;let{targets:m}=t;(!m||!m.length)&&(m=[{format:this.renderer.options.preferredFormat}]),m[0].format||(m[0].format=this.renderer.options.preferredFormat),this.options={...this.options,shaders:i,rendering:{useProjection:r,transparent:n,depth:a,depthWriteEnabled:h,depthCompare:u,depthFormat:l,cullMode:d,sampleCount:c,targets:m,verticesOrder:p,topology:g}},this.attributes=null,this.pipelineEntry=null}setPipelineEntry(){this.pipelineEntry=this.renderer.pipelineManager.createRenderPipeline({renderer:this.renderer,label:this.options.label+" render pipeline",shaders:this.options.shaders,useAsync:this.options.useAsyncPipeline,rendering:this.options.rendering,attributes:this.attributes,bindGroups:this.bindGroups})}async compilePipelineEntry(){await this.pipelineEntry.compilePipelineEntry()}async compileMaterial(){super.compileMaterial(),this.attributes&&!this.pipelineEntry&&this.setPipelineEntry(),this.pipelineEntry&&this.pipelineEntry.canCompile&&await this.compilePipelineEntry()}setRenderingOptions(e={}){const t=Nt(e,this.options.rendering),s={...this.options.rendering};if(this.options.rendering={...this.options.rendering,...e},this.pipelineEntry)if(this.pipelineEntry.ready&&t.length){if(!this.renderer.production){const i=t.map(n=>({[n]:Array.isArray(s[n])?s[n].map(a=>a):s[n]})),r=t.map(n=>({[n]:Array.isArray(e[n])?e[n].map(a=>a):e[n]}));D(`${this.options.label}: the change of rendering options is causing this RenderMaterial pipeline to be recompiled. This should be avoided.
Old rendering options: ${JSON.stringify(i.reduce((n,a)=>({...n,...a}),{}),null,4)}
@@ -108,7 +108,7 @@ ${this.geometry.wgslStructFragment}
New geometry layout:
-${s.wgslStructFragment}`),this.material.setAttributesFromGeometry(s),this.material.setPipelineEntry()),this.geometry.consumers.delete(this.uuid)),this.geometry=s,this.geometry.consumers.add(this.uuid),this.computeGeometry(),this.material){const i={...this.material.options.rendering,verticesOrder:s.verticesOrder,topology:s.topology};this.material.setRenderingOptions(i)}}computeGeometry(){this.geometry.shouldCompute&&this.geometry.computeGeometry()}setGeometry(){this.geometry&&(this.geometry.ready||this.geometry.createBuffers({renderer:this.renderer,label:this.options.label+" geometry"}),this.setMaterialGeometryAttributes())}setRenderingOptionsForRenderPass(s){const i={transparent:this.transparent,sampleCount:s.options.sampleCount,...s.options.colorAttachments.length&&{targets:s.options.colorAttachments.map((r,n)=>({format:r.targetFormat,...this.options.targets?.length&&this.options.targets[n]&&this.options.targets[n].blend&&{blend:this.options.targets[n].blend}}))},depth:s.options.useDepth,...s.options.useDepth&&{depthFormat:s.options.depthFormat}};this.material?.setRenderingOptions(i)}cleanupRenderMaterialParameters(s){return delete s.texturesOptions,delete s.outputTarget,delete s.autoRender,s}useMaterial(s){this.material=s,this.transparent=this.material.options.rendering.transparent,this.material.options.domTextures?.filter(i=>i instanceof he).forEach(i=>this.onDOMTextureAdded(i))}setMaterial(s){this.setShaders(),s.shaders=this.options.shaders,s.label=s.label+" material",this.useMaterial(new Nt(this.renderer,s))}setMaterialGeometryAttributes(){this.material&&!this.material.attributes&&this.material.setAttributesFromGeometry(this.geometry)}get transparent(){return this._transparent}set transparent(s){const i=this.transparent!==void 0&&s!==this.transparent;i&&this.removeFromScene(),this._transparent=s,i&&this.addToScene()}get visible(){return this._visible}set visible(s){this._visible=s}get domTextures(){return this.material?.domTextures||[]}get textures(){return this.material?.textures||[]}createDOMTexture(s){s.name||(s.name="texture"+(this.textures.length+this.domTextures.length)),s.label||(s.label=this.options.label+" "+s.name);const i=new he(this.renderer,{...s,...this.options.texturesOptions});return this.addDOMTexture(i),i}addDOMTexture(s){this.material.addTexture(s),this.onDOMTextureAdded(s)}onDOMTextureAdded(s){s.parentMesh=this}createTexture(s){s.name||(s.name="texture"+(this.textures.length+this.domTextures.length));const i=new te(this.renderer,s);return this.addTexture(i),i}addTexture(s){this.material.addTexture(s)}get uniforms(){return this.material?.uniforms}get storages(){return this.material?.storages}resize(s){super.resize&&super.resize(s),this.textures?.forEach(i=>{i.options.fromTexture&&i.copy(i.options.fromTexture)}),this.domTextures?.forEach(i=>{i.resize()}),this._onAfterResizeCallback&&this._onAfterResizeCallback()}onReady(s){return s&&(this._onReadyCallback=s),this}onBeforeRender(s){return s&&(this._onBeforeRenderCallback=s),this}onRender(s){return s&&(this._onRenderCallback=s),this}onAfterRender(s){return s&&(this._onAfterRenderCallback=s),this}onAfterResize(s){return s&&(this._onAfterResizeCallback=s),this}onBeforeRenderScene(){!this.renderer.ready||!this.ready||!this.visible||this._onBeforeRenderCallback&&this._onBeforeRenderCallback()}onBeforeRenderPass(){this.renderer.ready&&(this.ready=this.material&&this.material.ready&&this.geometry&&this.geometry.ready,this.setGeometry(),this.material.onBeforeRender())}onRenderPass(s){this.ready&&(this._onRenderCallback&&this._onRenderCallback(),this.material.render(s),this.geometry.render(s))}onAfterRenderPass(){this._onAfterRenderCallback&&this._onAfterRenderCallback()}render(s){this.onBeforeRenderPass(),!(!this.renderer.ready||!this.visible)&&(super.render&&super.render(),!this.renderer.production&&s.pushDebugGroup(this.options.label),this.onRenderPass(s),!this.renderer.production&&s.popDebugGroup(),this.onAfterRenderPass())}remove(){this.removeFromScene(!0),this.destroy(),this.renderer.meshes.length||this.renderer.onBeforeRenderScene.add(s=>{this.renderer.forceClear(s)},{once:!0})}destroy(){super.destroy&&super.destroy(),this.material?.destroy(),this.geometry.consumers.delete(this.uuid),this.geometry.consumers.size||this.geometry?.destroy(this.renderer)}},e=new WeakMap,t}class Ws{constructor(){this.planeGeometries=[]}getPlaneGeometry(e){return this.planeGeometries.find(t=>t.definition.id===e.definition.id)}getPlaneGeometryByID(e){return this.planeGeometries.find(t=>t.definition.id===e)}addPlaneGeometry(e){this.planeGeometries.push(e)}destroy(){this.planeGeometries=[]}}const ke=new Ws;class dt extends Yt(class{}){constructor(e,t={}){e=e&&e.renderer||e,$(e,t.label?t.label+" FullscreenQuadMesh":"FullscreenQuadMesh");let s=ke.getPlaneGeometryByID(2);s||(s=new ot({widthSegments:1,heightSegments:1}),ke.addPlaneGeometry(s)),(!t.shaders||!t.shaders.vertex)&&["uniforms","storages"].forEach(i=>{Object.values(t[i]??{}).forEach(r=>r.visibility=["fragment"])}),t.depthWriteEnabled=!1,t.label||(t.label="FullscreenQuadMesh"),super(e,null,{geometry:s,...t}),this.size={document:{width:this.renderer.boundingRect.width,height:this.renderer.boundingRect.height,top:this.renderer.boundingRect.top,left:this.renderer.boundingRect.left}},this.type="FullscreenQuadMesh"}resize(e=null){this.size.document=e??this.renderer.boundingRect,super.resize(e)}mouseToPlaneCoords(e=new E){return new E((e.x-this.size.document.left)/this.size.document.width*2-1,1-(e.y-this.size.document.top)/this.size.document.height*2)}}class re{constructor(e=new Float32Array([1,0,0,0,1,0,0,0,1])){this.type="Mat3",this.elements=e}set(e,t,s,i,r,n,a,h,u){const l=this.elements;return l[0]=e,l[1]=i,l[2]=a,l[3]=t,l[4]=r,l[5]=h,l[6]=s,l[7]=n,l[8]=u,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}setFromArray(e=new Float32Array([1,0,0,0,1,0,0,0,1])){for(let t=0;t{this.modelViewMatrix.multiplyMatrices(this.viewMatrix,this.worldMatrix)}},modelViewProjection:{matrix:new A,shouldUpdate:!0,onUpdate:()=>{this.modelViewProjectionMatrix.multiplyMatrices(this.projectionMatrix,this.modelViewMatrix)}},normal:{matrix:new re,shouldUpdate:!0,onUpdate:()=>{this.normalMatrix.getNormalMatrix(this.worldMatrix)}}}}get modelViewMatrix(){return this.matrices.modelView.matrix}set modelViewMatrix(e){this.matrices.modelView.matrix=e,this.matrices.modelView.shouldUpdate=!0}get viewMatrix(){return this.camera.viewMatrix}get projectionMatrix(){return this.camera.projectionMatrix}get modelViewProjectionMatrix(){return this.matrices.modelViewProjection.matrix}set modelViewProjectionMatrix(e){this.matrices.modelViewProjection.matrix=e,this.matrices.modelViewProjection.shouldUpdate=!0}get normalMatrix(){return this.matrices.normal.matrix}set normalMatrix(e){this.matrices.normal.matrix=e,this.matrices.normal.shouldUpdate=!0}shouldUpdateProjectionMatrixStack(){this.matrices.modelView.shouldUpdate=!0,this.matrices.modelViewProjection.shouldUpdate=!0}shouldUpdateWorldMatrix(){super.shouldUpdateWorldMatrix(),this.shouldUpdateProjectionMatrixStack(),this.matrices.normal.shouldUpdate=!0}shouldUpdateMatrixStack(){this.shouldUpdateModelMatrix(),this.shouldUpdateProjectionMatrixStack()}}var Ht=`
+${s.wgslStructFragment}`),this.material.setAttributesFromGeometry(s),this.material.setPipelineEntry()),this.geometry.consumers.delete(this.uuid)),this.geometry=s,this.geometry.consumers.add(this.uuid),this.computeGeometry(),this.material){const i={...this.material.options.rendering,verticesOrder:s.verticesOrder,topology:s.topology};this.material.setRenderingOptions(i)}}computeGeometry(){this.geometry.shouldCompute&&this.geometry.computeGeometry()}setGeometry(){this.geometry&&(this.geometry.ready||this.geometry.createBuffers({renderer:this.renderer,label:this.options.label+" geometry"}),this.setMaterialGeometryAttributes())}setRenderingOptionsForRenderPass(s){const i={transparent:this.transparent,sampleCount:s.options.sampleCount,...s.options.colorAttachments.length&&{targets:s.options.colorAttachments.map((r,n)=>({format:r.targetFormat,...this.options.targets?.length&&this.options.targets[n]&&this.options.targets[n].blend&&{blend:this.options.targets[n].blend}}))},depth:s.options.useDepth,...s.options.useDepth&&{depthFormat:s.options.depthFormat}};this.material?.setRenderingOptions(i)}cleanupRenderMaterialParameters(s){return delete s.texturesOptions,delete s.outputTarget,delete s.autoRender,s}useMaterial(s){this.material=s,this.transparent=this.material.options.rendering.transparent,this.material.options.domTextures?.filter(i=>i instanceof ue).forEach(i=>this.onDOMTextureAdded(i))}setMaterial(s){this.setShaders(),s.shaders=this.options.shaders,s.label=s.label+" material",this.useMaterial(new Wt(this.renderer,s))}setMaterialGeometryAttributes(){this.material&&!this.material.attributes&&this.material.setAttributesFromGeometry(this.geometry)}get transparent(){return this._transparent}set transparent(s){const i=this.transparent!==void 0&&s!==this.transparent;i&&this.removeFromScene(),this._transparent=s,i&&this.addToScene()}get visible(){return this._visible}set visible(s){this._visible=s}get domTextures(){return this.material?.domTextures||[]}get textures(){return this.material?.textures||[]}createDOMTexture(s){s.name||(s.name="texture"+(this.textures.length+this.domTextures.length)),s.label||(s.label=this.options.label+" "+s.name);const i=new ue(this.renderer,{...s,...this.options.texturesOptions});return this.addDOMTexture(i),i}addDOMTexture(s){this.material.addTexture(s),this.onDOMTextureAdded(s)}onDOMTextureAdded(s){s.parentMesh=this}createTexture(s){s.name||(s.name="texture"+(this.textures.length+this.domTextures.length));const i=new te(this.renderer,s);return this.addTexture(i),i}addTexture(s){this.material.addTexture(s)}get uniforms(){return this.material?.uniforms}get storages(){return this.material?.storages}resize(s){super.resize&&super.resize(s),this.textures?.forEach(i=>{i.options.fromTexture&&i.copy(i.options.fromTexture)}),this.domTextures?.forEach(i=>{i.resize()}),this._onAfterResizeCallback&&this._onAfterResizeCallback()}onReady(s){return s&&(this._onReadyCallback=s),this}onBeforeRender(s){return s&&(this._onBeforeRenderCallback=s),this}onRender(s){return s&&(this._onRenderCallback=s),this}onAfterRender(s){return s&&(this._onAfterRenderCallback=s),this}onAfterResize(s){return s&&(this._onAfterResizeCallback=s),this}onBeforeRenderScene(){!this.renderer.ready||!this.ready||!this.visible||this._onBeforeRenderCallback&&this._onBeforeRenderCallback()}onBeforeRenderPass(){this.renderer.ready&&(this.ready=this.material&&this.material.ready&&this.geometry&&this.geometry.ready,this.setGeometry(),this.material.onBeforeRender())}onRenderPass(s){this.ready&&(this._onRenderCallback&&this._onRenderCallback(),this.material.render(s),this.geometry.render(s))}onAfterRenderPass(){this._onAfterRenderCallback&&this._onAfterRenderCallback()}render(s){this.onBeforeRenderPass(),!(!this.renderer.ready||!this.visible)&&(super.render&&super.render(),!this.renderer.production&&s.pushDebugGroup(this.options.label),this.onRenderPass(s),!this.renderer.production&&s.popDebugGroup(),this.onAfterRenderPass())}remove(){this.removeFromScene(!0),this.destroy(),this.renderer.meshes.length||this.renderer.onBeforeRenderScene.add(s=>{this.renderer.forceClear(s)},{once:!0})}destroy(){super.destroy&&super.destroy(),this.material?.destroy(),this.geometry.consumers.delete(this.uuid),this.geometry.consumers.size||this.geometry?.destroy(this.renderer)}},e=new WeakMap,t}class Ns{constructor(){this.planeGeometries=[]}getPlaneGeometry(e){return this.planeGeometries.find(t=>t.definition.id===e.definition.id)}getPlaneGeometryByID(e){return this.planeGeometries.find(t=>t.definition.id===e)}addPlaneGeometry(e){this.planeGeometries.push(e)}destroy(){this.planeGeometries=[]}}const Ie=new Ns;class dt extends Yt(class{}){constructor(e,t={}){e=e&&e.renderer||e,$(e,t.label?t.label+" FullscreenQuadMesh":"FullscreenQuadMesh");let s=Ie.getPlaneGeometryByID(2);s||(s=new ot({widthSegments:1,heightSegments:1}),Ie.addPlaneGeometry(s)),(!t.shaders||!t.shaders.vertex)&&["uniforms","storages"].forEach(i=>{Object.values(t[i]??{}).forEach(r=>r.visibility=["fragment"])}),t.depthWriteEnabled=!1,t.label||(t.label="FullscreenQuadMesh"),super(e,null,{geometry:s,...t}),this.size={document:{width:this.renderer.boundingRect.width,height:this.renderer.boundingRect.height,top:this.renderer.boundingRect.top,left:this.renderer.boundingRect.left}},this.type="FullscreenQuadMesh"}resize(e=null){this.size.document=e??this.renderer.boundingRect,super.resize(e)}mouseToPlaneCoords(e=new E){return new E((e.x-this.size.document.left)/this.size.document.width*2-1,1-(e.y-this.size.document.top)/this.size.document.height*2)}}class re{constructor(e=new Float32Array([1,0,0,0,1,0,0,0,1])){this.type="Mat3",this.elements=e}set(e,t,s,i,r,n,a,h,u){const l=this.elements;return l[0]=e,l[1]=i,l[2]=a,l[3]=t,l[4]=r,l[5]=h,l[6]=s,l[7]=n,l[8]=u,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}setFromArray(e=new Float32Array([1,0,0,0,1,0,0,0,1])){for(let t=0;t{this.modelViewMatrix.multiplyMatrices(this.viewMatrix,this.worldMatrix)}},modelViewProjection:{matrix:new A,shouldUpdate:!0,onUpdate:()=>{this.modelViewProjectionMatrix.multiplyMatrices(this.projectionMatrix,this.modelViewMatrix)}},normal:{matrix:new re,shouldUpdate:!0,onUpdate:()=>{this.normalMatrix.getNormalMatrix(this.worldMatrix)}}}}get modelViewMatrix(){return this.matrices.modelView.matrix}set modelViewMatrix(e){this.matrices.modelView.matrix=e,this.matrices.modelView.shouldUpdate=!0}get viewMatrix(){return this.camera.viewMatrix}get projectionMatrix(){return this.camera.projectionMatrix}get modelViewProjectionMatrix(){return this.matrices.modelViewProjection.matrix}set modelViewProjectionMatrix(e){this.matrices.modelViewProjection.matrix=e,this.matrices.modelViewProjection.shouldUpdate=!0}get normalMatrix(){return this.matrices.normal.matrix}set normalMatrix(e){this.matrices.normal.matrix=e,this.matrices.normal.shouldUpdate=!0}shouldUpdateProjectionMatrixStack(){this.matrices.modelView.shouldUpdate=!0,this.matrices.modelViewProjection.shouldUpdate=!0}shouldUpdateWorldMatrix(){super.shouldUpdateWorldMatrix(),this.shouldUpdateProjectionMatrixStack(),this.matrices.normal.shouldUpdate=!0}shouldUpdateMatrixStack(){this.shouldUpdateModelMatrix(),this.shouldUpdateProjectionMatrixStack()}}var Ht=`
struct VSOutput {
@builtin(position) position: vec4f,
@location(0) uv: vec2f,
@@ -118,7 +118,7 @@ struct VSOutput {
@fragment fn main(fsInput: VSOutput) -> @location(0) vec4f {
// normals
return vec4(normalize(fsInput.normal) * 0.5 + 0.5, 1.0);
-}`;const Xt={frustumCulled:!0,DOMFrustumMargins:{top:0,right:0,bottom:0,left:0}};function Kt(o){return class extends Yt(o){constructor(...t){super(t[0],t[1],{...Xt,...t[2],useProjection:!0}),this._onReEnterViewCallback=()=>{},this._onLeaveViewCallback=()=>{};let s=t[0];const i={...Xt,...t[2],useProjection:!0};this.type="MeshTransformed",s=s&&s.renderer||s,Be(s,i.label?i.label+" "+this.type:this.type),this.renderer=s;const{frustumCulled:r,DOMFrustumMargins:n}=i;this.options={...this.options??{},frustumCulled:r,DOMFrustumMargins:n},this.setDOMFrustum()}setShaders(){const{shaders:t}=this.options;t?((!t.vertex||!t.vertex.code)&&(t.vertex={code:at,entryPoint:"main"}),(t.fragment===void 0||t.fragment&&!t.fragment.code)&&(t.fragment={code:Ht,entryPoint:"main"})):this.options.shaders={vertex:{code:at,entryPoint:"main"},fragment:{code:Ht,entryPoint:"main"}}}useGeometry(t){super.useGeometry(t),this.domFrustum&&(this.domFrustum.boundingBox=this.geometry.boundingBox),this.shouldUpdateMatrixStack()}setDOMFrustum(){this.domFrustum=new Vt({boundingBox:this.geometry?.boundingBox,modelViewProjectionMatrix:this.modelViewProjectionMatrix,containerBoundingRect:this.renderer.boundingRect,DOMFrustumMargins:this.options.DOMFrustumMargins,onReEnterView:()=>{this._onReEnterViewCallback&&this._onReEnterViewCallback()},onLeaveView:()=>{this._onLeaveViewCallback&&this._onLeaveViewCallback()}}),this.DOMFrustumMargins=this.domFrustum.DOMFrustumMargins,this.frustumCulled=this.options.frustumCulled}cleanupRenderMaterialParameters(t){return delete t.frustumCulled,delete t.DOMFrustumMargins,super.cleanupRenderMaterialParameters(t),t}setMaterial(t){const s={label:"Matrices",visibility:["vertex"],struct:{model:{type:"mat4x4f",value:this.worldMatrix},modelView:{type:"mat4x4f",value:this.modelViewMatrix},normal:{type:"mat3x3f",value:this.normalMatrix}}};t.uniforms||(t.uniforms={}),t.uniforms={matrices:s,...t.uniforms},super.setMaterial(t)}get visible(){return this._visible}set visible(t){this.shouldUpdateMatrixStack(),this._visible=t}resize(t){this.domFrustum&&this.domFrustum.setContainerBoundingRect(this.renderer.boundingRect),super.resize(t)}applyScale(){super.applyScale();for(const t of this.domTextures)t.resize()}get projectedBoundingRect(){return this.domFrustum?.projectedBoundingRect}onReEnterView(t){return t&&(this._onReEnterViewCallback=t),this}onLeaveView(t){return t&&(this._onLeaveViewCallback=t),this}checkFrustumCulling(){this.matricesNeedUpdate&&this.domFrustum&&this.frustumCulled&&this.domFrustum.computeProjectedToDocumentCoords()}onBeforeRenderPass(){this.material&&this.matricesNeedUpdate&&this.material.shouldUpdateInputsBindings("matrices"),super.onBeforeRenderPass()}onRenderPass(t){this.ready&&(this._onRenderCallback&&this._onRenderCallback(),(this.domFrustum&&this.domFrustum.isIntersecting||!this.frustumCulled)&&(this.material.render(t),this.geometry.render(t)))}}}class Zt extends Kt(ct){constructor(e,t={}){e=e&&e.renderer||e,Be(e,t.label?t.label+" Mesh":"Mesh"),super(e,null,t),this.type="Mesh"}}let Ns=0;class pt{constructor(e){this.type="PipelineEntry";let{renderer:t}=e;const{label:s,shaders:i,useAsync:r}=e;t=t&&t.renderer||t,$(t,s?s+" "+this.type:this.type),this.renderer=t,Object.defineProperty(this,"index",{value:Ns++}),this.layout=null,this.pipeline=null,this.status={compiling:!1,compiled:!1,error:null},this.options={label:s,shaders:i,useAsync:r!==void 0?r:!0}}get ready(){return!this.status.compiling&&this.status.compiled&&!this.status.error}get canCompile(){return!this.status.compiling&&!this.status.compiled&&!this.status.error}setPipelineEntryBindGroups(e){this.bindGroups=e}createShaderModule({code:e="",type:t="vertex"}){const s=this.renderer.createShaderModule({label:this.options.label+": "+t+" shader module",code:e});return"getCompilationInfo"in s&&!this.renderer.production&&s.getCompilationInfo().then(i=>{for(const r of i.messages){let n="";switch(r.lineNum&&(n+=`Line ${r.lineNum}:${r.linePos} - ${e.substring(r.offset,r.offset+r.length)}
+}`;const Xt={frustumCulled:!0,DOMFrustumMargins:{top:0,right:0,bottom:0,left:0}};function Kt(o){return class extends Yt(o){constructor(...t){super(t[0],t[1],{...Xt,...t[2],useProjection:!0}),this._onReEnterViewCallback=()=>{},this._onLeaveViewCallback=()=>{};let s=t[0];const i={...Xt,...t[2],useProjection:!0};this.type="MeshTransformed",s=s&&s.renderer||s,Ce(s,i.label?i.label+" "+this.type:this.type),this.renderer=s;const{frustumCulled:r,DOMFrustumMargins:n}=i;this.options={...this.options??{},frustumCulled:r,DOMFrustumMargins:n},this.setDOMFrustum()}setShaders(){const{shaders:t}=this.options;t?((!t.vertex||!t.vertex.code)&&(t.vertex={code:at,entryPoint:"main"}),(t.fragment===void 0||t.fragment&&!t.fragment.code)&&(t.fragment={code:Ht,entryPoint:"main"})):this.options.shaders={vertex:{code:at,entryPoint:"main"},fragment:{code:Ht,entryPoint:"main"}}}useGeometry(t){super.useGeometry(t),this.domFrustum&&(this.domFrustum.boundingBox=this.geometry.boundingBox),this.shouldUpdateMatrixStack()}setDOMFrustum(){this.domFrustum=new Vt({boundingBox:this.geometry?.boundingBox,modelViewProjectionMatrix:this.modelViewProjectionMatrix,containerBoundingRect:this.renderer.boundingRect,DOMFrustumMargins:this.options.DOMFrustumMargins,onReEnterView:()=>{this._onReEnterViewCallback&&this._onReEnterViewCallback()},onLeaveView:()=>{this._onLeaveViewCallback&&this._onLeaveViewCallback()}}),this.DOMFrustumMargins=this.domFrustum.DOMFrustumMargins,this.frustumCulled=this.options.frustumCulled}cleanupRenderMaterialParameters(t){return delete t.frustumCulled,delete t.DOMFrustumMargins,super.cleanupRenderMaterialParameters(t),t}setMaterial(t){const s={label:"Matrices",visibility:["vertex"],struct:{model:{type:"mat4x4f",value:this.worldMatrix},modelView:{type:"mat4x4f",value:this.modelViewMatrix},normal:{type:"mat3x3f",value:this.normalMatrix}}};t.uniforms||(t.uniforms={}),t.uniforms={matrices:s,...t.uniforms},super.setMaterial(t)}get visible(){return this._visible}set visible(t){this.shouldUpdateMatrixStack(),this._visible=t}resize(t){this.domFrustum&&this.domFrustum.setContainerBoundingRect(this.renderer.boundingRect),super.resize(t)}applyScale(){super.applyScale();for(const t of this.domTextures)t.resize()}get projectedBoundingRect(){return this.domFrustum?.projectedBoundingRect}onReEnterView(t){return t&&(this._onReEnterViewCallback=t),this}onLeaveView(t){return t&&(this._onLeaveViewCallback=t),this}checkFrustumCulling(){this.matricesNeedUpdate&&this.domFrustum&&this.frustumCulled&&this.domFrustum.computeProjectedToDocumentCoords()}onBeforeRenderPass(){this.material&&this.matricesNeedUpdate&&this.material.shouldUpdateInputsBindings("matrices"),super.onBeforeRenderPass()}onRenderPass(t){this.ready&&(this._onRenderCallback&&this._onRenderCallback(),(this.domFrustum&&this.domFrustum.isIntersecting||!this.frustumCulled)&&(this.material.render(t),this.geometry.render(t)))}}}class Zt extends Kt(ct){constructor(e,t={}){e=e&&e.renderer||e,Ce(e,t.label?t.label+" Mesh":"Mesh"),super(e,null,t),this.type="Mesh"}}let Ws=0;class pt{constructor(e){this.type="PipelineEntry";let{renderer:t}=e;const{label:s,shaders:i,useAsync:r}=e;t=t&&t.renderer||t,$(t,s?s+" "+this.type:this.type),this.renderer=t,Object.defineProperty(this,"index",{value:Ws++}),this.layout=null,this.pipeline=null,this.status={compiling:!1,compiled:!1,error:null},this.options={label:s,shaders:i,useAsync:r!==void 0?r:!0}}get ready(){return!this.status.compiling&&this.status.compiled&&!this.status.error}get canCompile(){return!this.status.compiling&&!this.status.compiled&&!this.status.error}setPipelineEntryBindGroups(e){this.bindGroups=e}createShaderModule({code:e="",type:t="vertex"}){const s=this.renderer.createShaderModule({label:this.options.label+": "+t+" shader module",code:e});return"getCompilationInfo"in s&&!this.renderer.production&&s.getCompilationInfo().then(i=>{for(const r of i.messages){let n="";switch(r.lineNum&&(n+=`Line ${r.lineNum}:${r.linePos} - ${e.substring(r.offset,r.offset+r.length)}
`),n+=r.message,r.type){case"error":console.error(`${this.options.label} compilation error:
${n}`);break;case"warning":console.warn(`${this.options.label} compilation warning:
${n}`);break;case"info":console.log(`${this.options.label} compilation information:
@@ -146,14 +146,14 @@ fn getVertex2DToUVCoords(vertex: vec2f) -> vec2f {
fn getVertex3DToUVCoords(vertex: vec3f) -> vec2f {
return getVertex2DToUVCoords( vec2(vertex.x, vertex.y) );
}
-`;const ue={vertex:{get_uv_cover:Qt},fragment:{get_uv_cover:Qt,get_vertex_to_uv_coords:Ys}},le={vertex:{get_output_position:js,get_normals:qs},fragment:{}};class ft extends pt{constructor(e){let{renderer:t,...s}=e;const{label:i,attributes:r,bindGroups:n,cacheKey:a,...h}=s;t=t&&t.renderer||t;const u="RenderPipelineEntry";$(t,i?i+" "+u:u),super(e),this.type=u,this.shaders={vertex:{head:"",code:"",module:null},fragment:{head:"",code:"",module:null},full:{head:"",code:"",module:null}},this.descriptor=null,this.options={...this.options,attributes:r,bindGroups:n,cacheKey:a,...h},this.setPipelineEntryProperties({attributes:r,bindGroups:n})}setPipelineEntryProperties(e){const{attributes:t,bindGroups:s}=e;this.attributes=t,this.setPipelineEntryBindGroups(s)}patchShaders(){this.shaders.vertex.head="",this.shaders.vertex.code="",this.shaders.fragment.head="",this.shaders.fragment.code="",this.shaders.full.head="",this.shaders.full.code="";for(const t in ue.vertex)this.shaders.vertex.head=`${ue.vertex[t]}
-${this.shaders.vertex.head}`,this.shaders.full.head=`${ue.vertex[t]}
-${this.shaders.full.head}`;if(this.options.shaders.fragment)for(const t in ue.fragment)this.shaders.fragment.head=`${ue.fragment[t]}
-${this.shaders.fragment.head}`,this.shaders.full.head.indexOf(ue.fragment[t])===-1&&(this.shaders.full.head=`${ue.fragment[t]}
-${this.shaders.full.head}`);if(this.options.rendering.useProjection){for(const t in le.vertex)this.shaders.vertex.head=`${le.vertex[t]}
+`;const le={vertex:{get_uv_cover:Qt},fragment:{get_uv_cover:Qt,get_vertex_to_uv_coords:Ys}},de={vertex:{get_output_position:js,get_normals:qs},fragment:{}};class ft extends pt{constructor(e){let{renderer:t,...s}=e;const{label:i,attributes:r,bindGroups:n,cacheKey:a,...h}=s;t=t&&t.renderer||t;const u="RenderPipelineEntry";$(t,i?i+" "+u:u),super(e),this.type=u,this.shaders={vertex:{head:"",code:"",module:null},fragment:{head:"",code:"",module:null},full:{head:"",code:"",module:null}},this.descriptor=null,this.options={...this.options,attributes:r,bindGroups:n,cacheKey:a,...h},this.setPipelineEntryProperties({attributes:r,bindGroups:n})}setPipelineEntryProperties(e){const{attributes:t,bindGroups:s}=e;this.attributes=t,this.setPipelineEntryBindGroups(s)}patchShaders(){this.shaders.vertex.head="",this.shaders.vertex.code="",this.shaders.fragment.head="",this.shaders.fragment.code="",this.shaders.full.head="",this.shaders.full.code="";for(const t in le.vertex)this.shaders.vertex.head=`${le.vertex[t]}
${this.shaders.vertex.head}`,this.shaders.full.head=`${le.vertex[t]}
${this.shaders.full.head}`;if(this.options.shaders.fragment)for(const t in le.fragment)this.shaders.fragment.head=`${le.fragment[t]}
${this.shaders.fragment.head}`,this.shaders.full.head.indexOf(le.fragment[t])===-1&&(this.shaders.full.head=`${le.fragment[t]}
+${this.shaders.full.head}`);if(this.options.rendering.useProjection){for(const t in de.vertex)this.shaders.vertex.head=`${de.vertex[t]}
+${this.shaders.vertex.head}`,this.shaders.full.head=`${de.vertex[t]}
+${this.shaders.full.head}`;if(this.options.shaders.fragment)for(const t in de.fragment)this.shaders.fragment.head=`${de.fragment[t]}
+${this.shaders.fragment.head}`,this.shaders.full.head.indexOf(de.fragment[t])===-1&&(this.shaders.full.head=`${de.fragment[t]}
${this.shaders.full.head}`)}const e=[];for(const t of this.bindGroups){let s=0;t.bindings.forEach((i,r)=>{i.wgslGroupFragment.forEach((n,a)=>{e.push({groupIndex:t.index,visibility:i.options.visibility,bindIndex:s,wgslStructFragment:i.wgslStructFragment,wgslGroupFragment:n,newLine:r===t.bindings.length-1&&a===i.wgslGroupFragment.length-1}),s++})})}for(const t of e)t.visibility.includes("vertex")&&(t.wgslStructFragment&&this.shaders.vertex.head.indexOf(t.wgslStructFragment)===-1&&(this.shaders.vertex.head=`
${t.wgslStructFragment}
${this.shaders.vertex.head}`),this.shaders.vertex.head.indexOf(t.wgslGroupFragment)===-1&&(this.shaders.vertex.head=`${this.shaders.vertex.head}
@@ -172,7 +172,7 @@ ${this.shaders.full.head}`,this.shaders.vertex.code=this.shaders.vertex.head+thi
${t.wgslStructFragment}
${this.shaders.compute.head}`),this.shaders.compute.head.indexOf(t.wgslGroupFragment)===-1&&(this.shaders.compute.head=`${this.shaders.compute.head}
@group(${t.groupIndex}) @binding(${t.bindIndex}) ${t.wgslGroupFragment}`),t.newLine&&(this.shaders.compute.head+=`
-`);this.shaders.compute.code=this.shaders.compute.head+this.options.shaders.compute.code}createShaders(){this.patchShaders(),this.shaders.compute.module=this.createShaderModule({code:this.shaders.compute.code,type:"compute"})}createPipelineDescriptor(){this.shaders.compute.module&&(this.descriptor={label:this.options.label,layout:this.layout,compute:{module:this.shaders.compute.module,entryPoint:this.options.shaders.compute.entryPoint}})}createComputePipeline(){if(this.shaders.compute.module)try{this.pipeline=this.renderer.createComputePipeline(this.descriptor)}catch(e){this.status.error=e,q(e)}}async createComputePipelineAsync(){if(this.shaders.compute.module)try{this.pipeline=await this.renderer.createComputePipelineAsync(this.descriptor),this.status.compiled=!0,this.status.compiling=!1,this.status.error=null}catch(e){this.status.error=e,q(e)}}async compilePipelineEntry(){super.compilePipelineEntry(),this.options.useAsync?await this.createComputePipelineAsync():(this.createComputePipeline(),this.status.compiled=!0,this.status.compiling=!1,this.status.error=null)}}class es{constructor(){this.type="PipelineManager",this.currentPipelineIndex=null,this.pipelineEntries=[],this.activeBindGroups=[]}compareShaders(e,t){return e.code===t.code&&e.entryPoint===t.entryPoint}isSameRenderPipeline(e){return this.pipelineEntries.filter(t=>t instanceof ft).find(t=>{const{options:s}=t,{shaders:i,rendering:r,cacheKey:n}=e,a=n===s.cacheKey,h=this.compareShaders(i.vertex,s.shaders.vertex),u=!i.fragment&&!s.shaders.fragment||this.compareShaders(i.fragment,s.shaders.fragment),l=Wt(r,s.rendering);return a&&!l.length&&h&&u})}createRenderPipeline(e){const{attributes:t,bindGroups:s}=e;let i=t.layoutCacheKey;s.forEach(n=>{n.bindings.forEach(a=>{i+=a.name+","}),i+=n.pipelineCacheKey});const r=this.isSameRenderPipeline({...e,cacheKey:i});if(r)return r;{const n=new ft({...e,cacheKey:i});return this.pipelineEntries.push(n),n}}createComputePipeline(e){const t=new Jt(e);return this.pipelineEntries.push(t),t}setCurrentPipeline(e,t){t.index!==this.currentPipelineIndex&&(e.setPipeline(t.pipeline),this.currentPipelineIndex=t.index)}setActiveBindGroups(e,t){t.forEach((s,i)=>{(!this.activeBindGroups[i]||this.activeBindGroups[i].uuid!==s.uuid||this.activeBindGroups[i].index!==s.index)&&(this.activeBindGroups[i]=s,e.setBindGroup(s.index,s.bindGroup))})}resetCurrentPipeline(){this.currentPipelineIndex=null,this.activeBindGroups=[]}}class Hs{constructor(){this.shouldWatch=!0,this.entries=[],typeof window=="object"&&"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(e=>{e.map(s=>this.entries.filter(i=>i.element.isSameNode(s.target))).flat().sort((s,i)=>i.priority-s.priority)?.forEach(s=>{s&&s.callback&&s.callback()})}))}useObserver(e=!0){this.shouldWatch=e}observe({element:e,priority:t,callback:s}){if(!e||!this.shouldWatch)return;this.resizeObserver?.observe(e);const i={element:e,priority:t,callback:s};this.entries.push(i)}unobserve(e){this.resizeObserver?.unobserve(e),this.entries=this.entries.filter(t=>!t.element.isSameNode(e))}destroy(){this.resizeObserver?.disconnect()}}const mt=new Hs;class gt{constructor({element:e=document.body,priority:t=1,onSizeChanged:s=(r=null)=>{},onPositionChanged:i=(r=null)=>{}}={}){if(typeof e=="string"){if(this.element=document.querySelector(e),!this.element){const r=typeof e=="string"?`'${e}' selector`:`${e} HTMLElement`;q(`DOMElement: corresponding ${r} not found.`)}}else this.element=e;this.priority=t,this.isResizing=!1,this.onSizeChanged=s,this.onPositionChanged=i,this.resizeManager=mt,this.resizeManager.observe({element:this.element,priority:this.priority,callback:()=>{this.setSize()}}),this.setSize()}compareBoundingRect(e,t){return!["x","y","left","top","right","bottom","width","height"].some(s=>e[s]!==t[s])}get boundingRect(){return this._boundingRect}set boundingRect(e){const t=!!this.boundingRect&&this.compareBoundingRect(e,this.boundingRect);this._boundingRect={top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.width,height:e.height,x:e.x,y:e.y},t||this.onSizeChanged(this.boundingRect)}updateScrollPosition(e={x:0,y:0}){this.isResizing||(this._boundingRect.top+=e.y,this._boundingRect.left+=e.x,(e.x||e.y)&&this.onPositionChanged(this.boundingRect))}setSize(e=null){!this.element||this.isResizing||(this.isResizing=!0,this.boundingRect=e??this.element.getBoundingClientRect(),setTimeout(()=>{this.isResizing=!1},10))}destroy(){this.resizeManager.unobserve(this.element)}}const Xs=new f,Ks=new f,yt=new f,xt=new f;class ts extends se{constructor({renderer:e}){super(),e=e&&e.renderer||e,$(e,"Scene"),this.renderer=e,this.computePassEntries=[],this.renderPassEntries={pingPong:[],renderTarget:[],screen:[]}}setMainRenderPassEntry(){this.renderPassEntries.screen.push({renderPass:this.renderer.renderPass,renderTexture:null,onBeforeRenderPass:null,onAfterRenderPass:null,element:null,stack:{unProjected:{opaque:[],transparent:[]},projected:{opaque:[],transparent:[]}}})}getRenderPassEntryLength(e){return e?e.element?e.element.visible?1:0:e.stack.unProjected.opaque.length+e.stack.unProjected.transparent.length+e.stack.projected.opaque.length+e.stack.projected.transparent.length:0}addComputePass(e){this.computePassEntries.push(e),this.computePassEntries.sort((t,s)=>t.renderOrder!==s.renderOrder?t.renderOrder-s.renderOrder:t.index-s.index)}removeComputePass(e){this.computePassEntries=this.computePassEntries.filter(t=>t.uuid!==e.uuid)}addRenderTarget(e){this.renderPassEntries.renderTarget.find(t=>t.renderPass.uuid===e.renderPass.uuid)||this.renderPassEntries.renderTarget.push({renderPass:e.renderPass,renderTexture:e.renderTexture,onBeforeRenderPass:null,onAfterRenderPass:null,element:null,stack:{unProjected:{opaque:[],transparent:[]},projected:{opaque:[],transparent:[]}}})}removeRenderTarget(e){this.renderPassEntries.renderTarget=this.renderPassEntries.renderTarget.filter(t=>t.renderPass.uuid!==e.renderPass.uuid)}getMeshProjectionStack(e){const t=e.outputTarget?this.renderPassEntries.renderTarget.find(i=>i.renderPass.uuid===e.outputTarget.renderPass.uuid):this.renderPassEntries.screen[0],{stack:s}=t;return e.material.options.rendering.useProjection?s.projected:s.unProjected}addMesh(e){const t=this.getMeshProjectionStack(e),s=e.transparent?t.transparent:t.opaque;s.push(e),s.sort((i,r)=>i.renderOrder-r.renderOrder||i.index-r.index),"parent"in e&&!e.parent&&e.material.options.rendering.useProjection&&(e.parent=this)}removeMesh(e){const t=this.getMeshProjectionStack(e);e.transparent?t.transparent=t.transparent.filter(s=>s.uuid!==e.uuid):t.opaque=t.opaque.filter(s=>s.uuid!==e.uuid),"parent"in e&&e.parent&&e.parent.object3DIndex===this.object3DIndex&&(e.parent=null)}addShaderPass(e){const t=e.inputTarget||e.outputTarget?null:(r,n)=>{e.renderTexture&&n&&r.copyTextureToTexture({texture:n},{texture:e.renderTexture.texture},[e.renderTexture.size.width,e.renderTexture.size.height]),this.renderer.postProcessingPass.setLoadOp("clear")},s=!e.outputTarget&&e.options.copyOutputToRenderTexture?(r,n)=>{e.renderTexture&&n&&r.copyTextureToTexture({texture:n},{texture:e.renderTexture.texture},[e.renderTexture.size.width,e.renderTexture.size.height])}:null,i={renderPass:e.outputTarget?e.outputTarget.renderPass:this.renderer.postProcessingPass,renderTexture:e.outputTarget?e.outputTarget.renderTexture:null,onBeforeRenderPass:t,onAfterRenderPass:s,element:e,stack:null};this.renderPassEntries.screen.push(i),this.renderPassEntries.screen.sort((r,n)=>{const a=r.element&&!r.element.outputTarget,h=r.element?r.element.renderOrder:0,u=r.element?r.element.index:0,l=n.element&&!n.element.outputTarget,d=n.element?n.element.renderOrder:0,c=n.element?n.element.index:0;return a&&!l?1:!a&&l?-1:h!==d?h-d:u-c})}removeShaderPass(e){this.renderPassEntries.screen=this.renderPassEntries.screen.filter(t=>!t.element||t.element.uuid!==e.uuid)}addPingPongPlane(e){this.renderPassEntries.pingPong.push({renderPass:e.outputTarget.renderPass,renderTexture:e.outputTarget.renderTexture,onBeforeRenderPass:null,onAfterRenderPass:(t,s)=>{t.copyTextureToTexture({texture:s},{texture:e.renderTexture.texture},[e.renderTexture.size.width,e.renderTexture.size.height])},element:e,stack:null}),this.renderPassEntries.pingPong.sort((t,s)=>t.element.renderOrder-s.element.renderOrder)}removePingPongPlane(e){this.renderPassEntries.pingPong=this.renderPassEntries.pingPong.filter(t=>t.element.uuid!==e.uuid)}getObjectRenderPassEntry(e){if(e.type==="RenderTarget")return this.renderPassEntries.renderTarget.find(t=>t.renderPass.uuid===e.renderPass.uuid);if(e.type==="PingPongPlane")return this.renderPassEntries.pingPong.find(t=>t.element.uuid===e.uuid);if(e.type==="ShaderPass")return this.renderPassEntries.screen.find(t=>t.element?.uuid===e.uuid);{const t=e.outputTarget?"renderTarget":"screen";return this.renderPassEntries[t].find(s=>[...s.stack.unProjected.opaque,...s.stack.unProjected.transparent,...s.stack.projected.opaque,...s.stack.projected.transparent].some(i=>i.uuid===e.uuid))}}sortTransparentMeshes(e){e.sort((t,s)=>{if(t.renderOrder!==s.renderOrder)return t.renderOrder-s.renderOrder;t.geometry?yt.copy(t.geometry.boundingBox.center).applyMat4(t.worldMatrix):t.worldMatrix.getTranslation(yt),s.geometry?xt.copy(s.geometry.boundingBox.center).applyMat4(s.worldMatrix):s.worldMatrix.getTranslation(xt);const i=t.geometry?t.geometry.boundingBox.radius*t.worldMatrix.getMaxScaleOnAxis():0,r=s.geometry?s.geometry.boundingBox.radius*s.worldMatrix.getMaxScaleOnAxis():0;return s.camera.worldMatrix.getTranslation(Ks).distance(xt)-r-(t.camera.worldMatrix.getTranslation(Xs).distance(yt)-i)})}renderSinglePassEntry(e,t){const s=t.renderPass.updateView(t.renderTexture?.texture);t.onBeforeRenderPass&&t.onBeforeRenderPass(e,s);const i=e.beginRenderPass(t.renderPass.descriptor);if(!this.renderer.production&&i.pushDebugGroup(t.element?`${t.element.options.label} render pass using ${t.renderPass.options.label} descriptor`:`Render stack pass using ${t.renderPass.options.label}${t.renderTexture?" onto "+t.renderTexture.options.label:""}`),t.element)t.element.render(i);else if(t.stack){for(const r of t.stack.unProjected.opaque)r.render(i);for(const r of t.stack.unProjected.transparent)r.render(i);if(t.stack.projected.opaque.length||t.stack.projected.transparent.length){for(const r of t.stack.projected.opaque)r.render(i);this.sortTransparentMeshes(t.stack.projected.transparent);for(const r of t.stack.projected.transparent)r.render(i)}}!this.renderer.production&&i.popDebugGroup(),i.end(),t.onAfterRenderPass&&t.onAfterRenderPass(e,s),this.renderer.pipelineManager.resetCurrentPipeline()}onBeforeRender(){for(let e=0,t=this.renderer.meshes.length;e{this.getRenderPassEntryLength(i)&&(i.renderPass.setLoadOp(t==="screen"&&s!==0?"load":"clear"),s++,this.renderSinglePassEntry(e,i))})}}}class Ie{constructor(e,{label:t="Render Pass",sampleCount:s=4,qualityRatio:i=1,useColorAttachments:r=!0,renderToSwapChain:n=!0,colorAttachments:a=[],useDepth:h=!0,depthTexture:u=null,depthLoadOp:l="clear",depthStoreOp:d="store",depthClearValue:c=1,depthFormat:p="depth24plus"}={}){if(e=e&&e.renderer||e,$(e,"RenderPass"),this.type="RenderPass",this.uuid=k(),this.renderer=e,r){const g={loadOp:"clear",storeOp:"store",clearValue:[0,0,0,0],targetFormat:this.renderer.options.preferredFormat};a.length?a=a.map(m=>({...g,...m})):a=[g]}this.options={label:t,sampleCount:s,qualityRatio:i,useColorAttachments:r,renderToSwapChain:n,colorAttachments:a,useDepth:h,...u!==void 0&&{depthTexture:u},depthLoadOp:l,depthStoreOp:d,depthClearValue:c,depthFormat:p},this.options.useDepth&&this.createDepthTexture(),this.viewTextures=[],this.resolveTargets=[],this.options.useColorAttachments&&(!this.options.renderToSwapChain||this.options.sampleCount>1)&&(this.createViewTextures(),this.createResolveTargets()),this.setRenderPassDescriptor()}createDepthTexture(){this.options.depthTexture?(this.depthTexture=this.options.depthTexture,this.options.depthFormat=this.options.depthTexture.options.format):this.depthTexture=new te(this.renderer,{label:this.options.label+" depth texture",name:"depthTexture",format:this.options.depthFormat,sampleCount:this.options.sampleCount,qualityRatio:this.options.qualityRatio,type:"depth",usage:["renderAttachment","textureBinding"]})}createViewTextures(){this.options.colorAttachments.forEach((e,t)=>{this.viewTextures.push(new te(this.renderer,{label:`${this.options.label} colorAttachment[${t}] view texture`,name:`colorAttachment${t}ViewTexture`,format:e.targetFormat,sampleCount:this.options.sampleCount,qualityRatio:this.options.qualityRatio,type:"texture",usage:["copySrc","copyDst","renderAttachment","textureBinding"]}))})}createResolveTargets(){this.options.sampleCount>1&&this.options.colorAttachments.forEach((e,t)=>{this.resolveTargets.push(this.options.renderToSwapChain&&t===0?null:new te(this.renderer,{label:`${this.options.label} resolve target[${t}] texture`,name:`resolveTarget${t}Texture`,format:e.targetFormat,sampleCount:1,qualityRatio:this.options.qualityRatio,type:"texture"}))})}get outputTextures(){return this.options.sampleCount>1?this.resolveTargets:this.viewTextures}setRenderPassDescriptor(){this.descriptor={label:this.options.label+" descriptor",colorAttachments:this.options.colorAttachments.map((e,t)=>({view:this.viewTextures[t]?.texture.createView({label:this.viewTextures[t]?.texture.label+" view"}),...this.resolveTargets.length&&{resolveTarget:this.resolveTargets[t]?.texture.createView({label:this.resolveTargets[t]?.texture.label+" view"})},clearValue:e.clearValue,loadOp:e.loadOp,storeOp:e.storeOp})),...this.options.useDepth&&{depthStencilAttachment:{view:this.depthTexture.texture.createView({label:this.depthTexture.texture.label+" view"}),depthClearValue:this.options.depthClearValue,depthLoadOp:this.options.depthLoadOp,depthStoreOp:this.options.depthStoreOp}}}}resize(){this.options.useDepth&&(this.descriptor.depthStencilAttachment.view=this.depthTexture.texture.createView({label:this.depthTexture.options.label+" view"})),this.viewTextures.forEach((e,t)=>{this.descriptor.colorAttachments[t].view=e.texture.createView({label:e.options.label+" view"})}),this.resolveTargets.forEach((e,t)=>{e&&(this.descriptor.colorAttachments[t].resolveTarget=e.texture.createView({label:e.options.label+" view"}))})}setLoadOp(e="clear",t=0){this.options.useColorAttachments&&(this.options.colorAttachments[t]&&(this.options.colorAttachments[t].loadOp=e),this.descriptor&&this.descriptor.colorAttachments&&this.descriptor.colorAttachments[t]&&(this.descriptor.colorAttachments[t].loadOp=e))}setDepthLoadOp(e="clear"){this.options.depthLoadOp=e,this.options.useDepth&&this.descriptor.depthStencilAttachment&&(this.descriptor.depthStencilAttachment.depthLoadOp=e)}setClearValue(e=[0,0,0,0],t=0){if(this.options.useColorAttachments){if(this.renderer.alphaMode==="premultiplied"){const s=e[3];e[0]=Math.min(e[0],s),e[1]=Math.min(e[1],s),e[2]=Math.min(e[2],s)}this.options.colorAttachments[t]&&(this.options.colorAttachments[t].clearValue=e),this.descriptor&&this.descriptor.colorAttachments&&this.descriptor.colorAttachments[t]&&(this.descriptor.colorAttachments[t].clearValue=e)}}updateView(e=null){return!this.options.colorAttachments.length||!this.options.renderToSwapChain||(e||(e=this.renderer.context.getCurrentTexture(),e.label=`${this.renderer.type} context current texture`),this.options.sampleCount>1?(this.descriptor.colorAttachments[0].view=this.viewTextures[0].texture.createView({label:this.viewTextures[0].options.label+" view"}),this.descriptor.colorAttachments[0].resolveTarget=e.createView({label:e.label+" resolve target view"})):this.descriptor.colorAttachments[0].view=e.createView({label:e.label+" view"})),e}destroy(){this.viewTextures.forEach(e=>e.destroy()),this.resolveTargets.forEach(e=>e?.destroy()),!this.options.depthTexture&&this.depthTexture&&this.depthTexture.destroy()}}var ss=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},is=(o,e,t)=>(ss(o,e,"read from private field"),t?t.call(o):e.get(o)),Zs=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},Qs=(o,e,t,s)=>(ss(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),Js=(o,e,t,s)=>({set _(i){Qs(o,e,i,t)},get _(){return is(o,e,s)}}),Ve;class We{constructor(){Zs(this,Ve,0),this.queue=[]}add(e=i=>{},{order:t=this.queue.length,once:s=!1}={}){const i={callback:e,order:t,once:s,id:is(this,Ve)};return Js(this,Ve)._++,this.queue.push(i),this.queue.sort((r,n)=>r.order-n.order),i.id}remove(e=0){this.queue=this.queue.filter(t=>t.id!==e)}execute(e){this.queue.forEach(t=>{t.callback(e),t.once&&this.remove(t.id)})}}Ve=new WeakMap;class bt{constructor({deviceManager:e,label:t="Main renderer",container:s,pixelRatio:i=1,autoResize:r=!0,preferredFormat:n,alphaMode:a="premultiplied",renderPass:h}){this._onBeforeRenderCallback=p=>{},this._onAfterRenderCallback=p=>{},this._onAfterResizeCallback=()=>{},this.type="GPURenderer",this.uuid=k(),e||q(`GPURenderer (${t}): no device manager provided: ${e}`),this.deviceManager=e,this.deviceManager.addRenderer(this),h={useDepth:!0,sampleCount:4,clearValue:[0,0,0,0],...h},n=n??this.deviceManager.gpu?.getPreferredCanvasFormat(),this.options={deviceManager:e,label:t,container:s,pixelRatio:i,autoResize:r,preferredFormat:n,alphaMode:a,renderPass:h},this.pixelRatio=i??window.devicePixelRatio??1,this.alphaMode=a;const u=s instanceof OffscreenCanvas,l=u||s instanceof HTMLCanvasElement;this.canvas=l?s:document.createElement("canvas");const{width:d,height:c}=this.canvas;this.rectBBox={width:d,height:c,top:0,left:0},this.setScene(),this.setTasksQueues(),this.setRendererObjects(),u||(this.domElement=new gt({element:s,priority:5,onSizeChanged:()=>{this.options.autoResize&&this.resize()}}),this.resize(),l||this.domElement.element.appendChild(this.canvas)),this.deviceManager.device&&this.setContext()}setSize(e=null){e={width:this.boundingRect.width,height:this.boundingRect.height,top:this.boundingRect.top,left:this.boundingRect.left,...e},this.rectBBox=e;const t={width:this.rectBBox.width,height:this.rectBBox.height};t.width*=this.pixelRatio,t.height*=this.pixelRatio,this.clampToMaxDimension(t),this.canvas.width=Math.floor(t.width),this.canvas.height=Math.floor(t.height),this.canvas.style&&(this.canvas.style.width=this.rectBBox.width+"px",this.canvas.style.height=this.rectBBox.height+"px")}setPixelRatio(e=1){this.pixelRatio=e,this.resize(this.rectBBox)}resize(e=null){this.setSize(e),this.onResize(),this._onAfterResizeCallback&&this._onAfterResizeCallback()}onResize(){this.textures.forEach(e=>{e.resize()}),this.renderPass?.resize(),this.postProcessingPass?.resize(),this.renderTargets.forEach(e=>e.resize()),this.computePasses.forEach(e=>e.resize()),this.pingPongPlanes.forEach(e=>e.resize(this.boundingRect)),this.shaderPasses.forEach(e=>e.resize(this.boundingRect)),this.resizeMeshes()}resizeMeshes(){this.meshes.forEach(e=>{e.resize(this.boundingRect)})}get boundingRect(){if(this.domElement&&this.domElement.boundingRect)return this.domElement.boundingRect;if(this.domElement){const e=this.domElement.element?.getBoundingClientRect();return{top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.width,height:e.height,x:e.x,y:e.y}}else return{top:this.rectBBox.top,right:this.rectBBox.left+this.rectBBox.width,bottom:this.rectBBox.top+this.rectBBox.height,left:this.rectBBox.left,width:this.rectBBox.width,height:this.rectBBox.height,x:this.rectBBox.left,y:this.rectBBox.top}}clampToMaxDimension(e){this.device&&(e.width=Math.min(this.device.limits.maxTextureDimension2D,e.width),e.height=Math.min(this.device.limits.maxTextureDimension2D,e.height))}get device(){return this.deviceManager.device}get ready(){return this.deviceManager.ready&&!!this.context&&!!this.canvas.width&&!!this.canvas.height}get production(){return this.deviceManager.production}get samplers(){return this.deviceManager.samplers}get buffers(){return this.deviceManager.buffers}get pipelineManager(){return this.deviceManager.pipelineManager}get deviceRenderedObjects(){return this.deviceManager.deviceRenderedObjects}configureContext(){this.context.configure({device:this.device,format:this.options.preferredFormat,alphaMode:this.alphaMode,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC|GPUTextureUsage.COPY_DST})}setContext(){this.context=this.canvas.getContext("webgpu"),this.device&&(this.configureContext(),this.setMainRenderPasses())}loseContext(){this.renderedObjects.forEach(e=>e.loseContext())}restoreContext(){this.configureContext(),this.textures.forEach(e=>{e.createTexture()}),this.renderPass?.resize(),this.postProcessingPass?.resize(),this.renderTargets.forEach(e=>e.resize()),this.renderedObjects.forEach(e=>e.restoreContext())}setMainRenderPasses(){this.renderPass=new Ie(this,{label:this.options.label+" render pass",...this.options.renderPass}),this.scene.setMainRenderPassEntry(),this.postProcessingPass=new Ie(this,{label:this.options.label+" post processing render pass",useDepth:!1,sampleCount:1})}setScene(){this.scene=new ts({renderer:this})}createBuffer(e){const t=this.deviceManager.device?.createBuffer(e.options);return this.deviceManager.addBuffer(e),t}removeBuffer(e){this.deviceManager.removeBuffer(e)}queueWriteBuffer(e,t,s){this.deviceManager.device?.queue.writeBuffer(e,t,s)}copyBufferToBuffer({srcBuffer:e,dstBuffer:t,commandEncoder:s}){if(!e||!e.GPUBuffer)return D(`${this.type} (${this.options.label}): cannot copy to buffer because the source buffer has not been provided`),null;if(t||(t=new de),t.GPUBuffer||t.createBuffer(this,{label:`GPURenderer (${this.options.label}): destination copy buffer from: ${e.options.label}`,size:e.GPUBuffer.size,usage:["copyDst","mapRead"]}),e.GPUBuffer.mapState!=="unmapped"){D(`${this.type} (${this.options.label}): Cannot copy from ${e.GPUBuffer} because it is currently mapped`);return}if(t.GPUBuffer.mapState!=="unmapped"){D(`${this.type} (${this.options.label}): Cannot copy from ${t.GPUBuffer} because it is currently mapped`);return}const i=!!s;if(i||(s=this.deviceManager.device?.createCommandEncoder({label:`${this.type} (${this.options.label}): Copy buffer command encoder`}),!this.production&&s.pushDebugGroup(`${this.type} (${this.options.label}): Copy buffer command encoder`)),s.copyBufferToBuffer(e.GPUBuffer,0,t.GPUBuffer,0,t.GPUBuffer.size),!i){!this.production&&s.popDebugGroup();const r=s.finish();this.deviceManager.device?.queue.submit([r])}return t}get bindGroups(){return this.deviceManager.bindGroups}addBindGroup(e){this.deviceManager.addBindGroup(e)}removeBindGroup(e){this.deviceManager.removeBindGroup(e)}createBindGroupLayout(e){return this.deviceManager.device?.createBindGroupLayout(e)}createBindGroup(e){return this.deviceManager.device?.createBindGroup(e)}createShaderModule(e){return this.device?.createShaderModule(e)}createPipelineLayout(e){return this.device?.createPipelineLayout(e)}createRenderPipeline(e){return this.device?.createRenderPipeline(e)}async createRenderPipelineAsync(e){return await this.device?.createRenderPipelineAsync(e)}createComputePipeline(e){return this.device?.createComputePipeline(e)}async createComputePipelineAsync(e){return await this.device?.createComputePipelineAsync(e)}get domTextures(){return this.deviceManager.domTextures}addDOMTexture(e){this.deviceManager.addDOMTexture(e)}removeDOMTexture(e){this.deviceManager.removeDOMTexture(e)}addTexture(e){this.textures.push(e)}removeTexture(e){this.textures=this.textures.filter(t=>t.uuid!==e.uuid)}createTexture(e){return this.deviceManager.device?.createTexture(e)}uploadTexture(e){this.deviceManager.uploadTexture(e)}importExternalTexture(e){return this.deviceManager.device?.importExternalTexture({source:e})}createSampler(e){const t=this.samplers.find(s=>JSON.stringify(s.options)===JSON.stringify(e.options)&&s.sampler);if(t)return t.sampler;{const{type:s,...i}=e.options,r=this.deviceManager.device?.createSampler({label:e.label,...i});return this.deviceManager.addSampler(e),r}}removeSampler(e){this.deviceManager.removeSampler(e)}setTasksQueues(){this.onBeforeCommandEncoderCreation=new We,this.onBeforeRenderScene=new We,this.onAfterRenderScene=new We,this.onAfterCommandEncoderSubmission=new We}setRendererObjects(){this.computePasses=[],this.pingPongPlanes=[],this.shaderPasses=[],this.renderTargets=[],this.meshes=[],this.textures=[]}get renderedObjects(){return[...this.computePasses,...this.meshes,...this.shaderPasses,...this.pingPongPlanes]}getObjectsByBindGroup(e){return this.deviceRenderedObjects.filter(t=>[...t.material.bindGroups,...t.material.inputsBindGroups,...t.material.clonedBindGroups].some(s=>s.uuid===e.uuid))}getObjectsByTexture(e){return this.deviceRenderedObjects.filter(t=>[...t.material.domTextures,...t.material.textures].some(s=>s.uuid===e.uuid))}onBeforeRender(e){return e&&(this._onBeforeRenderCallback=e),this}onAfterRender(e){return e&&(this._onAfterRenderCallback=e),this}onAfterResize(e){return e&&(this._onAfterResizeCallback=e),this}renderSingleComputePass(e,t){const s=e.beginComputePass();t.render(s),s.end(),t.copyBufferToResult(e)}renderSingleMesh(e,t){const s=e.beginRenderPass(this.renderPass.descriptor);t.render(s),s.end()}renderOnce(e){const t=this.device?.createCommandEncoder({label:"Render once command encoder"});!this.production&&t.pushDebugGroup("Render once command encoder"),this.pipelineManager.resetCurrentPipeline(),e.forEach(i=>{i.type==="ComputePass"?this.renderSingleComputePass(t,i):this.renderSingleMesh(t,i)}),!this.production&&t.popDebugGroup();const s=t.finish();this.device?.queue.submit([s]),this.pipelineManager.resetCurrentPipeline()}forceClear(e){const t=!!e;if(t||(e=this.device?.createCommandEncoder({label:`${this.type} (${this.options.label}): Force clear command encoder`}),!this.production&&e.pushDebugGroup(`${this.type} (${this.options.label}): Force clear command encoder`)),this.renderPass.updateView(),e.beginRenderPass(this.renderPass.descriptor).end(),!t){!this.production&&e.popDebugGroup();const i=e.finish();this.device?.queue.submit([i])}}onBeforeCommandEncoder(){this.ready&&this.onBeforeCommandEncoderCreation.execute()}onAfterCommandEncoder(){this.ready&&this.onAfterCommandEncoderSubmission.execute()}render(e){this.ready&&(this._onBeforeRenderCallback&&this._onBeforeRenderCallback(e),this.onBeforeRenderScene.execute(e),this.scene?.render(e),this._onAfterRenderCallback&&this._onAfterRenderCallback(e),this.onAfterRenderScene.execute(e))}destroy(){this.domElement?.destroy(),this.renderPass?.destroy(),this.postProcessingPass?.destroy(),this.renderTargets.forEach(e=>e.destroy()),this.renderedObjects.forEach(e=>e.remove()),this.textures.forEach(e=>e.destroy()),this.context?.unconfigure()}}class vt extends bt{constructor({deviceManager:e,label:t,container:s,pixelRatio:i=1,autoResize:r=!0,preferredFormat:n,alphaMode:a="premultiplied",renderPass:h,camera:u={}}){super({deviceManager:e,label:t,container:s,pixelRatio:i,autoResize:r,preferredFormat:n,alphaMode:a,renderPass:h}),this.type="GPUCameraRenderer",u={fov:50,near:.1,far:1e3,...u},this.options={...this.options,camera:u},this.setCamera(u),this.setCameraBindGroupAndBinding()}loseContext(){super.loseContext(),this.cameraBindGroup.loseContext()}restoreContext(){super.restoreContext(),this.cameraBindGroup?.restoreContext(),this.updateCameraBindings()}setCamera(e){const{width:t,height:s}=this.rectBBox;this.camera=new Ft({fov:e.fov,near:e.near,far:e.far,width:t,height:s,pixelRatio:this.pixelRatio,onMatricesChanged:()=>{this.onCameraMatricesChanged()}}),this.camera.parent=this.scene}onCameraMatricesChanged(){this.updateCameraBindings();for(const e of this.meshes)"modelViewMatrix"in e&&e.shouldUpdateMatrixStack()}setCameraBindGroupAndBinding(){this.cameraBufferBinding=new ce({label:"Camera",name:"camera",visibility:["vertex"],struct:{view:{type:"mat4x4f",value:this.camera.viewMatrix},projection:{type:"mat4x4f",value:this.camera.projectionMatrix},position:{type:"vec3f",value:this.camera.position.clone().setFromMatrixPosition(this.camera.worldMatrix),onBeforeUpdate:()=>{this.cameraBufferBinding.inputs.position.value.copy(this.camera.position).setFromMatrixPosition(this.camera.worldMatrix)}}}}),this.cameraBindGroup=new Re(this,{label:"Camera Uniform bind group",bindings:[this.cameraBufferBinding]}),this.cameraBindGroup.consumers.add(this.uuid)}setCameraBindGroup(){this.cameraBindGroup&&this.cameraBindGroup.shouldCreateBindGroup&&(this.cameraBindGroup.setIndex(0),this.cameraBindGroup.createBindGroup())}updateCameraBindings(){this.cameraBufferBinding?.shouldUpdateBinding("view"),this.cameraBufferBinding?.shouldUpdateBinding("projection"),this.cameraBufferBinding?.shouldUpdateBinding("position"),this.cameraBindGroup?.update()}getObjectsByBindGroup(e){return this.deviceRenderedObjects.filter(t=>[...t.material.bindGroups,...t.material.inputsBindGroups,...t.material.clonedBindGroups,this.cameraBindGroup].some(s=>s.uuid===e.uuid))}setPerspective({fov:e,near:t,far:s}={}){this.camera?.setPerspective({fov:e,near:t,far:s,width:this.rectBBox.width,height:this.rectBBox.height,pixelRatio:this.pixelRatio})}setCameraPosition(e=new f(0,0,1)){this.camera.position.copy(e)}onResize(){super.onResize(),this.setPerspective()}render(e){this.ready&&(this.setCameraBindGroup(),super.render(e))}destroy(){this.cameraBindGroup?.destroy(),super.destroy()}}class rs{constructor({label:e,production:t=!1,adapterOptions:s={},onError:i=()=>{},onDeviceLost:r=n=>{}}={}){this.index=0,this.label=e??"GPUDeviceManager instance",this.production=t,this.ready=!1,this.adapterOptions=s,this.onError=i,this.onDeviceLost=r,this.gpu=navigator.gpu,this.setPipelineManager(),this.setDeviceObjects()}async setAdapterAndDevice({adapter:e=null,device:t=null}={}){await this.setAdapter(e),await this.setDevice(t)}async init({adapter:e=null,device:t=null}={}){if(await this.setAdapterAndDevice({adapter:e,device:t}),this.device)for(const s of this.renderers)s.context||s.setContext()}async setAdapter(e=null){this.gpu||(this.onError(),q("GPUDeviceManager: WebGPU is not supported on your browser/OS. No 'gpu' object in 'navigator'.")),e?this.adapter=e:(this.adapter=await this.gpu?.requestAdapter(this.adapterOptions),this.adapter||(this.onError(),q("GPUDeviceManager: WebGPU is not supported on your browser/OS. 'requestAdapter' failed."))),this.adapter?.requestAdapterInfo().then(t=>{this.adapterInfos=t})}async setDevice(e=null){if(e)this.device=e,this.ready=!0,this.index++;else try{this.device=await this.adapter?.requestDevice({label:this.label+" "+this.index}),this.device&&(this.ready=!0,this.index++)}catch(t){this.onError(),q(`${this.label}: WebGPU is not supported on your browser/OS. 'requestDevice' failed: ${t}`)}this.device?.lost.then(t=>{D(`${this.label}: WebGPU device was lost: ${t.message}`),this.loseDevice(),t.reason!=="destroyed"&&this.onDeviceLost(t)})}setPipelineManager(){this.pipelineManager=new es}loseDevice(){this.ready=!1,this.pipelineManager.resetCurrentPipeline(),this.samplers.forEach(e=>e.sampler=null),this.renderers.forEach(e=>e.loseContext()),this.bindGroupLayouts.clear(),this.buffers.clear()}async restoreDevice({adapter:e=null,device:t=null}={}){await this.setAdapterAndDevice({adapter:e,device:t}),this.device&&(this.samplers.forEach(s=>{const{type:i,...r}=s.options;s.sampler=this.device.createSampler({label:s.label,...r})}),this.renderers.forEach(s=>s.restoreContext()))}setDeviceObjects(){this.renderers=[],this.bindGroups=new Map,this.buffers=new Map,this.bindGroupLayouts=new Map,this.bufferBindings=new Map,this.samplers=[],this.domTextures=[],this.texturesQueue=[]}addRenderer(e){this.renderers.push(e)}removeRenderer(e){this.renderers=this.renderers.filter(t=>t.uuid!==e.uuid)}get deviceRenderedObjects(){return this.renderers.map(e=>e.renderedObjects).flat()}addBindGroup(e){this.bindGroups.set(e.uuid,e)}removeBindGroup(e){this.bindGroups.delete(e.uuid)}addBuffer(e){this.buffers.set(e.uuid,e)}removeBuffer(e){this.buffers.delete(e?.uuid)}addSampler(e){this.samplers.push(e)}removeSampler(e){this.samplers=this.samplers.filter(t=>t.uuid!==e.uuid)}addDOMTexture(e){this.domTextures.push(e)}uploadTexture(e){if(e.source)try{this.device?.queue.copyExternalImageToTexture({source:e.source,flipY:e.options.flipY},{texture:e.texture,premultipliedAlpha:e.options.premultipliedAlpha},{width:e.size.width,height:e.size.height}),e.texture.mipLevelCount>1&&Pt(this.device,e.texture),this.texturesQueue.push(e)}catch({message:t}){q(`GPUDeviceManager: could not upload texture: ${e.options.name} because: ${t}`)}else this.device?.queue.writeTexture({texture:e.texture},new Uint8Array(e.options.placeholderColor),{bytesPerRow:e.size.width*4},{width:e.size.width,height:e.size.height})}removeDOMTexture(e){this.domTextures=this.domTextures.filter(t=>t.uuid!==e.uuid)}render(){if(!this.ready)return;for(const s of this.renderers)s.onBeforeCommandEncoder();const e=this.device?.createCommandEncoder({label:this.label+" command encoder"});!this.production&&e.pushDebugGroup(this.label+" command encoder: main render loop"),this.renderers.forEach(s=>s.render(e)),!this.production&&e.popDebugGroup();const t=e.finish();this.device?.queue.submit([t]),this.domTextures.filter(s=>!s.parentMesh&&s.sourceLoaded&&!s.sourceUploaded).forEach(s=>this.uploadTexture(s));for(const s of this.texturesQueue)s.sourceUploaded=!0;this.texturesQueue=[];for(const s of this.renderers)s.onAfterCommandEncoder()}destroy(){this.device?.destroy(),this.device=null,this.renderers.forEach(e=>e.destroy()),this.bindGroups.forEach(e=>e.destroy()),this.buffers.forEach(e=>e?.destroy()),this.domTextures.forEach(e=>e.destroy()),this.setDeviceObjects()}}var ns=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},os=(o,e,t)=>(ns(o,e,"read from private field"),t?t.call(o):e.get(o)),ei=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},ti=(o,e,t,s)=>(ns(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),ve;class as{constructor(e,t={}){ei(this,ve,!0),e=e&&e.renderer||e,$(e,"RenderTarget"),this.type="RenderTarget",this.renderer=e,this.uuid=k();const{label:s,colorAttachments:i,depthTexture:r,autoRender:n,...a}=t,h=r||(this.renderer.renderPass.options.sampleCount===(t.sampleCount??4)?this.renderer.renderPass.depthTexture:null);this.options={label:s,...a,...h&&{depthTexture:h},...i&&{colorAttachments:i},autoRender:n===void 0?!0:n},n!==void 0&&ti(this,ve,n),this.renderPass=new Ie(this.renderer,{label:this.options.label?`${this.options.label} Render Pass`:"Render Target Render Pass",...i&&{colorAttachments:i},depthTexture:this.options.depthTexture,...a}),a.useColorAttachments!==!1&&(this.renderTexture=new te(this.renderer,{label:this.options.label?`${this.options.label} Render Texture`:"Render Target render texture",name:"renderTexture",format:i&&i.length&&i[0].targetFormat?i[0].targetFormat:this.renderer.options.preferredFormat,...this.options.qualityRatio!==void 0&&{qualityRatio:this.options.qualityRatio},usage:["copySrc","renderAttachment","textureBinding"]})),this.addToScene()}get outputTextures(){return this.renderPass.outputTextures.length?this.renderPass.outputTextures.map((e,t)=>t===0&&this.renderPass.options.renderToSwapChain?this.renderTexture:e):this.renderTexture?[this.renderTexture]:[]}addToScene(){this.renderer.renderTargets.push(this),os(this,ve)&&this.renderer.scene.addRenderTarget(this)}removeFromScene(){os(this,ve)&&this.renderer.scene.removeRenderTarget(this),this.renderer.renderTargets=this.renderer.renderTargets.filter(e=>e.uuid!==this.uuid)}resize(){this.options.depthTexture&&(this.renderPass.options.depthTexture.texture=this.options.depthTexture.texture),this.renderPass?.resize()}remove(){this.destroy()}destroy(){this.renderer.meshes.forEach(e=>{e.outputTarget&&e.outputTarget.uuid===this.uuid&&e.setOutputTarget(null)}),this.renderer.shaderPasses.forEach(e=>{e.outputTarget&&e.outputTarget.uuid===this.uuid&&(e.outputTarget=null,e.setOutputTarget(null))}),this.removeFromScene(),this.renderPass?.destroy(),this.renderTexture?.destroy()}}ve=new WeakMap;var si=`
+`);this.shaders.compute.code=this.shaders.compute.head+this.options.shaders.compute.code}createShaders(){this.patchShaders(),this.shaders.compute.module=this.createShaderModule({code:this.shaders.compute.code,type:"compute"})}createPipelineDescriptor(){this.shaders.compute.module&&(this.descriptor={label:this.options.label,layout:this.layout,compute:{module:this.shaders.compute.module,entryPoint:this.options.shaders.compute.entryPoint}})}createComputePipeline(){if(this.shaders.compute.module)try{this.pipeline=this.renderer.createComputePipeline(this.descriptor)}catch(e){this.status.error=e,q(e)}}async createComputePipelineAsync(){if(this.shaders.compute.module)try{this.pipeline=await this.renderer.createComputePipelineAsync(this.descriptor),this.status.compiled=!0,this.status.compiling=!1,this.status.error=null}catch(e){this.status.error=e,q(e)}}async compilePipelineEntry(){super.compilePipelineEntry(),this.options.useAsync?await this.createComputePipelineAsync():(this.createComputePipeline(),this.status.compiled=!0,this.status.compiling=!1,this.status.error=null)}}class es{constructor(){this.type="PipelineManager",this.currentPipelineIndex=null,this.pipelineEntries=[],this.activeBindGroups=[]}compareShaders(e,t){return e.code===t.code&&e.entryPoint===t.entryPoint}isSameRenderPipeline(e){return this.pipelineEntries.filter(t=>t instanceof ft).find(t=>{const{options:s}=t,{shaders:i,rendering:r,cacheKey:n}=e,a=n===s.cacheKey,h=this.compareShaders(i.vertex,s.shaders.vertex),u=!i.fragment&&!s.shaders.fragment||this.compareShaders(i.fragment,s.shaders.fragment),l=Nt(r,s.rendering);return a&&!l.length&&h&&u})}createRenderPipeline(e){const{attributes:t,bindGroups:s}=e;let i=t.layoutCacheKey;s.forEach(n=>{n.bindings.forEach(a=>{i+=a.name+","}),i+=n.pipelineCacheKey});const r=this.isSameRenderPipeline({...e,cacheKey:i});if(r)return r;{const n=new ft({...e,cacheKey:i});return this.pipelineEntries.push(n),n}}createComputePipeline(e){const t=new Jt(e);return this.pipelineEntries.push(t),t}setCurrentPipeline(e,t){t.index!==this.currentPipelineIndex&&(e.setPipeline(t.pipeline),this.currentPipelineIndex=t.index)}setActiveBindGroups(e,t){t.forEach((s,i)=>{(!this.activeBindGroups[i]||this.activeBindGroups[i].uuid!==s.uuid||this.activeBindGroups[i].index!==s.index)&&(this.activeBindGroups[i]=s,e.setBindGroup(s.index,s.bindGroup))})}resetCurrentPipeline(){this.currentPipelineIndex=null,this.activeBindGroups=[]}}class Hs{constructor(){this.shouldWatch=!0,this.entries=[],typeof window=="object"&&"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(e=>{e.map(s=>this.entries.filter(i=>i.element.isSameNode(s.target))).flat().sort((s,i)=>i.priority-s.priority)?.forEach(s=>{s&&s.callback&&s.callback()})}))}useObserver(e=!0){this.shouldWatch=e}observe({element:e,priority:t,callback:s}){if(!e||!this.shouldWatch)return;this.resizeObserver?.observe(e);const i={element:e,priority:t,callback:s};this.entries.push(i)}unobserve(e){this.resizeObserver?.unobserve(e),this.entries=this.entries.filter(t=>!t.element.isSameNode(e))}destroy(){this.resizeObserver?.disconnect()}}const mt=new Hs;class gt{constructor({element:e=document.body,priority:t=1,onSizeChanged:s=(r=null)=>{},onPositionChanged:i=(r=null)=>{}}={}){if(typeof e=="string"){if(this.element=document.querySelector(e),!this.element){const r=typeof e=="string"?`'${e}' selector`:`${e} HTMLElement`;q(`DOMElement: corresponding ${r} not found.`)}}else this.element=e;this.priority=t,this.isResizing=!1,this.onSizeChanged=s,this.onPositionChanged=i,this.resizeManager=mt,this.resizeManager.observe({element:this.element,priority:this.priority,callback:()=>{this.setSize()}}),this.setSize()}compareBoundingRect(e,t){return!["x","y","left","top","right","bottom","width","height"].some(s=>e[s]!==t[s])}get boundingRect(){return this._boundingRect}set boundingRect(e){const t=!!this.boundingRect&&this.compareBoundingRect(e,this.boundingRect);this._boundingRect={top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.width,height:e.height,x:e.x,y:e.y},t||this.onSizeChanged(this.boundingRect)}updateScrollPosition(e={x:0,y:0}){this.isResizing||(this._boundingRect.top+=e.y,this._boundingRect.left+=e.x,(e.x||e.y)&&this.onPositionChanged(this.boundingRect))}setSize(e=null){!this.element||this.isResizing||(this.isResizing=!0,this.boundingRect=e??this.element.getBoundingClientRect(),setTimeout(()=>{this.isResizing=!1},10))}destroy(){this.resizeManager.unobserve(this.element)}}const Xs=new f,Ks=new f,yt=new f,xt=new f;class ts extends se{constructor({renderer:e}){super(),e=e&&e.renderer||e,$(e,"Scene"),this.renderer=e,this.computePassEntries=[],this.renderPassEntries={pingPong:[],renderTarget:[],screen:[]}}setMainRenderPassEntry(){this.renderPassEntries.screen.push({renderPass:this.renderer.renderPass,renderTexture:null,onBeforeRenderPass:null,onAfterRenderPass:null,element:null,stack:{unProjected:{opaque:[],transparent:[]},projected:{opaque:[],transparent:[]}}})}getRenderPassEntryLength(e){return e?e.element?e.element.visible?1:0:e.stack.unProjected.opaque.length+e.stack.unProjected.transparent.length+e.stack.projected.opaque.length+e.stack.projected.transparent.length:0}addComputePass(e){this.computePassEntries.push(e),this.computePassEntries.sort((t,s)=>t.renderOrder!==s.renderOrder?t.renderOrder-s.renderOrder:t.index-s.index)}removeComputePass(e){this.computePassEntries=this.computePassEntries.filter(t=>t.uuid!==e.uuid)}addRenderTarget(e){this.renderPassEntries.renderTarget.find(t=>t.renderPass.uuid===e.renderPass.uuid)||this.renderPassEntries.renderTarget.push({renderPass:e.renderPass,renderTexture:e.renderTexture,onBeforeRenderPass:null,onAfterRenderPass:null,element:null,stack:{unProjected:{opaque:[],transparent:[]},projected:{opaque:[],transparent:[]}}})}removeRenderTarget(e){this.renderPassEntries.renderTarget=this.renderPassEntries.renderTarget.filter(t=>t.renderPass.uuid!==e.renderPass.uuid)}getMeshProjectionStack(e){const t=e.outputTarget?this.renderPassEntries.renderTarget.find(i=>i.renderPass.uuid===e.outputTarget.renderPass.uuid):this.renderPassEntries.screen[0],{stack:s}=t;return e.material.options.rendering.useProjection?s.projected:s.unProjected}addMesh(e){const t=this.getMeshProjectionStack(e),s=e.transparent?t.transparent:t.opaque;s.push(e),s.sort((i,r)=>i.renderOrder-r.renderOrder||i.index-r.index),"parent"in e&&!e.parent&&e.material.options.rendering.useProjection&&(e.parent=this)}removeMesh(e){const t=this.getMeshProjectionStack(e);e.transparent?t.transparent=t.transparent.filter(s=>s.uuid!==e.uuid):t.opaque=t.opaque.filter(s=>s.uuid!==e.uuid),"parent"in e&&e.parent&&e.parent.object3DIndex===this.object3DIndex&&(e.parent=null)}addShaderPass(e){const t=e.inputTarget||e.outputTarget?null:(r,n)=>{e.renderTexture&&n&&r.copyTextureToTexture({texture:n},{texture:e.renderTexture.texture},[e.renderTexture.size.width,e.renderTexture.size.height]),this.renderer.postProcessingPass.setLoadOp("clear")},s=!e.outputTarget&&e.options.copyOutputToRenderTexture?(r,n)=>{e.renderTexture&&n&&r.copyTextureToTexture({texture:n},{texture:e.renderTexture.texture},[e.renderTexture.size.width,e.renderTexture.size.height])}:null,i={renderPass:e.outputTarget?e.outputTarget.renderPass:this.renderer.postProcessingPass,renderTexture:e.outputTarget?e.outputTarget.renderTexture:null,onBeforeRenderPass:t,onAfterRenderPass:s,element:e,stack:null};this.renderPassEntries.screen.push(i),this.renderPassEntries.screen.sort((r,n)=>{const a=r.element&&!r.element.outputTarget,h=r.element?r.element.renderOrder:0,u=r.element?r.element.index:0,l=n.element&&!n.element.outputTarget,d=n.element?n.element.renderOrder:0,c=n.element?n.element.index:0;return a&&!l?1:!a&&l?-1:h!==d?h-d:u-c})}removeShaderPass(e){this.renderPassEntries.screen=this.renderPassEntries.screen.filter(t=>!t.element||t.element.uuid!==e.uuid)}addPingPongPlane(e){this.renderPassEntries.pingPong.push({renderPass:e.outputTarget.renderPass,renderTexture:e.outputTarget.renderTexture,onBeforeRenderPass:null,onAfterRenderPass:(t,s)=>{t.copyTextureToTexture({texture:s},{texture:e.renderTexture.texture},[e.renderTexture.size.width,e.renderTexture.size.height])},element:e,stack:null}),this.renderPassEntries.pingPong.sort((t,s)=>t.element.renderOrder-s.element.renderOrder)}removePingPongPlane(e){this.renderPassEntries.pingPong=this.renderPassEntries.pingPong.filter(t=>t.element.uuid!==e.uuid)}getObjectRenderPassEntry(e){if(e.type==="RenderTarget")return this.renderPassEntries.renderTarget.find(t=>t.renderPass.uuid===e.renderPass.uuid);if(e.type==="PingPongPlane")return this.renderPassEntries.pingPong.find(t=>t.element.uuid===e.uuid);if(e.type==="ShaderPass")return this.renderPassEntries.screen.find(t=>t.element?.uuid===e.uuid);{const t=e.outputTarget?"renderTarget":"screen";return this.renderPassEntries[t].find(s=>[...s.stack.unProjected.opaque,...s.stack.unProjected.transparent,...s.stack.projected.opaque,...s.stack.projected.transparent].some(i=>i.uuid===e.uuid))}}sortTransparentMeshes(e){e.sort((t,s)=>{if(t.renderOrder!==s.renderOrder)return t.renderOrder-s.renderOrder;t.geometry?yt.copy(t.geometry.boundingBox.center).applyMat4(t.worldMatrix):t.worldMatrix.getTranslation(yt),s.geometry?xt.copy(s.geometry.boundingBox.center).applyMat4(s.worldMatrix):s.worldMatrix.getTranslation(xt);const i=t.geometry?t.geometry.boundingBox.radius*t.worldMatrix.getMaxScaleOnAxis():0,r=s.geometry?s.geometry.boundingBox.radius*s.worldMatrix.getMaxScaleOnAxis():0;return s.camera.worldMatrix.getTranslation(Ks).distance(xt)-r-(t.camera.worldMatrix.getTranslation(Xs).distance(yt)-i)})}renderSinglePassEntry(e,t){const s=t.renderPass.updateView(t.renderTexture?.texture);t.onBeforeRenderPass&&t.onBeforeRenderPass(e,s);const i=e.beginRenderPass(t.renderPass.descriptor);if(!this.renderer.production&&i.pushDebugGroup(t.element?`${t.element.options.label} render pass using ${t.renderPass.options.label} descriptor`:`Render stack pass using ${t.renderPass.options.label}${t.renderTexture?" onto "+t.renderTexture.options.label:""}`),t.element)t.element.render(i);else if(t.stack){for(const r of t.stack.unProjected.opaque)r.render(i);for(const r of t.stack.unProjected.transparent)r.render(i);if(t.stack.projected.opaque.length||t.stack.projected.transparent.length){for(const r of t.stack.projected.opaque)r.render(i);this.sortTransparentMeshes(t.stack.projected.transparent);for(const r of t.stack.projected.transparent)r.render(i)}}!this.renderer.production&&i.popDebugGroup(),i.end(),t.onAfterRenderPass&&t.onAfterRenderPass(e,s),this.renderer.pipelineManager.resetCurrentPipeline()}onBeforeRender(){for(let e=0,t=this.renderer.meshes.length;e{this.getRenderPassEntryLength(i)&&(i.renderPass.setLoadOp(t==="screen"&&s!==0?"load":"clear"),s++,this.renderSinglePassEntry(e,i))})}}}class Ve{constructor(e,{label:t="Render Pass",sampleCount:s=4,qualityRatio:i=1,useColorAttachments:r=!0,renderToSwapChain:n=!0,colorAttachments:a=[],useDepth:h=!0,depthTexture:u=null,depthLoadOp:l="clear",depthStoreOp:d="store",depthClearValue:c=1,depthFormat:p="depth24plus"}={}){if(e=e&&e.renderer||e,$(e,"RenderPass"),this.type="RenderPass",this.uuid=k(),this.renderer=e,r){const g={loadOp:"clear",storeOp:"store",clearValue:[0,0,0,0],targetFormat:this.renderer.options.preferredFormat};a.length?a=a.map(m=>({...g,...m})):a=[g]}this.options={label:t,sampleCount:s,qualityRatio:i,useColorAttachments:r,renderToSwapChain:n,colorAttachments:a,useDepth:h,...u!==void 0&&{depthTexture:u},depthLoadOp:l,depthStoreOp:d,depthClearValue:c,depthFormat:p},this.options.useDepth&&this.createDepthTexture(),this.viewTextures=[],this.resolveTargets=[],this.options.useColorAttachments&&(!this.options.renderToSwapChain||this.options.sampleCount>1)&&(this.createViewTextures(),this.createResolveTargets()),this.setRenderPassDescriptor()}createDepthTexture(){this.options.depthTexture?(this.depthTexture=this.options.depthTexture,this.options.depthFormat=this.options.depthTexture.options.format):this.depthTexture=new te(this.renderer,{label:this.options.label+" depth texture",name:"depthTexture",format:this.options.depthFormat,sampleCount:this.options.sampleCount,qualityRatio:this.options.qualityRatio,type:"depth",usage:["renderAttachment","textureBinding"]})}createViewTextures(){this.options.colorAttachments.forEach((e,t)=>{this.viewTextures.push(new te(this.renderer,{label:`${this.options.label} colorAttachment[${t}] view texture`,name:`colorAttachment${t}ViewTexture`,format:e.targetFormat,sampleCount:this.options.sampleCount,qualityRatio:this.options.qualityRatio,type:"texture",usage:["copySrc","copyDst","renderAttachment","textureBinding"]}))})}createResolveTargets(){this.options.sampleCount>1&&this.options.colorAttachments.forEach((e,t)=>{this.resolveTargets.push(this.options.renderToSwapChain&&t===0?null:new te(this.renderer,{label:`${this.options.label} resolve target[${t}] texture`,name:`resolveTarget${t}Texture`,format:e.targetFormat,sampleCount:1,qualityRatio:this.options.qualityRatio,type:"texture"}))})}get outputTextures(){return this.options.sampleCount>1?this.resolveTargets:this.viewTextures}setRenderPassDescriptor(){this.descriptor={label:this.options.label+" descriptor",colorAttachments:this.options.colorAttachments.map((e,t)=>({view:this.viewTextures[t]?.texture.createView({label:this.viewTextures[t]?.texture.label+" view"}),...this.resolveTargets.length&&{resolveTarget:this.resolveTargets[t]?.texture.createView({label:this.resolveTargets[t]?.texture.label+" view"})},clearValue:e.clearValue,loadOp:e.loadOp,storeOp:e.storeOp})),...this.options.useDepth&&{depthStencilAttachment:{view:this.depthTexture.texture.createView({label:this.depthTexture.texture.label+" view"}),depthClearValue:this.options.depthClearValue,depthLoadOp:this.options.depthLoadOp,depthStoreOp:this.options.depthStoreOp}}}}resize(){this.options.useDepth&&(this.descriptor.depthStencilAttachment.view=this.depthTexture.texture.createView({label:this.depthTexture.options.label+" view"})),this.viewTextures.forEach((e,t)=>{this.descriptor.colorAttachments[t].view=e.texture.createView({label:e.options.label+" view"})}),this.resolveTargets.forEach((e,t)=>{e&&(this.descriptor.colorAttachments[t].resolveTarget=e.texture.createView({label:e.options.label+" view"}))})}setLoadOp(e="clear",t=0){this.options.useColorAttachments&&(this.options.colorAttachments[t]&&(this.options.colorAttachments[t].loadOp=e),this.descriptor&&this.descriptor.colorAttachments&&this.descriptor.colorAttachments[t]&&(this.descriptor.colorAttachments[t].loadOp=e))}setDepthLoadOp(e="clear"){this.options.depthLoadOp=e,this.options.useDepth&&this.descriptor.depthStencilAttachment&&(this.descriptor.depthStencilAttachment.depthLoadOp=e)}setClearValue(e=[0,0,0,0],t=0){if(this.options.useColorAttachments){if(this.renderer.alphaMode==="premultiplied"){const s=e[3];e[0]=Math.min(e[0],s),e[1]=Math.min(e[1],s),e[2]=Math.min(e[2],s)}this.options.colorAttachments[t]&&(this.options.colorAttachments[t].clearValue=e),this.descriptor&&this.descriptor.colorAttachments&&this.descriptor.colorAttachments[t]&&(this.descriptor.colorAttachments[t].clearValue=e)}}updateView(e=null){return!this.options.colorAttachments.length||!this.options.renderToSwapChain||(e||(e=this.renderer.context.getCurrentTexture(),e.label=`${this.renderer.type} context current texture`),this.options.sampleCount>1?(this.descriptor.colorAttachments[0].view=this.viewTextures[0].texture.createView({label:this.viewTextures[0].options.label+" view"}),this.descriptor.colorAttachments[0].resolveTarget=e.createView({label:e.label+" resolve target view"})):this.descriptor.colorAttachments[0].view=e.createView({label:e.label+" view"})),e}destroy(){this.viewTextures.forEach(e=>e.destroy()),this.resolveTargets.forEach(e=>e?.destroy()),!this.options.depthTexture&&this.depthTexture&&this.depthTexture.destroy()}}var ss=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},is=(o,e,t)=>(ss(o,e,"read from private field"),t?t.call(o):e.get(o)),Zs=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},Qs=(o,e,t,s)=>(ss(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),Js=(o,e,t,s)=>({set _(i){Qs(o,e,i,t)},get _(){return is(o,e,s)}}),Ne;class We{constructor(){Zs(this,Ne,0),this.queue=[]}add(e=i=>{},{order:t=this.queue.length,once:s=!1}={}){const i={callback:e,order:t,once:s,id:is(this,Ne)};return Js(this,Ne)._++,this.queue.push(i),this.queue.sort((r,n)=>r.order-n.order),i.id}remove(e=0){this.queue=this.queue.filter(t=>t.id!==e)}execute(e){this.queue.forEach(t=>{t.callback(e),t.once&&this.remove(t.id)})}}Ne=new WeakMap;class bt{constructor({deviceManager:e,label:t="Main renderer",container:s,pixelRatio:i=1,autoResize:r=!0,preferredFormat:n,alphaMode:a="premultiplied",renderPass:h}){this._onBeforeRenderCallback=p=>{},this._onAfterRenderCallback=p=>{},this._onAfterResizeCallback=()=>{},this.type="GPURenderer",this.uuid=k(),e||q(`GPURenderer (${t}): no device manager provided: ${e}`),this.deviceManager=e,this.deviceManager.addRenderer(this),h={useDepth:!0,sampleCount:4,clearValue:[0,0,0,0],...h},n=n??this.deviceManager.gpu?.getPreferredCanvasFormat(),this.options={deviceManager:e,label:t,container:s,pixelRatio:i,autoResize:r,preferredFormat:n,alphaMode:a,renderPass:h},this.pixelRatio=i??window.devicePixelRatio??1,this.alphaMode=a;const u=s instanceof OffscreenCanvas,l=u||s instanceof HTMLCanvasElement;this.canvas=l?s:document.createElement("canvas");const{width:d,height:c}=this.canvas;this.rectBBox={width:d,height:c,top:0,left:0},this.setScene(),this.setTasksQueues(),this.setRendererObjects(),u||(this.domElement=new gt({element:s,priority:5,onSizeChanged:()=>{this.options.autoResize&&this.resize()}}),this.resize(),l||this.domElement.element.appendChild(this.canvas)),this.deviceManager.device&&this.setContext()}setSize(e=null){e={width:this.boundingRect.width,height:this.boundingRect.height,top:this.boundingRect.top,left:this.boundingRect.left,...e},this.rectBBox=e;const t={width:this.rectBBox.width,height:this.rectBBox.height};t.width*=this.pixelRatio,t.height*=this.pixelRatio,this.clampToMaxDimension(t),this.canvas.width=Math.floor(t.width),this.canvas.height=Math.floor(t.height),this.canvas.style&&(this.canvas.style.width=this.rectBBox.width+"px",this.canvas.style.height=this.rectBBox.height+"px")}setPixelRatio(e=1){this.pixelRatio=e,this.resize(this.rectBBox)}resize(e=null){this.setSize(e),this.onResize(),this._onAfterResizeCallback&&this._onAfterResizeCallback()}onResize(){this.textures.forEach(e=>{e.resize()}),this.renderPass?.resize(),this.postProcessingPass?.resize(),this.renderTargets.forEach(e=>e.resize()),this.computePasses.forEach(e=>e.resize()),this.pingPongPlanes.forEach(e=>e.resize(this.boundingRect)),this.shaderPasses.forEach(e=>e.resize(this.boundingRect)),this.resizeMeshes()}resizeMeshes(){this.meshes.forEach(e=>{e.resize(this.boundingRect)})}get boundingRect(){if(this.domElement&&this.domElement.boundingRect)return this.domElement.boundingRect;if(this.domElement){const e=this.domElement.element?.getBoundingClientRect();return{top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.width,height:e.height,x:e.x,y:e.y}}else return{top:this.rectBBox.top,right:this.rectBBox.left+this.rectBBox.width,bottom:this.rectBBox.top+this.rectBBox.height,left:this.rectBBox.left,width:this.rectBBox.width,height:this.rectBBox.height,x:this.rectBBox.left,y:this.rectBBox.top}}clampToMaxDimension(e){this.device&&(e.width=Math.min(this.device.limits.maxTextureDimension2D,e.width),e.height=Math.min(this.device.limits.maxTextureDimension2D,e.height))}get device(){return this.deviceManager.device}get ready(){return this.deviceManager.ready&&!!this.context&&!!this.canvas.width&&!!this.canvas.height}get production(){return this.deviceManager.production}get samplers(){return this.deviceManager.samplers}get buffers(){return this.deviceManager.buffers}get pipelineManager(){return this.deviceManager.pipelineManager}get deviceRenderedObjects(){return this.deviceManager.deviceRenderedObjects}configureContext(){this.context.configure({device:this.device,format:this.options.preferredFormat,alphaMode:this.alphaMode,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC|GPUTextureUsage.COPY_DST})}setContext(){this.context=this.canvas.getContext("webgpu"),this.device&&(this.configureContext(),this.setMainRenderPasses())}loseContext(){this.renderedObjects.forEach(e=>e.loseContext())}restoreContext(){this.configureContext(),this.textures.forEach(e=>{e.createTexture()}),this.renderPass?.resize(),this.postProcessingPass?.resize(),this.renderTargets.forEach(e=>e.resize()),this.renderedObjects.forEach(e=>e.restoreContext())}setMainRenderPasses(){this.renderPass=new Ve(this,{label:this.options.label+" render pass",...this.options.renderPass}),this.scene.setMainRenderPassEntry(),this.postProcessingPass=new Ve(this,{label:this.options.label+" post processing render pass",useDepth:!1,sampleCount:1})}setScene(){this.scene=new ts({renderer:this})}createBuffer(e){const t=this.deviceManager.device?.createBuffer(e.options);return this.deviceManager.addBuffer(e),t}removeBuffer(e){this.deviceManager.removeBuffer(e)}queueWriteBuffer(e,t,s){this.deviceManager.device?.queue.writeBuffer(e,t,s)}copyBufferToBuffer({srcBuffer:e,dstBuffer:t,commandEncoder:s}){if(!e||!e.GPUBuffer)return D(`${this.type} (${this.options.label}): cannot copy to buffer because the source buffer has not been provided`),null;if(t||(t=new pe),t.GPUBuffer||t.createBuffer(this,{label:`GPURenderer (${this.options.label}): destination copy buffer from: ${e.options.label}`,size:e.GPUBuffer.size,usage:["copyDst","mapRead"]}),e.GPUBuffer.mapState!=="unmapped"){D(`${this.type} (${this.options.label}): Cannot copy from ${e.GPUBuffer} because it is currently mapped`);return}if(t.GPUBuffer.mapState!=="unmapped"){D(`${this.type} (${this.options.label}): Cannot copy from ${t.GPUBuffer} because it is currently mapped`);return}const i=!!s;if(i||(s=this.deviceManager.device?.createCommandEncoder({label:`${this.type} (${this.options.label}): Copy buffer command encoder`}),!this.production&&s.pushDebugGroup(`${this.type} (${this.options.label}): Copy buffer command encoder`)),s.copyBufferToBuffer(e.GPUBuffer,0,t.GPUBuffer,0,t.GPUBuffer.size),!i){!this.production&&s.popDebugGroup();const r=s.finish();this.deviceManager.device?.queue.submit([r])}return t}get bindGroups(){return this.deviceManager.bindGroups}addBindGroup(e){this.deviceManager.addBindGroup(e)}removeBindGroup(e){this.deviceManager.removeBindGroup(e)}createBindGroupLayout(e){return this.deviceManager.device?.createBindGroupLayout(e)}createBindGroup(e){return this.deviceManager.device?.createBindGroup(e)}createShaderModule(e){return this.device?.createShaderModule(e)}createPipelineLayout(e){return this.device?.createPipelineLayout(e)}createRenderPipeline(e){return this.device?.createRenderPipeline(e)}async createRenderPipelineAsync(e){return await this.device?.createRenderPipelineAsync(e)}createComputePipeline(e){return this.device?.createComputePipeline(e)}async createComputePipelineAsync(e){return await this.device?.createComputePipelineAsync(e)}get domTextures(){return this.deviceManager.domTextures}addDOMTexture(e){this.deviceManager.addDOMTexture(e)}removeDOMTexture(e){this.deviceManager.removeDOMTexture(e)}addTexture(e){this.textures.push(e)}removeTexture(e){this.textures=this.textures.filter(t=>t.uuid!==e.uuid)}createTexture(e){return this.deviceManager.device?.createTexture(e)}uploadTexture(e){this.deviceManager.uploadTexture(e)}importExternalTexture(e){return this.deviceManager.device?.importExternalTexture({source:e})}createSampler(e){const t=this.samplers.find(s=>JSON.stringify(s.options)===JSON.stringify(e.options)&&s.sampler);if(t)return t.sampler;{const{type:s,...i}=e.options,r=this.deviceManager.device?.createSampler({label:e.label,...i});return this.deviceManager.addSampler(e),r}}removeSampler(e){this.deviceManager.removeSampler(e)}setTasksQueues(){this.onBeforeCommandEncoderCreation=new We,this.onBeforeRenderScene=new We,this.onAfterRenderScene=new We,this.onAfterCommandEncoderSubmission=new We}setRendererObjects(){this.computePasses=[],this.pingPongPlanes=[],this.shaderPasses=[],this.renderTargets=[],this.meshes=[],this.textures=[]}get renderedObjects(){return[...this.computePasses,...this.meshes,...this.shaderPasses,...this.pingPongPlanes]}getObjectsByBindGroup(e){return this.deviceRenderedObjects.filter(t=>[...t.material.bindGroups,...t.material.inputsBindGroups,...t.material.clonedBindGroups].some(s=>s.uuid===e.uuid))}getObjectsByTexture(e){return this.deviceRenderedObjects.filter(t=>[...t.material.domTextures,...t.material.textures].some(s=>s.uuid===e.uuid))}onBeforeRender(e){return e&&(this._onBeforeRenderCallback=e),this}onAfterRender(e){return e&&(this._onAfterRenderCallback=e),this}onAfterResize(e){return e&&(this._onAfterResizeCallback=e),this}renderSingleComputePass(e,t){const s=e.beginComputePass();t.render(s),s.end(),t.copyBufferToResult(e)}renderSingleMesh(e,t){const s=e.beginRenderPass(this.renderPass.descriptor);t.render(s),s.end()}renderOnce(e){const t=this.device?.createCommandEncoder({label:"Render once command encoder"});!this.production&&t.pushDebugGroup("Render once command encoder"),this.pipelineManager.resetCurrentPipeline(),e.forEach(i=>{i.type==="ComputePass"?this.renderSingleComputePass(t,i):this.renderSingleMesh(t,i)}),!this.production&&t.popDebugGroup();const s=t.finish();this.device?.queue.submit([s]),this.pipelineManager.resetCurrentPipeline()}forceClear(e){const t=!!e;if(t||(e=this.device?.createCommandEncoder({label:`${this.type} (${this.options.label}): Force clear command encoder`}),!this.production&&e.pushDebugGroup(`${this.type} (${this.options.label}): Force clear command encoder`)),this.renderPass.updateView(),e.beginRenderPass(this.renderPass.descriptor).end(),!t){!this.production&&e.popDebugGroup();const i=e.finish();this.device?.queue.submit([i])}}onBeforeCommandEncoder(){this.ready&&this.onBeforeCommandEncoderCreation.execute()}onAfterCommandEncoder(){this.ready&&this.onAfterCommandEncoderSubmission.execute()}render(e){this.ready&&(this._onBeforeRenderCallback&&this._onBeforeRenderCallback(e),this.onBeforeRenderScene.execute(e),this.scene?.render(e),this._onAfterRenderCallback&&this._onAfterRenderCallback(e),this.onAfterRenderScene.execute(e))}destroy(){this.domElement?.destroy(),this.renderPass?.destroy(),this.postProcessingPass?.destroy(),this.renderTargets.forEach(e=>e.destroy()),this.renderedObjects.forEach(e=>e.remove()),this.textures.forEach(e=>e.destroy()),this.context?.unconfigure()}}class vt extends bt{constructor({deviceManager:e,label:t,container:s,pixelRatio:i=1,autoResize:r=!0,preferredFormat:n,alphaMode:a="premultiplied",renderPass:h,camera:u={}}){super({deviceManager:e,label:t,container:s,pixelRatio:i,autoResize:r,preferredFormat:n,alphaMode:a,renderPass:h}),this.type="GPUCameraRenderer",u={fov:50,near:.1,far:1e3,...u},this.options={...this.options,camera:u},this.setCamera(u),this.setCameraBindGroupAndBinding()}loseContext(){super.loseContext(),this.cameraBindGroup.loseContext()}restoreContext(){super.restoreContext(),this.cameraBindGroup?.restoreContext(),this.updateCameraBindings()}setCamera(e){const{width:t,height:s}=this.rectBBox;this.camera=new Ft({fov:e.fov,near:e.near,far:e.far,width:t,height:s,pixelRatio:this.pixelRatio,onMatricesChanged:()=>{this.onCameraMatricesChanged()}}),this.camera.parent=this.scene}onCameraMatricesChanged(){this.updateCameraBindings();for(const e of this.meshes)"modelViewMatrix"in e&&e.shouldUpdateMatrixStack()}setCameraBindGroupAndBinding(){this.cameraBufferBinding=new fe({label:"Camera",name:"camera",visibility:["vertex"],struct:{view:{type:"mat4x4f",value:this.camera.viewMatrix},projection:{type:"mat4x4f",value:this.camera.projectionMatrix},position:{type:"vec3f",value:this.camera.position.clone().setFromMatrixPosition(this.camera.worldMatrix),onBeforeUpdate:()=>{this.cameraBufferBinding.inputs.position.value.copy(this.camera.position).setFromMatrixPosition(this.camera.worldMatrix)}}}}),this.cameraBindGroup=new Se(this,{label:"Camera Uniform bind group",bindings:[this.cameraBufferBinding]}),this.cameraBindGroup.consumers.add(this.uuid)}setCameraBindGroup(){this.cameraBindGroup&&this.cameraBindGroup.shouldCreateBindGroup&&(this.cameraBindGroup.setIndex(0),this.cameraBindGroup.createBindGroup())}updateCameraBindings(){this.cameraBufferBinding?.shouldUpdateBinding("view"),this.cameraBufferBinding?.shouldUpdateBinding("projection"),this.cameraBufferBinding?.shouldUpdateBinding("position"),this.cameraBindGroup?.update()}getObjectsByBindGroup(e){return this.deviceRenderedObjects.filter(t=>[...t.material.bindGroups,...t.material.inputsBindGroups,...t.material.clonedBindGroups,this.cameraBindGroup].some(s=>s.uuid===e.uuid))}setPerspective({fov:e,near:t,far:s}={}){this.camera?.setPerspective({fov:e,near:t,far:s,width:this.rectBBox.width,height:this.rectBBox.height,pixelRatio:this.pixelRatio})}setCameraPosition(e=new f(0,0,1)){this.camera.position.copy(e)}onResize(){this.setPerspective(),super.onResize()}render(e){this.ready&&(this.setCameraBindGroup(),super.render(e))}destroy(){this.cameraBindGroup?.destroy(),super.destroy()}}class rs{constructor({label:e,production:t=!1,adapterOptions:s={},onError:i=()=>{},onDeviceLost:r=n=>{}}={}){this.index=0,this.label=e??"GPUDeviceManager instance",this.production=t,this.ready=!1,this.adapterOptions=s,this.onError=i,this.onDeviceLost=r,this.gpu=navigator.gpu,this.setPipelineManager(),this.setDeviceObjects()}async setAdapterAndDevice({adapter:e=null,device:t=null}={}){await this.setAdapter(e),await this.setDevice(t)}async init({adapter:e=null,device:t=null}={}){if(await this.setAdapterAndDevice({adapter:e,device:t}),this.device)for(const s of this.renderers)s.context||s.setContext()}async setAdapter(e=null){this.gpu||(this.onError(),q("GPUDeviceManager: WebGPU is not supported on your browser/OS. No 'gpu' object in 'navigator'.")),e?this.adapter=e:(this.adapter=await this.gpu?.requestAdapter(this.adapterOptions),this.adapter||(this.onError(),q("GPUDeviceManager: WebGPU is not supported on your browser/OS. 'requestAdapter' failed."))),this.adapter?.requestAdapterInfo().then(t=>{this.adapterInfos=t})}async setDevice(e=null){if(e)this.device=e,this.ready=!0,this.index++;else try{this.device=await this.adapter?.requestDevice({label:this.label+" "+this.index}),this.device&&(this.ready=!0,this.index++)}catch(t){this.onError(),q(`${this.label}: WebGPU is not supported on your browser/OS. 'requestDevice' failed: ${t}`)}this.device?.lost.then(t=>{D(`${this.label}: WebGPU device was lost: ${t.message}`),this.loseDevice(),t.reason!=="destroyed"&&this.onDeviceLost(t)})}setPipelineManager(){this.pipelineManager=new es}loseDevice(){this.ready=!1,this.pipelineManager.resetCurrentPipeline(),this.samplers.forEach(e=>e.sampler=null),this.renderers.forEach(e=>e.loseContext()),this.bindGroupLayouts.clear(),this.buffers.clear()}async restoreDevice({adapter:e=null,device:t=null}={}){await this.setAdapterAndDevice({adapter:e,device:t}),this.device&&(this.samplers.forEach(s=>{const{type:i,...r}=s.options;s.sampler=this.device.createSampler({label:s.label,...r})}),this.renderers.forEach(s=>s.restoreContext()))}setDeviceObjects(){this.renderers=[],this.bindGroups=new Map,this.buffers=new Map,this.bindGroupLayouts=new Map,this.bufferBindings=new Map,this.samplers=[],this.domTextures=[],this.texturesQueue=[]}addRenderer(e){this.renderers.push(e)}removeRenderer(e){this.renderers=this.renderers.filter(t=>t.uuid!==e.uuid)}get deviceRenderedObjects(){return this.renderers.map(e=>e.renderedObjects).flat()}addBindGroup(e){this.bindGroups.set(e.uuid,e)}removeBindGroup(e){this.bindGroups.delete(e.uuid)}addBuffer(e){this.buffers.set(e.uuid,e)}removeBuffer(e){this.buffers.delete(e?.uuid)}addSampler(e){this.samplers.push(e)}removeSampler(e){this.samplers=this.samplers.filter(t=>t.uuid!==e.uuid)}addDOMTexture(e){this.domTextures.push(e)}uploadTexture(e){if(e.source)try{this.device?.queue.copyExternalImageToTexture({source:e.source,flipY:e.options.flipY},{texture:e.texture,premultipliedAlpha:e.options.premultipliedAlpha},{width:e.size.width,height:e.size.height}),e.texture.mipLevelCount>1&&Pt(this.device,e.texture),this.texturesQueue.push(e)}catch({message:t}){q(`GPUDeviceManager: could not upload texture: ${e.options.name} because: ${t}`)}else this.device?.queue.writeTexture({texture:e.texture},new Uint8Array(e.options.placeholderColor),{bytesPerRow:e.size.width*4},{width:e.size.width,height:e.size.height})}removeDOMTexture(e){this.domTextures=this.domTextures.filter(t=>t.uuid!==e.uuid)}render(){if(!this.ready)return;for(const s of this.renderers)s.onBeforeCommandEncoder();const e=this.device?.createCommandEncoder({label:this.label+" command encoder"});!this.production&&e.pushDebugGroup(this.label+" command encoder: main render loop"),this.renderers.forEach(s=>s.render(e)),!this.production&&e.popDebugGroup();const t=e.finish();this.device?.queue.submit([t]),this.domTextures.filter(s=>!s.parentMesh&&s.sourceLoaded&&!s.sourceUploaded).forEach(s=>this.uploadTexture(s));for(const s of this.texturesQueue)s.sourceUploaded=!0;this.texturesQueue=[];for(const s of this.renderers)s.onAfterCommandEncoder()}destroy(){this.device?.destroy(),this.device=null,this.renderers.forEach(e=>e.destroy()),this.bindGroups.forEach(e=>e.destroy()),this.buffers.forEach(e=>e?.destroy()),this.domTextures.forEach(e=>e.destroy()),this.setDeviceObjects()}}var ns=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},os=(o,e,t)=>(ns(o,e,"read from private field"),t?t.call(o):e.get(o)),ei=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},ti=(o,e,t,s)=>(ns(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),we;class as{constructor(e,t={}){ei(this,we,!0),e=e&&e.renderer||e,$(e,"RenderTarget"),this.type="RenderTarget",this.renderer=e,this.uuid=k();const{label:s,colorAttachments:i,depthTexture:r,autoRender:n,...a}=t,h=r||(this.renderer.renderPass.options.sampleCount===(t.sampleCount??4)?this.renderer.renderPass.depthTexture:null);this.options={label:s,...a,...h&&{depthTexture:h},...i&&{colorAttachments:i},autoRender:n===void 0?!0:n},n!==void 0&&ti(this,we,n),this.renderPass=new Ve(this.renderer,{label:this.options.label?`${this.options.label} Render Pass`:"Render Target Render Pass",...i&&{colorAttachments:i},depthTexture:this.options.depthTexture,...a}),a.useColorAttachments!==!1&&(this.renderTexture=new te(this.renderer,{label:this.options.label?`${this.options.label} Render Texture`:"Render Target render texture",name:"renderTexture",format:i&&i.length&&i[0].targetFormat?i[0].targetFormat:this.renderer.options.preferredFormat,...this.options.qualityRatio!==void 0&&{qualityRatio:this.options.qualityRatio},usage:["copySrc","renderAttachment","textureBinding"]})),this.addToScene()}get outputTextures(){return this.renderPass.outputTextures.length?this.renderPass.outputTextures.map((e,t)=>t===0&&this.renderPass.options.renderToSwapChain?this.renderTexture:e):this.renderTexture?[this.renderTexture]:[]}addToScene(){this.renderer.renderTargets.push(this),os(this,we)&&this.renderer.scene.addRenderTarget(this)}removeFromScene(){os(this,we)&&this.renderer.scene.removeRenderTarget(this),this.renderer.renderTargets=this.renderer.renderTargets.filter(e=>e.uuid!==this.uuid)}resize(){this.options.depthTexture&&(this.renderPass.options.depthTexture.texture=this.options.depthTexture.texture),this.renderPass?.resize()}remove(){this.destroy()}destroy(){this.renderer.meshes.forEach(e=>{e.outputTarget&&e.outputTarget.uuid===this.uuid&&e.setOutputTarget(null)}),this.renderer.shaderPasses.forEach(e=>{e.outputTarget&&e.outputTarget.uuid===this.uuid&&(e.outputTarget=null,e.setOutputTarget(null))}),this.removeFromScene(),this.renderPass?.destroy(),this.renderTexture?.destroy()}}we=new WeakMap;var si=`
struct VSOutput {
@builtin(position) position: vec4f,
@location(0) uv: vec2f,
@@ -180,7 +180,7 @@ struct VSOutput {
@fragment fn main(fsInput: VSOutput) -> @location(0) vec4f {
return textureSample(renderTexture, defaultSampler, fsInput.uv);
-}`;class ii extends dt{constructor(e,t={}){e=e&&e.renderer||e,$(e,t.label?t.label+" ShaderPass":"ShaderPass"),t.depth=!1,t.transparent=!0,t.label=t.label??"ShaderPass "+e.shaderPasses?.length,t.sampleCount=t.sampleCount?t.sampleCount:e&&e.postProcessingPass?e&&e.postProcessingPass.options.sampleCount:1,t.shaders||(t.shaders={}),t.shaders.fragment||(t.shaders.fragment={code:si,entryPoint:"main"}),t.depth=!1,super(e,t),t.inputTarget&&this.setInputTarget(t.inputTarget),this.outputTarget&&this.setRenderingOptionsForRenderPass(this.outputTarget.renderPass),this.type="ShaderPass",this.createTexture({label:t.label?`${t.label} render texture`:"Shader pass render texture",name:"renderTexture",fromTexture:this.inputTarget?this.inputTarget.renderTexture:null,usage:["copySrc","copyDst","textureBinding"],...this.outputTarget&&this.outputTarget.options.qualityRatio&&{qualityRatio:this.outputTarget.options.qualityRatio}})}cleanupRenderMaterialParameters(e){return delete e.copyOutputToRenderTexture,delete e.inputTarget,super.cleanupRenderMaterialParameters(e),e}get renderTexture(){return this.textures.find(e=>e.options.name==="renderTexture")}setInputTarget(e){if(e&&e.type!=="RenderTarget"){D(`${this.options.label??this.type}: inputTarget is not a RenderTarget: ${e}`);return}this.removeFromScene(),this.inputTarget=e,this.addToScene(),this.renderTexture&&(e?this.renderTexture.copy(this.inputTarget.renderTexture):(this.renderTexture.options.fromTexture=null,this.renderTexture.createTexture()))}addToScene(e=!1){e&&this.renderer.shaderPasses.push(this),this.setRenderingOptionsForRenderPass(this.outputTarget?this.outputTarget.renderPass:this.renderer.postProcessingPass),this.autoRender&&this.renderer.scene.addShaderPass(this)}removeFromScene(e=!1){this.outputTarget&&this.outputTarget.destroy(),this.autoRender&&this.renderer.scene.removeShaderPass(this),e&&(this.renderer.shaderPasses=this.renderer.shaderPasses.filter(t=>t.uuid!==this.uuid))}}var hs=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},pe=(o,e,t)=>(hs(o,e,"read from private field"),t?t.call(o):e.get(o)),wt=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},ri=(o,e,t,s)=>(hs(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),we,Ne,je;class us extends ct{constructor(e,t,s){super(e),wt(this,we,new f),wt(this,Ne,new f(1)),wt(this,je,1),this.boundingBox=new ie(new f(-1),new f(1)),this._onAfterDOMElementResizeCallback=()=>{},e=e&&e.renderer||e,Qe(e,"DOM3DObject"),this.renderer=e,this.size={document:{width:0,height:0,top:0,left:0},normalizedWorld:{size:new E(1),position:new E},cameraWorld:{size:new E(1),position:new E},scaledWorld:{size:new f(1),position:new f}},this.watchScroll=s.watchScroll,this.camera=this.renderer.camera,this.boundingBox.min.onChange(()=>this.updateSizeAndPosition()),this.boundingBox.max.onChange(()=>this.updateSizeAndPosition()),this.setDOMElement(t),this.renderer.domObjects.push(this)}setDOMElement(e){this.domElement=new gt({element:e,onSizeChanged:t=>this.resize(t),onPositionChanged:t=>this.onPositionChanged(t)})}onPositionChanged(e){this.watchScroll&&(this.size.document=e??this.domElement.element.getBoundingClientRect(),this.updateSizeAndPosition())}resetDOMElement(e){this.domElement&&this.domElement.destroy(),this.setDOMElement(e)}updateSizeAndPosition(){this.setWorldSizes(),this.applyPosition()}resize(e=null){!e&&(!this.domElement||this.domElement?.isResizing)||(this.size.document=e??this.domElement.element.getBoundingClientRect(),this.updateSizeAndPosition(),this._onAfterDOMElementResizeCallback&&this._onAfterDOMElementResizeCallback())}get boundingRect(){return this.domElement.boundingRect}setTransforms(){super.setTransforms(),this.transforms.origin.model.set(.5,.5,0),this.transforms.origin.world=new f,this.transforms.position.document=new f,this.documentPosition.onChange(()=>this.applyPosition()),this.transformOrigin.onChange(()=>this.setWorldTransformOrigin())}get documentPosition(){return this.transforms.position.document}set documentPosition(e){this.transforms.position.document=e,this.applyPosition()}get DOMObjectWorldScale(){return pe(this,Ne).clone()}get worldScale(){return this.DOMObjectWorldScale.multiply(this.scale)}get worldPosition(){return pe(this,we).clone()}get transformOrigin(){return this.transforms.origin.model}set transformOrigin(e){this.transforms.origin.model=e,this.setWorldTransformOrigin()}get worldTransformOrigin(){return this.transforms.origin.world}set worldTransformOrigin(e){this.transforms.origin.world=e}applyPosition(){this.applyDocumentPosition(),super.applyPosition()}applyDocumentPosition(){let e=new f(0,0,0);this.documentPosition.equals(e)||(e=this.documentToWorldSpace(this.documentPosition)),pe(this,we).set(this.position.x+this.size.scaledWorld.position.x+e.x,this.position.y+this.size.scaledWorld.position.y+e.y,this.position.z+this.size.scaledWorld.position.z+this.documentPosition.z/this.camera.CSSPerspective)}applyTransformOrigin(){this.size&&(this.setWorldTransformOrigin(),super.applyTransformOrigin())}updateModelMatrix(){this.modelMatrix.composeFromOrigin(pe(this,we),this.quaternion,this.scale,this.worldTransformOrigin),this.modelMatrix.scale(this.DOMObjectWorldScale),this.shouldUpdateWorldMatrix()}documentToWorldSpace(e=new f){return new f(e.x*this.renderer.pixelRatio/this.renderer.boundingRect.width*this.camera.screenRatio.width,-(e.y*this.renderer.pixelRatio/this.renderer.boundingRect.height)*this.camera.screenRatio.height,e.z)}computeWorldSizes(){const e=this.renderer.boundingRect,t={x:this.size.document.width/2+this.size.document.left,y:this.size.document.height/2+this.size.document.top},s={x:e.width/2+e.left,y:e.height/2+e.top},{size:i,center:r}=this.boundingBox;i.x!==0&&i.y!==0&&i.z!==0&&r.divide(i),this.size.normalizedWorld.size.set(this.size.document.width/e.width,this.size.document.height/e.height),this.size.normalizedWorld.position.set((t.x-s.x)/e.width,(s.y-t.y)/e.height),this.size.cameraWorld.size.set(this.size.normalizedWorld.size.x*this.camera.screenRatio.width,this.size.normalizedWorld.size.y*this.camera.screenRatio.height),this.size.cameraWorld.position.set(this.size.normalizedWorld.position.x*this.camera.screenRatio.width,this.size.normalizedWorld.position.y*this.camera.screenRatio.height),this.size.scaledWorld.size.set(this.size.cameraWorld.size.x/i.x,this.size.cameraWorld.size.y/i.y,1),this.size.scaledWorld.size.z=this.size.scaledWorld.size.y*(i.x/i.y/(this.size.document.width/this.size.document.height)),this.size.scaledWorld.position.set(this.size.cameraWorld.position.x-r.x*this.size.scaledWorld.size.x*i.x,this.size.cameraWorld.position.y-r.y*this.size.scaledWorld.size.y*i.y,-r.z)}setWorldSizes(){this.computeWorldSizes(),this.setWorldScale(),this.setWorldTransformOrigin()}setWorldScale(){pe(this,Ne).set(this.size.scaledWorld.size.x,this.size.scaledWorld.size.y,this.size.scaledWorld.size.z*pe(this,je)),this.shouldUpdateMatrixStack()}set DOMObjectDepthScaleRatio(e){ri(this,je,e),this.setWorldScale()}setWorldTransformOrigin(){this.transforms.origin.world=new f((this.transformOrigin.x*2-1)*this.size.scaledWorld.size.x,-(this.transformOrigin.y*2-1)*this.size.scaledWorld.size.y,this.transformOrigin.z*this.size.scaledWorld.size.z),this.shouldUpdateMatrixStack()}updateScrollPosition(e={x:0,y:0}){(e.x||e.y)&&this.domElement.updateScrollPosition(e)}onAfterDOMElementResize(e){return e&&(this._onAfterDOMElementResizeCallback=e),this}destroy(){super.destroy(),this.domElement?.destroy()}}we=new WeakMap,Ne=new WeakMap,je=new WeakMap;const ls={autoloadSources:!0,watchScroll:!0};class ds extends Kt(us){constructor(e,t,s){super(e,t,{...ls,...s}),this._onLoadingCallback=r=>{},s={...ls,...s},e=e&&e.renderer||e,Qe(e,s.label?s.label+" DOMMesh":"DOMMesh"),this.type="DOMMesh";const{autoloadSources:i}=s;this.autoloadSources=i,this.sourcesReady=!1,this.setInitSources()}get ready(){return this._ready}set ready(e){e&&!this._ready&&this.sourcesReady&&this._onReadyCallback&&this._onReadyCallback(),this._ready=e}get sourcesReady(){return this._sourcesReady}set sourcesReady(e){e&&!this._sourcesReady&&this.ready&&this._onReadyCallback&&this._onReadyCallback(),this._sourcesReady=e}addToScene(e=!1){super.addToScene(e),e&&this.renderer.domMeshes.push(this)}removeFromScene(e=!1){super.removeFromScene(e),e&&(this.renderer.domMeshes=this.renderer.domMeshes.filter(t=>t.uuid!==this.uuid))}setInitSources(){let e=0,t=0;if(this.autoloadSources){const s=this.domElement.element.querySelectorAll("img"),i=this.domElement.element.querySelectorAll("video"),r=this.domElement.element.querySelectorAll("canvas");e=s.length+i.length+r.length;const n=a=>{t++,this._onLoadingCallback&&this._onLoadingCallback(a),t===e&&(this.sourcesReady=!0)};e||(this.sourcesReady=!0),s.length&&s.forEach(a=>{const h=this.createDOMTexture({name:a.getAttribute("data-texture-name")??"texture"+this.domTextures.length});h.onSourceUploaded(()=>n(h)).loadImage(a.src)}),i.length&&i.forEach(a=>{const h=this.createDOMTexture({name:a.getAttribute("data-texture-name")??"texture"+this.domTextures.length});h.onSourceUploaded(()=>n(h)).loadVideo(a)}),r.length&&r.forEach(a=>{const h=this.createDOMTexture({name:a.getAttribute("data-texture-name")??"texture"+this.domTextures.length});h.onSourceUploaded(()=>n(h)).loadCanvas(a)})}else this.sourcesReady=!0}resetDOMElement(e){e?super.resetDOMElement(e):!e&&!this.renderer.production&&D(`${this.options.label}: You are trying to reset a ${this.type} with a HTML element that does not exist. The old HTML element will be kept instead.`)}get pixelRatioBoundingRect(){const e=window.devicePixelRatio??1,t=this.renderer.pixelRatio/e;return Object.keys(this.domElement.boundingRect).reduce((s,i)=>({...s,[i]:this.domElement.boundingRect[i]*t}),{x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0})}computeGeometry(){super.computeGeometry(),this.boundingBox.copy(this.geometry.boundingBox)}onLoading(e){return e&&(this._onLoadingCallback=e),this}}class ni extends dt{constructor(e,t={}){e=e&&e.renderer||e,$(e,t.label?t.label+" PingPongPlane":"PingPongPlane");const s=t.targets&&t.targets.length&&t.targets.map(i=>({targetFormat:i.format}));t.outputTarget=new as(e,{label:t.label?t.label+" render target":"Ping Pong render target",useDepth:!1,...s&&{colorAttachments:s}}),t.transparent=!1,t.depth=!1,t.label=t.label??"PingPongPlane "+e.pingPongPlanes?.length,super(e,t),this.type="PingPongPlane",this.createTexture({label:t.label?`${t.label} render texture`:"PingPongPlane render texture",name:"renderTexture",...t.targets&&t.targets.length&&{format:t.targets[0].format},usage:["copyDst","textureBinding"]})}get renderTexture(){return this.textures.find(e=>e.options.name==="renderTexture")}addToScene(e=!1){e&&this.renderer.pingPongPlanes.push(this),this.autoRender&&this.renderer.scene.addPingPongPlane(this)}removeFromScene(e=!1){this.outputTarget&&this.outputTarget.destroy(),this.autoRender&&this.renderer.scene.removePingPongPlane(this),e&&(this.renderer.pingPongPlanes=this.renderer.pingPongPlanes.filter(t=>t.uuid!==this.uuid))}}const oi={label:"Plane",instancesCount:1,vertexBuffers:[]};class cs extends ds{constructor(e,t,s={}){e=e&&e.renderer||e,Qe(e,s.label?s.label+" Plane":"Plane");const i={...oi,...s};let{geometry:r,widthSegments:n,heightSegments:a,...h}=i;const{instancesCount:u,vertexBuffers:l,...d}=h;if(!r||r.type!=="PlaneGeometry"){n=n??1,a=a??1;const c=n*a+n;l.length||(r=ke.getPlaneGeometryByID(c)),r?r.instancesCount=u:(r=new ot({widthSegments:n,heightSegments:a,instancesCount:u,vertexBuffers:l}),ke.addPlaneGeometry(r))}super(e,t,{geometry:r,...d}),this.type="Plane"}mouseToPlaneCoords(e=new E){const t={x:2*(e.x/this.renderer.boundingRect.width)-1,y:2*(1-e.y/this.renderer.boundingRect.height)-1},s=this.camera.position.clone(),i=new f(t.x,t.y,-.5);i.unproject(this.camera),i.sub(s).normalize();const r=new f(0,0,1);r.applyQuat(this.quaternion).normalize();const n=new f(0,0,0),a=r.dot(i);if(Math.abs(a)>=1e-4){const h=this.worldMatrix.getInverse().premultiply(this.camera.viewMatrix),u=this.worldTransformOrigin.clone().add(this.worldPosition),l=new f(this.worldPosition.x-u.x,this.worldPosition.y-u.y,this.worldPosition.z-u.z);l.applyQuat(this.quaternion),u.add(l);const d=r.dot(u.clone().sub(s))/a;n.copy(s.add(i.multiplyScalar(d))),n.applyMat4(h)}else n.set(1/0,1/0,1/0);return new E(n.x,n.y)}}class qe extends vt{constructor({deviceManager:e,label:t,container:s,pixelRatio:i=1,autoResize:r=!0,preferredFormat:n,alphaMode:a="premultiplied",renderPass:h,camera:u}){super({deviceManager:e,label:t,container:s,pixelRatio:i,autoResize:r,preferredFormat:n,alphaMode:a,renderPass:h,camera:u}),this.type="GPUCurtainsRenderer"}setRendererObjects(){super.setRendererObjects(),this.domMeshes=[],this.domObjects=[]}onCameraMatricesChanged(){super.onCameraMatricesChanged(),this.domObjects.forEach(e=>{e.updateSizeAndPosition()})}resizeMeshes(){this.meshes.forEach(e=>{"domElement"in e||e.resize(this.boundingRect)}),this.domObjects.forEach(e=>{this.onBeforeCommandEncoderCreation.add(()=>{e.domElement.isResizing||e.domElement.setSize()},{once:!0})})}}class ai{constructor({scroll:e={x:0,y:0},delta:t={x:0,y:0},shouldWatch:s=!0,onScroll:i=(r={x:0,y:0})=>{}}={}){this.scroll=e,this.delta=t,this.shouldWatch=s,this.onScroll=i,this.shouldWatch&&window.addEventListener("scroll",this.setScroll.bind(this),{passive:!0})}setScroll(){this.updateScrollValues({x:window.pageXOffset,y:window.pageYOffset})}updateScrollValues({x:e,y:t}){const s=this.scroll;this.scroll={x:e,y:t},this.delta={x:s.x-this.scroll.x,y:s.y-this.scroll.y},this.onScroll&&this.onScroll(this.delta)}destroy(){this.shouldWatch&&window.removeEventListener("scroll",this.setScroll.bind(this),{passive:!0})}}class hi{constructor({container:e,label:t,pixelRatio:s=window.devicePixelRatio??1,preferredFormat:i,alphaMode:r="premultiplied",production:n=!1,adapterOptions:a={},renderPass:h,camera:u,autoRender:l=!0,autoResize:d=!0,watchScroll:c=!0}={}){this._onRenderCallback=()=>{},this._onScrollCallback=()=>{},this._onErrorCallback=()=>{},this._onContextLostCallback=()=>{},this.type="CurtainsGPU",this.options={container:e,label:t,pixelRatio:s,camera:u,production:n,adapterOptions:a,preferredFormat:i,alphaMode:r,renderPass:h,autoRender:l,autoResize:d,watchScroll:c},this.setDeviceManager(),e&&this.setContainer(e)}setContainer(e){if(e)if(typeof e=="string")if(e=document.querySelector(e),e)this.options.container=e;else{const t=document.createElement("div");t.setAttribute("id","curtains-gpu-canvas"),document.body.appendChild(t),this.options.container=t}else e instanceof Element&&(this.options.container=e);else{const t=document.createElement("div");t.setAttribute("id","curtains-gpu-canvas"),document.body.appendChild(t),this.options.container=t}this.container=this.options.container,this.setCurtains()}setMainRenderer(){this.createCurtainsRenderer({deviceManager:this.deviceManager,label:this.options.label,container:this.options.container,pixelRatio:this.options.pixelRatio,autoResize:this.options.autoResize,preferredFormat:this.options.preferredFormat,alphaMode:this.options.alphaMode,renderPass:this.options.renderPass,camera:this.options.camera})}patchRendererOptions(e){return e.pixelRatio===void 0&&(e.pixelRatio=this.options.pixelRatio),e.autoResize===void 0&&(e.autoResize=this.options.autoResize),e}createRenderer(e){return e=this.patchRendererOptions(e),new bt({...e,deviceManager:this.deviceManager})}createCameraRenderer(e){return e=this.patchRendererOptions(e),new vt({...e,deviceManager:this.deviceManager})}createCurtainsRenderer(e){return e=this.patchRendererOptions(e),new qe({...e,deviceManager:this.deviceManager})}setDeviceManager(){this.deviceManager=new rs({label:"GPUCurtains default device",production:this.options.production,adapterOptions:this.options.adapterOptions,onError:()=>setTimeout(()=>{this._onErrorCallback&&this._onErrorCallback()},0),onDeviceLost:e=>this._onContextLostCallback&&this._onContextLostCallback(e)})}get renderers(){return this.deviceManager.renderers}get renderer(){return this.renderers[0]}async setDevice({adapter:e=null,device:t=null}={}){await this.deviceManager.init({adapter:e,device:t})}async restoreContext(){await this.deviceManager.restoreDevice()}setCurtains(){this.initEvents(),this.setMainRenderer(),this.options.autoRender&&this.animate()}get pingPongPlanes(){return this.renderers?.map(e=>e.pingPongPlanes).flat()}get shaderPasses(){return this.renderers?.map(e=>e.shaderPasses).flat()}get meshes(){return this.renderers?.map(e=>e.meshes).flat()}get domMeshes(){return this.renderers?.filter(e=>e instanceof qe).map(e=>e.domMeshes).flat()}get domObjects(){return this.renderers?.filter(e=>e instanceof qe).map(e=>e.domObjects).flat()}get planes(){return this.domMeshes.filter(e=>e instanceof cs)}get computePasses(){return this.renderers?.map(e=>e.computePasses).flat()}get camera(){return this.renderer?.camera}setPerspective({fov:e=50,near:t=.01,far:s=50}={}){this.renderer?.setPerspective({fov:e,near:t,far:s})}setCameraPosition(e=new f(0,0,1)){this.renderer?.setCameraPosition(e)}get boundingRect(){return this.renderer?.boundingRect}initScroll(){this.scrollManager=new ai({scroll:{x:window.pageXOffset,y:window.pageYOffset},delta:{x:0,y:0},shouldWatch:this.options.watchScroll,onScroll:e=>this.updateScroll(e)})}updateScroll(e={x:0,y:0}){this.domObjects.forEach(t=>{t.domElement&&t.updateScrollPosition(e)}),this._onScrollCallback&&this._onScrollCallback()}updateScrollValues(e={x:0,y:0}){this.scrollManager.updateScrollValues(e)}get scrollDelta(){return this.scrollManager.delta}get scrollValues(){return this.scrollManager.scroll}initEvents(){mt.useObserver(this.options.autoResize),this.initScroll()}onRender(e){return e&&(this._onRenderCallback=e),this}onScroll(e){return e&&(this._onScrollCallback=e),this}onError(e){return e&&(this._onErrorCallback=e),this}onContextLost(e){return e&&(this._onContextLostCallback=e),this}animate(){this.render(),this.animationFrameID=window.requestAnimationFrame(this.animate.bind(this))}render(){this._onRenderCallback&&this._onRenderCallback(),this.deviceManager.render()}destroy(){this.animationFrameID&&window.cancelAnimationFrame(this.animationFrameID),this.deviceManager.destroy(),this.scrollManager?.destroy(),mt.destroy()}}var ps=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},V=(o,e,t)=>(ps(o,e,"read from private field"),t?t.call(o):e.get(o)),Mt=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},Bt=(o,e,t,s)=>(ps(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),fe,Me,H;class ui extends se{constructor(e,{camera:t=null,element:s=null}={}){super(),Mt(this,fe,new E),Mt(this,Me,!1),this.constrainXOrbit=!0,this.constrainYOrbit=!1,this.minOrbit=new E(-Math.PI*.5,-Math.PI),this.maxOrbit=new E(Math.PI*.5,Math.PI),this.orbitStep=new E(.025),this.constrainZoom=!0,this.minZoom=0,this.maxZoom=20,this.zoomStep=.005,Mt(this,H,null),this.renderer=e,this.parent=this.renderer.scene,this.quaternion.setAxisOrder("YXZ"),this.camera=t||this.renderer.camera,this.camera.parent=this,this.element=s??this.renderer.domElement.element}set element(e){V(this,H)&&(!e||V(this,H)!==e)&&this.removeEvents(),Bt(this,H,e),e&&this.addEvents()}get element(){return V(this,H)}addEvents(){V(this,H).addEventListener("pointerdown",this.onPointerDown.bind(this)),V(this,H).addEventListener("pointermove",this.onPointerMove.bind(this)),V(this,H).addEventListener("pointerup",this.onPointerUp.bind(this)),V(this,H).addEventListener("wheel",this.onMouseWheel.bind(this))}removeEvents(){V(this,H).removeEventListener("pointerdown",this.onPointerDown.bind(this)),V(this,H).removeEventListener("pointermove",this.onPointerMove.bind(this)),V(this,H).removeEventListener("pointerup",this.onPointerUp.bind(this)),V(this,H).removeEventListener("wheel",this.onMouseWheel.bind(this))}onPointerDown(e){e.isPrimary&&Bt(this,Me,!0),V(this,fe).set(e.pageX,e.pageY)}onPointerMove(e){let t,s;document.pointerLockElement?(t=e.movementX,s=e.movementY,this.orbit(t*this.orbitStep.x,s*this.orbitStep.y)):V(this,Me)&&(t=e.pageX-V(this,fe).x,s=e.pageY-V(this,fe).y,V(this,fe).set(e.pageX,e.pageY),this.orbit(t*this.orbitStep.x,s*this.orbitStep.y))}onPointerUp(e){e.isPrimary&&Bt(this,Me,!1)}onMouseWheel(e){this.zoom(this.position.z+e.deltaY*this.zoomStep),e.preventDefault()}reset(){this.position.set(0),this.rotation.set(0)}orbit(e,t){if(e||t){if(this.rotation.y-=e,this.constrainYOrbit)this.rotation.y=Math.min(Math.max(this.rotation.y,this.minOrbit.y),this.maxOrbit.y);else{for(;this.rotation.y<-Math.PI;)this.rotation.y+=Math.PI*2;for(;this.rotation.y>=Math.PI;)this.rotation.y-=Math.PI*2}if(this.rotation.x-=t,this.constrainXOrbit)this.rotation.x=Math.min(Math.max(this.rotation.x,this.minOrbit.x),this.maxOrbit.x);else{for(;this.rotation.x<-Math.PI;)this.rotation.x+=Math.PI*2;for(;this.rotation.x>=Math.PI;)this.rotation.x-=Math.PI*2}}}zoom(e){this.position.z=e,this.constrainZoom&&(this.position.z=Math.min(Math.max(this.position.z,this.minZoom),this.maxZoom))}updateModelMatrix(){this.modelMatrix.identity().rotateFromQuaternion(this.quaternion).translate(this.position),this.shouldUpdateWorldMatrix()}destroy(){this.camera.parent=this.renderer.scene,this.parent=null,this.element=null}}fe=new WeakMap,Me=new WeakMap,H=new WeakMap;class li extends be{constructor({instancesCount:e=1,vertexBuffers:t=[],topology:s,mapBuffersAtCreation:i=!0,widthSegments:r=1,heightSegments:n=1,depthSegments:a=1}={}){super({verticesOrder:"ccw",topology:s,instancesCount:e,vertexBuffers:t,mapBuffersAtCreation:i}),this.type="BoxGeometry",r=Math.floor(r),n=Math.floor(n),a=Math.floor(a);const h=[],u=[],l=[],d=[];let c=0;const p=(g,m,y,b,M,T,v,C,B,x)=>{const R=T/B,S=v/x,z=T/2,U=v/2,K=C/2,W=B+1,Z=x+1;let G=0;const F=new f;for(let _=0;_0?1:-1,l.push(F.x,F.y,F.z),u.push(N/B),u.push(_/x),G+=1}}for(let _=0;_0)&&b.push(x,R,z),(C!==n-1||cu.arrayBuffer())}const r=[];for(let n=0;ncreateImageBitmap(await h.blob()));else{const h=e.bufferViews[a.bufferView];r[n]=i[h.buffer].then(u=>{const l=new Blob([new Uint8Array(u,h.byteOffset,h.byteLength)],{type:a.mimeType});return createImageBitmap(l)})}}return{...e,arrayBuffers:await Promise.all(i),imagesBitmaps:await Promise.all(r)}}async loadFromBinary(e,t){const s=new DataView(e,0,12),i=s.getUint32(0,!0),r=s.getUint32(4,!0),n=s.getUint32(8,!0);if(i!==ci)throw new Error("Invalid magic string in binary header.");if(r!==2)throw new Error("Incompatible version in binary header.");const a={};let h=12;for(;h{if(!e.has(o))throw TypeError("Cannot "+t)},Tt=(o,e,t)=>(ms(o,e,"read from private field"),t?t.call(o):e.get(o)),xi=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},bi=(o,e,t,s)=>(ms(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),me;const L=WebGLRenderingContext,He=new A,vi=class ne{constructor({renderer:e,gltf:t}){xi(this,me,void 0),e=e&&e.renderer||e,Be(e,"GLTFScenesManager"),this.renderer=e,this.gltf=t,bi(this,me,new Map);const s=i=>[i.node,...i.children?.map(r=>[...s(r)]).flat()].flat();this.scenesManager={node:new se,boundingBox:new ie,samplers:[],materialsTextures:[],scenes:[],meshes:[],meshesDescriptors:[],getScenesNodes:()=>this.scenesManager.scenes.map(i=>s(i)).flat()},this.createSamplers(),this.createMaterialTextures(),this.createScenes()}static getVertexAttributeParamsFromType(e){switch(e){case"VEC2":return{type:"vec2f",bufferFormat:"float32x2",size:2};case"VEC3":return{type:"vec3f",bufferFormat:"float32x3",size:3};case"VEC4":return{type:"vec4f",bufferFormat:"float32x4",size:4};case"SCALAR":default:return{type:"f32",bufferFormat:"float32",size:1}}}static getTypedArrayConstructorFromComponentType(e){switch(e){case L.BYTE:return Int8Array;case L.UNSIGNED_BYTE:return Uint8Array;case L.SHORT:return Int16Array;case L.UNSIGNED_SHORT:return Uint16Array;case L.UNSIGNED_INT:return Uint32Array;case L.FLOAT:default:return Float32Array}}static gpuPrimitiveTopologyForMode(e){switch(e){case L.TRIANGLES:return"triangle-list";case L.TRIANGLE_STRIP:return"triangle-strip";case L.LINES:return"line-list";case L.LINE_STRIP:return"line-strip";case L.POINTS:return"point-list"}}static gpuAddressModeForWrap(e){switch(e){case L.CLAMP_TO_EDGE:return"clamp-to-edge";case L.MIRRORED_REPEAT:return"mirror-repeat";default:return"repeat"}}createSamplers(){if(this.gltf.samplers)for(const[e,t]of Object.entries(this.gltf.samplers)){const s={label:"glTF sampler "+e,name:"gltfSampler"+e,addressModeU:ne.gpuAddressModeForWrap(t.wrapS),addressModeV:ne.gpuAddressModeForWrap(t.wrapT)};switch((!t.magFilter||t.magFilter===L.LINEAR)&&(s.magFilter="linear"),t.minFilter){case L.NEAREST:break;case L.LINEAR:case L.LINEAR_MIPMAP_NEAREST:s.minFilter="linear";break;case L.NEAREST_MIPMAP_LINEAR:s.mipmapFilter="linear";break;case L.LINEAR_MIPMAP_LINEAR:default:s.minFilter="linear",s.mipmapFilter="linear";break}this.scenesManager.samplers.push(new Le(this.renderer,s))}else this.scenesManager.samplers.push(new Le(this.renderer,{label:"Default sampler",name:"defaultSampler"}))}createTexture(e,t,s){const i=(()=>{switch(s){case"baseColorTexture":case"emissiveTexture":return"bgra8unorm-srgb";case"occlusionTexture":return"r8unorm";default:return"bgra8unorm"}})(),r=new te(this.renderer,{label:e.name?e.name+": "+s:s,name:s,format:i,visibility:["fragment"],generateMips:!0,fixedSize:{width:t.width,height:t.height}});return r.uploadSource({source:t}),r}createMaterialTextures(){if(this.scenesManager.materialsTextures=[],this.gltf.materials)for(const[e,t]of Object.entries(this.gltf.materials)){const s={material:e,texturesDescriptors:[]},i=r=>r.texCoord&&r.texCoord!==0?"uv"+r.texCoord:"uv";if(this.scenesManager.materialsTextures[e]=s,t.pbrMetallicRoughness){if(t.pbrMetallicRoughness.baseColorTexture&&t.pbrMetallicRoughness.baseColorTexture.index!==void 0){const r=t.pbrMetallicRoughness.baseColorTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"baseColorTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.pbrMetallicRoughness.baseColorTexture)})}if(t.pbrMetallicRoughness.metallicRoughnessTexture&&t.pbrMetallicRoughness.metallicRoughnessTexture.index!==void 0){const r=t.pbrMetallicRoughness.metallicRoughnessTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"metallicRoughnessTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.pbrMetallicRoughness.metallicRoughnessTexture)})}}if(t.normalTexture&&t.normalTexture.index!==void 0){const r=t.normalTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"normalTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.normalTexture)})}if(t.occlusionTexture&&t.occlusionTexture.index!==void 0){const r=t.occlusionTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"occlusionTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.occlusionTexture)})}if(t.emissiveTexture&&t.emissiveTexture.index!==void 0){const r=t.emissiveTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"emissiveTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.emissiveTexture)})}}}createNode(e,t){if(t.camera!==void 0)return;const s={name:t.name,node:new se,children:[]};e.children.push(s),s.node.parent=e.node,t.matrix?(s.node.modelMatrix.setFromArray(new Float32Array(t.matrix)),s.node.matrices.model.shouldUpdate=!1):(t.translation&&s.node.position.set(t.translation[0],t.translation[1],t.translation[2]),t.scale&&s.node.scale.set(t.scale[0],t.scale[1],t.scale[2]),t.rotation&&s.node.quaternion.setFromArray(new Float32Array(t.rotation)));const i=this.gltf.meshes[t.mesh];t.children&&t.children.forEach(r=>{const n=this.gltf.nodes[r];this.createNode(s,n)}),i&&i.primitives.forEach((r,n)=>{const a={parent:s.node,attributes:[],textures:[],parameters:{label:i.name?i.name+" "+n:"glTF mesh "+n},nodes:[]};let h=Tt(this,me).get(r);h||(h={instances:[],nodes:[],meshDescriptor:a},Tt(this,me).set(r,h)),h.instances.push(t),h.nodes.push(s.node)})}createScenes(){this.scenesManager.node.parent=this.renderer.scene,this.gltf.scenes.forEach(e=>{const t={name:e.name,children:[],node:new se};t.node.parent=this.scenesManager.node,this.scenesManager.scenes.push(t),e.nodes.forEach(s=>{const i=this.gltf.nodes[s];this.createNode(t,i)})}),this.scenesManager.scenes.forEach(e=>{e.node.shouldUpdateModelMatrix(),e.node.updateMatrixStack()});for(const[e,t]of Tt(this,me)){const{instances:s,nodes:i,meshDescriptor:r}=t,n=s.length;r.nodes=i,this.scenesManager.meshesDescriptors.push(r);const a=new ie,h=[];let u=null,l=null,d=0;for(const[M,T]of Object.entries(e.attributes)){const v=this.gltf.accessors[T],C=ne.getTypedArrayConstructorFromComponentType(v.componentType),B=this.gltf.bufferViews[v.bufferView],x=M==="TEXCOORD_0"?"uv":M.replace("_","").replace("TEXCOORD","uv").toLowerCase(),R=B.byteStride||0,S=v.byteOffset||0;R&&S&&S0){const M=Object.values(e.attributes).map(T=>this.gltf.accessors[T].bufferView);if(M.every(T=>T===M[0]))u=new Float32Array(this.gltf.arrayBuffers[l.buffer],l.byteOffset,Math.ceil(l.byteLength/4)*4/Float32Array.BYTES_PER_ELEMENT);else{let T=0;const v={},C=Object.values(e.attributes).reduce((B,x)=>{const R=this.gltf.accessors[x],S=ne.getVertexAttributeParamsFromType(R.type).size;return v[R.bufferView]||(v[R.bufferView]=0),v[R.bufferView]=Math.max(v[R.bufferView],R.byteOffset+S*Float32Array.BYTES_PER_ELEMENT),T+=S*Float32Array.BYTES_PER_ELEMENT,B+R.count*S},0);u=new Float32Array(Math.ceil(C/4)*4),Object.values(e.attributes).forEach(B=>{const x=this.gltf.accessors[B],R=this.gltf.bufferViews[x.bufferView],S=ne.getVertexAttributeParamsFromType(x.type).size;for(let z=0;z{let C=M.findIndex(x=>x===T.name);C=C===-1?1/0:C;let B=M.findIndex(x=>x===v.name);return B=B===-1?1/0:B,C-B})}const c={instancesCount:n,topology:ne.gpuPrimitiveTopologyForMode(e.mode),vertexBuffers:[{name:"attributes",stepMode:"vertex",attributes:h,...u&&{array:u}}]},p="indices"in e,g=p?be:nt;if(r.parameters.geometry=new g(c),r.parameters.geometry.boundingBox=a,p){const M=this.gltf.accessors[e.indices],T=this.gltf.bufferViews[M.bufferView],v=ne.getTypedArrayConstructorFromComponentType(M.componentType),C=M.byteOffset+T.byteOffset,B=this.gltf.arrayBuffers[T.buffer],x=Math.min((B.byteLength-C)/v.BYTES_PER_ELEMENT,Math.ceil(M.count/4)*4),R=v.name==="Uint8Array"?Uint16Array.from(new v(B,C,x)):new v(B,C,x);r.parameters.geometry.setIndexBuffer({bufferFormat:v.name==="Uint32Array"?"uint32":"uint16",array:R})}const m=this.scenesManager.materialsTextures[e.material];r.parameters.samplers=[],r.parameters.textures=[],m?.texturesDescriptors.forEach(M=>{r.textures.push({texture:M.texture.options.name,sampler:M.sampler.name,texCoordAttributeName:M.texCoordAttributeName}),r.parameters.samplers.find(v=>v.uuid===M.sampler.uuid)||r.parameters.samplers.push(M.sampler),r.parameters.textures.push(M.texture)});const y=this.gltf.materials&&this.gltf.materials[e.material]||{};r.parameters.cullMode=y.doubleSided?"none":"back",(y.alphaMode==="BLEND"||y.extensions&&y.extensions.KHR_materials_transmission)&&(r.parameters.transparent=!0,r.parameters.targets=[{blend:{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one"}}}]);const b={baseColorFactor:{type:"vec4f",value:y.pbrMetallicRoughness?.baseColorFactor||[1,1,1,1]},alphaCutoff:{type:"f32",value:y.alphaCutoff!==void 0?y.alphaCutoff:y.alphaMode==="MASK"?.5:0},metallicFactor:{type:"f32",value:y.pbrMetallicRoughness?.metallicFactor||0},roughnessFactor:{type:"f32",value:y.pbrMetallicRoughness?.roughnessFactor||1},normalMapScale:{type:"f32",value:y.normalTexture?.scale||1},occlusionStrength:{type:"f32",value:y.occlusionTexture?.strength||1},emissiveFactor:{type:"vec3f",value:y.emissiveFactor!==void 0?y.emissiveFactor:[1,1,1]}};if(Object.keys(b).length&&(r.parameters.uniforms={material:{visibility:["vertex","fragment"],struct:b}}),n>1){const M=new Float32Array(n*16),T=new Float32Array(n*16);for(let v=0;v",value:M},normalMatrix:{type:"array",value:T}}}}}for(let M=0;M{}){return this.scenesManager.meshesDescriptors.map(t=>{if(t.parameters.geometry){e(t);const s=new Zt(this.renderer,{...t.parameters});if(t.nodes.length>1){const i=s.updateWorldMatrix.bind(s);s.updateWorldMatrix=()=>{i(),t.nodes.forEach((r,n)=>{s.storages.instances.modelMatrix.value.set(r.worldMatrix.elements,n*16),He.copy(r.worldMatrix).invert().transpose(),s.storages.instances.normalMatrix.value.set(He.elements,n*16)}),s.storages.instances.modelMatrix.shouldUpdate=!0,s.storages.instances.normalMatrix.shouldUpdate=!0}}return s.parent=t.parent,this.scenesManager.meshes.push(s),s}})}destroy(){this.scenesManager.meshes.forEach(t=>t.remove()),this.scenesManager.meshes=[],this.scenesManager.getScenesNodes().forEach(t=>{t.destroy()}),this.scenesManager.node.destroy()}};me=new WeakMap;let wi=vi;const gs=(o,e=null)=>{const t=o.textures.find(P=>P.texture==="baseColorTexture"),s=o.textures.find(P=>P.texture==="normalTexture"),i=o.textures.find(P=>P.texture==="emissiveTexture"),r=o.textures.find(P=>P.texture==="occlusionTexture"),n=o.textures.find(P=>P.texture==="metallicRoughnessTexture"),a=o.attributes.filter(P=>P.name!=="position"),h=a.map((P,N)=>`@location(${N}) ${P.name}: ${P.type},`).join(`
+}`;class ii extends dt{constructor(e,t={}){e=e&&e.renderer||e,$(e,t.label?t.label+" ShaderPass":"ShaderPass"),t.depth=!1,t.transparent=!0,t.label=t.label??"ShaderPass "+e.shaderPasses?.length,t.sampleCount=t.sampleCount?t.sampleCount:e&&e.postProcessingPass?e&&e.postProcessingPass.options.sampleCount:1,t.shaders||(t.shaders={}),t.shaders.fragment||(t.shaders.fragment={code:si,entryPoint:"main"}),t.depth=!1,super(e,t),t.inputTarget&&this.setInputTarget(t.inputTarget),this.outputTarget&&this.setRenderingOptionsForRenderPass(this.outputTarget.renderPass),this.type="ShaderPass",this.createTexture({label:t.label?`${t.label} render texture`:"Shader pass render texture",name:"renderTexture",fromTexture:this.inputTarget?this.inputTarget.renderTexture:null,usage:["copySrc","copyDst","textureBinding"],...this.outputTarget&&this.outputTarget.options.qualityRatio&&{qualityRatio:this.outputTarget.options.qualityRatio}})}cleanupRenderMaterialParameters(e){return delete e.copyOutputToRenderTexture,delete e.inputTarget,super.cleanupRenderMaterialParameters(e),e}get renderTexture(){return this.textures.find(e=>e.options.name==="renderTexture")}setInputTarget(e){if(e&&e.type!=="RenderTarget"){D(`${this.options.label??this.type}: inputTarget is not a RenderTarget: ${e}`);return}this.removeFromScene(),this.inputTarget=e,this.addToScene(),this.renderTexture&&(e?this.renderTexture.copy(this.inputTarget.renderTexture):(this.renderTexture.options.fromTexture=null,this.renderTexture.createTexture()))}addToScene(e=!1){e&&this.renderer.shaderPasses.push(this),this.setRenderingOptionsForRenderPass(this.outputTarget?this.outputTarget.renderPass:this.renderer.postProcessingPass),this.autoRender&&this.renderer.scene.addShaderPass(this)}removeFromScene(e=!1){this.outputTarget&&this.outputTarget.destroy(),this.autoRender&&this.renderer.scene.removeShaderPass(this),e&&(this.renderer.shaderPasses=this.renderer.shaderPasses.filter(t=>t.uuid!==this.uuid))}}var hs=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},ne=(o,e,t)=>(hs(o,e,"read from private field"),t?t.call(o):e.get(o)),wt=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},ri=(o,e,t,s)=>(hs(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),Me,ce,je;class us extends ct{constructor(e,t,s={}){super(e),wt(this,Me,new f),wt(this,ce,new f(1)),wt(this,je,1),this.boundingBox=new ie(new f(-1),new f(1)),this._onAfterDOMElementResizeCallback=()=>{},e=e&&e.renderer||e,Qe(e,"DOM3DObject"),this.renderer=e,this.size={document:{width:0,height:0,top:0,left:0},normalizedWorld:{size:new E(1),position:new E},cameraWorld:{size:new E(1)},scaledWorld:{size:new f(1),position:new f}},this.watchScroll=s.watchScroll,this.camera=this.renderer.camera,this.boundingBox.min.onChange(()=>this.updateSizeAndPosition()),this.boundingBox.max.onChange(()=>this.updateSizeAndPosition()),this.setDOMElement(t),this.renderer.domObjects.push(this)}setDOMElement(e){this.domElement=new gt({element:e,onSizeChanged:t=>this.resize(t),onPositionChanged:t=>this.onPositionChanged(t)})}onPositionChanged(e){this.watchScroll&&(this.size.document=e??this.domElement.element.getBoundingClientRect(),this.updateSizeAndPosition())}resetDOMElement(e){this.domElement&&this.domElement.destroy(),this.setDOMElement(e)}updateSizeAndPosition(){this.setWorldSizes(),this.applyPosition()}resize(e=null){!e&&(!this.domElement||this.domElement?.isResizing)||(this.size.document=e??this.domElement.element.getBoundingClientRect(),this.updateSizeAndPosition(),this._onAfterDOMElementResizeCallback&&this._onAfterDOMElementResizeCallback())}get boundingRect(){return this.domElement.boundingRect}setTransforms(){super.setTransforms(),this.transforms.origin.model.set(.5,.5,0),this.transforms.origin.world=new f,this.transforms.position.document=new f,this.documentPosition.onChange(()=>this.applyPosition()),this.transformOrigin.onChange(()=>this.setWorldTransformOrigin())}get documentPosition(){return this.transforms.position.document}set documentPosition(e){this.transforms.position.document=e,this.applyPosition()}get DOMObjectWorldScale(){return ne(this,ce).clone()}get worldScale(){return this.DOMObjectWorldScale.multiply(this.scale)}get worldPosition(){return ne(this,Me).clone()}get transformOrigin(){return this.transforms.origin.model}set transformOrigin(e){this.transforms.origin.model=e,this.setWorldTransformOrigin()}get worldTransformOrigin(){return this.transforms.origin.world}set worldTransformOrigin(e){this.transforms.origin.world=e}applyPosition(){this.applyDocumentPosition(),super.applyPosition()}applyDocumentPosition(){let e=new f(0,0,0);this.documentPosition.equals(e)||(e=this.documentToWorldSpace(this.documentPosition)),ne(this,Me).set(this.position.x+this.size.scaledWorld.position.x+e.x,this.position.y+this.size.scaledWorld.position.y+e.y,this.position.z+this.size.scaledWorld.position.z+this.documentPosition.z/this.camera.CSSPerspective)}applyTransformOrigin(){this.size&&(this.setWorldTransformOrigin(),super.applyTransformOrigin())}updateModelMatrix(){this.modelMatrix.composeFromOrigin(ne(this,Me),this.quaternion,this.scale,this.worldTransformOrigin),this.modelMatrix.scale(this.DOMObjectWorldScale),this.shouldUpdateWorldMatrix()}documentToWorldSpace(e=new f){return new f(e.x*this.renderer.pixelRatio/this.renderer.boundingRect.width*this.camera.visibleSize.width,-(e.y*this.renderer.pixelRatio/this.renderer.boundingRect.height)*this.camera.visibleSize.height,e.z)}computeWorldSizes(){const e=this.renderer.boundingRect,t={x:this.size.document.width/2+this.size.document.left,y:this.size.document.height/2+this.size.document.top},s={x:e.width/2+e.left,y:e.height/2+e.top},{size:i,center:r}=this.boundingBox;i.x!==0&&i.y!==0&&i.z!==0&&r.divide(i),this.size.normalizedWorld.size.set(this.size.document.width/e.width,this.size.document.height/e.height),this.size.normalizedWorld.position.set((t.x-s.x)/e.width,(s.y-t.y)/e.height),this.size.cameraWorld.size.set(this.size.normalizedWorld.size.x*this.camera.visibleSize.width,this.size.normalizedWorld.size.y*this.camera.visibleSize.height),this.size.scaledWorld.size.set(this.size.cameraWorld.size.x/i.x,this.size.cameraWorld.size.y/i.y,1),this.size.scaledWorld.size.z=this.size.scaledWorld.size.y*(i.x/i.y/(this.size.document.width/this.size.document.height)),this.size.scaledWorld.position.set(this.size.normalizedWorld.position.x*this.camera.visibleSize.width,this.size.normalizedWorld.position.y*this.camera.visibleSize.height,0)}setWorldSizes(){this.computeWorldSizes(),this.setWorldScale(),this.setWorldTransformOrigin()}setWorldScale(){ne(this,ce).set(this.size.scaledWorld.size.x,this.size.scaledWorld.size.y,this.size.scaledWorld.size.z*ne(this,je)),this.shouldUpdateMatrixStack()}set DOMObjectDepthScaleRatio(e){ri(this,je,e),this.setWorldScale()}setWorldTransformOrigin(){this.transforms.origin.world=new f((this.transformOrigin.x*2-1)*ne(this,ce).x,-(this.transformOrigin.y*2-1)*ne(this,ce).y,this.transformOrigin.z*ne(this,ce).z),this.shouldUpdateMatrixStack()}updateScrollPosition(e={x:0,y:0}){(e.x||e.y)&&this.domElement.updateScrollPosition(e)}onAfterDOMElementResize(e){return e&&(this._onAfterDOMElementResizeCallback=e),this}destroy(){super.destroy(),this.domElement?.destroy()}}Me=new WeakMap,ce=new WeakMap,je=new WeakMap;const ls={autoloadSources:!0,watchScroll:!0};class ds extends Kt(us){constructor(e,t,s){super(e,t,{...ls,...s}),this._onLoadingCallback=r=>{},s={...ls,...s},e=e&&e.renderer||e,Qe(e,s.label?s.label+" DOMMesh":"DOMMesh"),this.type="DOMMesh";const{autoloadSources:i}=s;this.autoloadSources=i,this.sourcesReady=!1,this.setInitSources()}get ready(){return this._ready}set ready(e){e&&!this._ready&&this.sourcesReady&&this._onReadyCallback&&this._onReadyCallback(),this._ready=e}get sourcesReady(){return this._sourcesReady}set sourcesReady(e){e&&!this._sourcesReady&&this.ready&&this._onReadyCallback&&this._onReadyCallback(),this._sourcesReady=e}addToScene(e=!1){super.addToScene(e),e&&this.renderer.domMeshes.push(this)}removeFromScene(e=!1){super.removeFromScene(e),e&&(this.renderer.domMeshes=this.renderer.domMeshes.filter(t=>t.uuid!==this.uuid))}setInitSources(){let e=0,t=0;if(this.autoloadSources){const s=this.domElement.element.querySelectorAll("img"),i=this.domElement.element.querySelectorAll("video"),r=this.domElement.element.querySelectorAll("canvas");e=s.length+i.length+r.length;const n=a=>{t++,this._onLoadingCallback&&this._onLoadingCallback(a),t===e&&(this.sourcesReady=!0)};e||(this.sourcesReady=!0),s.length&&s.forEach(a=>{const h=this.createDOMTexture({name:a.getAttribute("data-texture-name")??"texture"+this.domTextures.length});h.onSourceUploaded(()=>n(h)).loadImage(a.src)}),i.length&&i.forEach(a=>{const h=this.createDOMTexture({name:a.getAttribute("data-texture-name")??"texture"+this.domTextures.length});h.onSourceUploaded(()=>n(h)).loadVideo(a)}),r.length&&r.forEach(a=>{const h=this.createDOMTexture({name:a.getAttribute("data-texture-name")??"texture"+this.domTextures.length});h.onSourceUploaded(()=>n(h)).loadCanvas(a)})}else this.sourcesReady=!0}resetDOMElement(e){e?super.resetDOMElement(e):!e&&!this.renderer.production&&D(`${this.options.label}: You are trying to reset a ${this.type} with a HTML element that does not exist. The old HTML element will be kept instead.`)}get pixelRatioBoundingRect(){const e=window.devicePixelRatio??1,t=this.renderer.pixelRatio/e;return Object.keys(this.domElement.boundingRect).reduce((s,i)=>({...s,[i]:this.domElement.boundingRect[i]*t}),{x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0})}computeGeometry(){super.computeGeometry(),this.boundingBox.copy(this.geometry.boundingBox)}onLoading(e){return e&&(this._onLoadingCallback=e),this}}const ni={label:"Plane",instancesCount:1,vertexBuffers:[]};class cs extends ds{constructor(e,t,s={}){e=e&&e.renderer||e,Qe(e,s.label?s.label+" Plane":"Plane");const i={...ni,...s};let{geometry:r,widthSegments:n,heightSegments:a,...h}=i;const{instancesCount:u,vertexBuffers:l,...d}=h;if(!r||r.type!=="PlaneGeometry"){n=n??1,a=a??1;const c=n*a+n;l.length||(r=Ie.getPlaneGeometryByID(c)),r?r.instancesCount=u:(r=new ot({widthSegments:n,heightSegments:a,instancesCount:u,vertexBuffers:l}),Ie.addPlaneGeometry(r))}super(e,t,{geometry:r,...d}),this.type="Plane"}mouseToPlaneCoords(e=new E){const t={x:2*(e.x/this.renderer.boundingRect.width)-1,y:2*(1-e.y/this.renderer.boundingRect.height)-1},s=this.camera.position.clone(),i=new f(t.x,t.y,-.5);i.unproject(this.camera),i.sub(s).normalize();const r=new f(0,0,1);r.applyQuat(this.quaternion).normalize();const n=new f(0,0,0),a=r.dot(i);if(Math.abs(a)>=1e-4){const h=this.worldMatrix.getInverse().premultiply(this.camera.viewMatrix),u=this.worldTransformOrigin.clone().add(this.worldPosition),l=new f(this.worldPosition.x-u.x,this.worldPosition.y-u.y,this.worldPosition.z-u.z);l.applyQuat(this.quaternion),u.add(l);const d=r.dot(u.clone().sub(s))/a;n.copy(s.add(i.multiplyScalar(d))),n.applyMat4(h)}else n.set(1/0,1/0,1/0);return new E(n.x,n.y)}}class qe extends vt{constructor({deviceManager:e,label:t,container:s,pixelRatio:i=1,autoResize:r=!0,preferredFormat:n,alphaMode:a="premultiplied",renderPass:h,camera:u}){super({deviceManager:e,label:t,container:s,pixelRatio:i,autoResize:r,preferredFormat:n,alphaMode:a,renderPass:h,camera:u}),this.type="GPUCurtainsRenderer"}setRendererObjects(){super.setRendererObjects(),this.domMeshes=[],this.domObjects=[]}onCameraMatricesChanged(){super.onCameraMatricesChanged(),this.domObjects.forEach(e=>{e.updateSizeAndPosition()})}resizeMeshes(){this.meshes.forEach(e=>{"domElement"in e||e.resize(this.boundingRect)}),this.domObjects.forEach(e=>{e.domElement.isResizing||e.domElement.setSize()})}}class oi{constructor({scroll:e={x:0,y:0},delta:t={x:0,y:0},shouldWatch:s=!0,onScroll:i=(r={x:0,y:0})=>{}}={}){this.scroll=e,this.delta=t,this.shouldWatch=s,this.onScroll=i,this.shouldWatch&&window.addEventListener("scroll",this.setScroll.bind(this),{passive:!0})}setScroll(){this.updateScrollValues({x:window.pageXOffset,y:window.pageYOffset})}updateScrollValues({x:e,y:t}){const s=this.scroll;this.scroll={x:e,y:t},this.delta={x:s.x-this.scroll.x,y:s.y-this.scroll.y},this.onScroll&&this.onScroll(this.delta)}destroy(){this.shouldWatch&&window.removeEventListener("scroll",this.setScroll.bind(this),{passive:!0})}}class ai{constructor({container:e,label:t,pixelRatio:s=window.devicePixelRatio??1,preferredFormat:i,alphaMode:r="premultiplied",production:n=!1,adapterOptions:a={},renderPass:h,camera:u,autoRender:l=!0,autoResize:d=!0,watchScroll:c=!0}={}){this._onRenderCallback=()=>{},this._onScrollCallback=()=>{},this._onErrorCallback=()=>{},this._onContextLostCallback=()=>{},this.type="CurtainsGPU",this.options={container:e,label:t,pixelRatio:s,camera:u,production:n,adapterOptions:a,preferredFormat:i,alphaMode:r,renderPass:h,autoRender:l,autoResize:d,watchScroll:c},this.setDeviceManager(),e&&this.setContainer(e)}setContainer(e){if(e)if(typeof e=="string")if(e=document.querySelector(e),e)this.options.container=e;else{const t=document.createElement("div");t.setAttribute("id","curtains-gpu-canvas"),document.body.appendChild(t),this.options.container=t}else e instanceof Element&&(this.options.container=e);else{const t=document.createElement("div");t.setAttribute("id","curtains-gpu-canvas"),document.body.appendChild(t),this.options.container=t}this.container=this.options.container,this.setCurtains()}setMainRenderer(){this.createCurtainsRenderer({deviceManager:this.deviceManager,label:this.options.label,container:this.options.container,pixelRatio:this.options.pixelRatio,autoResize:this.options.autoResize,preferredFormat:this.options.preferredFormat,alphaMode:this.options.alphaMode,renderPass:this.options.renderPass,camera:this.options.camera})}patchRendererOptions(e){return e.pixelRatio===void 0&&(e.pixelRatio=this.options.pixelRatio),e.autoResize===void 0&&(e.autoResize=this.options.autoResize),e}createRenderer(e){return e=this.patchRendererOptions(e),new bt({...e,deviceManager:this.deviceManager})}createCameraRenderer(e){return e=this.patchRendererOptions(e),new vt({...e,deviceManager:this.deviceManager})}createCurtainsRenderer(e){return e=this.patchRendererOptions(e),new qe({...e,deviceManager:this.deviceManager})}setDeviceManager(){this.deviceManager=new rs({label:"GPUCurtains default device",production:this.options.production,adapterOptions:this.options.adapterOptions,onError:()=>setTimeout(()=>{this._onErrorCallback&&this._onErrorCallback()},0),onDeviceLost:e=>this._onContextLostCallback&&this._onContextLostCallback(e)})}get renderers(){return this.deviceManager.renderers}get renderer(){return this.renderers[0]}async setDevice({adapter:e=null,device:t=null}={}){await this.deviceManager.init({adapter:e,device:t})}async restoreContext(){await this.deviceManager.restoreDevice()}setCurtains(){this.initEvents(),this.setMainRenderer(),this.options.autoRender&&this.animate()}get pingPongPlanes(){return this.renderers?.map(e=>e.pingPongPlanes).flat()}get shaderPasses(){return this.renderers?.map(e=>e.shaderPasses).flat()}get meshes(){return this.renderers?.map(e=>e.meshes).flat()}get domMeshes(){return this.renderers?.filter(e=>e instanceof qe).map(e=>e.domMeshes).flat()}get domObjects(){return this.renderers?.filter(e=>e instanceof qe).map(e=>e.domObjects).flat()}get planes(){return this.domMeshes.filter(e=>e instanceof cs)}get computePasses(){return this.renderers?.map(e=>e.computePasses).flat()}get camera(){return this.renderer?.camera}setPerspective({fov:e=50,near:t=.01,far:s=50}={}){this.renderer?.setPerspective({fov:e,near:t,far:s})}setCameraPosition(e=new f(0,0,1)){this.renderer?.setCameraPosition(e)}get boundingRect(){return this.renderer?.boundingRect}initScroll(){this.scrollManager=new oi({scroll:{x:window.pageXOffset,y:window.pageYOffset},delta:{x:0,y:0},shouldWatch:this.options.watchScroll,onScroll:e=>this.updateScroll(e)})}updateScroll(e={x:0,y:0}){this.domObjects.forEach(t=>{t.domElement&&t.updateScrollPosition(e)}),this._onScrollCallback&&this._onScrollCallback()}updateScrollValues(e={x:0,y:0}){this.scrollManager.updateScrollValues(e)}get scrollDelta(){return this.scrollManager.delta}get scrollValues(){return this.scrollManager.scroll}initEvents(){mt.useObserver(this.options.autoResize),this.initScroll()}onRender(e){return e&&(this._onRenderCallback=e),this}onScroll(e){return e&&(this._onScrollCallback=e),this}onError(e){return e&&(this._onErrorCallback=e),this}onContextLost(e){return e&&(this._onContextLostCallback=e),this}animate(){this.render(),this.animationFrameID=window.requestAnimationFrame(this.animate.bind(this))}render(){this._onRenderCallback&&this._onRenderCallback(),this.deviceManager.render()}destroy(){this.animationFrameID&&window.cancelAnimationFrame(this.animationFrameID),this.deviceManager.destroy(),this.scrollManager?.destroy(),mt.destroy()}}var ps=(o,e,t)=>{if(!e.has(o))throw TypeError("Cannot "+t)},V=(o,e,t)=>(ps(o,e,"read from private field"),t?t.call(o):e.get(o)),Mt=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},Bt=(o,e,t,s)=>(ps(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),me,Be,H;class hi extends se{constructor(e,{camera:t=null,element:s=null}={}){super(),Mt(this,me,new E),Mt(this,Be,!1),this.constrainXOrbit=!0,this.constrainYOrbit=!1,this.minOrbit=new E(-Math.PI*.5,-Math.PI),this.maxOrbit=new E(Math.PI*.5,Math.PI),this.orbitStep=new E(.025),this.constrainZoom=!0,this.minZoom=0,this.maxZoom=20,this.zoomStep=.005,Mt(this,H,null),this.renderer=e,this.parent=this.renderer.scene,this.quaternion.setAxisOrder("YXZ"),this.camera=t||this.renderer.camera,this.camera.parent=this,this.element=s??this.renderer.domElement.element}set element(e){V(this,H)&&(!e||V(this,H)!==e)&&this.removeEvents(),Bt(this,H,e),e&&this.addEvents()}get element(){return V(this,H)}addEvents(){V(this,H).addEventListener("pointerdown",this.onPointerDown.bind(this)),V(this,H).addEventListener("pointermove",this.onPointerMove.bind(this)),V(this,H).addEventListener("pointerup",this.onPointerUp.bind(this)),V(this,H).addEventListener("wheel",this.onMouseWheel.bind(this))}removeEvents(){V(this,H).removeEventListener("pointerdown",this.onPointerDown.bind(this)),V(this,H).removeEventListener("pointermove",this.onPointerMove.bind(this)),V(this,H).removeEventListener("pointerup",this.onPointerUp.bind(this)),V(this,H).removeEventListener("wheel",this.onMouseWheel.bind(this))}onPointerDown(e){e.isPrimary&&Bt(this,Be,!0),V(this,me).set(e.pageX,e.pageY)}onPointerMove(e){let t,s;document.pointerLockElement?(t=e.movementX,s=e.movementY,this.orbit(t*this.orbitStep.x,s*this.orbitStep.y)):V(this,Be)&&(t=e.pageX-V(this,me).x,s=e.pageY-V(this,me).y,V(this,me).set(e.pageX,e.pageY),this.orbit(t*this.orbitStep.x,s*this.orbitStep.y))}onPointerUp(e){e.isPrimary&&Bt(this,Be,!1)}onMouseWheel(e){this.zoom(this.position.z+e.deltaY*this.zoomStep),e.preventDefault()}reset(){this.position.set(0),this.rotation.set(0)}orbit(e,t){if(e||t){if(this.rotation.y-=e,this.constrainYOrbit)this.rotation.y=Math.min(Math.max(this.rotation.y,this.minOrbit.y),this.maxOrbit.y);else{for(;this.rotation.y<-Math.PI;)this.rotation.y+=Math.PI*2;for(;this.rotation.y>=Math.PI;)this.rotation.y-=Math.PI*2}if(this.rotation.x-=t,this.constrainXOrbit)this.rotation.x=Math.min(Math.max(this.rotation.x,this.minOrbit.x),this.maxOrbit.x);else{for(;this.rotation.x<-Math.PI;)this.rotation.x+=Math.PI*2;for(;this.rotation.x>=Math.PI;)this.rotation.x-=Math.PI*2}}}zoom(e){this.position.z=e,this.constrainZoom&&(this.position.z=Math.min(Math.max(this.position.z,this.minZoom),this.maxZoom))}updateModelMatrix(){this.modelMatrix.identity().rotateFromQuaternion(this.quaternion).translate(this.position),this.shouldUpdateWorldMatrix()}destroy(){this.camera.parent=this.renderer.scene,this.parent=null,this.element=null}}me=new WeakMap,Be=new WeakMap,H=new WeakMap;class ui extends ve{constructor({instancesCount:e=1,vertexBuffers:t=[],topology:s,mapBuffersAtCreation:i=!0,widthSegments:r=1,heightSegments:n=1,depthSegments:a=1}={}){super({verticesOrder:"ccw",topology:s,instancesCount:e,vertexBuffers:t,mapBuffersAtCreation:i}),this.type="BoxGeometry",r=Math.floor(r),n=Math.floor(n),a=Math.floor(a);const h=[],u=[],l=[],d=[];let c=0;const p=(g,m,y,b,M,T,v,C,B,x)=>{const R=T/B,S=v/x,z=T/2,U=v/2,K=C/2,N=B+1,Z=x+1;let G=0;const F=new f;for(let _=0;_0?1:-1,l.push(F.x,F.y,F.z),u.push(W/B),u.push(_/x),G+=1}}for(let _=0;_0)&&b.push(x,R,z),(C!==n-1||c({targetFormat:i.format}));t.outputTarget=new as(e,{label:t.label?t.label+" render target":"Ping Pong render target",useDepth:!1,...s&&{colorAttachments:s}}),t.transparent=!1,t.depth=!1,t.label=t.label??"PingPongPlane "+e.pingPongPlanes?.length,super(e,t),this.type="PingPongPlane",this.createTexture({label:t.label?`${t.label} render texture`:"PingPongPlane render texture",name:"renderTexture",...t.targets&&t.targets.length&&{format:t.targets[0].format},usage:["copyDst","textureBinding"]})}get renderTexture(){return this.textures.find(e=>e.options.name==="renderTexture")}addToScene(e=!1){e&&this.renderer.pingPongPlanes.push(this),this.autoRender&&this.renderer.scene.addPingPongPlane(this)}removeFromScene(e=!1){this.outputTarget&&this.outputTarget.destroy(),this.autoRender&&this.renderer.scene.removePingPongPlane(this),e&&(this.renderer.pingPongPlanes=this.renderer.pingPongPlanes.filter(t=>t.uuid!==this.uuid))}}const fs=WebGLRenderingContext,ci=1179937895,Ct={JSON:1313821514,BIN:5130562},pi=[0,0,0],fi=[0,0,0,1],mi=[1,1,1],gi=new RegExp(`^${window.location.protocol}`,"i"),yi=/^data:/;class Ye{constructor(){this.gltf=null}static resolveUri(e,t){return e.match(gi)||e.match(yi)?e:t+e}async loadFromUrl(e){const t=e.lastIndexOf("/"),s=t!==0?e.substring(0,t+1):"",i=await fetch(e);if(e.endsWith(".gltf"))return this.loadFromJson(await i.json(),s);if(e.endsWith(".glb"))return this.loadFromBinary(await i.arrayBuffer(),s);throw new Error("Unrecognized file extension")}async loadFromJsonBase(e,t,s=null){if(!t)throw new Error("baseUrl must be specified.");if(!e.asset)throw new Error("Missing asset description.");if(e.asset.minVersion!=="2.0"&&e.asset.version!=="2.0")throw new Error("Incompatible asset version.");for(const n of e.accessors)n.byteOffset=n.byteOffset??0,n.normalized=n.normalized??!1;for(const n of e.bufferViews)n.byteOffset=n.byteOffset??0;for(const n of e.nodes)n.matrix||(n.rotation=n.rotation??fi,n.scale=n.scale??mi,n.translation=n.translation??pi);if(e.samplers)for(const n of e.samplers)n.wrapS=n.wrapS??fs.REPEAT,n.wrapT=n.wrapT??fs.REPEAT;const i=[];if(s)i.push(Promise.resolve(s));else for(const n in e.buffers){const a=e.buffers[n],h=Ye.resolveUri(a.uri,t);i[n]=fetch(h).then(u=>u.arrayBuffer())}const r=[];for(let n=0;ncreateImageBitmap(await h.blob()));else{const h=e.bufferViews[a.bufferView];r[n]=i[h.buffer].then(u=>{const l=new Blob([new Uint8Array(u,h.byteOffset,h.byteLength)],{type:a.mimeType});return createImageBitmap(l)})}}return{...e,arrayBuffers:await Promise.all(i),imagesBitmaps:await Promise.all(r)}}async loadFromBinary(e,t){const s=new DataView(e,0,12),i=s.getUint32(0,!0),r=s.getUint32(4,!0),n=s.getUint32(8,!0);if(i!==ci)throw new Error("Invalid magic string in binary header.");if(r!==2)throw new Error("Incompatible version in binary header.");const a={};let h=12;for(;h{if(!e.has(o))throw TypeError("Cannot "+t)},Tt=(o,e,t)=>(ms(o,e,"read from private field"),t?t.call(o):e.get(o)),xi=(o,e,t)=>{if(e.has(o))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(o):e.set(o,t)},bi=(o,e,t,s)=>(ms(o,e,"write to private field"),s?s.call(o,t):e.set(o,t),t),ge;const L=WebGLRenderingContext,He=new A,vi=class oe{constructor({renderer:e,gltf:t}){xi(this,ge,void 0),e=e&&e.renderer||e,Ce(e,"GLTFScenesManager"),this.renderer=e,this.gltf=t,bi(this,ge,new Map);const s=i=>[i.node,...i.children?.map(r=>[...s(r)]).flat()].flat();this.scenesManager={node:new se,boundingBox:new ie,samplers:[],materialsTextures:[],scenes:[],meshes:[],meshesDescriptors:[],getScenesNodes:()=>this.scenesManager.scenes.map(i=>s(i)).flat()},this.createSamplers(),this.createMaterialTextures(),this.createScenes()}static getVertexAttributeParamsFromType(e){switch(e){case"VEC2":return{type:"vec2f",bufferFormat:"float32x2",size:2};case"VEC3":return{type:"vec3f",bufferFormat:"float32x3",size:3};case"VEC4":return{type:"vec4f",bufferFormat:"float32x4",size:4};case"SCALAR":default:return{type:"f32",bufferFormat:"float32",size:1}}}static getTypedArrayConstructorFromComponentType(e){switch(e){case L.BYTE:return Int8Array;case L.UNSIGNED_BYTE:return Uint8Array;case L.SHORT:return Int16Array;case L.UNSIGNED_SHORT:return Uint16Array;case L.UNSIGNED_INT:return Uint32Array;case L.FLOAT:default:return Float32Array}}static gpuPrimitiveTopologyForMode(e){switch(e){case L.TRIANGLES:return"triangle-list";case L.TRIANGLE_STRIP:return"triangle-strip";case L.LINES:return"line-list";case L.LINE_STRIP:return"line-strip";case L.POINTS:return"point-list"}}static gpuAddressModeForWrap(e){switch(e){case L.CLAMP_TO_EDGE:return"clamp-to-edge";case L.MIRRORED_REPEAT:return"mirror-repeat";default:return"repeat"}}createSamplers(){if(this.gltf.samplers)for(const[e,t]of Object.entries(this.gltf.samplers)){const s={label:"glTF sampler "+e,name:"gltfSampler"+e,addressModeU:oe.gpuAddressModeForWrap(t.wrapS),addressModeV:oe.gpuAddressModeForWrap(t.wrapT)};switch((!t.magFilter||t.magFilter===L.LINEAR)&&(s.magFilter="linear"),t.minFilter){case L.NEAREST:break;case L.LINEAR:case L.LINEAR_MIPMAP_NEAREST:s.minFilter="linear";break;case L.NEAREST_MIPMAP_LINEAR:s.mipmapFilter="linear";break;case L.LINEAR_MIPMAP_LINEAR:default:s.minFilter="linear",s.mipmapFilter="linear";break}this.scenesManager.samplers.push(new De(this.renderer,s))}else this.scenesManager.samplers.push(new De(this.renderer,{label:"Default sampler",name:"defaultSampler"}))}createTexture(e,t,s){const i=(()=>{switch(s){case"baseColorTexture":case"emissiveTexture":return"bgra8unorm-srgb";case"occlusionTexture":return"r8unorm";default:return"bgra8unorm"}})(),r=new te(this.renderer,{label:e.name?e.name+": "+s:s,name:s,format:i,visibility:["fragment"],generateMips:!0,fixedSize:{width:t.width,height:t.height}});return r.uploadSource({source:t}),r}createMaterialTextures(){if(this.scenesManager.materialsTextures=[],this.gltf.materials)for(const[e,t]of Object.entries(this.gltf.materials)){const s={material:e,texturesDescriptors:[]},i=r=>r.texCoord&&r.texCoord!==0?"uv"+r.texCoord:"uv";if(this.scenesManager.materialsTextures[e]=s,t.pbrMetallicRoughness){if(t.pbrMetallicRoughness.baseColorTexture&&t.pbrMetallicRoughness.baseColorTexture.index!==void 0){const r=t.pbrMetallicRoughness.baseColorTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"baseColorTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.pbrMetallicRoughness.baseColorTexture)})}if(t.pbrMetallicRoughness.metallicRoughnessTexture&&t.pbrMetallicRoughness.metallicRoughnessTexture.index!==void 0){const r=t.pbrMetallicRoughness.metallicRoughnessTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"metallicRoughnessTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.pbrMetallicRoughness.metallicRoughnessTexture)})}}if(t.normalTexture&&t.normalTexture.index!==void 0){const r=t.normalTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"normalTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.normalTexture)})}if(t.occlusionTexture&&t.occlusionTexture.index!==void 0){const r=t.occlusionTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"occlusionTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.occlusionTexture)})}if(t.emissiveTexture&&t.emissiveTexture.index!==void 0){const r=t.emissiveTexture.index,n=this.gltf.imagesBitmaps[this.gltf.textures[r].source],a=this.createTexture(t,n,"emissiveTexture"),h=this.gltf.textures.find(u=>u.source===r)?.sampler;s.texturesDescriptors.push({texture:a,sampler:this.scenesManager.samplers[h??0],texCoordAttributeName:i(t.emissiveTexture)})}}}createNode(e,t){if(t.camera!==void 0)return;const s={name:t.name,node:new se,children:[]};e.children.push(s),s.node.parent=e.node,t.matrix?(s.node.modelMatrix.setFromArray(new Float32Array(t.matrix)),s.node.matrices.model.shouldUpdate=!1):(t.translation&&s.node.position.set(t.translation[0],t.translation[1],t.translation[2]),t.scale&&s.node.scale.set(t.scale[0],t.scale[1],t.scale[2]),t.rotation&&s.node.quaternion.setFromArray(new Float32Array(t.rotation)));const i=this.gltf.meshes[t.mesh];t.children&&t.children.forEach(r=>{const n=this.gltf.nodes[r];this.createNode(s,n)}),i&&i.primitives.forEach((r,n)=>{const a={parent:s.node,attributes:[],textures:[],parameters:{label:i.name?i.name+" "+n:"glTF mesh "+n},nodes:[]};let h=Tt(this,ge).get(r);h||(h={instances:[],nodes:[],meshDescriptor:a},Tt(this,ge).set(r,h)),h.instances.push(t),h.nodes.push(s.node)})}createScenes(){this.scenesManager.node.parent=this.renderer.scene,this.gltf.scenes.forEach(e=>{const t={name:e.name,children:[],node:new se};t.node.parent=this.scenesManager.node,this.scenesManager.scenes.push(t),e.nodes.forEach(s=>{const i=this.gltf.nodes[s];this.createNode(t,i)})}),this.scenesManager.scenes.forEach(e=>{e.node.shouldUpdateModelMatrix(),e.node.updateMatrixStack()});for(const[e,t]of Tt(this,ge)){const{instances:s,nodes:i,meshDescriptor:r}=t,n=s.length;r.nodes=i,this.scenesManager.meshesDescriptors.push(r);const a=new ie,h=[];let u=null,l=null,d=0;for(const[M,T]of Object.entries(e.attributes)){const v=this.gltf.accessors[T],C=oe.getTypedArrayConstructorFromComponentType(v.componentType),B=this.gltf.bufferViews[v.bufferView],x=M==="TEXCOORD_0"?"uv":M.replace("_","").replace("TEXCOORD","uv").toLowerCase(),R=B.byteStride||0,S=v.byteOffset||0;R&&S&&S0){const M=Object.values(e.attributes).map(T=>this.gltf.accessors[T].bufferView);if(M.every(T=>T===M[0]))u=new Float32Array(this.gltf.arrayBuffers[l.buffer],l.byteOffset,Math.ceil(l.byteLength/4)*4/Float32Array.BYTES_PER_ELEMENT);else{let T=0;const v={},C=Object.values(e.attributes).reduce((B,x)=>{const R=this.gltf.accessors[x],S=oe.getVertexAttributeParamsFromType(R.type).size;return v[R.bufferView]||(v[R.bufferView]=0),v[R.bufferView]=Math.max(v[R.bufferView],R.byteOffset+S*Float32Array.BYTES_PER_ELEMENT),T+=S*Float32Array.BYTES_PER_ELEMENT,B+R.count*S},0);u=new Float32Array(Math.ceil(C/4)*4),Object.values(e.attributes).forEach(B=>{const x=this.gltf.accessors[B],R=this.gltf.bufferViews[x.bufferView],S=oe.getVertexAttributeParamsFromType(x.type).size;for(let z=0;z{let C=M.findIndex(x=>x===T.name);C=C===-1?1/0:C;let B=M.findIndex(x=>x===v.name);return B=B===-1?1/0:B,C-B})}const c={instancesCount:n,topology:oe.gpuPrimitiveTopologyForMode(e.mode),vertexBuffers:[{name:"attributes",stepMode:"vertex",attributes:h,...u&&{array:u}}]},p="indices"in e,g=p?ve:nt;if(r.parameters.geometry=new g(c),r.parameters.geometry.boundingBox=a,p){const M=this.gltf.accessors[e.indices],T=this.gltf.bufferViews[M.bufferView],v=oe.getTypedArrayConstructorFromComponentType(M.componentType),C=M.byteOffset+T.byteOffset,B=this.gltf.arrayBuffers[T.buffer],x=Math.min((B.byteLength-C)/v.BYTES_PER_ELEMENT,Math.ceil(M.count/4)*4),R=v.name==="Uint8Array"?Uint16Array.from(new v(B,C,x)):new v(B,C,x);r.parameters.geometry.setIndexBuffer({bufferFormat:v.name==="Uint32Array"?"uint32":"uint16",array:R})}const m=this.scenesManager.materialsTextures[e.material];r.parameters.samplers=[],r.parameters.textures=[],m?.texturesDescriptors.forEach(M=>{r.textures.push({texture:M.texture.options.name,sampler:M.sampler.name,texCoordAttributeName:M.texCoordAttributeName}),r.parameters.samplers.find(v=>v.uuid===M.sampler.uuid)||r.parameters.samplers.push(M.sampler),r.parameters.textures.push(M.texture)});const y=this.gltf.materials&&this.gltf.materials[e.material]||{};r.parameters.cullMode=y.doubleSided?"none":"back",(y.alphaMode==="BLEND"||y.extensions&&y.extensions.KHR_materials_transmission)&&(r.parameters.transparent=!0,r.parameters.targets=[{blend:{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one"}}}]);const b={baseColorFactor:{type:"vec4f",value:y.pbrMetallicRoughness?.baseColorFactor||[1,1,1,1]},alphaCutoff:{type:"f32",value:y.alphaCutoff!==void 0?y.alphaCutoff:y.alphaMode==="MASK"?.5:0},metallicFactor:{type:"f32",value:y.pbrMetallicRoughness?.metallicFactor||0},roughnessFactor:{type:"f32",value:y.pbrMetallicRoughness?.roughnessFactor||1},normalMapScale:{type:"f32",value:y.normalTexture?.scale||1},occlusionStrength:{type:"f32",value:y.occlusionTexture?.strength||1},emissiveFactor:{type:"vec3f",value:y.emissiveFactor!==void 0?y.emissiveFactor:[1,1,1]}};if(Object.keys(b).length&&(r.parameters.uniforms={material:{visibility:["vertex","fragment"],struct:b}}),n>1){const M=new Float32Array(n*16),T=new Float32Array(n*16);for(let v=0;v",value:M},normalMatrix:{type:"array",value:T}}}}}for(let M=0;M{}){return this.scenesManager.meshesDescriptors.map(t=>{if(t.parameters.geometry){e(t);const s=new Zt(this.renderer,{...t.parameters});if(t.nodes.length>1){const i=s.updateWorldMatrix.bind(s);s.updateWorldMatrix=()=>{i(),t.nodes.forEach((r,n)=>{s.storages.instances.modelMatrix.value.set(r.worldMatrix.elements,n*16),He.copy(r.worldMatrix).invert().transpose(),s.storages.instances.normalMatrix.value.set(He.elements,n*16)}),s.storages.instances.modelMatrix.shouldUpdate=!0,s.storages.instances.normalMatrix.shouldUpdate=!0}}return s.parent=t.parent,this.scenesManager.meshes.push(s),s}})}destroy(){this.scenesManager.meshes.forEach(t=>t.remove()),this.scenesManager.meshes=[],this.scenesManager.getScenesNodes().forEach(t=>{t.destroy()}),this.scenesManager.node.destroy()}};ge=new WeakMap;let wi=vi;const gs=(o,e=null)=>{const t=o.textures.find(P=>P.texture==="baseColorTexture"),s=o.textures.find(P=>P.texture==="normalTexture"),i=o.textures.find(P=>P.texture==="emissiveTexture"),r=o.textures.find(P=>P.texture==="occlusionTexture"),n=o.textures.find(P=>P.texture==="metallicRoughnessTexture"),a=o.attributes.filter(P=>P.name!=="position"),h=a.map((P,W)=>`@location(${W}) ${P.name}: ${P.type},`).join(`
`);let u=`
let worldPos = matrices.model * vec4(attributes.position, 1.0);
vsOutput.position = camera.projection * camera.view * worldPos;
@@ -260,11 +260,11 @@ struct VSOutput {
var ambientContribution: vec3f;
var lightContribution: vec3f;
color = baseColor;
- `,U="",K="",W=`
+ `,U="",K="",N=`
ambientContribution = vec3(1.0);
`,Z=`
lightContribution = vec3(0.0);
- `;let{chunks:G}=e;G?(G.additionalFragmentHead||(G.additionalFragmentHead=U),G.ambientContribution||(G.ambientContribution=W),G.lightContribution||(G.lightContribution=Z),G.additionalColorContribution||(G.additionalColorContribution=K)):G={additionalFragmentHead:U,ambientContribution:W,lightContribution:Z,additionalColorContribution:K};const _=`
+ `;let{chunks:G}=e;G?(G.additionalFragmentHead||(G.additionalFragmentHead=U),G.ambientContribution||(G.ambientContribution=N),G.lightContribution||(G.lightContribution=Z),G.additionalColorContribution||(G.additionalColorContribution=K)):G={additionalFragmentHead:U,ambientContribution:N,lightContribution:Z,additionalColorContribution:K};const _=`
// PBR
const PI = ${Math.PI};
@@ -436,5 +436,5 @@ struct VSOutput {
// Add IBL spec to alpha for reflections on transparent surfaces (glass)
color.a = max(color.a, max(max(iblContribution.specular.r, iblContribution.specular.g), iblContribution.specular.b));
- `}let{chunks:u}=e;return u?(u.additionalFragmentHead?u.additionalFragmentHead+=a:u.additionalFragmentHead=a,u.additionalColorContribution?u.additionalColorContribution+=h:u.additionalColorContribution=h):u={additionalFragmentHead:a,additionalColorContribution:h},ys(o,e)},Bi=o=>{const{scene:e}=o;if(!e)return;const t=[];e.computePassEntries.forEach(s=>{t.push({command:"Render ComputePass",content:s.options.label}),s.material.bindGroups.forEach(i=>{i.bufferBindings.forEach(r=>{r.shouldCopyResult&&t.push({command:"Copy buffer to buffer",source:`${r.name} buffer`,destination:`${r.name} result buffer`})})})});for(const s in e.renderPassEntries){let i=0;e.renderPassEntries[s].forEach(r=>{if(!e.getRenderPassEntryLength(r))return;const n=r.renderPass.options.useColorAttachments?r.renderPass.options.colorAttachments.length===0&&r.renderPass.options.useDepth?`${r.renderTexture.options.label} depth pass`:r.renderPass.options.colorAttachments.length>1?`${r.renderTexture.options.label} multiple targets`:r.renderTexture?`${r.renderTexture.options.label}`:"Context current texture":void 0;let a=r.renderPass.options.label;const h={loadOp:r.renderPass.options.useColorAttachments?s==="screen"&&i>0?"load":r.renderPass.options.loadOp:void 0,depthLoadOp:void 0,sampleCount:r.renderPass.options.sampleCount,...r.renderPass.options.qualityRatio!==1&&{qualityRatio:r.renderPass.options.qualityRatio}};if(r.renderPass.options.useDepth&&(h.depthLoadOp=r.renderPass.options.depthLoadOp),i++,r.element)r.element.type==="ShaderPass"&&!(r.element.inputTarget||r.element.outputTarget)&&(t.push({command:"Copy texture to texture",source:n,destination:`${r.element.options.label} renderTexture`}),h.loadOp="clear"),a+=" "+JSON.stringify(h),t.push({command:`Render ${r.element.type}`,source:r.element.options.label,destination:n,descriptor:a}),r.element.type==="ShaderPass"&&!r.element.outputTarget&&r.element.options.copyOutputToRenderTexture?t.push({command:"Copy texture to texture",source:n,destination:`${r.element.options.label} renderTexture`}):r.element.type==="PingPongPlane"&&t.push({command:"Copy texture to texture",source:n,destination:`${r.element.renderTexture.options.label}`});else if(r.stack){a+=" "+JSON.stringify(h);for(const u in r.stack)for(const l in r.stack[u])r.stack[u][l].length&&t.push({command:`Render stack (${u} ${l} objects)`,source:r.stack[u][l],destination:n,descriptor:a})}})}console.table(t)};w.BindGroup=Re,w.Binding=Te,w.Box3=ie,w.BoxGeometry=li,w.Buffer=de,w.BufferBinding=ce,w.Camera=Ft,w.ComputeMaterial=Lt,w.ComputePass=Ds,w.ComputePipelineEntry=Jt,w.DOMElement=gt,w.DOMFrustum=Vt,w.DOMMesh=ds,w.DOMObject3D=us,w.DOMTexture=he,w.FullscreenPlane=dt,w.GLTFLoader=Ye,w.GLTFScenesManager=wi,w.GPUCameraRenderer=vt,w.GPUCurtains=hi,w.GPUCurtainsRenderer=qe,w.GPUDeviceManager=rs,w.GPURenderer=bt,w.Geometry=nt,w.IndexedGeometry=be,w.Mat3=re,w.Mat4=A,w.Material=rt,w.Mesh=Zt,w.Object3D=se,w.OrbitControls=ui,w.PingPongPlane=ni,w.PipelineEntry=pt,w.PipelineManager=es,w.Plane=cs,w.PlaneGeometry=ot,w.ProjectedObject3D=ct,w.Quat=J,w.RenderMaterial=Nt,w.RenderPass=Ie,w.RenderPipelineEntry=ft,w.RenderTarget=as,w.Sampler=Le,w.SamplerBinding=At,w.Scene=ts,w.ShaderPass=ii,w.SphereGeometry=di,w.Texture=te,w.TextureBindGroup=it,w.TextureBinding=st,w.Vec2=E,w.Vec3=f,w.WritableBufferBinding=tt,w.buildIBLShaders=Mi,w.buildPBRShaders=ys,w.buildShaders=gs,w.logSceneCommands=Bi});
+ `}let{chunks:u}=e;return u?(u.additionalFragmentHead?u.additionalFragmentHead+=a:u.additionalFragmentHead=a,u.additionalColorContribution?u.additionalColorContribution+=h:u.additionalColorContribution=h):u={additionalFragmentHead:a,additionalColorContribution:h},ys(o,e)},Bi=o=>{const{scene:e}=o;if(!e)return;const t=[];e.computePassEntries.forEach(s=>{t.push({command:"Render ComputePass",content:s.options.label}),s.material.bindGroups.forEach(i=>{i.bufferBindings.forEach(r=>{r.shouldCopyResult&&t.push({command:"Copy buffer to buffer",source:`${r.name} buffer`,destination:`${r.name} result buffer`})})})});for(const s in e.renderPassEntries){let i=0;e.renderPassEntries[s].forEach(r=>{if(!e.getRenderPassEntryLength(r))return;const n=r.renderPass.options.useColorAttachments?r.renderPass.options.colorAttachments.length===0&&r.renderPass.options.useDepth?`${r.renderTexture.options.label} depth pass`:r.renderPass.options.colorAttachments.length>1?`${r.renderTexture.options.label} multiple targets`:r.renderTexture?`${r.renderTexture.options.label}`:"Context current texture":void 0;let a=r.renderPass.options.label;const h={loadOp:r.renderPass.options.useColorAttachments?s==="screen"&&i>0?"load":r.renderPass.options.loadOp:void 0,depthLoadOp:void 0,sampleCount:r.renderPass.options.sampleCount,...r.renderPass.options.qualityRatio!==1&&{qualityRatio:r.renderPass.options.qualityRatio}};if(r.renderPass.options.useDepth&&(h.depthLoadOp=r.renderPass.options.depthLoadOp),i++,r.element)r.element.type==="ShaderPass"&&!(r.element.inputTarget||r.element.outputTarget)&&(t.push({command:"Copy texture to texture",source:n,destination:`${r.element.options.label} renderTexture`}),h.loadOp="clear"),a+=" "+JSON.stringify(h),t.push({command:`Render ${r.element.type}`,source:r.element.options.label,destination:n,descriptor:a}),r.element.type==="ShaderPass"&&!r.element.outputTarget&&r.element.options.copyOutputToRenderTexture?t.push({command:"Copy texture to texture",source:n,destination:`${r.element.options.label} renderTexture`}):r.element.type==="PingPongPlane"&&t.push({command:"Copy texture to texture",source:n,destination:`${r.element.renderTexture.options.label}`});else if(r.stack){a+=" "+JSON.stringify(h);for(const u in r.stack)for(const l in r.stack[u])r.stack[u][l].length&&t.push({command:`Render stack (${u} ${l} objects)`,source:r.stack[u][l],destination:n,descriptor:a})}})}console.table(t)};w.BindGroup=Se,w.Binding=Pe,w.Box3=ie,w.BoxGeometry=ui,w.Buffer=pe,w.BufferBinding=fe,w.Camera=Ft,w.ComputeMaterial=Lt,w.ComputePass=Ds,w.ComputePipelineEntry=Jt,w.DOMElement=gt,w.DOMFrustum=Vt,w.DOMMesh=ds,w.DOMObject3D=us,w.DOMTexture=ue,w.FullscreenPlane=dt,w.GLTFLoader=Ye,w.GLTFScenesManager=wi,w.GPUCameraRenderer=vt,w.GPUCurtains=ai,w.GPUCurtainsRenderer=qe,w.GPUDeviceManager=rs,w.GPURenderer=bt,w.Geometry=nt,w.IndexedGeometry=ve,w.Mat3=re,w.Mat4=A,w.Material=rt,w.Mesh=Zt,w.Object3D=se,w.OrbitControls=hi,w.PingPongPlane=di,w.PipelineEntry=pt,w.PipelineManager=es,w.Plane=cs,w.PlaneGeometry=ot,w.ProjectedObject3D=ct,w.Quat=J,w.RenderMaterial=Wt,w.RenderPass=Ve,w.RenderPipelineEntry=ft,w.RenderTarget=as,w.Sampler=De,w.SamplerBinding=At,w.Scene=ts,w.ShaderPass=ii,w.SphereGeometry=li,w.Texture=te,w.TextureBindGroup=it,w.TextureBinding=st,w.Vec2=E,w.Vec3=f,w.WritableBufferBinding=tt,w.buildIBLShaders=Mi,w.buildPBRShaders=ys,w.buildShaders=gs,w.logSceneCommands=Bi});
//# sourceMappingURL=gpu-curtains.umd.min.js.map
diff --git a/dist/gpu-curtains.umd.min.js.map b/dist/gpu-curtains.umd.min.js.map
index 624a1830e..4727fcedc 100644
--- a/dist/gpu-curtains.umd.min.js.map
+++ b/dist/gpu-curtains.umd.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"gpu-curtains.umd.min.js","sources":["../src/utils/utils.ts","../src/core/renderers/utils.ts","../src/core/bindings/utils.ts","../src/core/bindings/Binding.ts","../src/math/Vec2.ts","../src/math/Quat.ts","../src/math/Vec3.ts","../src/core/bindings/bufferElements/BufferElement.ts","../src/core/bindings/bufferElements/BufferArrayElement.ts","../src/core/bindings/bufferElements/BufferInterleavedArrayElement.ts","../src/core/buffers/utils.ts","../src/core/buffers/Buffer.ts","../src/core/bindings/BufferBinding.ts","../src/core/bindings/WritableBufferBinding.ts","../src/core/bindGroups/BindGroup.ts","../src/core/bindings/TextureBinding.ts","../src/math/Mat4.ts","../src/core/objects3D/Object3D.ts","../src/core/textures/utils.ts","../src/core/textures/DOMTexture.ts","../src/core/bindGroups/TextureBindGroup.ts","../src/core/bindings/SamplerBinding.ts","../src/core/camera/Camera.ts","../src/core/samplers/Sampler.ts","../src/core/textures/Texture.ts","../src/core/materials/Material.ts","../src/core/materials/ComputeMaterial.ts","../src/core/computePasses/ComputePass.ts","../src/math/Box3.ts","../src/core/DOM/DOMFrustum.ts","../src/core/geometries/Geometry.ts","../src/core/geometries/IndexedGeometry.ts","../src/core/geometries/PlaneGeometry.ts","../src/core/materials/utils.ts","../src/core/shaders/chunks/default_projected_vs.wgsl.js","../src/core/shaders/chunks/default_vs.wgsl.js","../src/core/shaders/chunks/default_fs.wgsl.js","../src/core/materials/RenderMaterial.ts","../src/core/meshes/mixins/MeshBaseMixin.ts","../src/utils/CacheManager.ts","../src/core/meshes/FullscreenPlane.ts","../src/math/Mat3.ts","../src/core/objects3D/ProjectedObject3D.ts","../src/core/shaders/chunks/default_normal_fs.wgsl.js","../src/core/meshes/mixins/ProjectedMeshBaseMixin.ts","../src/core/meshes/Mesh.ts","../src/core/pipelines/PipelineEntry.ts","../src/core/shaders/chunks/get_output_position.wgsl.js","../src/core/shaders/chunks/get_normals.wgsl.js","../src/core/shaders/chunks/get_uv_cover.wgsl.js","../src/core/shaders/chunks/get_vertex_to_uv_coords.wgsl.js","../src/core/shaders/ShaderChunks.ts","../src/core/pipelines/RenderPipelineEntry.ts","../src/core/pipelines/ComputePipelineEntry.ts","../src/core/pipelines/PipelineManager.ts","../src/utils/ResizeManager.ts","../src/core/DOM/DOMElement.ts","../src/core/scenes/Scene.ts","../src/core/renderPasses/RenderPass.ts","../src/utils/TasksQueueManager.ts","../src/core/renderers/GPURenderer.ts","../src/core/renderers/GPUCameraRenderer.ts","../src/core/renderers/GPUDeviceManager.ts","../src/core/renderPasses/RenderTarget.ts","../src/core/shaders/chunks/default_pass_fs.wgsl.js","../src/core/renderPasses/ShaderPass.ts","../src/curtains/objects3D/DOMObject3D.ts","../src/curtains/meshes/DOMMesh.ts","../src/curtains/meshes/PingPongPlane.ts","../src/curtains/meshes/Plane.ts","../src/curtains/renderers/GPUCurtainsRenderer.ts","../src/utils/ScrollManager.ts","../src/curtains/GPUCurtains.ts","../src/extras/controls/OrbitControls.ts","../src/extras/geometries/BoxGeometry.ts","../src/extras/geometries/SphereGeometry.ts","../src/extras/gltf/GLTFLoader.ts","../src/extras/gltf/GLTFScenesManager.ts","../src/extras/gltf/utils.ts","../src/utils/debug.ts"],"sourcesContent":["/**\n * Generate a unique universal id\n * @returns - unique universal id generated\n */\nexport const generateUUID = (): string => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0,\n v = c === 'x' ? r : (r & 0x3) | 0x8\n return v.toString(16).toUpperCase()\n })\n}\n\n/**\n * Turns a string into a camel case string\n * @param string - string to transform\n * @returns - camel case string created\n */\nexport const toCamelCase = (string: string): string => {\n return string\n .replace(/(?:^\\w|[A-Z]|\\b\\w)/g, (ltr, idx) => (idx === 0 ? ltr.toLowerCase() : ltr.toUpperCase()))\n .replace(/\\s+/g, '')\n}\n\n/**\n * Turns a string into a kebab case string\n * @param string - string to transform\n * @returns - kebab case string created\n */\nexport const toKebabCase = (string: string): string => {\n const camelCase = toCamelCase(string)\n return camelCase.charAt(0).toUpperCase() + camelCase.slice(1)\n}\n\nlet warningThrown = 0\n\n/**\n * Throw a console warning with the passed arguments\n * @param warning - warning to be thrown\n */\nexport const throwWarning = (warning: string) => {\n if (warningThrown > 100) {\n return\n } else if (warningThrown === 100) {\n console.warn('GPUCurtains: too many warnings thrown, stop logging.')\n } else {\n console.warn(warning)\n }\n\n warningThrown++\n}\n\n/**\n * Throw a javascript error with the passed arguments\n * @param error - error to be thrown\n */\nexport const throwError = (error: string) => {\n throw new Error(error)\n}\n","import { throwError } from '../../utils/utils'\r\nimport { GPURenderer } from './GPURenderer'\r\nimport { GPUCameraRenderer } from './GPUCameraRenderer'\r\nimport { GPUCurtainsRenderer } from '../../curtains/renderers/GPUCurtainsRenderer'\r\n\r\n/**\r\n * A Renderer could be either a {@link GPURenderer}, a {@link GPUCameraRenderer} or a {@link GPUCurtainsRenderer}\r\n * @type {Renderer}\r\n */\r\nexport type Renderer = GPUCurtainsRenderer | GPUCameraRenderer | GPURenderer\r\n/**\r\n * A CameraRenderer could be either a {@link GPUCameraRenderer} or a {@link GPUCurtainsRenderer}\r\n * @type {CameraRenderer}\r\n */\r\nexport type CameraRenderer = GPUCurtainsRenderer | GPUCameraRenderer\r\n\r\n/**\r\n * Format a renderer error based on given renderer, renderer type and object type\r\n * @param renderer - renderer that failed the test\r\n * @param rendererType - expected renderer type\r\n * @param type - object type\r\n */\r\nconst formatRendererError = (renderer: Renderer, rendererType = 'GPURenderer', type: string | null): void => {\r\n const error = type\r\n ? `Unable to create ${type} because the ${rendererType} is not defined: ${renderer}`\r\n : `The ${rendererType} is not defined: ${renderer}`\r\n throwError(error)\r\n}\r\n\r\n/**\r\n * Check if the given renderer is a {@link Renderer}\r\n * @param renderer - renderer to test\r\n * @param type - object type used to format the error if needed\r\n * @returns - whether the given renderer is a {@link Renderer}\r\n */\r\nexport const isRenderer = (renderer: Renderer | undefined, type: string | null): boolean => {\r\n const isRenderer =\r\n renderer &&\r\n (renderer.type === 'GPURenderer' ||\r\n renderer.type === 'GPUCameraRenderer' ||\r\n renderer.type === 'GPUCurtainsRenderer')\r\n\r\n if (!isRenderer) {\r\n formatRendererError(renderer, 'GPURenderer', type)\r\n }\r\n\r\n return isRenderer\r\n}\r\n\r\n/**\r\n * Check if the given renderer is a {@link CameraRenderer}\r\n * @param renderer - renderer to test\r\n * @param type - object type used to format the error if needed\r\n * @returns - whether the given renderer is a {@link CameraRenderer}\r\n */\r\nexport const isCameraRenderer = (renderer: CameraRenderer | undefined, type: string | null): boolean => {\r\n const isCameraRenderer =\r\n renderer && (renderer.type === 'GPUCameraRenderer' || renderer.type === 'GPUCurtainsRenderer')\r\n\r\n if (!isCameraRenderer) {\r\n formatRendererError(renderer, 'GPUCameraRenderer', type)\r\n }\r\n\r\n return isCameraRenderer\r\n}\r\n\r\n/**\r\n * Check if the given renderer is a {@link GPUCurtainsRenderer}\r\n * @param renderer - renderer to test\r\n * @param type - object type used to format the error if needed\r\n * @returns - whether the given renderer is a {@link GPUCurtainsRenderer}\r\n */\r\nexport const isCurtainsRenderer = (renderer: GPUCurtainsRenderer | undefined, type: string | null): boolean => {\r\n const isCurtainsRenderer = renderer && renderer.type === 'GPUCurtainsRenderer'\r\n\r\n if (!isCurtainsRenderer) {\r\n formatRendererError(renderer, 'GPUCurtainsRenderer', type)\r\n }\r\n\r\n return isCurtainsRenderer\r\n}\r\n\r\n/**\r\n * Helper to generate mips on the GPU\r\n * Taken from https://webgpufundamentals.org/webgpu/lessons/webgpu-importing-textures.html\r\n */\r\nexport const generateMips = (() => {\r\n let sampler\r\n let module\r\n const pipelineByFormat = {}\r\n\r\n return function generateMips(device: GPUDevice, texture: GPUTexture) {\r\n if (!module) {\r\n module = device.createShaderModule({\r\n label: 'textured quad shaders for mip level generation',\r\n code: `\r\n struct VSOutput {\r\n @builtin(position) position: vec4f,\r\n @location(0) texcoord: vec2f,\r\n };\r\n\r\n @vertex fn vs(\r\n @builtin(vertex_index) vertexIndex : u32\r\n ) -> VSOutput {\r\n var pos = array(\r\n\r\n vec2f( 0.0, 0.0), // center\r\n vec2f( 1.0, 0.0), // right, center\r\n vec2f( 0.0, 1.0), // center, top\r\n\r\n // 2st triangle\r\n vec2f( 0.0, 1.0), // center, top\r\n vec2f( 1.0, 0.0), // right, center\r\n vec2f( 1.0, 1.0), // right, top\r\n );\r\n\r\n var vsOutput: VSOutput;\r\n let xy = pos[vertexIndex];\r\n vsOutput.position = vec4f(xy * 2.0 - 1.0, 0.0, 1.0);\r\n vsOutput.texcoord = vec2f(xy.x, 1.0 - xy.y);\r\n return vsOutput;\r\n }\r\n\r\n @group(0) @binding(0) var ourSampler: sampler;\r\n @group(0) @binding(1) var ourTexture: texture_2d;\r\n\r\n @fragment fn fs(fsInput: VSOutput) -> @location(0) vec4f {\r\n return textureSample(ourTexture, ourSampler, fsInput.texcoord);\r\n }\r\n `,\r\n })\r\n\r\n sampler = device.createSampler({\r\n minFilter: 'linear',\r\n })\r\n }\r\n\r\n if (!pipelineByFormat[texture.format]) {\r\n pipelineByFormat[texture.format] = device.createRenderPipeline({\r\n label: 'mip level generator pipeline',\r\n layout: 'auto',\r\n vertex: {\r\n module,\r\n entryPoint: 'vs',\r\n },\r\n fragment: {\r\n module,\r\n entryPoint: 'fs',\r\n targets: [{ format: texture.format }],\r\n },\r\n })\r\n }\r\n const pipeline = pipelineByFormat[texture.format]\r\n\r\n const encoder = device.createCommandEncoder({\r\n label: 'mip gen encoder',\r\n })\r\n\r\n let width = texture.width\r\n let height = texture.height\r\n let baseMipLevel = 0\r\n while (width > 1 || height > 1) {\r\n width = Math.max(1, (width / 2) | 0)\r\n height = Math.max(1, (height / 2) | 0)\r\n\r\n const bindGroup = device.createBindGroup({\r\n layout: pipeline.getBindGroupLayout(0),\r\n entries: [\r\n { binding: 0, resource: sampler },\r\n {\r\n binding: 1,\r\n resource: texture.createView({\r\n baseMipLevel,\r\n mipLevelCount: 1,\r\n }),\r\n },\r\n ],\r\n })\r\n\r\n ++baseMipLevel\r\n\r\n const renderPassDescriptor = {\r\n label: 'our basic canvas renderPass',\r\n colorAttachments: [\r\n {\r\n view: texture.createView({ baseMipLevel, mipLevelCount: 1 }),\r\n loadOp: 'clear',\r\n storeOp: 'store',\r\n },\r\n ],\r\n }\r\n\r\n const pass = encoder.beginRenderPass(renderPassDescriptor as GPURenderPassDescriptor)\r\n pass.setPipeline(pipeline)\r\n pass.setBindGroup(0, bindGroup)\r\n pass.draw(6) // call our vertex shader 6 times\r\n pass.end()\r\n }\r\n\r\n const commandBuffer = encoder.finish()\r\n device.queue.submit([commandBuffer])\r\n }\r\n})()\r\n","import { BufferBinding } from './BufferBinding'\r\nimport { TextureBinding } from './TextureBinding'\r\nimport { MaterialShadersType } from '../../types/Materials'\r\n\r\n/**\r\n * Map {@link MaterialShadersType | shaders types names} with actual {@link GPUShaderStageFlags | shaders visibility bitwise flags}.\r\n */\r\nconst bindingVisibilities: Map = new Map([\r\n ['vertex', GPUShaderStage.VERTEX],\r\n ['fragment', GPUShaderStage.FRAGMENT],\r\n ['compute', GPUShaderStage.COMPUTE],\r\n])\r\n\r\n/**\r\n * Get the corresponding {@link GPUShaderStageFlags | shaders visibility bitwise flags} based on an array of {@link MaterialShadersType | shaders types names}.\r\n * @param visibilities - array of {@link MaterialShadersType | shaders types names}.\r\n * @returns - corresponding {@link GPUShaderStageFlags | shaders visibility bitwise flags}.\r\n */\r\nexport const getBindingVisibility = (visibilities: MaterialShadersType[] = []): GPUShaderStageFlags => {\r\n return visibilities.reduce((acc, v) => {\r\n return acc | bindingVisibilities.get(v)\r\n }, 0)\r\n}\r\n\r\n/** Defines a typed array */\r\nexport type TypedArray =\r\n | Int8Array\r\n | Uint8Array\r\n | Uint8ClampedArray\r\n | Int16Array\r\n | Uint16Array\r\n | Int32Array\r\n | Uint32Array\r\n | Float32Array\r\n | Float64Array\r\n\r\n/** Defines a typed array constructor */\r\nexport type TypedArrayConstructor =\r\n | Int8ArrayConstructor\r\n | Uint8ArrayConstructor\r\n | Int16ArrayConstructor\r\n | Uint16ArrayConstructor\r\n | Int32ArrayConstructor\r\n | Uint32ArrayConstructor\r\n | Float32ArrayConstructor\r\n | Float64ArrayConstructor\r\n\r\n/** Defines the possible WGSL variable types */\r\nexport type WGSLVariableType = string // TODO 'mat4x4f', 'mat3x3f', 'vec3f', 'vec2f', 'f32' etc\r\n\r\n/**\r\n * Defines a {@link BufferLayout} object used to pad our {@link GPUBuffer} arrays\r\n */\r\nexport type BufferLayout = {\r\n /** Number of elements hold by this variable type */\r\n numElements: number\r\n /** Required alignment by this variable type */\r\n align: number\r\n /** Size in bytes of this variable type */\r\n size: number\r\n /** Variable type */\r\n type: WGSLVariableType\r\n /** Typed array constructor required by this variable type */\r\n View: TypedArrayConstructor\r\n /** Pad values required by this variable type */\r\n pad?: number[]\r\n}\r\n\r\n/** Object containing all buffer layouts */\r\nconst bufferLayouts: Record = {\r\n i32: { numElements: 1, align: 4, size: 4, type: 'i32', View: Int32Array },\r\n u32: { numElements: 1, align: 4, size: 4, type: 'u32', View: Uint32Array },\r\n f32: { numElements: 1, align: 4, size: 4, type: 'f32', View: Float32Array },\r\n f16: { numElements: 1, align: 2, size: 2, type: 'u16', View: Uint16Array },\r\n\r\n vec2f: { numElements: 2, align: 8, size: 8, type: 'f32', View: Float32Array },\r\n vec2i: { numElements: 2, align: 8, size: 8, type: 'i32', View: Int32Array },\r\n vec2u: { numElements: 2, align: 8, size: 8, type: 'u32', View: Uint32Array },\r\n vec2h: { numElements: 2, align: 4, size: 4, type: 'u16', View: Uint16Array },\r\n vec3i: { numElements: 3, align: 16, size: 12, type: 'i32', View: Int32Array },\r\n vec3u: { numElements: 3, align: 16, size: 12, type: 'u32', View: Uint32Array },\r\n vec3f: { numElements: 3, align: 16, size: 12, type: 'f32', View: Float32Array },\r\n vec3h: { numElements: 3, align: 8, size: 6, type: 'u16', View: Uint16Array },\r\n vec4i: { numElements: 4, align: 16, size: 16, type: 'i32', View: Int32Array },\r\n vec4u: { numElements: 4, align: 16, size: 16, type: 'u32', View: Uint32Array },\r\n vec4f: { numElements: 4, align: 16, size: 16, type: 'f32', View: Float32Array },\r\n vec4h: { numElements: 4, align: 8, size: 8, type: 'u16', View: Uint16Array },\r\n\r\n // AlignOf(vecR)\tSizeOf(array)\r\n mat2x2f: { numElements: 4, align: 8, size: 16, type: 'f32', View: Float32Array },\r\n mat2x2h: { numElements: 4, align: 4, size: 8, type: 'u16', View: Uint16Array },\r\n mat3x2f: { numElements: 6, align: 8, size: 24, type: 'f32', View: Float32Array },\r\n mat3x2h: { numElements: 6, align: 4, size: 12, type: 'u16', View: Uint16Array },\r\n mat4x2f: { numElements: 8, align: 8, size: 32, type: 'f32', View: Float32Array },\r\n mat4x2h: { numElements: 8, align: 4, size: 16, type: 'u16', View: Uint16Array },\r\n mat2x3f: { numElements: 8, align: 16, size: 32, pad: [3, 1], type: 'f32', View: Float32Array },\r\n mat2x3h: { numElements: 8, align: 8, size: 16, pad: [3, 1], type: 'u16', View: Uint16Array },\r\n mat3x3f: { numElements: 12, align: 16, size: 48, pad: [3, 1], type: 'f32', View: Float32Array },\r\n mat3x3h: { numElements: 12, align: 8, size: 24, pad: [3, 1], type: 'u16', View: Uint16Array },\r\n mat4x3f: { numElements: 16, align: 16, size: 64, pad: [3, 1], type: 'f32', View: Float32Array },\r\n mat4x3h: { numElements: 16, align: 8, size: 32, pad: [3, 1], type: 'u16', View: Uint16Array },\r\n mat2x4f: { numElements: 8, align: 16, size: 32, type: 'f32', View: Float32Array },\r\n mat2x4h: { numElements: 8, align: 8, size: 16, type: 'u16', View: Uint16Array },\r\n mat3x4f: { numElements: 12, align: 16, size: 48, pad: [3, 1], type: 'f32', View: Float32Array },\r\n mat3x4h: { numElements: 12, align: 8, size: 24, pad: [3, 1], type: 'u16', View: Uint16Array },\r\n mat4x4f: { numElements: 16, align: 16, size: 64, type: 'f32', View: Float32Array },\r\n mat4x4h: { numElements: 16, align: 8, size: 32, type: 'u16', View: Uint16Array },\r\n}\r\n\r\n// from https://github.com/greggman/webgpu-utils/blob/main/src/buffer-views.ts\r\n/**\r\n * Get the correct {@link BufferLayout | buffer layout} for given {@link WGSLVariableType | variable type}\r\n * @param bufferType - [{@link WGSLVariableType | variable type} to use\r\n * @returns - the ={@link BufferLayout | buffer layout}\r\n */\r\nexport const getBufferLayout = (bufferType: WGSLVariableType): BufferLayout => {\r\n return bufferLayouts[bufferType]\r\n}\r\n\r\n/**\r\n * Get the correct WGSL variable declaration code fragment based on the given {@link BufferBinding}\r\n * @param binding - {@link BufferBinding} to use\r\n * @returns - WGSL variable declaration code fragment\r\n */\r\nexport const getBindingWGSLVarType = (binding: BufferBinding): string => {\r\n return (() => {\r\n switch (binding.bindingType) {\r\n case 'storage':\r\n return `var<${binding.bindingType}, ${binding.options.access}>`\r\n case 'uniform':\r\n default:\r\n return 'var'\r\n }\r\n })()\r\n}\r\n\r\n/**\r\n * Get the correct WGSL variable declaration code fragment based on the given {@link TextureBinding}\r\n * @param binding - {@link TextureBinding} to use\r\n * @returns - WGSL variable declaration code fragment\r\n */\r\nexport const getTextureBindingWGSLVarType = (binding: TextureBinding): string => {\r\n if (binding.bindingType === 'externalTexture') {\r\n return `var ${binding.name}: texture_external;`\r\n }\r\n\r\n return binding.bindingType === 'storage'\r\n ? `var ${binding.name}: texture_storage_${binding.options.viewDimension}<${binding.options.format}, ${binding.options.access}>;`\r\n : binding.bindingType === 'depth'\r\n ? `var ${binding.name}: texture_depth${binding.options.multisampled ? '_multisampled' : ''}_${\r\n binding.options.viewDimension\r\n };`\r\n : `var ${binding.name}: texture${binding.options.multisampled ? '_multisampled' : ''}_${\r\n binding.options.viewDimension\r\n };`\r\n}\r\n\r\n/**\r\n * Get the correct {@link GPUBindGroupLayout | bind group layout} resource type based on the given {@link core/bindings/Binding.BindingType | binding type}\r\n * @param binding - {@link BufferBinding | buffer binding} to use\r\n * @returns - {@link GPUBindGroupLayout | bind group layout} resource type\r\n */\r\nexport const getBindGroupLayoutBindingType = (binding: BufferBinding): GPUBufferBindingType => {\r\n if (binding.bindingType === 'storage' && binding.options.access === 'read_write') {\r\n return 'storage'\r\n } else if (binding.bindingType === 'storage') {\r\n return 'read-only-storage'\r\n } else {\r\n return 'uniform'\r\n }\r\n}\r\n\r\n/**\r\n * Get the correct {@link GPUBindGroupLayout} resource type based on the given {@link core/bindings/Binding.BindingType | texture binding type}\r\n * @param binding - {@link TextureBinding | texture binding} to use\r\n * @returns - {@link GPUBindGroupLayout} resource type\r\n */\r\nexport const getBindGroupLayoutTextureBindingType = (\r\n binding: TextureBinding\r\n): GPUTextureBindingLayout | GPUExternalTextureBindingLayout | GPUStorageTextureBindingLayout | null => {\r\n return (() => {\r\n switch (binding.bindingType) {\r\n case 'externalTexture':\r\n return { externalTexture: {} }\r\n case 'storage':\r\n return {\r\n storageTexture: {\r\n format: binding.options.format,\r\n viewDimension: binding.options.viewDimension,\r\n } as GPUStorageTextureBindingLayout,\r\n }\r\n case 'texture':\r\n return {\r\n texture: {\r\n multisampled: binding.options.multisampled,\r\n viewDimension: binding.options.viewDimension,\r\n sampleType: binding.options.multisampled ? 'unfilterable-float' : 'float',\r\n } as GPUTextureBindingLayout,\r\n }\r\n case 'depth':\r\n return {\r\n texture: {\r\n multisampled: binding.options.multisampled,\r\n viewDimension: binding.options.viewDimension,\r\n sampleType: 'depth',\r\n } as GPUTextureBindingLayout,\r\n }\r\n default:\r\n return null\r\n }\r\n })()\r\n}\r\n\r\n/**\r\n * Get the correct {@link TextureBinding | texture binding} cache key.\r\n * @param binding - {@link TextureBinding | texture binding} to use\r\n * @returns - binding cache key\r\n */\r\nexport const getBindGroupLayoutTextureBindingCacheKey = (binding: TextureBinding): string => {\r\n return (() => {\r\n switch (binding.bindingType) {\r\n case 'externalTexture':\r\n return `externalTexture,${binding.visibility},`\r\n case 'storage':\r\n return `storageTexture,${binding.options.format},${binding.options.viewDimension},${binding.visibility},`\r\n case 'texture':\r\n return `texture,${binding.options.multisampled},${binding.options.viewDimension},${\r\n binding.options.multisampled ? 'unfilterable-float' : 'float'\r\n },${binding.visibility},`\r\n case 'depth':\r\n return `depthTexture,${binding.options.format},${binding.options.viewDimension},${binding.visibility},`\r\n default:\r\n return `${binding.visibility},`\r\n }\r\n })()\r\n}\r\n","import { toCamelCase } from '../../utils/utils'\r\nimport { MaterialShadersType } from '../../types/Materials'\r\nimport { TextureBinding } from './TextureBinding'\r\nimport { SamplerBinding } from './SamplerBinding'\r\nimport { getBindingVisibility } from './utils'\r\n\r\n/** Defines all kind of buffer binding types */\r\nexport type BufferBindingType = 'uniform' | 'storage'\r\n/** Defines all kind of texture binding types */\r\nexport type TextureBindingType = 'texture' | 'storage' | 'depth'\r\n/** Defines all kind of DOM texture binding types */\r\nexport type DOMTextureBindingType = 'externalTexture' | TextureBindingType\r\n/** Defines all kind of sampler binding types */\r\nexport type SamplerBindingType = 'sampler'\r\n/** Defines all kind of binding types */\r\nexport type BindingType = BufferBindingType | DOMTextureBindingType | SamplerBindingType\r\n\r\n// see https://www.w3.org/TR/WGSL/#memory-access-mode\r\n/** Defines buffer binding memory access types (read only or read/write) */\r\nexport type BufferBindingMemoryAccessType = 'read' | 'read_write'\r\n/** Defines texture binding memory access types (read only, write only or read/write) */\r\nexport type BindingMemoryAccessType = BufferBindingMemoryAccessType | 'write'\r\n\r\n/**\r\n * Defines all kind of {@link Binding} that are related to textures or samplers\r\n */\r\nexport type TextureSamplerBindings = TextureBinding | SamplerBinding\r\n\r\n/**\r\n * An object defining all possible {@link Binding} class instancing parameters\r\n */\r\nexport interface BindingParams {\r\n /** {@link Binding} label */\r\n label?: string\r\n /** {@link Binding} name/key */\r\n name?: string\r\n /** {@link BindingType | binding type} to use with this {@link Binding} */\r\n bindingType?: BindingType\r\n /** {@link Binding} variables shaders visibility as an array of {@link MaterialShadersType | shaders types names} */\r\n visibility?: MaterialShadersType[]\r\n}\r\n\r\n/**\r\n * Used as a shell to build actual bindings upon, like {@link core/bindings/BufferBinding.BufferBinding | BufferBinding}, {@link core/bindings/WritableBufferBinding.WritableBufferBinding | WritableBufferBinding}, {@link TextureBinding} and {@link SamplerBinding}.\r\n *\r\n * Ultimately the goal of a {@link Binding} element is to provide correct resources for {@link GPUBindGroupLayoutEntry} and {@link GPUBindGroupEntry}\r\n *\r\n * ## WGSL\r\n *\r\n * Each {@link Binding} creates its own WGSL code snippet variable declaration, using structured types or not.\r\n */\r\nexport class Binding {\r\n /** The label of the {@link Binding} */\r\n label: string\r\n /** The name/key of the {@link Binding} */\r\n name: string\r\n /** The binding type of the {@link Binding} */\r\n bindingType: BindingType\r\n /** The visibility of the {@link Binding} in the shaders */\r\n visibility: GPUShaderStageFlags\r\n /** Options used to create this {@link Binding} */\r\n options: BindingParams\r\n\r\n /** Flag indicating whether we should recreate the parentMesh {@link core/bindGroups/BindGroup.BindGroup#bindGroup | bind group}, usually when a resource has changed */\r\n shouldResetBindGroup: boolean\r\n /** Flag indicating whether we should recreate the parentMesh {@link core/bindGroups/BindGroup.BindGroup#bindGroupLayout | GPU bind group layout}, usually when a resource layout has changed */\r\n shouldResetBindGroupLayout: boolean\r\n\r\n /** A cache key allowing to get / set bindings from the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#bufferBindings | device manager map cache}. Used for {@link core/bindings/BufferBinding.BufferBinding | BufferBinding} only at the moment. */\r\n cacheKey: string\r\n\r\n /**\r\n * Binding constructor\r\n * @param parameters - {@link BindingParams | parameters} used to create our {@link Binding}\r\n */\r\n constructor({\r\n label = 'Uniform',\r\n name = 'uniform',\r\n bindingType = 'uniform',\r\n visibility = ['vertex', 'fragment', 'compute'],\r\n }: BindingParams) {\r\n this.label = label\r\n this.name = toCamelCase(name)\r\n this.bindingType = bindingType\r\n\r\n this.visibility = getBindingVisibility(visibility)\r\n\r\n this.options = {\r\n label,\r\n name,\r\n bindingType,\r\n visibility,\r\n }\r\n\r\n this.shouldResetBindGroup = false\r\n this.shouldResetBindGroupLayout = false\r\n\r\n this.cacheKey = `${bindingType},${this.visibility},`\r\n }\r\n}\r\n","/**\n * Really basic 2D vector class used for vector calculations\n * @see https://github.com/mrdoob/three.js/blob/dev/src/math/Vector2.js\n * @see http://glmatrix.net/docs/vec2.js.html\n */\nexport class Vec2 {\n /** The type of the {@link Vec2} */\n type: string\n /** X component of our {@link Vec2} */\n private _x: number\n /** Y component of our {@link Vec2} */\n private _y: number\n\n /** function assigned to the {@link onChange} callback */\n _onChangeCallback?(): void\n\n /**\n * Vec2 constructor\n * @param x - X component of our {@link Vec2}\n * @param y - Y component of our {@link Vec2}\n */\n constructor(x = 0, y = x) {\n this.type = 'Vec2'\n\n this._x = x\n this._y = y\n }\n\n /**\n * Get the X component of the {@link Vec2}\n */\n get x(): number {\n return this._x\n }\n\n /**\n * Set the X component of the {@link Vec2}\n * Can trigger {@link onChange} callback\n * @param value - X component to set\n */\n set x(value: number) {\n const changed = value !== this._x\n this._x = value\n changed && this._onChangeCallback && this._onChangeCallback()\n }\n\n /**\n * Get the Y component of the {@link Vec2}\n */\n get y(): number {\n return this._y\n }\n\n /**\n * Set the Y component of the {@link Vec2}\n * Can trigger {@link onChange} callback\n * @param value - Y component to set\n */\n set y(value: number) {\n const changed = value !== this._y\n this._y = value\n changed && this._onChangeCallback && this._onChangeCallback()\n }\n\n /**\n * Called when at least one component of the {@link Vec2} has changed\n * @param callback - callback to run when at least one component of the {@link Vec2} has changed\n * @returns - our {@link Vec2}\n */\n onChange(callback: () => void): Vec2 {\n if (callback) {\n this._onChangeCallback = callback\n }\n\n return this\n }\n\n /**\n * Set the {@link Vec2} from values\n * @param x - new X component to set\n * @param y - new Y component to set\n * @returns - this {@link Vec2} after being set\n */\n set(x = 0, y = x): Vec2 {\n this.x = x\n this.y = y\n\n return this\n }\n\n /**\n * Add a {@link Vec2} to this {@link Vec2}\n * @param vector - {@link Vec2} to add\n * @returns - this {@link Vec2} after addition\n */\n add(vector: Vec2 = new Vec2()): Vec2 {\n this.x += vector.x\n this.y += vector.y\n\n return this\n }\n\n /**\n * Add a scalar to all the components of this {@link Vec2}\n * @param value - number to add\n * @returns - this {@link Vec2} after addition\n */\n addScalar(value = 0): Vec2 {\n this.x += value\n this.y += value\n\n return this\n }\n\n /**\n * Subtract a {@link Vec2} from this {@link Vec2}\n * @param vector - {@link Vec2} to subtract\n * @returns - this {@link Vec2} after subtraction\n */\n sub(vector: Vec2 = new Vec2()): Vec2 {\n this.x -= vector.x\n this.y -= vector.y\n\n return this\n }\n\n /**\n * Subtract a scalar to all the components of this {@link Vec2}\n * @param value - number to subtract\n * @returns - this {@link Vec2} after subtraction\n */\n subScalar(value = 0): Vec2 {\n this.x -= value\n this.y -= value\n\n return this\n }\n\n /**\n * Multiply a {@link Vec2} with this {@link Vec2}\n * @param vector - {@link Vec2} to multiply with\n * @returns - this {@link Vec2} after multiplication\n */\n multiply(vector: Vec2 = new Vec2(1)): Vec2 {\n this.x *= vector.x\n this.y *= vector.y\n\n return this\n }\n\n /**\n * Multiply all components of this {@link Vec2} with a scalar\n * @param value - number to multiply with\n * @returns - this {@link Vec2} after multiplication\n */\n multiplyScalar(value = 1): Vec2 {\n this.x *= value\n this.y *= value\n\n return this\n }\n\n /**\n * Divide a {@link Vec2} with this {@link Vec2}\n * @param vector - {@link Vec2} to divide with\n * @returns - this {@link Vec2} after division\n */\n divide(vector: Vec2 = new Vec2(1)): Vec2 {\n this.x /= vector.x\n this.y /= vector.y\n\n return this\n }\n\n /**\n * Divide all components of this {@link Vec2} with a scalar\n * @param value - number to divide with\n * @returns - this {@link Vec2} after division\n */\n divideScalar(value = 1): Vec2 {\n this.x /= value\n this.y /= value\n\n return this\n }\n\n /**\n * Copy a {@link Vec2} into this {@link Vec2}\n * @param vector - {@link Vec2} to copy\n * @returns - this {@link Vec2} after copy\n */\n copy(vector: Vec2 = new Vec2()): Vec2 {\n this.x = vector.x\n this.y = vector.y\n\n return this\n }\n\n /**\n * Clone this {@link Vec2}\n * @returns - cloned {@link Vec2}\n */\n clone(): Vec2 {\n return new Vec2(this.x, this.y)\n }\n\n /**\n * Apply max values to this {@link Vec2} components\n * @param vector - {@link Vec2} representing max values\n * @returns - {@link Vec2} with max values applied\n */\n max(vector: Vec2 = new Vec2()): Vec2 {\n this.x = Math.max(this.x, vector.x)\n this.y = Math.max(this.y, vector.y)\n\n return this\n }\n\n /**\n * Apply min values to this {@link Vec2} components\n * @param vector - {@link Vec2} representing min values\n * @returns - {@link Vec2} with min values applied\n */\n min(vector: Vec2 = new Vec2()): Vec2 {\n this.x = Math.min(this.x, vector.x)\n this.y = Math.min(this.y, vector.y)\n\n return this\n }\n\n /**\n * Clamp this {@link Vec2} components by min and max {@link Vec2} vectors\n * @param min - minimum {@link Vec2} components to compare with\n * @param max - maximum {@link Vec2} components to compare with\n * @returns - clamped {@link Vec2}\n */\n clamp(min: Vec2 = new Vec2(), max: Vec2 = new Vec2()): Vec2 {\n this.x = Math.max(min.x, Math.min(max.x, this.x))\n this.y = Math.max(min.y, Math.min(max.y, this.y))\n\n return this\n }\n\n /**\n * Check if 2 {@link Vec2} are equal\n * @param vector - {@link Vec2} to compare\n * @returns - whether the {@link Vec2} are equals or not\n */\n equals(vector: Vec2 = new Vec2()): boolean {\n return this.x === vector.x && this.y === vector.y\n }\n\n /**\n * Get the square length of this {@link Vec2}\n * @returns - square length of this {@link Vec2}\n */\n lengthSq(): number {\n return this.x * this.x + this.y * this.y\n }\n\n /**\n * Get the length of this {@link Vec2}\n * @returns - length of this {@link Vec2}\n */\n length(): number {\n return Math.sqrt(this.lengthSq())\n }\n\n /**\n * Normalize this {@link Vec2}\n * @returns - normalized {@link Vec2}\n */\n normalize(): Vec2 {\n // normalize\n let len = this.x * this.x + this.y * this.y\n if (len > 0) {\n len = 1 / Math.sqrt(len)\n }\n this.x *= len\n this.y *= len\n\n return this\n }\n\n /**\n * Calculate the dot product of 2 {@link Vec2}\n * @param vector - {@link Vec2} to use for dot product\n * @returns - dot product of the 2 {@link Vec2}\n */\n dot(vector: Vec2 = new Vec2()): number {\n return this.x * vector.x + this.y * vector.y\n }\n\n /**\n * Calculate the linear interpolation of this {@link Vec2} by given {@link Vec2} and alpha, where alpha is the percent distance along the line\n * @param vector - {@link Vec2} to interpolate towards\n * @param [alpha=1] - interpolation factor in the [0, 1] interval\n * @returns - this {@link Vec2} after linear interpolation\n */\n lerp(vector: Vec2 = new Vec2(), alpha = 1): Vec2 {\n this.x += (vector.x - this.x) * alpha\n this.y += (vector.y - this.y) * alpha\n\n return this\n }\n}\n","import { Vec3 } from './Vec3'\nimport { Mat4 } from './Mat4'\n\n/** Defines all possible rotations axis orders */\nexport type AxisOrder = 'XYZ' | 'XZY' | 'YXZ' | 'YZX' | 'ZXY' | 'ZYX'\n\n/**\n * Really basic quaternion class used for 3D rotation calculations\n * @see https://github.com/mrdoosb/three.js/blob/dev/src/math/Quaternion.js\n */\nexport class Quat {\n /** The type of the {@link Quat} */\n type: string\n /** Our quaternion array */\n elements: Float32Array\n /** Rotation axis order */\n axisOrder: AxisOrder\n\n /**\n * Quat constructor\n * @param [elements] - initial array to use\n * @param [axisOrder='XYZ'] - axis order to use\n */\n constructor(elements: Float32Array = new Float32Array([0, 0, 0, 1]), axisOrder: AxisOrder = 'XYZ') {\n this.type = 'Quat'\n this.elements = elements\n // rotation axis order\n this.axisOrder = axisOrder\n }\n\n /**\n * Sets the {@link Quat} values from an array\n * @param array - an array of at least 4 elements\n * @returns - this {@link Quat} after being set\n */\n setFromArray(array: Float32Array | number[] = new Float32Array([0, 0, 0, 1])): Quat {\n this.elements[0] = array[0]\n this.elements[1] = array[1]\n this.elements[2] = array[2]\n this.elements[3] = array[3]\n\n return this\n }\n\n /**\n * Sets the {@link Quat} axis order\n * @param axisOrder - axis order to use\n * @returns - this {@link Quat} after axis order has been set\n */\n setAxisOrder(axisOrder: AxisOrder | string = 'XYZ'): Quat {\n // force uppercase for strict equality tests\n axisOrder = axisOrder.toUpperCase()\n\n switch (axisOrder) {\n case 'XYZ':\n case 'YXZ':\n case 'ZXY':\n case 'ZYX':\n case 'YZX':\n case 'XZY':\n this.axisOrder = axisOrder\n break\n default:\n // apply a default axis order\n this.axisOrder = 'XYZ'\n }\n\n return this\n }\n\n /**\n * Copy a {@link Quat} into this {@link Quat}\n * @param quaternion - {@link Quat} to copy\n * @returns - this {@link Quat} after copy\n */\n copy(quaternion: Quat = new Quat()): Quat {\n this.elements = quaternion.elements\n this.axisOrder = quaternion.axisOrder\n\n return this\n }\n\n /**\n * Clone a {@link Quat}\n * @returns - cloned {@link Quat}\n */\n clone(): Quat {\n return new Quat().copy(this)\n }\n\n /**\n * Check if 2 {@link Quat} are equal\n * @param quaternion - {@link Quat} to check against\n * @returns - whether the {@link Quat} are equal or not\n */\n equals(quaternion: Quat = new Quat()): boolean {\n return (\n this.elements[0] === quaternion.elements[0] &&\n this.elements[1] === quaternion.elements[1] &&\n this.elements[2] === quaternion.elements[2] &&\n this.elements[3] === quaternion.elements[3] &&\n this.axisOrder === quaternion.axisOrder\n )\n }\n\n /**\n * Sets a rotation {@link Quat} using Euler angles {@link Vec3 | vector} and its axis order\n * @param vector - rotation {@link Vec3 | vector} to set our {@link Quat} from\n * @returns - {@link Quat} after having applied the rotation\n */\n setFromVec3(vector: Vec3): Quat {\n const ax = vector.x * 0.5\n const ay = vector.y * 0.5\n const az = vector.z * 0.5\n\n const cosx = Math.cos(ax)\n const cosy = Math.cos(ay)\n const cosz = Math.cos(az)\n const sinx = Math.sin(ax)\n const siny = Math.sin(ay)\n const sinz = Math.sin(az)\n\n // XYZ order\n if (this.axisOrder === 'XYZ') {\n this.elements[0] = sinx * cosy * cosz + cosx * siny * sinz\n this.elements[1] = cosx * siny * cosz - sinx * cosy * sinz\n this.elements[2] = cosx * cosy * sinz + sinx * siny * cosz\n this.elements[3] = cosx * cosy * cosz - sinx * siny * sinz\n } else if (this.axisOrder === 'YXZ') {\n this.elements[0] = sinx * cosy * cosz + cosx * siny * sinz\n this.elements[1] = cosx * siny * cosz - sinx * cosy * sinz\n this.elements[2] = cosx * cosy * sinz - sinx * siny * cosz\n this.elements[3] = cosx * cosy * cosz + sinx * siny * sinz\n } else if (this.axisOrder === 'ZXY') {\n this.elements[0] = sinx * cosy * cosz - cosx * siny * sinz\n this.elements[1] = cosx * siny * cosz + sinx * cosy * sinz\n this.elements[2] = cosx * cosy * sinz + sinx * siny * cosz\n this.elements[3] = cosx * cosy * cosz - sinx * siny * sinz\n } else if (this.axisOrder === 'ZYX') {\n this.elements[0] = sinx * cosy * cosz - cosx * siny * sinz\n this.elements[1] = cosx * siny * cosz + sinx * cosy * sinz\n this.elements[2] = cosx * cosy * sinz - sinx * siny * cosz\n this.elements[3] = cosx * cosy * cosz + sinx * siny * sinz\n } else if (this.axisOrder === 'YZX') {\n this.elements[0] = sinx * cosy * cosz + cosx * siny * sinz\n this.elements[1] = cosx * siny * cosz + sinx * cosy * sinz\n this.elements[2] = cosx * cosy * sinz - sinx * siny * cosz\n this.elements[3] = cosx * cosy * cosz - sinx * siny * sinz\n } else if (this.axisOrder === 'XZY') {\n this.elements[0] = sinx * cosy * cosz - cosx * siny * sinz\n this.elements[1] = cosx * siny * cosz - sinx * cosy * sinz\n this.elements[2] = cosx * cosy * sinz + sinx * siny * cosz\n this.elements[3] = cosx * cosy * cosz + sinx * siny * sinz\n }\n\n return this\n }\n\n /**\n * Set a {@link Quat} from a rotation axis {@link Vec3 | vector} and an angle\n * @param axis - normalized {@link Vec3 | vector} around which to rotate\n * @param angle - angle (in radians) to rotate\n * @returns - {@link Quat} after having applied the rotation\n */\n setFromAxisAngle(axis: Vec3, angle = 0): Quat {\n // https://github.com/mrdoob/three.js/blob/dev/src/math/Quaternion.js#L275\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm\n\n // assumes axis is normalized\n\n const halfAngle = angle / 2,\n s = Math.sin(halfAngle)\n\n this.elements[0] = axis.x * s\n this.elements[1] = axis.y * s\n this.elements[2] = axis.z * s\n this.elements[3] = Math.cos(halfAngle)\n\n return this\n }\n\n /**\n * Set a {@link Quat} from a rotation {@link Mat4 | matrix}\n * @param matrix - rotation {@link Mat4 | matrix} to use\n * @returns - {@link Quat} after having applied the rotation\n */\n setFromRotationMatrix(matrix: Mat4): Quat {\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n const te = matrix.elements,\n m11 = te[0],\n m12 = te[4],\n m13 = te[8],\n m21 = te[1],\n m22 = te[5],\n m23 = te[9],\n m31 = te[2],\n m32 = te[6],\n m33 = te[10],\n trace = m11 + m22 + m33\n\n if (trace > 0) {\n const s = 0.5 / Math.sqrt(trace + 1.0)\n\n this.elements[3] = 0.25 / s\n this.elements[0] = (m32 - m23) * s\n this.elements[1] = (m13 - m31) * s\n this.elements[2] = (m21 - m12) * s\n } else if (m11 > m22 && m11 > m33) {\n const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33)\n\n this.elements[3] = (m32 - m23) / s\n this.elements[0] = 0.25 * s\n this.elements[1] = (m12 + m21) / s\n this.elements[2] = (m13 + m31) / s\n } else if (m22 > m33) {\n const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33)\n\n this.elements[3] = (m13 - m31) / s\n this.elements[0] = (m12 + m21) / s\n this.elements[1] = 0.25 * s\n this.elements[2] = (m23 + m32) / s\n } else {\n const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22)\n\n this.elements[3] = (m21 - m12) / s\n this.elements[0] = (m13 + m31) / s\n this.elements[1] = (m23 + m32) / s\n this.elements[2] = 0.25 * s\n }\n\n return this\n }\n}\n","import { Mat4 } from './Mat4'\nimport { Quat } from './Quat'\nimport { Camera } from '../core/camera/Camera'\n\n/**\n * Really basic 3D vector class used for vector calculations\n * @see https://github.com/mrdoob/three.js/blob/dev/src/math/Vector3.js\n * @see http://glmatrix.net/docs/vec3.js.html\n */\nexport class Vec3 {\n /** The type of the {@link Vec3} */\n type: string\n /** X component of our {@link Vec3} */\n private _x: number\n /** Y component of our {@link Vec3} */\n private _y: number\n /** Z component of our {@link Vec3} */\n private _z: number\n\n /** function assigned to the {@link onChange} callback */\n _onChangeCallback?(): void\n\n /**\n * Vec3 constructor\n * @param x - X component of our {@link Vec3}\n * @param y - Y component of our {@link Vec3}\n * @param z - Z component of our {@link Vec3}\n */\n constructor(x = 0, y = x, z = x) {\n this.type = 'Vec3'\n\n this._x = x\n this._y = y\n this._z = z\n }\n\n /**\n * Get the X component of the {@link Vec3}\n */\n get x(): number {\n return this._x\n }\n\n /**\n * Set the X component of the {@link Vec3}\n * Can trigger {@link onChange} callback\n * @param value - X component to set\n */\n set x(value: number) {\n const changed = value !== this._x\n this._x = value\n changed && this._onChangeCallback && this._onChangeCallback()\n }\n\n /**\n * Get the Y component of the {@link Vec3}\n */\n get y(): number {\n return this._y\n }\n\n /**\n * Set the Y component of the {@link Vec3}\n * Can trigger {@link onChange} callback\n * @param value - Y component to set\n */\n set y(value: number) {\n const changed = value !== this._y\n this._y = value\n changed && this._onChangeCallback && this._onChangeCallback()\n }\n\n /**\n * Get the Z component of the {@link Vec3}\n */\n get z(): number {\n return this._z\n }\n\n /**\n * Set the Z component of the {@link Vec3}\n * Can trigger {@link onChange} callback\n * @param value - Z component to set\n */\n set z(value: number) {\n const changed = value !== this._z\n this._z = value\n changed && this._onChangeCallback && this._onChangeCallback()\n }\n\n /**\n * Called when at least one component of the {@link Vec3} has changed\n * @param callback - callback to run when at least one component of the {@link Vec3} has changed\n * @returns - our {@link Vec3}\n */\n onChange(callback: () => void): Vec3 {\n if (callback) {\n this._onChangeCallback = callback\n }\n\n return this\n }\n\n /**\n * Set the {@link Vec3} from values\n * @param x - new X component to set\n * @param y - new Y component to set\n * @param z - new Z component to set\n * @returns - this {@link Vec3} after being set\n */\n set(x = 0, y = x, z = x): Vec3 {\n this.x = x\n this.y = y\n this.z = z\n\n return this\n }\n\n /**\n * Add a {@link Vec3} to this {@link Vec3}\n * @param vector - {@link Vec3} to add\n * @returns - this {@link Vec3} after addition\n */\n add(vector: Vec3 = new Vec3()): Vec3 {\n this.x += vector.x\n this.y += vector.y\n this.z += vector.z\n\n return this\n }\n\n /**\n * Add a scalar to all the components of this {@link Vec3}\n * @param value - number to add\n * @returns - this {@link Vec3} after addition\n */\n addScalar(value = 0): Vec3 {\n this.x += value\n this.y += value\n this.z += value\n\n return this\n }\n\n /**\n * Subtract a {@link Vec3} from this {@link Vec3}\n * @param vector - {@link Vec3} to subtract\n * @returns - this {@link Vec3} after subtraction\n */\n sub(vector: Vec3 = new Vec3()): Vec3 {\n this.x -= vector.x\n this.y -= vector.y\n this.z -= vector.z\n\n return this\n }\n\n /**\n * Subtract a scalar to all the components of this {@link Vec3}\n * @param value - number to subtract\n * @returns - this {@link Vec3} after subtraction\n */\n subScalar(value = 0): Vec3 {\n this.x -= value\n this.y -= value\n this.z -= value\n\n return this\n }\n\n /**\n * Multiply a {@link Vec3} with this {@link Vec3}\n * @param vector - {@link Vec3} to multiply with\n * @returns - this {@link Vec3} after multiplication\n */\n multiply(vector: Vec3 = new Vec3(1)): Vec3 {\n this.x *= vector.x\n this.y *= vector.y\n this.z *= vector.z\n\n return this\n }\n\n /**\n * Multiply all components of this {@link Vec3} with a scalar\n * @param value - number to multiply with\n * @returns - this {@link Vec3} after multiplication\n */\n multiplyScalar(value = 1): Vec3 {\n this.x *= value\n this.y *= value\n this.z *= value\n\n return this\n }\n\n /**\n * Divide a {@link Vec3} with this {@link Vec3}\n * @param vector - {@link Vec3} to divide with\n * @returns - this {@link Vec3} after division\n */\n divide(vector: Vec3 = new Vec3(1)): Vec3 {\n this.x /= vector.x\n this.y /= vector.y\n this.z /= vector.z\n\n return this\n }\n\n /**\n * Divide all components of this {@link Vec3} with a scalar\n * @param value - number to divide with\n * @returns - this {@link Vec3} after division\n */\n divideScalar(value = 1): Vec3 {\n this.x /= value\n this.y /= value\n this.z /= value\n\n return this\n }\n\n /**\n * Copy a {@link Vec3} into this {@link Vec3}\n * @param vector - {@link Vec3} to copy\n * @returns - this {@link Vec3} after copy\n */\n copy(vector: Vec3 = new Vec3()): Vec3 {\n this.x = vector.x\n this.y = vector.y\n this.z = vector.z\n\n return this\n }\n\n /**\n * Clone this {@link Vec3}\n * @returns - cloned {@link Vec3}\n */\n clone(): Vec3 {\n return new Vec3(this.x, this.y, this.z)\n }\n\n /**\n * Apply max values to this {@link Vec3} components\n * @param vector - {@link Vec3} representing max values\n * @returns - {@link Vec3} with max values applied\n */\n max(vector: Vec3 = new Vec3()): Vec3 {\n this.x = Math.max(this.x, vector.x)\n this.y = Math.max(this.y, vector.y)\n this.z = Math.max(this.z, vector.z)\n\n return this\n }\n\n /**\n * Apply min values to this {@link Vec3} components\n * @param vector - {@link Vec3} representing min values\n * @returns - {@link Vec3} with min values applied\n */\n min(vector: Vec3 = new Vec3()): Vec3 {\n this.x = Math.min(this.x, vector.x)\n this.y = Math.min(this.y, vector.y)\n this.z = Math.min(this.z, vector.z)\n\n return this\n }\n\n /**\n * Clamp this {@link Vec3} components by min and max {@link Vec3} vectors\n * @param min - minimum {@link Vec3} components to compare with\n * @param max - maximum {@link Vec3} components to compare with\n * @returns - clamped {@link Vec3}\n */\n clamp(min: Vec3 = new Vec3(), max: Vec3 = new Vec3()): Vec3 {\n this.x = Math.max(min.x, Math.min(max.x, this.x))\n this.y = Math.max(min.y, Math.min(max.y, this.y))\n this.z = Math.max(min.z, Math.min(max.z, this.z))\n\n return this\n }\n\n /**\n * Check if 2 {@link Vec3} are equal\n * @param vector - {@link Vec3} to compare\n * @returns - whether the {@link Vec3} are equals or not\n */\n equals(vector: Vec3 = new Vec3()): boolean {\n return this.x === vector.x && this.y === vector.y && this.z === vector.z\n }\n\n /**\n * Get the square length of this {@link Vec3}\n * @returns - square length of this {@link Vec3}\n */\n lengthSq(): number {\n return this.x * this.x + this.y * this.y + this.z * this.z\n }\n\n /**\n * Get the length of this {@link Vec3}\n * @returns - length of this {@link Vec3}\n */\n length(): number {\n return Math.sqrt(this.lengthSq())\n }\n\n /**\n * Get the euclidian distance between this {@link Vec3} and another {@link Vec3}\n * @param vector - {@link Vec3} to use for distance calculation\n * @returns - euclidian distance\n */\n distance(vector: Vec3 = new Vec3()): number {\n return Math.hypot(vector.x - this.x, vector.y - this.y, vector.z - this.z)\n }\n\n /**\n * Normalize this {@link Vec3}\n * @returns - normalized {@link Vec3}\n */\n normalize(): Vec3 {\n // normalize\n let len = this.lengthSq()\n if (len > 0) {\n len = 1 / Math.sqrt(len)\n }\n this.x *= len\n this.y *= len\n this.z *= len\n\n return this\n }\n\n /**\n * Calculate the dot product of 2 {@link Vec3}\n * @param vector - {@link Vec3} to use for dot product\n * @returns - dot product of the 2 {@link Vec3}\n */\n dot(vector: Vec3 = new Vec3()): number {\n return this.x * vector.x + this.y * vector.y + this.z * vector.z\n }\n\n /**\n * Get the cross product of this {@link Vec3} with another {@link Vec3}\n * @param vector - {@link Vec3} to use for cross product\n * @returns - this {@link Vec3} after cross product\n */\n cross(vector: Vec3 = new Vec3()): Vec3 {\n return this.crossVectors(this, vector)\n }\n\n /**\n * Set this {@link Vec3} as the result of the cross product of two {@link Vec3}\n * @param a - first {@link Vec3} to use for cross product\n * @param b - second {@link Vec3} to use for cross product\n * @returns - this {@link Vec3} after cross product\n */\n crossVectors(a: Vec3 = new Vec3(), b: Vec3 = new Vec3()): Vec3 {\n const ax = a.x,\n ay = a.y,\n az = a.z\n const bx = b.x,\n by = b.y,\n bz = b.z\n\n this.x = ay * bz - az * by\n this.y = az * bx - ax * bz\n this.z = ax * by - ay * bx\n\n return this\n }\n\n /**\n * Calculate the linear interpolation of this {@link Vec3} by given {@link Vec3} and alpha, where alpha is the percent distance along the line\n * @param vector - {@link Vec3} to interpolate towards\n * @param alpha - interpolation factor in the [0, 1] interval\n * @returns - this {@link Vec3} after linear interpolation\n */\n lerp(vector: Vec3 = new Vec3(), alpha = 1): Vec3 {\n this.x += (vector.x - this.x) * alpha\n this.y += (vector.y - this.y) * alpha\n this.z += (vector.z - this.z) * alpha\n\n return this\n }\n\n /**\n * Apply a {@link Mat4 | matrix} to a {@link Vec3}\n * Useful to convert a position {@link Vec3} from plane local world to webgl space using projection view matrix for example\n * Source code from: http://glmatrix.net/docs/vec3.js.html\n * @param matrix - {@link Mat4 | matrix} to use\n * @returns - this {@link Vec3} after {@link Mat4 | matrix} application\n */\n applyMat4(matrix: Mat4): Vec3 {\n const x = this._x,\n y = this._y,\n z = this._z\n const mArray = matrix.elements\n\n let w = mArray[3] * x + mArray[7] * y + mArray[11] * z + mArray[15]\n w = w || 1\n\n this.x = (mArray[0] * x + mArray[4] * y + mArray[8] * z + mArray[12]) / w\n this.y = (mArray[1] * x + mArray[5] * y + mArray[9] * z + mArray[13]) / w\n this.z = (mArray[2] * x + mArray[6] * y + mArray[10] * z + mArray[14]) / w\n\n return this\n }\n\n /**\n * Set this {@link Vec3} to the translation component of a {@link Mat4 | matrix}.\n * @param matrix - {@link Mat4 | matrix} to use\n * @returns - this {@link Vec3} after {@link Mat4 | matrix} application.\n */\n setFromMatrixPosition(matrix: Mat4) {\n const e = matrix.elements\n\n this.x = e[12]\n this.y = e[13]\n this.z = e[14]\n\n return this\n }\n\n /**\n * Apply a {@link Quat | quaternion} (rotation in 3D space) to this {@link Vec3}\n * @param quaternion - {@link Quat | quaternion} to use\n * @returns - this {@link Vec3} with the transformation applied\n */\n applyQuat(quaternion: Quat = new Quat()): Vec3 {\n const x = this.x,\n y = this.y,\n z = this.z\n const qx = quaternion.elements[0],\n qy = quaternion.elements[1],\n qz = quaternion.elements[2],\n qw = quaternion.elements[3]\n\n // calculate quat * vector\n\n const ix = qw * x + qy * z - qz * y\n const iy = qw * y + qz * x - qx * z\n const iz = qw * z + qx * y - qy * x\n const iw = -qx * x - qy * y - qz * z\n\n // calculate result * inverse quat\n\n this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy\n this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz\n this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx\n\n return this\n }\n\n /**\n * Rotate a {@link Vec3} around and axis by a given angle\n * @param axis - normalized {@link Vec3} around which to rotate\n * @param angle - angle (in radians) to rotate\n * @param quaternion - optional {@link Quat | quaternion} to use for rotation computations\n * @returns - this {@link Vec3} with the rotation applied\n */\n applyAxisAngle(axis = new Vec3(), angle = 0, quaternion = new Quat()) {\n // https://github.com/mrdoob/three.js/blob/master/src/math/Vector3.js#L212\n return this.applyQuat(quaternion.setFromAxisAngle(axis, angle))\n }\n\n /**\n * Project a 3D coordinate {@link Vec3} to a 2D coordinate {@link Vec3}\n * @param camera - {@link Camera} to use for projection\n * @returns - projected {@link Vec3}\n */\n project(camera: Camera): Vec3 {\n this.applyMat4(camera.viewMatrix).applyMat4(camera.projectionMatrix)\n return this\n }\n\n /**\n * Unproject a 2D coordinate {@link Vec3} to 3D coordinate {@link Vec3}\n * @param camera - {@link Camera} to use for projection\n * @returns - unprojected {@link Vec3}\n */\n unproject(camera: Camera): Vec3 {\n this.applyMat4(camera.projectionMatrix.getInverse()).applyMat4(camera.modelMatrix)\n return this\n }\n}\n","import { BufferLayout, getBufferLayout, TypedArray, WGSLVariableType } from '../utils'\r\nimport { Vec2 } from '../../../math/Vec2'\r\nimport { Vec3 } from '../../../math/Vec3'\r\nimport { Quat } from '../../../math/Quat'\r\nimport { Mat4 } from '../../../math/Mat4'\r\nimport { throwWarning } from '../../../utils/utils'\r\nimport { Mat3 } from '../../../math/Mat3'\r\nimport { InputValue } from '../../../types/BindGroups'\r\n\r\n/** Number of slots per row */\r\nexport const slotsPerRow = 4\r\n/** Number of bytes per slot */\r\nexport const bytesPerSlot = 4\r\n/** Number of bytes per row */\r\nexport const bytesPerRow = slotsPerRow * bytesPerSlot\r\n\r\n/**\r\n * Defines a position in our array buffer with a row index and a byte index\r\n */\r\nexport interface BufferElementAlignmentPosition {\r\n /** row index of that position */\r\n row: number\r\n /** byte index of that position */\r\n byte: number\r\n}\r\n\r\n/**\r\n * Defines our {@link BufferElement} alignment:\r\n * Keep track of an entry start and end row and bytes indexes (16 bytes per row)\r\n */\r\nexport interface BufferElementAlignment {\r\n /** The row and byte indexes at which this {@link BufferElement} starts */\r\n start: BufferElementAlignmentPosition\r\n /** The row and byte indexes at which this {@link BufferElement} ends */\r\n end: BufferElementAlignmentPosition\r\n}\r\n\r\n/**\r\n * Parameters used to create a {@link BufferElement}\r\n */\r\nexport interface BufferElementParams {\r\n /** The name of the {@link BufferElement} */\r\n name: string\r\n /** The key of the {@link BufferElement} */\r\n key: string\r\n /** The WGSL variable type of the {@link BufferElement} */\r\n type: WGSLVariableType\r\n}\r\n\r\n/**\r\n * Used to handle each {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array} view and data layout alignment.\r\n * Compute the exact alignment offsets needed to fill an {@link ArrayBuffer} that will be sent to a {@link GPUBuffer}, based on an input type and value.\r\n * Also update the view array at the correct offset.\r\n *\r\n * So all our struct need to be packed into our arrayBuffer using a precise layout.\r\n * They will be stored in rows, each row made of 4 slots and each slots made of 4 bytes. Depending on the binding element type, its row and slot may vary and we may have to insert empty padded values.\r\n * All in all it looks like that:
\r\n * \r\n * slot 0 slot 1 slot 2 slot 3\r\n * row 0 | _ _ _ _ | _ _ _ _ | _ _ _ _ | _ _ _ _ |\r\n * row 1 | _ _ _ _ | _ _ _ _ | _ _ _ _ | _ _ _ _ |\r\n * row 2 | _ _ _ _ | _ _ _ _ | _ _ _ _ | _ _ _ _ |\r\n *
\r\n * see https://webgpufundamentals.org/webgpu/lessons/resources/wgsl-offset-computer.html\r\n */\r\nexport class BufferElement {\r\n /** The name of the {@link BufferElement} */\r\n name: string\r\n /** The WGSL variable type of the {@link BufferElement} */\r\n type: WGSLVariableType\r\n /** The key of the {@link BufferElement} */\r\n key: string\r\n\r\n /** {@link BufferLayout} used to fill the {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array} at the right offsets */\r\n bufferLayout: BufferLayout\r\n\r\n /**\r\n * Object defining exactly at which place a binding should be inserted into the {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array}\r\n */\r\n alignment: BufferElementAlignment\r\n\r\n /** Array containing the {@link BufferElement} values */\r\n view?: TypedArray\r\n\r\n /** Function assigned to set the {@link view} values */\r\n setValue: (value: InputValue) => void | null\r\n\r\n /**\r\n * BufferElement constructor\r\n * @param parameters - {@link BufferElementParams | parameters} used to create our {@link BufferElement}\r\n */\r\n constructor({ name, key, type = 'f32' }: BufferElementParams) {\r\n this.name = name\r\n this.key = key\r\n this.type = type\r\n\r\n this.bufferLayout = getBufferLayout(this.type.replace('array', '').replace('<', '').replace('>', ''))\r\n\r\n // set init alignment\r\n this.alignment = {\r\n start: {\r\n row: 0,\r\n byte: 0,\r\n },\r\n end: {\r\n row: 0,\r\n byte: 0,\r\n },\r\n }\r\n\r\n this.setValue = null\r\n }\r\n\r\n /**\r\n * Get the total number of rows used by this {@link BufferElement}\r\n * @readonly\r\n */\r\n get rowCount(): number {\r\n return this.alignment.end.row - this.alignment.start.row + 1\r\n }\r\n\r\n /**\r\n * Get the total number of bytes used by this {@link BufferElement} based on {@link BufferElementAlignment | alignment} start and end offsets\r\n * @readonly\r\n */\r\n get byteCount(): number {\r\n return Math.abs(this.endOffset - this.startOffset) + 1\r\n }\r\n\r\n /**\r\n * Get the total number of bytes used by this {@link BufferElement}, including final padding\r\n * @readonly\r\n */\r\n get paddedByteCount(): number {\r\n return (this.alignment.end.row + 1) * bytesPerRow\r\n }\r\n\r\n /**\r\n * Get the offset (i.e. byte index) at which our {@link BufferElement} starts\r\n * @readonly\r\n */\r\n get startOffset(): number {\r\n return this.getByteCountAtPosition(this.alignment.start)\r\n }\r\n\r\n /**\r\n * Get the array offset (i.e. array index) at which our {@link BufferElement} starts\r\n * @readonly\r\n */\r\n get startOffsetToIndex(): number {\r\n return this.startOffset / bytesPerSlot\r\n }\r\n\r\n /**\r\n * Get the offset (i.e. byte index) at which our {@link BufferElement} ends\r\n * @readonly\r\n */\r\n get endOffset(): number {\r\n return this.getByteCountAtPosition(this.alignment.end)\r\n }\r\n\r\n /**\r\n * Get the array offset (i.e. array index) at which our {@link BufferElement} ends\r\n * @readonly\r\n */\r\n get endOffsetToIndex(): number {\r\n return Math.floor(this.endOffset / bytesPerSlot)\r\n }\r\n\r\n /**\r\n * Get the position at given offset (i.e. byte index)\r\n * @param offset - byte index to use\r\n */\r\n getPositionAtOffset(offset = 0): BufferElementAlignmentPosition {\r\n return {\r\n row: Math.floor(offset / bytesPerRow),\r\n byte: offset % bytesPerRow,\r\n }\r\n }\r\n\r\n /**\r\n * Get the number of bytes at a given {@link BufferElementAlignmentPosition | position}\r\n * @param position - {@link BufferElementAlignmentPosition | position} from which to count\r\n * @returns - byte count at the given {@link BufferElementAlignmentPosition | position}\r\n */\r\n getByteCountAtPosition(position: BufferElementAlignmentPosition = { row: 0, byte: 0 }): number {\r\n return position.row * bytesPerRow + position.byte\r\n }\r\n\r\n /**\r\n * Check that a {@link BufferElementAlignmentPosition#byte | byte position} does not overflow its max value (16)\r\n * @param position - {@link BufferElementAlignmentPosition | position}\r\n * @returns - updated {@link BufferElementAlignmentPosition | position}\r\n */\r\n applyOverflowToPosition(\r\n position: BufferElementAlignmentPosition = { row: 0, byte: 0 }\r\n ): BufferElementAlignmentPosition {\r\n if (position.byte > bytesPerRow - 1) {\r\n const overflow = position.byte % bytesPerRow\r\n position.row += Math.floor(position.byte / bytesPerRow)\r\n position.byte = overflow\r\n }\r\n\r\n return position\r\n }\r\n\r\n /**\r\n * Get the number of bytes between two {@link BufferElementAlignmentPosition | positions}\r\n * @param p1 - first {@link BufferElementAlignmentPosition | position}\r\n * @param p2 - second {@link BufferElementAlignmentPosition | position}\r\n * @returns - number of bytes\r\n */\r\n getByteCountBetweenPositions(\r\n p1: BufferElementAlignmentPosition = { row: 0, byte: 0 },\r\n p2: BufferElementAlignmentPosition = { row: 0, byte: 0 }\r\n ): number {\r\n return Math.abs(this.getByteCountAtPosition(p2) - this.getByteCountAtPosition(p1))\r\n }\r\n\r\n /**\r\n * Compute the right alignment (i.e. start and end rows and bytes) given the size and align properties and the next available {@link BufferElementAlignmentPosition | position}\r\n * @param nextPositionAvailable - next {@link BufferElementAlignmentPosition | position} at which we should insert this element\r\n * @returns - computed {@link BufferElementAlignment | alignment}\r\n */\r\n getElementAlignment(\r\n nextPositionAvailable: BufferElementAlignmentPosition = { row: 0, byte: 0 }\r\n ): BufferElementAlignment {\r\n const alignment = {\r\n start: nextPositionAvailable,\r\n end: nextPositionAvailable,\r\n }\r\n\r\n const { size, align } = this.bufferLayout\r\n\r\n // check the alignment, i.e. even if there's enough space for our binding\r\n // we might have to pad the slot because some types need a specific alignment\r\n if (nextPositionAvailable.byte % align !== 0) {\r\n nextPositionAvailable.byte += nextPositionAvailable.byte % align\r\n }\r\n\r\n if (size <= bytesPerRow && nextPositionAvailable.byte + size > bytesPerRow) {\r\n // in the case of a binding that could fit on one row\r\n // but we don't have space on the current row for this binding element\r\n // go to next row\r\n nextPositionAvailable.row += 1\r\n nextPositionAvailable.byte = 0\r\n } else if (size > bytesPerRow && nextPositionAvailable.byte > bytesPerRow) {\r\n // there's also the case where the binding size is too big\r\n // and we have already padded it above\r\n // just go to next row as well\r\n nextPositionAvailable.row += 1\r\n nextPositionAvailable.byte = 0\r\n }\r\n\r\n alignment.end = {\r\n row: nextPositionAvailable.row + Math.ceil(size / bytesPerRow) - 1,\r\n byte: nextPositionAvailable.byte + (size % bytesPerRow === 0 ? bytesPerRow - 1 : (size % bytesPerRow) - 1), // end of row ? then it ends on slot (bytesPerRow - 1)\r\n }\r\n\r\n // now final check, if end slot has overflown\r\n alignment.end = this.applyOverflowToPosition(alignment.end)\r\n\r\n return alignment\r\n }\r\n\r\n /**\r\n * Set the {@link BufferElementAlignment | alignment} from a {@link BufferElementAlignmentPosition | position}\r\n * @param position - {@link BufferElementAlignmentPosition | position} at which to start inserting the values in the {@link !core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array}\r\n */\r\n setAlignmentFromPosition(position: BufferElementAlignmentPosition = { row: 0, byte: 0 }) {\r\n this.alignment = this.getElementAlignment(position)\r\n }\r\n\r\n /**\r\n * Set the {@link BufferElementAlignment | alignment} from an offset (byte count)\r\n * @param startOffset - offset at which to start inserting the values in the {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array}\r\n */\r\n setAlignment(startOffset = 0) {\r\n this.setAlignmentFromPosition(this.getPositionAtOffset(startOffset))\r\n }\r\n\r\n /**\r\n * Set the {@link view}\r\n * @param arrayBuffer - the {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array}\r\n * @param arrayView - the {@link core/bindings/BufferBinding.BufferBinding#arrayView | buffer binding array view}\r\n */\r\n setView(arrayBuffer: ArrayBuffer, arrayView: DataView) {\r\n this.view = new this.bufferLayout.View(\r\n arrayBuffer,\r\n this.startOffset,\r\n this.byteCount / this.bufferLayout.View.BYTES_PER_ELEMENT\r\n )\r\n }\r\n\r\n /**\r\n * Set the {@link view} value from a float or an int\r\n * @param value - float or int to use\r\n */\r\n setValueFromFloat(value: number) {\r\n this.view[0] = value as number\r\n }\r\n\r\n /**\r\n * Set the {@link view} value from a {@link Vec2} or an array\r\n * @param value - {@link Vec2} or array to use\r\n */\r\n setValueFromVec2(value: Vec2 | number[]) {\r\n this.view[0] = (value as Vec2).x ?? value[0] ?? 0\r\n this.view[1] = (value as Vec2).y ?? value[1] ?? 0\r\n }\r\n\r\n /**\r\n * Set the {@link view} value from a {@link Vec3} or an array\r\n * @param value - {@link Vec3} or array to use\r\n */\r\n setValueFromVec3(value: Vec3 | number[]) {\r\n this.view[0] = (value as Vec3).x ?? value[0] ?? 0\r\n this.view[1] = (value as Vec3).y ?? value[1] ?? 0\r\n this.view[2] = (value as Vec3).z ?? value[2] ?? 0\r\n }\r\n\r\n /**\r\n * Set the {@link view} value from a {@link Mat4} or {@link Quat}\r\n * @param value - {@link Mat4} or {@link Quat} to use\r\n */\r\n setValueFromMat4OrQuat(value: Mat4 | Quat) {\r\n this.view.set(value.elements)\r\n }\r\n\r\n /**\r\n * Set the {@link view} value from a {@link Mat3}\r\n * @param value - {@link Mat3} to use\r\n */\r\n setValueFromMat3(value: Mat3) {\r\n // mat3x3f are padded!\r\n this.setValueFromArrayWithPad(value.elements)\r\n }\r\n\r\n /**\r\n * Set the {@link view} value from an array\r\n * @param value - array to use\r\n */\r\n setValueFromArray(value: number[] | TypedArray) {\r\n this.view.set(value as number[] | TypedArray)\r\n }\r\n\r\n /**\r\n * Set the {@link view} value from an array with pad applied\r\n * @param value - array to use\r\n */\r\n setValueFromArrayWithPad(value: number[] | TypedArray) {\r\n for (\r\n let i = 0, offset = 0;\r\n i < this.view.length;\r\n i += this.bufferLayout.pad[0] + this.bufferLayout.pad[1], offset++\r\n ) {\r\n for (let j = 0; j < this.bufferLayout.pad[0]; j++) {\r\n this.view[i + j] = value[i + j - offset]\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Update the {@link view} based on the new value\r\n * @param value - new value to use\r\n */\r\n update(value: InputValue) {\r\n if (!this.setValue) {\r\n this.setValue = ((value) => {\r\n if (this.type === 'f32' || this.type === 'u32' || this.type === 'i32') {\r\n return this.setValueFromFloat\r\n } else if (this.type === 'vec2f') {\r\n return this.setValueFromVec2\r\n } else if (this.type === 'vec3f') {\r\n return this.setValueFromVec3\r\n } else if (this.type === 'mat3x3f') {\r\n return (value as Mat3).elements ? this.setValueFromMat3 : this.setValueFromArrayWithPad\r\n } else if ((value as Quat | Mat4).elements) {\r\n return this.setValueFromMat4OrQuat\r\n } else if (ArrayBuffer.isView(value) || Array.isArray(value)) {\r\n if (!this.bufferLayout.pad) {\r\n return this.setValueFromArray\r\n } else {\r\n return this.setValueFromArrayWithPad\r\n }\r\n } else {\r\n throwWarning(`${this.constructor.name}: value passed to ${this.name} cannot be used: ${value}`)\r\n }\r\n })(value)\r\n }\r\n\r\n this.setValue(value)\r\n }\r\n\r\n /**\r\n * Extract the data corresponding to this specific {@link BufferElement} from a {@link Float32Array} holding the {@link GPUBuffer} data of the parentMesh {@link core/bindings/BufferBinding.BufferBinding | BufferBinding}\r\n * @param result - {@link Float32Array} holding {@link GPUBuffer} data\r\n * @returns - extracted data from the {@link Float32Array}\r\n */\r\n extractDataFromBufferResult(result: Float32Array) {\r\n return result.slice(this.startOffsetToIndex, this.endOffsetToIndex)\r\n }\r\n}\r\n","import { BufferElement, BufferElementParams, bytesPerSlot } from './BufferElement'\r\nimport { TypedArray } from '../utils'\r\n\r\n/**\r\n * Parameters used to create a {@link BufferArrayElement}\r\n */\r\nexport interface BufferArrayElementParams extends BufferElementParams {\r\n /** Initial length of the input {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array} */\r\n arrayLength: number\r\n}\r\n\r\n/**\r\n * Used to handle specific array {@link core/bindings/BufferBinding.BufferBinding | BufferBinding} types\r\n */\r\nexport class BufferArrayElement extends BufferElement {\r\n /** Initial length of the input {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array} */\r\n arrayLength: number\r\n /** Total number of elements (i.e. {@link arrayLength} divided by {@link core/bindings/utils.BufferLayout | buffer layout} number of elements */\r\n numElements: number\r\n /** Number of bytes in the {@link ArrayBuffer} between two elements {@link startOffset} */\r\n arrayStride: number\r\n\r\n /**\r\n * BufferArrayElement constructor\r\n * @param parameters - {@link BufferArrayElementParams | parameters} used to create our {@link BufferArrayElement}\r\n */\r\n constructor({ name, key, type = 'f32', arrayLength = 1 }: BufferArrayElementParams) {\r\n super({ name, key, type })\r\n\r\n this.arrayLength = arrayLength\r\n this.numElements = Math.ceil(this.arrayLength / this.bufferLayout.numElements)\r\n }\r\n\r\n /**\r\n * Get the array stride between two elements of the array, in indices\r\n * @readonly\r\n */\r\n get arrayStrideToIndex(): number {\r\n return this.arrayStride / bytesPerSlot\r\n }\r\n\r\n /**\r\n * Set the {@link core/bindings/bufferElements/BufferElement.BufferElementAlignment | alignment}\r\n * To compute how arrays are packed, we get the second item alignment as well and use it to calculate the arrayStride between two array elements. Using the arrayStride and the total number of elements, we can easily get the end alignment position.\r\n * @param startOffset - offset at which to start inserting the values in the {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array buffer}\r\n */\r\n setAlignment(startOffset = 0) {\r\n super.setAlignment(startOffset)\r\n\r\n // repeat for a second element to know how things are laid out\r\n const nextAlignment = this.getElementAlignment(this.getPositionAtOffset(this.endOffset + 1))\r\n this.arrayStride = this.getByteCountBetweenPositions(this.alignment.end, nextAlignment.end)\r\n\r\n this.alignment.end = this.getPositionAtOffset(this.endOffset + this.arrayStride * (this.numElements - 1))\r\n }\r\n\r\n /**\r\n * Set the strided {@link view} value from an array\r\n * @param value - array to use\r\n */\r\n setValueFromArray(value: number[] | TypedArray) {\r\n let valueIndex = 0\r\n\r\n const viewLength = this.byteCount / this.bufferLayout.View.BYTES_PER_ELEMENT\r\n // arrayStride is our view length divided by the number of elements in our array\r\n const stride = Math.ceil(viewLength / this.numElements)\r\n\r\n for (let i = 0; i < this.numElements; i++) {\r\n for (let j = 0; j < this.bufferLayout.numElements; j++) {\r\n this.view[j + i * stride] = value[valueIndex]\r\n\r\n valueIndex++\r\n }\r\n }\r\n }\r\n}\r\n","import { BufferArrayElement, BufferArrayElementParams } from './BufferArrayElement'\r\nimport { InputValue } from '../../../types/BindGroups'\r\n\r\n/**\r\n * Used to compute alignment when dealing with arrays of Struct\r\n */\r\nexport class BufferInterleavedArrayElement extends BufferArrayElement {\r\n /** Corresponding {@link DataView} set function based on {@link view} type */\r\n viewSetFunction: DataView['setInt32'] | DataView['setUint16'] | DataView['setUint32'] | DataView['setFloat32']\r\n\r\n /**\r\n * BufferInterleavedArrayElement constructor\r\n * @param parameters - {@link BufferArrayElementParams | parameters} used to create our {@link BufferInterleavedArrayElement}\r\n */\r\n constructor({ name, key, type = 'f32', arrayLength = 1 }: BufferArrayElementParams) {\r\n super({ name, key, type, arrayLength })\r\n\r\n this.arrayStride = 1\r\n\r\n this.arrayLength = arrayLength\r\n this.numElements = Math.ceil(this.arrayLength / this.bufferLayout.numElements)\r\n }\r\n\r\n /**\r\n * Get the total number of slots used by this {@link BufferInterleavedArrayElement} based on buffer layout size and total number of elements\r\n * @readonly\r\n */\r\n get byteCount(): number {\r\n return this.bufferLayout.size * this.numElements\r\n }\r\n\r\n /**\r\n * Set the {@link core/bindings/bufferElements/BufferElement.BufferElementAlignment | alignment}\r\n * To compute how arrays are packed, we need to compute the arrayStride between two elements beforehand and pass it here. Using the arrayStride and the total number of elements, we can easily get the end alignment position.\r\n * @param startOffset - offset at which to start inserting the values in the {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array}\r\n * @param stride - Stride in the {@link ArrayBuffer} between two elements of the array\r\n */\r\n setAlignment(startOffset = 0, stride = 0) {\r\n this.alignment = this.getElementAlignment(this.getPositionAtOffset(startOffset))\r\n\r\n this.arrayStride = stride\r\n\r\n this.alignment.end = this.getPositionAtOffset(this.endOffset + stride * (this.numElements - 1))\r\n }\r\n\r\n /**\r\n * Set the {@link view} and {@link viewSetFunction}\r\n * @param arrayBuffer - the {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array}\r\n * @param arrayView - the {@link core/bindings/BufferBinding.BufferBinding#arrayView | buffer binding array view}\r\n */\r\n setView(arrayBuffer: ArrayBuffer, arrayView: DataView) {\r\n // our view will be a simple typed array, not linked to the array buffer\r\n this.view = new this.bufferLayout.View(this.bufferLayout.numElements * this.numElements)\r\n\r\n // but our viewSetFunction is linked to the array view\r\n this.viewSetFunction = ((arrayView) => {\r\n switch (this.bufferLayout.View) {\r\n case Int32Array:\r\n return arrayView.setInt32.bind(arrayView) as DataView['setInt32']\r\n case Uint16Array:\r\n return arrayView.setUint16.bind(arrayView) as DataView['setUint16']\r\n case Uint32Array:\r\n return arrayView.setUint32.bind(arrayView) as DataView['setUint32']\r\n case Float32Array:\r\n default:\r\n return arrayView.setFloat32.bind(arrayView) as DataView['setFloat32']\r\n }\r\n })(arrayView)\r\n }\r\n\r\n /**\r\n * Update the {@link view} based on the new value, and then update the {@link core/bindings/BufferBinding.BufferBinding#arrayView | buffer binding array view} using sub arrays\r\n * @param value - new value to use\r\n */\r\n update(value: InputValue) {\r\n super.update(value)\r\n\r\n // now use our viewSetFunction to fill the array view with interleaved alignment\r\n for (let i = 0; i < this.numElements; i++) {\r\n const subarray = this.view.subarray(\r\n i * this.bufferLayout.numElements,\r\n i * this.bufferLayout.numElements + this.bufferLayout.numElements\r\n )\r\n\r\n const startByteOffset = this.startOffset + i * this.arrayStride\r\n\r\n // view set function need to be called for each subarray entry, so loop over subarray entries\r\n subarray.forEach((value, index) => {\r\n this.viewSetFunction(startByteOffset + index * this.bufferLayout.View.BYTES_PER_ELEMENT, value, true)\r\n })\r\n }\r\n }\r\n\r\n /**\r\n * Extract the data corresponding to this specific {@link BufferInterleavedArrayElement} from a {@link Float32Array} holding the {@link GPUBuffer} data of the parentMesh {@link core/bindings/BufferBinding.BufferBinding | BufferBinding}\r\n * @param result - {@link Float32Array} holding {@link GPUBuffer} data\r\n */\r\n extractDataFromBufferResult(result: Float32Array) {\r\n const interleavedResult = new Float32Array(this.arrayLength)\r\n for (let i = 0; i < this.numElements; i++) {\r\n const resultOffset = this.startOffsetToIndex + i * this.arrayStrideToIndex\r\n\r\n for (let j = 0; j < this.bufferLayout.numElements; j++) {\r\n interleavedResult[i * this.bufferLayout.numElements + j] = result[resultOffset + j]\r\n }\r\n }\r\n return interleavedResult\r\n }\r\n}\r\n","// buffers bitwise flags\r\nimport { BufferBindingType } from '../bindings/Binding'\r\n\r\n/** Defines all kinds of allowed buffer usages as camel case strings. */\r\nexport type BufferUsageKeys =\r\n | 'copySrc'\r\n | 'copyDst'\r\n | 'index'\r\n | 'indirect'\r\n | 'mapRead'\r\n | 'mapWrite'\r\n | 'queryResolve'\r\n | 'vertex'\r\n | BufferBindingType\r\n\r\n/**\r\n * Map {@link BufferUsageKeys | buffer usage names} with actual {@link GPUBufferUsageFlags | buffer usage bitwise flags}.\r\n */\r\nconst bufferUsages: Map = new Map([\r\n ['copySrc', GPUBufferUsage.COPY_SRC],\r\n ['copyDst', GPUBufferUsage.COPY_DST],\r\n ['index', GPUBufferUsage.INDEX],\r\n ['indirect', GPUBufferUsage.INDIRECT],\r\n ['mapRead', GPUBufferUsage.MAP_READ],\r\n ['mapWrite', GPUBufferUsage.MAP_WRITE],\r\n ['queryResolve', GPUBufferUsage.QUERY_RESOLVE],\r\n ['storage', GPUBufferUsage.STORAGE],\r\n ['uniform', GPUBufferUsage.UNIFORM],\r\n ['vertex', GPUBufferUsage.VERTEX],\r\n])\r\n\r\n/**\r\n * Get the corresponding {@link GPUBufferUsageFlags | buffer usage bitwise flags} based on an array of {@link BufferUsageKeys | buffer usage names}.\r\n * @param usages - array of {@link BufferUsageKeys | buffer usage names}.\r\n * @returns - corresponding {@link GPUBufferUsageFlags | buffer usage bitwise flags}.\r\n */\r\nexport const getBufferUsages = (usages: BufferUsageKeys[] = []): GPUBufferUsageFlags => {\r\n return usages.reduce((acc, v) => {\r\n return acc | bufferUsages.get(v)\r\n }, 0)\r\n}\r\n","import { generateUUID } from '../../utils/utils'\r\nimport { BufferUsageKeys, getBufferUsages } from './utils'\r\n\r\n/**\r\n * Parameters used to create a {@link Buffer}.\r\n */\r\nexport interface BufferParams extends Partial> {\r\n /** Allowed usages for the {@link Buffer#GPUBuffer | GPU buffer} as an array of {@link BufferUsageKeys | buffer usages names} */\r\n usage?: BufferUsageKeys[]\r\n}\r\n\r\n/**\r\n * Used as a wrapper around {@link GPUBuffer}.\r\n *\r\n * Useful to keep tracks of all the {@link GPUBuffer} created thanks to the {@link uuid} property.\r\n */\r\nexport class Buffer {\r\n /** The type of the {@link Buffer} */\r\n type: string\r\n /** The universal unique id of the {@link Buffer} */\r\n uuid: string\r\n /** Options used to create this {@link Buffer}, also used as {@link GPUBufferDescriptor} */\r\n options: GPUBufferDescriptor\r\n\r\n /** The actual {@link GPUBuffer} after having been created. */\r\n GPUBuffer: null | GPUBuffer\r\n\r\n /** A Set to store this {@link Buffer} consumers (usually {@link core/geometries/Geometry.Geometry#uuid | Geometry uuid} or {@link core/bindGroups/BindGroup.BindGroup#uuid | BindGroup uuid}) */\r\n consumers: Set\r\n\r\n /**\r\n * Buffer constructor\r\n * @param parameters - {@link GPUBufferDescriptor | parameters} used to create our Buffer\r\n */\r\n constructor(\r\n {\r\n label = 'Buffer',\r\n size = 0,\r\n usage = ['copySrc', 'copyDst'],\r\n mappedAtCreation = false,\r\n }: BufferParams = {} as BufferParams\r\n ) {\r\n this.type = 'Buffer'\r\n\r\n this.reset()\r\n\r\n this.uuid = generateUUID()\r\n\r\n this.consumers = new Set()\r\n\r\n this.options = {\r\n label,\r\n size,\r\n usage: getBufferUsages(usage),\r\n mappedAtCreation,\r\n }\r\n }\r\n\r\n /** Reset the {@link GPUBuffer} value to `null`. */\r\n reset() {\r\n this.GPUBuffer = null\r\n }\r\n\r\n /** Allow to dynamically set the size of the {@link GPUBuffer}. */\r\n set size(value: number) {\r\n this.options.size = value\r\n }\r\n\r\n /**\r\n * Create a {@link GPUBuffer} based on the descriptor stored in the {@link options | Buffer options}.\r\n * @param renderer - {@link core/renderers/GPURenderer.GPURenderer | renderer} used to create the {@link GPUBuffer}.\r\n * @param options - optional way to update the {@link options} previously set before creating the {@link GPUBuffer}.\r\n */\r\n createBuffer(renderer, options: BufferParams = {}) {\r\n const { usage, ...staticOptions } = options\r\n\r\n this.options = {\r\n ...this.options,\r\n ...staticOptions,\r\n ...(usage !== undefined && { usage: getBufferUsages(usage) }),\r\n }\r\n\r\n this.setBuffer(renderer.createBuffer(this))\r\n }\r\n\r\n /**\r\n * Set the {@link GPUBuffer}. This allows to use a {@link Buffer} with a {@link GPUBuffer} created separately.\r\n * @param GPUBuffer - GPU buffer to use.\r\n */\r\n setBuffer(GPUBuffer: GPUBuffer) {\r\n this.GPUBuffer = GPUBuffer\r\n }\r\n\r\n /**\r\n * Copy an {@link Buffer#GPUBuffer | Buffer GPUBuffer} and its {@link options} into this {@link Buffer}.\r\n * @param buffer - {@link Buffer} to use for the copy.\r\n * @param destroyPreviousBuffer - whether to destroy the previous {@link Buffer} before the copy.\r\n */\r\n copy(buffer: Buffer, destroyPreviousBuffer: boolean = false) {\r\n if (destroyPreviousBuffer) {\r\n this.destroy()\r\n }\r\n\r\n this.options = buffer.options\r\n this.GPUBuffer = buffer.GPUBuffer\r\n this.consumers = new Set([...this.consumers, ...buffer.consumers])\r\n }\r\n\r\n /**\r\n * Map the {@link GPUBuffer} and put a copy of the data into a {@link Float32Array}.\r\n * @async\r\n * @returns - {@link Float32Array} holding the {@link GPUBuffer} data.\r\n */\r\n async mapBufferAsync() {\r\n if (!this.GPUBuffer || this.GPUBuffer.mapState !== 'unmapped') return new Float32Array(0)\r\n\r\n await this.GPUBuffer.mapAsync(GPUMapMode.READ)\r\n const result = new Float32Array(this.GPUBuffer.getMappedRange().slice(0))\r\n this.GPUBuffer.unmap()\r\n\r\n return result\r\n }\r\n\r\n /**\r\n * Destroy the {@link GPUBuffer} and {@link reset} its value.\r\n */\r\n destroy() {\r\n this.GPUBuffer?.destroy()\r\n this.reset()\r\n this.consumers.clear()\r\n }\r\n}\r\n","import { Binding, BindingParams, BufferBindingMemoryAccessType, BufferBindingType } from './Binding'\nimport { getBindGroupLayoutBindingType, getBindingWGSLVarType, getBufferLayout, TypedArray } from './utils'\nimport { throwWarning, toCamelCase, toKebabCase } from '../../utils/utils'\nimport { Vec2 } from '../../math/Vec2'\nimport { Vec3 } from '../../math/Vec3'\nimport { Input, InputBase, InputValue } from '../../types/BindGroups'\nimport { BufferElement } from './bufferElements/BufferElement'\nimport { BufferArrayElement } from './bufferElements/BufferArrayElement'\nimport { BufferInterleavedArrayElement } from './bufferElements/BufferInterleavedArrayElement'\nimport { Buffer, BufferParams } from '../buffers/Buffer'\n\n/**\n * Defines a {@link BufferBinding} input object that can set a value and run a callback function when this happens\n */\nexport interface BufferBindingInput extends InputBase {\n /** Original {@link InputValue | input value} */\n _value: InputValue\n\n /** Get the {@link InputValue | input value} */\n get value(): InputValue\n\n /** Set the {@link InputValue | input value} */\n set value(value: InputValue)\n\n /** Whether the {@link InputValue | input value} has changed and we should update the {@link BufferBinding#arrayBuffer | buffer binding array} */\n shouldUpdate: boolean\n\n /** {@link BufferBindingInput} name */\n name: string\n}\n\n/**\n * Base parameters used to create a {@link BufferBinding}\n */\nexport interface BufferBindingBaseParams {\n /** Whether this {@link BufferBinding} should use structured WGSL variables */\n useStruct?: boolean\n /** {@link BufferBinding} memory access types (read only or read/write) */\n access?: BufferBindingMemoryAccessType\n /** Object containing one or multiple {@link Input | inputs} describing the structure of the {@link BufferBinding} */\n struct?: Record\n /** Allowed usages for the {@link BufferBinding#buffer} as an array of {@link core/buffers/utils.BufferUsageKeys | buffer usages names} */\n usage?: BufferParams['usage']\n}\n\n/**\n * Parameters used to create a {@link BufferBinding}\n */\nexport interface BufferBindingParams extends BindingParams, BufferBindingBaseParams {\n /** The binding type of the {@link BufferBinding} */\n bindingType?: BufferBindingType\n}\n\n/** All allowed {@link BufferElement | buffer elements} */\nexport type AllowedBufferElement = BufferElement | BufferArrayElement | BufferInterleavedArrayElement\n\n/**\n * Used to format {@link BufferBindingParams#struct | uniforms or storages struct inputs} and create a single typed array that will hold all those inputs values. The array needs to be correctly padded depending on every value type, so it can be safely used as a GPUBuffer input.
\n * It will also create WGSL Structs and variables according to the BufferBindings inputs parameters.
\n * The WGSL structs and variables declaration may vary based on the input types, especially if there's one or more arrays involved (i.e. `array`, `array` etc.).\n *\n * @example\n * ```javascript\n * // create a GPU buffer binding\n * const bufferBinding = new BufferBinding({\n * name: 'params', // name of the WGSL object\n * bindingType: 'uniform', // should be 'storage' for large arrays\n * struct: {\n * opacity: {\n * type: 'f32',\n * value: 1,\n * },\n * mousePosition: {\n * type: 'vec2f',\n * value: new Vec2(),\n * },\n * },\n * })\n * ```\n */\nexport class BufferBinding extends Binding {\n /** The binding type of the {@link BufferBinding} */\n bindingType: BufferBindingType\n /** Flag to indicate whether this {@link BufferBinding} {@link bufferElements | buffer elements} should be packed in a single structured object or if each one of them should be a separate binding. */\n useStruct: boolean\n /** All the {@link BufferBinding} data inputs */\n inputs: Record\n\n /** Flag to indicate whether one of the {@link inputs} value has changed and we need to update the GPUBuffer linked to the {@link arrayBuffer} array */\n shouldUpdate: boolean\n\n /** An array describing how each corresponding {@link inputs} should be inserted into our {@link arrayView} array */\n bufferElements: AllowedBufferElement[]\n\n /** Total size of our {@link arrayBuffer} array in bytes */\n arrayBufferSize: number\n /** Array buffer that will be sent to the {@link GPUBuffer} */\n arrayBuffer: ArrayBuffer\n /** Data view of our {@link arrayBuffer | array buffer} */\n arrayView: DataView\n\n /** The {@link Buffer} holding the {@link GPUBuffer} */\n buffer: Buffer\n\n /** A string to append to our shaders code describing the WGSL structure representing this {@link BufferBinding} */\n wgslStructFragment: string\n /** An array of strings to append to our shaders code declaring all the WGSL variables representing this {@link BufferBinding} */\n wgslGroupFragment: string[]\n /** Options used to create this {@link BufferBinding} */\n options: BufferBindingParams\n\n /**\n * BufferBinding constructor\n * @param parameters - {@link BufferBindingParams | parameters} used to create our BufferBindings\n */\n constructor({\n label = 'Uniform',\n name = 'uniform',\n bindingType,\n visibility,\n useStruct = true,\n access = 'read',\n usage = [],\n struct = {},\n }: BufferBindingParams) {\n bindingType = bindingType ?? 'uniform'\n\n super({ label, name, bindingType, visibility })\n\n this.options = {\n ...this.options,\n useStruct,\n access,\n usage,\n struct,\n }\n\n this.cacheKey += `${useStruct},${access},`\n\n this.arrayBufferSize = 0\n\n this.shouldUpdate = false\n this.useStruct = useStruct\n\n this.bufferElements = []\n this.inputs = {}\n this.buffer = new Buffer()\n\n if (Object.keys(struct).length) {\n this.setBindings(struct)\n this.setBufferAttributes()\n\n this.setWGSLFragment()\n }\n }\n\n /**\n * Get {@link GPUBindGroupLayoutEntry#buffer | bind group layout entry resource}\n * @readonly\n */\n get resourceLayout(): {\n /** {@link GPUBindGroupLayout | bind group layout} resource */\n buffer: GPUBufferBindingLayout\n } {\n return {\n buffer: {\n type: getBindGroupLayoutBindingType(this),\n },\n }\n }\n\n /**\n * Get the resource cache key\n * @readonly\n */\n get resourceLayoutCacheKey(): string {\n return `buffer,${getBindGroupLayoutBindingType(this)},${this.visibility},`\n }\n\n /**\n * Get {@link GPUBindGroupEntry#resource | bind group resource}\n * @readonly\n */\n get resource(): {\n /** {@link GPUBindGroup | bind group} resource */\n buffer: GPUBuffer | null\n } {\n return { buffer: this.buffer.GPUBuffer }\n }\n\n /**\n * Clone this {@link BufferBinding} into a new one. Allows to skip buffer layout alignment computations.\n * @param params - params to use for cloning\n */\n clone(params: BufferBindingParams) {\n const { struct, ...defaultParams } = params\n\n const bufferBindingCopy = new (this.constructor as typeof BufferBinding)(defaultParams)\n bufferBindingCopy.setBindings(struct)\n bufferBindingCopy.options.struct = struct\n\n bufferBindingCopy.arrayBufferSize = this.arrayBufferSize\n\n bufferBindingCopy.arrayBuffer = new ArrayBuffer(bufferBindingCopy.arrayBufferSize)\n bufferBindingCopy.arrayView = new DataView(\n bufferBindingCopy.arrayBuffer,\n 0,\n bufferBindingCopy.arrayBuffer.byteLength\n )\n\n bufferBindingCopy.buffer.size = bufferBindingCopy.arrayBuffer.byteLength\n\n this.bufferElements.forEach((bufferElement: BufferArrayElement) => {\n const newBufferElement = new (bufferElement.constructor as typeof BufferArrayElement)({\n name: bufferElement.name,\n key: bufferElement.key,\n type: bufferElement.type,\n ...(bufferElement.arrayLength && {\n arrayLength: bufferElement.arrayLength,\n }),\n })\n\n newBufferElement.alignment = bufferElement.alignment\n if (bufferElement.arrayStride) {\n newBufferElement.arrayStride = bufferElement.arrayStride\n }\n\n newBufferElement.setView(bufferBindingCopy.arrayBuffer, bufferBindingCopy.arrayView)\n bufferBindingCopy.bufferElements.push(newBufferElement)\n })\n\n if (this.name === bufferBindingCopy.name && this.label === bufferBindingCopy.label) {\n bufferBindingCopy.wgslStructFragment = this.wgslStructFragment\n bufferBindingCopy.wgslGroupFragment = this.wgslGroupFragment\n } else {\n bufferBindingCopy.setWGSLFragment()\n }\n\n bufferBindingCopy.shouldUpdate = bufferBindingCopy.arrayBufferSize > 0\n\n return bufferBindingCopy\n }\n\n /**\n * Format bindings struct and set our {@link inputs}\n * @param bindings - bindings inputs\n */\n setBindings(bindings: Record) {\n for (const bindingKey of Object.keys(bindings)) {\n const binding = {} as BufferBindingInput\n\n for (const key in bindings[bindingKey]) {\n if (key !== 'value') {\n binding[key] = bindings[bindingKey][key]\n }\n }\n\n // binding name is the key\n binding.name = bindingKey\n\n // define a \"value\" getter/setter so we can now when to update the buffer binding\n Object.defineProperty(binding, 'value', {\n get() {\n return binding._value\n },\n set(v) {\n binding._value = v\n binding.shouldUpdate = true\n },\n })\n\n binding.value = bindings[bindingKey].value\n\n if (binding.value instanceof Vec2 || binding.value instanceof Vec3) {\n binding.value.onChange(() => (binding.shouldUpdate = true))\n }\n\n this.inputs[bindingKey] = binding\n\n this.cacheKey += `${bindingKey},${bindings[bindingKey].type},`\n }\n }\n\n /**\n * Set our buffer attributes:\n * Takes all the {@link inputs} and adds them to the {@link bufferElements} array with the correct start and end offsets (padded), then fill our {@link arrayBuffer} typed array accordingly.\n */\n setBufferAttributes() {\n // early on, check if there's at least one array binding\n // If there's one and only one, put it at the end of the binding elements array, treat it as a single entry of the type, but loop on it by array.length / size to fill the alignment\n // If there's more than one, create buffer interleaved elements.\n\n // if length === 0, OK\n // if length === 1, put it at the end of our struct\n // if length > 1, create a buffer interleaved elements\n let orderedBindings = Object.keys(this.inputs)\n\n const arrayBindings = orderedBindings.filter((bindingKey) => {\n return this.inputs[bindingKey].type.includes('array')\n })\n\n // put the array struct at the end\n if (arrayBindings.length) {\n orderedBindings.sort((bindingKeyA, bindingKeyB) => {\n // 0 if it's an array, -1 else\n const isBindingAArray = Math.min(0, this.inputs[bindingKeyA].type.indexOf('array'))\n const isBindingBArray = Math.min(0, this.inputs[bindingKeyB].type.indexOf('array'))\n\n return isBindingAArray - isBindingBArray\n })\n\n if (arrayBindings.length > 1) {\n // remove interleaved arrays from the ordered struct key array\n orderedBindings = orderedBindings.filter((bindingKey) => !arrayBindings.includes(bindingKey))\n }\n }\n\n // handle buffer (non interleaved) elements\n for (const bindingKey of orderedBindings) {\n const binding = this.inputs[bindingKey]\n\n const bufferElementOptions = {\n name: toCamelCase(binding.name ?? bindingKey),\n key: bindingKey,\n type: binding.type,\n }\n\n const isArray =\n binding.type.includes('array') && (Array.isArray(binding.value) || ArrayBuffer.isView(binding.value))\n\n this.bufferElements.push(\n isArray\n ? new BufferArrayElement({\n ...bufferElementOptions,\n arrayLength: (binding.value as number[]).length,\n })\n : new BufferElement(bufferElementOptions)\n )\n }\n\n // set their alignments\n this.bufferElements.forEach((bufferElement, index) => {\n const startOffset = index === 0 ? 0 : this.bufferElements[index - 1].endOffset + 1\n\n bufferElement.setAlignment(startOffset)\n })\n\n // now create our interleaved buffer elements\n if (arrayBindings.length > 1) {\n // first get the sizes of the arrays\n const arraySizes = arrayBindings.map((bindingKey) => {\n const binding = this.inputs[bindingKey]\n const bufferLayout = getBufferLayout(binding.type.replace('array', '').replace('<', '').replace('>', ''))\n\n return Math.ceil((binding.value as number[] | TypedArray).length / bufferLayout.numElements)\n })\n\n // are they all of the same size?\n const equalSize = arraySizes.every((size, i, array) => size === array[0])\n\n if (equalSize) {\n // this will hold our interleaved buffer elements\n const interleavedBufferElements = arrayBindings.map((bindingKey) => {\n const binding = this.inputs[bindingKey]\n return new BufferInterleavedArrayElement({\n name: toCamelCase(binding.name ?? bindingKey),\n key: bindingKey,\n type: binding.type,\n arrayLength: (binding.value as number[]).length,\n })\n })\n\n // now create temp buffer elements that we'll use to fill the interleaved buffer elements alignments\n const tempBufferElements = arrayBindings.map((bindingKey) => {\n const binding = this.inputs[bindingKey]\n return new BufferElement({\n name: toCamelCase(binding.name ?? bindingKey),\n key: bindingKey,\n type: binding.type.replace('array', '').replace('<', '').replace('>', ''),\n })\n })\n\n // set temp buffer alignments as if it was regular buffer elements\n tempBufferElements.forEach((bufferElement, index) => {\n if (index === 0) {\n if (this.bufferElements.length) {\n // if there are already buffer elements\n // get last one end row, and start at the next row\n bufferElement.setAlignmentFromPosition({\n row: this.bufferElements[this.bufferElements.length - 1].alignment.end.row + 1,\n byte: 0,\n })\n } else {\n bufferElement.setAlignment(0)\n }\n } else {\n bufferElement.setAlignment(tempBufferElements[index - 1].endOffset + 1)\n }\n })\n\n // now use last temp buffer end offset as our interleaved arrayStride\n const totalStride =\n tempBufferElements[tempBufferElements.length - 1].endOffset + 1 - tempBufferElements[0].startOffset\n\n // finally, set interleaved buffer elements alignment\n interleavedBufferElements.forEach((bufferElement, index) => {\n bufferElement.setAlignment(tempBufferElements[index].startOffset, totalStride)\n })\n\n // add to our buffer elements array\n this.bufferElements = [...this.bufferElements, ...interleavedBufferElements]\n } else {\n throwWarning(\n `BufferBinding: \"${\n this.label\n }\" contains multiple array inputs that should use an interleaved array, but their sizes do not match. These inputs cannot be added to the BufferBinding: \"${arrayBindings.join(\n ', '\n )}\"`\n )\n }\n }\n\n this.arrayBufferSize = this.bufferElements.length\n ? this.bufferElements[this.bufferElements.length - 1].paddedByteCount\n : 0\n\n this.arrayBuffer = new ArrayBuffer(this.arrayBufferSize)\n this.arrayView = new DataView(this.arrayBuffer, 0, this.arrayBuffer.byteLength)\n\n this.buffer.size = this.arrayBuffer.byteLength\n\n for (const bufferElement of this.bufferElements) {\n bufferElement.setView(this.arrayBuffer, this.arrayView)\n }\n\n this.shouldUpdate = this.arrayBufferSize > 0\n }\n\n /**\n * Set the WGSL code snippet to append to the shaders code. It consists of variable (and Struct structures if needed) declarations.\n */\n setWGSLFragment() {\n if (!this.bufferElements.length) return\n\n const kebabCaseLabel = toKebabCase(this.label)\n\n if (this.useStruct) {\n const bufferElements = this.bufferElements.filter(\n (bufferElement) => !(bufferElement instanceof BufferInterleavedArrayElement)\n )\n const interleavedBufferElements = this.bufferElements.filter(\n (bufferElement) => bufferElement instanceof BufferInterleavedArrayElement\n ) as BufferInterleavedArrayElement[]\n\n if (interleavedBufferElements.length) {\n const arrayLength = this.bindingType === 'uniform' ? `, ${interleavedBufferElements[0].numElements}` : ''\n\n if (bufferElements.length) {\n this.wgslStructFragment = `struct ${kebabCaseLabel}Element {\\n\\t${interleavedBufferElements\n .map((binding) => binding.name + ': ' + binding.type.replace('array', '').replace('<', '').replace('>', ''))\n .join(',\\n\\t')}\n};\\n\\n`\n\n const interleavedBufferStructDeclaration = `${this.name}Element: array<${kebabCaseLabel}Element${arrayLength}>,`\n\n this.wgslStructFragment += `struct ${kebabCaseLabel} {\\n\\t${bufferElements\n .map((bufferElement) => bufferElement.name + ': ' + bufferElement.type)\n .join(',\\n\\t')}\n\\t${interleavedBufferStructDeclaration}\n};`\n\n const varType = getBindingWGSLVarType(this)\n this.wgslGroupFragment = [`${varType} ${this.name}: ${kebabCaseLabel};`]\n } else {\n this.wgslStructFragment = `struct ${kebabCaseLabel} {\\n\\t${this.bufferElements\n .map((binding) => binding.name + ': ' + binding.type.replace('array', '').replace('<', '').replace('>', ''))\n .join(',\\n\\t')}\n};`\n\n const varType = getBindingWGSLVarType(this)\n this.wgslGroupFragment = [`${varType} ${this.name}: array<${kebabCaseLabel}${arrayLength}>;`]\n }\n } else {\n this.wgslStructFragment = `struct ${kebabCaseLabel} {\\n\\t${this.bufferElements\n .map((binding) => {\n // now add array length if needed\n const bindingType =\n this.bindingType === 'uniform' && 'numElements' in binding\n ? `array<${binding.type.replace('array', '').replace('<', '').replace('>', '')}, ${\n binding.numElements\n }>`\n : binding.type\n return binding.name + ': ' + bindingType\n })\n .join(',\\n\\t')}\n};`\n\n const varType = getBindingWGSLVarType(this)\n this.wgslGroupFragment = [`${varType} ${this.name}: ${kebabCaseLabel};`]\n }\n } else {\n this.wgslStructFragment = ''\n this.wgslGroupFragment = this.bufferElements.map((binding) => {\n const varType = getBindingWGSLVarType(this)\n return `${varType} ${binding.name}: ${binding.type};`\n })\n }\n }\n\n /**\n * Set a {@link BufferBinding#shouldUpdate | binding shouldUpdate} flag to `true` to update our {@link arrayBuffer} array during next render.\n * @param bindingName - the binding name/key to update\n */\n shouldUpdateBinding(bindingName = '') {\n if (this.inputs[bindingName]) {\n this.inputs[bindingName].shouldUpdate = true\n }\n }\n\n /**\n * Executed at the beginning of a Material render call.\n * If any of the {@link inputs} has changed, run its onBeforeUpdate callback then updates our {@link arrayBuffer} array.\n * Also sets the {@link shouldUpdate} property to true so the {@link core/bindGroups/BindGroup.BindGroup | BindGroup} knows it will need to update the {@link GPUBuffer}.\n */\n update() {\n const inputs = Object.values(this.inputs)\n for (const binding of inputs) {\n const bufferElement = this.bufferElements.find((bufferEl) => bufferEl.key === binding.name)\n\n if (binding.shouldUpdate && bufferElement) {\n binding.onBeforeUpdate && binding.onBeforeUpdate()\n // we're going to directly update the arrayBuffer from the buffer element update method\n bufferElement.update(binding.value)\n\n this.shouldUpdate = true\n binding.shouldUpdate = false\n }\n }\n }\n\n /**\n * Extract the data corresponding to a specific {@link BufferElement} from a {@link Float32Array} holding the {@link BufferBinding#buffer | GPU buffer} data of this {@link BufferBinding}\n * @param parameters - parameters used to extract the data\n * @param parameters.result - {@link Float32Array} holding {@link GPUBuffer} data\n * @param parameters.bufferElementName - name of the {@link BufferElement} to use to extract the data\n * @returns - extracted data from the {@link Float32Array}\n */\n extractBufferElementDataFromBufferResult({\n result,\n bufferElementName,\n }: {\n result: Float32Array\n bufferElementName: BufferElement['name']\n }): Float32Array {\n const bufferElement = this.bufferElements.find((bufferElement) => bufferElement.name === bufferElementName)\n if (bufferElement) {\n return bufferElement.extractDataFromBufferResult(result)\n } else {\n return result\n }\n }\n}\n","import { BufferBinding, BufferBindingParams } from './BufferBinding'\r\nimport { Buffer } from '../buffers/Buffer'\r\n\r\n/**\r\n * Parameters used to create a {@link WritableBufferBinding}\r\n */\r\nexport interface WritableBufferBindingParams extends BufferBindingParams {\r\n /** Whether whe should automatically copy the {@link WritableBufferBinding#buffer | GPU buffer} content into our {@link WritableBufferBinding#resultBuffer | result GPU buffer} */\r\n shouldCopyResult?: boolean\r\n}\r\n\r\n/**\r\n * Used to create a {@link BufferBinding} that can hold read/write storage bindings along with a {@link WritableBufferBinding#resultBuffer | result GPU buffer} that can be used to get data back from the GPU.\r\n *\r\n * Note that it is automatically created by the {@link core/bindGroups/BindGroup.BindGroup | BindGroup} when a {@link types/BindGroups.BindGroupInputs#storages | storages input} has its {@link BufferBindingParams#access | access} property set to `\"read_write\"`.\r\n */\r\nexport class WritableBufferBinding extends BufferBinding {\r\n /** Flag indicating whether whe should automatically copy the {@link buffer | GPU buffer} content into our {@link resultBuffer | result GPU buffer} */\r\n shouldCopyResult: boolean\r\n /** The result GPUBuffer */\r\n resultBuffer: Buffer\r\n /** Options used to create this {@link WritableBufferBinding} */\r\n options: WritableBufferBindingParams\r\n\r\n /**\r\n * WritableBufferBinding constructor\r\n * @param parameters - {@link WritableBufferBindingParams | parameters} used to create our {@link WritableBufferBinding}\r\n */\r\n constructor({\r\n label = 'Work',\r\n name = 'work',\r\n bindingType,\r\n visibility,\r\n useStruct = true,\r\n access = 'read_write',\r\n usage = [],\r\n struct = {},\r\n shouldCopyResult = false,\r\n }: WritableBufferBindingParams) {\r\n bindingType = 'storage'\r\n visibility = ['compute']\r\n\r\n super({ label, name, bindingType, visibility, useStruct, access, usage, struct })\r\n\r\n this.options = {\r\n ...this.options,\r\n shouldCopyResult,\r\n }\r\n\r\n this.shouldCopyResult = shouldCopyResult\r\n this.cacheKey += `${shouldCopyResult},`\r\n\r\n // can be used as our buffer copy destination\r\n this.resultBuffer = new Buffer()\r\n }\r\n}\r\n","import { isRenderer, Renderer } from '../renderers/utils'\r\nimport { generateUUID, toKebabCase } from '../../utils/utils'\r\nimport { WritableBufferBinding, WritableBufferBindingParams } from '../bindings/WritableBufferBinding'\r\nimport { BufferBinding } from '../bindings/BufferBinding'\r\nimport {\r\n AllowedBindGroups,\r\n BindGroupBindingElement,\r\n BindGroupBufferBindingElement,\r\n BindGroupEntries,\r\n BindGroupParams,\r\n ReadOnlyInputBindings,\r\n} from '../../types/BindGroups'\r\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\r\nimport { TextureBindGroupParams } from './TextureBindGroup'\r\nimport { BufferBindingType } from '../bindings/Binding'\r\nimport { BufferUsageKeys } from '../buffers/utils'\r\n\r\n/**\r\n * Used to handle all inputs data sent to the GPU.
\r\n * In WebGPU, data (buffers, textures or samplers, called bindings) are organised by bind groups, containing those bindings.\r\n *\r\n * ## Bindings\r\n *\r\n * A {@link BindGroup} is responsible for creating each {@link BufferBinding} {@link GPUBuffer} and then the {@link GPUBindGroup} and {@link GPUBindGroupLayout} that are used to create {@link GPUComputePipeline} or {@link GPURenderPipeline}.
\r\n * Those are generally automatically created by the {@link core/materials/Material.Material | Material} using this {@link BindGroup}. If you need to manually create them, you will have to call its {@link BindGroup#createBindGroup | `createBindGroup()` method}\r\n *\r\n * ### Samplers and textures\r\n *\r\n * A {@link BindGroup} is best suited to handle {@link GPUBuffer} only bindings. If you need to handle {@link GPUSampler}, a {@link GPUTexture} or a {@link GPUExternalTexture}, you should use a {@link core/bindGroups/TextureBindGroup.TextureBindGroup | TextureBindGroup} instead.\r\n *\r\n * ### Updating a GPUBindGroup or GPUBindGroupLayout\r\n *\r\n * Each time one of the {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createBindGroup#resource | binding resource} changes, its {@link BindGroup#bindGroup | bindGroup} will be recreated (usually, when a {@link GPUTexture} is uploaded).
\r\n * Each time one of the {@link https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createBindGroupLayout#resource_layout_objects | binding resource layout} changes, its {@link BindGroup#bindGroupLayout | bindGroupLayout} and {@link BindGroup#bindGroup | bindGroup} will be recreated, and the {@link GPUComputePipeline} or {@link GPURenderPipeline} will be recreated as well.\r\n *\r\n * @example\r\n * ```javascript\r\n * // set our main GPUCurtains instance\r\n * const gpuCurtains = new GPUCurtains({\r\n * container: '#canvas' // selector of our WebGPU canvas container\r\n * })\r\n *\r\n * // set the GPU device\r\n * // note this is asynchronous\r\n * await gpuCurtains.setDevice()\r\n *\r\n * const bindGroup = new BindGroup(gpuCurtains, {\r\n * label: 'My bind group',\r\n * uniforms: {\r\n * params: {\r\n * visibility: ['fragment'],\r\n * struct: {\r\n * opacity: {\r\n * value: 'f32',\r\n * value: 1,\r\n * },\r\n * mousePosition: {\r\n * value: 'vec2f',\r\n * value: new Vec2(),\r\n * },\r\n * },\r\n * },\r\n * },\r\n * })\r\n *\r\n * // create the GPU buffer, bindGroupLayout and bindGroup\r\n * bindGroup.createBindGroup()\r\n * ```\r\n */\r\nexport class BindGroup {\r\n /** The type of the {@link BindGroup} */\r\n type: string\r\n /** The universal unique id of the {@link BindGroup} */\r\n uuid: string\r\n /** The {@link Renderer} used */\r\n renderer: Renderer\r\n /** Options used to create this {@link BindGroup} */\r\n options: TextureBindGroupParams\r\n /** Index of this {@link BindGroup}, used to link struct in the shaders */\r\n index: number\r\n\r\n /** List of {@link BindGroupBindingElement | bindings} (buffers, texture, etc.) handled by this {@link BindGroup} */\r\n bindings: BindGroupBindingElement[]\r\n\r\n /** Our {@link BindGroup} {@link BindGroupEntries | entries} objects */\r\n entries: BindGroupEntries\r\n\r\n /** Our {@link BindGroup}{@link GPUBindGroupLayout} */\r\n bindGroupLayout: null | GPUBindGroupLayout\r\n /** Our {@link BindGroup} {@link GPUBindGroup} */\r\n bindGroup: null | GPUBindGroup\r\n\r\n /** A cache key allowing to get / set {@link GPUBindGroupLayout} from the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#bindGroupLayouts | device manager map cache}. */\r\n layoutCacheKey: string\r\n /** A cache key allowing the {@link core/pipelines/PipelineManager.PipelineManager | PipelineManager} to compare {@link core/pipelines/RenderPipelineEntry.RenderPipelineEntry | RenderPipelineEntry} bind groups content. */\r\n pipelineCacheKey: string\r\n\r\n /** Flag indicating whether we need to flush and recreate the pipeline using this {@link BindGroup} s*/\r\n needsPipelineFlush: boolean\r\n\r\n /** A Set to store this {@link BindGroup} consumers ({@link core/materials/Material.Material#uuid | Material uuid}) */\r\n consumers: Set\r\n\r\n /**\r\n * BindGroup constructor\r\n * @param renderer - a {@link Renderer} class object or a {@link GPUCurtains} class object\r\n * @param parameters - {@link BindGroupParams | parameters} used to create our {@link BindGroup}\r\n */\r\n constructor(\r\n renderer: Renderer | GPUCurtains,\r\n { label = 'BindGroup', index = 0, bindings = [], uniforms, storages }: BindGroupParams = {}\r\n ) {\r\n this.type = 'BindGroup'\r\n\r\n // we could pass our curtains object OR our curtains renderer object\r\n renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as Renderer)\r\n\r\n isRenderer(renderer, this.type)\r\n\r\n this.renderer = renderer\r\n this.options = {\r\n label,\r\n index,\r\n bindings,\r\n ...(uniforms && { uniforms }),\r\n ...(storages && { storages }),\r\n }\r\n\r\n this.index = index\r\n this.uuid = generateUUID()\r\n\r\n this.bindings = []\r\n bindings.length && this.addBindings(bindings)\r\n if (this.options.uniforms || this.options.storages) this.setInputBindings()\r\n\r\n this.layoutCacheKey = ''\r\n this.pipelineCacheKey = ''\r\n this.resetEntries()\r\n\r\n this.bindGroupLayout = null\r\n this.bindGroup = null\r\n\r\n // if we ever update our bind group layout\r\n // we will have to recreate the whole pipeline again\r\n this.needsPipelineFlush = false\r\n\r\n this.consumers = new Set()\r\n\r\n // add the bind group to the buffers consumers\r\n for (const binding of this.bufferBindings) {\r\n if ('buffer' in binding) {\r\n binding.buffer.consumers.add(this.uuid)\r\n }\r\n\r\n if ('resultBuffer' in binding) {\r\n binding.resultBuffer.consumers.add(this.uuid)\r\n }\r\n }\r\n\r\n this.renderer.addBindGroup(this)\r\n }\r\n\r\n /**\r\n * Sets our {@link BindGroup#index | bind group index}\r\n * @param index - {@link BindGroup#index | bind group index} to set\r\n */\r\n setIndex(index: number) {\r\n this.index = index\r\n }\r\n\r\n /**\r\n * Adds an array of already created {@link bindings} (buffers, texture, etc.) to the {@link bindings} array\r\n * @param bindings - {@link bindings} to add\r\n */\r\n addBindings(bindings: BindGroupBindingElement[] = []) {\r\n bindings.forEach((binding) => {\r\n if ('buffer' in binding) {\r\n this.renderer.deviceManager.bufferBindings.set(binding.cacheKey, binding)\r\n binding.buffer.consumers.add(this.uuid)\r\n }\r\n })\r\n\r\n this.bindings = [...this.bindings, ...bindings]\r\n }\r\n\r\n /**\r\n * Adds an already created {@link bindings} (buffers, texture, etc.) to the {@link bindings} array\r\n * @param binding - binding to add\r\n */\r\n addBinding(binding: BindGroupBindingElement) {\r\n this.bindings.push(binding)\r\n }\r\n\r\n /**\r\n * Creates Bindings based on a list of inputs\r\n * @param bindingType - {@link core/bindings/Binding.Binding#bindingType | binding type}\r\n * @param inputs - {@link ReadOnlyInputBindings | inputs (uniform or storage)} that will be used to create the binding\r\n * @returns - a {@link bindings} array\r\n */\r\n createInputBindings(\r\n bindingType: BufferBindingType = 'uniform',\r\n inputs: ReadOnlyInputBindings = {}\r\n ): BindGroupBindingElement[] {\r\n let bindings = [\r\n ...Object.keys(inputs).map((inputKey) => {\r\n const binding = inputs[inputKey] as WritableBufferBindingParams\r\n\r\n // bail if no struct\r\n if (!binding.struct) return\r\n\r\n const bindingParams: WritableBufferBindingParams = {\r\n label: toKebabCase(binding.label || inputKey),\r\n name: inputKey,\r\n bindingType,\r\n visibility: binding.access === 'read_write' ? ['compute'] : binding.visibility,\r\n useStruct: true, // by default\r\n access: binding.access ?? 'read', // read by default\r\n ...(binding.usage && { usage: binding.usage }),\r\n struct: binding.struct,\r\n ...(binding.shouldCopyResult !== undefined && { shouldCopyResult: binding.shouldCopyResult }),\r\n }\r\n\r\n if (binding.useStruct !== false) {\r\n let key = `${bindingType},${\r\n binding.visibility === undefined ? 'all' : binding.access === 'read_write' ? 'compute' : binding.visibility\r\n },true,${binding.access ?? 'read'},`\r\n\r\n Object.keys(binding.struct).forEach((bindingKey) => {\r\n key += `${bindingKey},${binding.struct[bindingKey].type},`\r\n })\r\n\r\n if (binding.shouldCopyResult !== undefined) {\r\n key += `${binding.shouldCopyResult},`\r\n }\r\n\r\n const cachedBinding = this.renderer.deviceManager.bufferBindings.get(key)\r\n\r\n if (cachedBinding) {\r\n return cachedBinding.clone(bindingParams)\r\n }\r\n }\r\n\r\n const BufferBindingConstructor = bindingParams.access === 'read_write' ? WritableBufferBinding : BufferBinding\r\n\r\n return binding.useStruct !== false\r\n ? new BufferBindingConstructor(bindingParams)\r\n : Object.keys(binding.struct).map((bindingKey) => {\r\n bindingParams.label = toKebabCase(binding.label ? binding.label + bindingKey : inputKey + bindingKey)\r\n bindingParams.name = inputKey + bindingKey\r\n bindingParams.useStruct = false\r\n bindingParams.struct = { [bindingKey]: binding.struct[bindingKey] }\r\n\r\n return new BufferBindingConstructor(bindingParams)\r\n })\r\n }),\r\n ].flat()\r\n\r\n // filter to leave only valid bindings\r\n bindings = bindings.filter(Boolean)\r\n\r\n bindings.forEach((binding) => {\r\n this.renderer.deviceManager.bufferBindings.set(binding.cacheKey, binding)\r\n })\r\n\r\n return bindings\r\n }\r\n\r\n /**\r\n * Create and adds {@link bindings} based on inputs provided upon creation\r\n */\r\n setInputBindings() {\r\n this.addBindings([\r\n ...this.createInputBindings('uniform', this.options.uniforms),\r\n ...this.createInputBindings('storage', this.options.storages),\r\n ])\r\n }\r\n\r\n /**\r\n * Get whether the GPU bind group is ready to be created\r\n * It can be created if it has {@link bindings} and has not been created yet\r\n * @readonly\r\n */\r\n get shouldCreateBindGroup(): boolean {\r\n return !this.bindGroup && !!this.bindings.length\r\n }\r\n\r\n /**\r\n * Reset our {@link BindGroup} {@link entries}\r\n */\r\n resetEntries() {\r\n this.entries = {\r\n bindGroupLayout: [],\r\n bindGroup: [],\r\n }\r\n }\r\n\r\n /**\r\n * Create the GPU buffers, {@link bindings}, {@link entries}, {@link bindGroupLayout} and {@link bindGroup}\r\n */\r\n createBindGroup() {\r\n this.fillEntries()\r\n this.setBindGroupLayout()\r\n this.setBindGroup()\r\n }\r\n\r\n /**\r\n * Reset the {@link BindGroup#entries.bindGroup | bindGroup entries}, recreates them and then recreate the {@link BindGroup#bindGroup | GPU bind group}\r\n */\r\n resetBindGroup() {\r\n this.entries.bindGroup = []\r\n this.pipelineCacheKey = ''\r\n\r\n for (const binding of this.bindings) {\r\n this.addBindGroupEntry(binding)\r\n }\r\n\r\n this.setBindGroup()\r\n }\r\n\r\n /**\r\n * Add a {@link BindGroup#entries.bindGroup | bindGroup entry}\r\n * @param binding - {@link BindGroupBindingElement | binding} to add\r\n */\r\n addBindGroupEntry(binding: BindGroupBindingElement) {\r\n this.entries.bindGroup.push({\r\n binding: this.entries.bindGroup.length,\r\n resource: binding.resource,\r\n })\r\n\r\n this.pipelineCacheKey += binding.cacheKey\r\n }\r\n\r\n /**\r\n * Reset the {@link BindGroup#entries.bindGroupLayout | bindGroupLayout entries}, recreates them and then recreate the {@link BindGroup#bindGroupLayout | GPU bind group layout}\r\n */\r\n resetBindGroupLayout() {\r\n this.entries.bindGroupLayout = []\r\n this.layoutCacheKey = ''\r\n\r\n for (const binding of this.bindings) {\r\n this.addBindGroupLayoutEntry(binding)\r\n }\r\n\r\n this.setBindGroupLayout()\r\n }\r\n\r\n /**\r\n * Add a {@link BindGroup#entries.bindGroupLayout | bindGroupLayout entry}\r\n * @param binding - {@link BindGroupBindingElement | binding} to add\r\n */\r\n addBindGroupLayoutEntry(binding: BindGroupBindingElement) {\r\n this.entries.bindGroupLayout.push({\r\n binding: this.entries.bindGroupLayout.length,\r\n ...binding.resourceLayout,\r\n visibility: binding.visibility,\r\n })\r\n\r\n this.layoutCacheKey += binding.resourceLayoutCacheKey\r\n }\r\n\r\n /**\r\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been lost to prepare everything for restoration\r\n */\r\n loseContext() {\r\n this.resetEntries()\r\n\r\n for (const binding of this.bufferBindings) {\r\n binding.buffer.reset()\r\n\r\n if ('resultBuffer' in binding) {\r\n binding.resultBuffer.reset()\r\n }\r\n }\r\n\r\n this.bindGroup = null\r\n this.bindGroupLayout = null\r\n this.needsPipelineFlush = true\r\n }\r\n\r\n /**\r\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been restored to update our bindings.\r\n */\r\n restoreContext() {\r\n if (this.shouldCreateBindGroup) {\r\n this.createBindGroup()\r\n }\r\n\r\n // finally re-write all our buffers\r\n for (const bufferBinding of this.bufferBindings) {\r\n bufferBinding.shouldUpdate = true\r\n }\r\n }\r\n\r\n /**\r\n * Get all {@link BindGroup#bindings | bind group bindings} that handle a {@link GPUBuffer}\r\n */\r\n get bufferBindings(): BindGroupBufferBindingElement[] {\r\n return this.bindings.filter(\r\n (binding) => binding instanceof BufferBinding || binding instanceof WritableBufferBinding\r\n ) as BindGroupBufferBindingElement[]\r\n }\r\n\r\n /**\r\n * Creates binding GPUBuffer with correct params\r\n * @param binding - the binding element\r\n */\r\n createBindingBuffer(binding: BindGroupBufferBindingElement) {\r\n // [Kangz](https://github.com/Kangz) said:\r\n // \"In general though COPY_SRC/DST is free (at least in Dawn / Chrome because we add it all the time for our own purpose).\"\r\n binding.buffer.createBuffer(this.renderer, {\r\n label: this.options.label + ': ' + binding.bindingType + ' buffer from: ' + binding.label,\r\n usage: [...(['copySrc', 'copyDst', binding.bindingType] as BufferUsageKeys[]), ...binding.options.usage],\r\n })\r\n\r\n if ('resultBuffer' in binding) {\r\n binding.resultBuffer.createBuffer(this.renderer, {\r\n label: this.options.label + ': Result buffer from: ' + binding.label,\r\n size: binding.arrayBuffer.byteLength,\r\n usage: ['copyDst', 'mapRead'],\r\n })\r\n }\r\n }\r\n\r\n /**\r\n * Fill in our entries bindGroupLayout and bindGroup arrays with the correct binding resources.\r\n * For buffer struct, create a GPUBuffer first if needed\r\n */\r\n fillEntries() {\r\n for (const binding of this.bindings) {\r\n // if no visibility specified, just set it to the maximum default capabilities\r\n if (!binding.visibility) {\r\n binding.visibility = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE\r\n }\r\n\r\n // if it's a buffer binding, create the GPUBuffer\r\n if ('buffer' in binding) {\r\n if (!binding.buffer.GPUBuffer) {\r\n this.createBindingBuffer(binding)\r\n }\r\n }\r\n\r\n // now that everything is ready, fill our entries\r\n this.addBindGroupLayoutEntry(binding)\r\n this.addBindGroupEntry(binding)\r\n }\r\n }\r\n\r\n /**\r\n * Get a bind group binding by name/key\r\n * @param bindingName - the binding name or key\r\n * @returns - the found binding, or null if not found\r\n */\r\n getBindingByName(bindingName = ''): BindGroupBindingElement | null {\r\n return this.bindings.find((binding) => binding.name === bindingName)\r\n }\r\n\r\n /**\r\n * Create a GPUBindGroupLayout and set our {@link bindGroupLayout}\r\n */\r\n setBindGroupLayout() {\r\n const bindGroupLayout = this.renderer.deviceManager.bindGroupLayouts.get(this.layoutCacheKey)\r\n\r\n if (bindGroupLayout) {\r\n this.bindGroupLayout = bindGroupLayout\r\n } else {\r\n this.bindGroupLayout = this.renderer.createBindGroupLayout({\r\n label: this.options.label + ' layout',\r\n entries: this.entries.bindGroupLayout,\r\n })\r\n\r\n this.renderer.deviceManager.bindGroupLayouts.set(this.layoutCacheKey, this.bindGroupLayout)\r\n }\r\n }\r\n\r\n /**\r\n * Create a GPUBindGroup and set our {@link bindGroup}\r\n */\r\n setBindGroup() {\r\n this.bindGroup = this.renderer.createBindGroup({\r\n label: this.options.label,\r\n layout: this.bindGroupLayout,\r\n entries: this.entries.bindGroup,\r\n })\r\n }\r\n\r\n /**\r\n * Check whether we should update (write) our {@link GPUBuffer} or not.\r\n */\r\n updateBufferBindings() {\r\n this.bindings.forEach((binding, index) => {\r\n if ('buffer' in binding) {\r\n // update binding elements\r\n binding.update()\r\n\r\n // now write to the GPUBuffer if needed\r\n if (binding.shouldUpdate) {\r\n // bufferOffset is always equals to 0 in our case\r\n if (!binding.useStruct && binding.bufferElements.length > 1) {\r\n // we're in a non struct buffer binding with multiple entries\r\n // that should not happen but that way we're covered\r\n this.renderer.queueWriteBuffer(binding.buffer.GPUBuffer, 0, binding.bufferElements[index].view)\r\n } else {\r\n this.renderer.queueWriteBuffer(binding.buffer.GPUBuffer, 0, binding.arrayBuffer)\r\n }\r\n }\r\n\r\n // reset update flag\r\n binding.shouldUpdate = false\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * Update the {@link BindGroup}, which means update its {@link BindGroup#bufferBindings | buffer bindings} and {@link BindGroup#resetBindGroup | reset it} if needed.\r\n * Called at each render from the parentMesh {@link core/materials/Material.Material | material}\r\n */\r\n update() {\r\n this.updateBufferBindings()\r\n\r\n const needBindGroupReset = this.bindings.some((binding) => binding.shouldResetBindGroup)\r\n const needBindGroupLayoutReset = this.bindings.some((binding) => binding.shouldResetBindGroupLayout)\r\n\r\n // since other bind groups might be using that binding\r\n // wait for the end of the render loop to reset the bindings flags\r\n if (needBindGroupReset || needBindGroupLayoutReset) {\r\n this.renderer.onAfterCommandEncoderSubmission.add(\r\n () => {\r\n for (const binding of this.bindings) {\r\n binding.shouldResetBindGroup = false\r\n binding.shouldResetBindGroupLayout = false\r\n }\r\n },\r\n { once: true }\r\n )\r\n }\r\n\r\n if (needBindGroupLayoutReset) {\r\n this.resetBindGroupLayout()\r\n // bind group layout has changed, we have to recreate the pipeline\r\n this.needsPipelineFlush = true\r\n }\r\n\r\n if (needBindGroupReset) {\r\n this.resetBindGroup()\r\n }\r\n }\r\n\r\n /**\r\n * Clones a {@link BindGroup} from a list of {@link bindings}\r\n * Useful to create a new bind group with already created buffers, but swapped\r\n * @param parameters - parameters to use for cloning\r\n * @param parameters.bindings - our input {@link bindings}\r\n * @param [parameters.keepLayout=false] - whether we should keep original {@link bindGroupLayout} or not\r\n * @returns - the cloned {@link BindGroup}\r\n */\r\n clone({\r\n bindings = [],\r\n keepLayout = false,\r\n }: {\r\n bindings?: BindGroupBindingElement[]\r\n keepLayout?: boolean\r\n } = {}): AllowedBindGroups {\r\n const params = { ...this.options }\r\n params.label += ' (copy)'\r\n\r\n const bindGroupCopy = new (this.constructor as typeof BindGroup)(this.renderer, {\r\n label: params.label,\r\n })\r\n\r\n bindGroupCopy.setIndex(this.index)\r\n bindGroupCopy.options = params\r\n\r\n const bindingsRef = bindings.length ? bindings : this.bindings\r\n\r\n for (const binding of bindingsRef) {\r\n bindGroupCopy.addBinding(binding)\r\n\r\n // if it's a buffer binding without a GPUBuffer, create it now\r\n if ('buffer' in binding) {\r\n if (!binding.buffer.GPUBuffer) {\r\n this.createBindingBuffer(binding)\r\n }\r\n\r\n binding.buffer.consumers.add(bindGroupCopy.uuid)\r\n\r\n if ('resultBuffer' in binding) {\r\n binding.resultBuffer.consumers.add(bindGroupCopy.uuid)\r\n }\r\n }\r\n\r\n // if we should create a new bind group layout, fill it\r\n if (!keepLayout) {\r\n bindGroupCopy.addBindGroupLayoutEntry(binding)\r\n }\r\n\r\n bindGroupCopy.addBindGroupEntry(binding)\r\n }\r\n\r\n // if we should copy the given bind group layout\r\n if (keepLayout) {\r\n bindGroupCopy.entries.bindGroupLayout = [...this.entries.bindGroupLayout]\r\n bindGroupCopy.layoutCacheKey = this.layoutCacheKey\r\n }\r\n\r\n bindGroupCopy.setBindGroupLayout()\r\n bindGroupCopy.setBindGroup()\r\n\r\n return bindGroupCopy\r\n }\r\n\r\n /**\r\n * Destroy our {@link BindGroup}\r\n * Most important is to destroy the GPUBuffers to free the memory\r\n */\r\n destroy() {\r\n this.renderer.removeBindGroup(this)\r\n\r\n for (const binding of this.bufferBindings) {\r\n if ('buffer' in binding) {\r\n this.renderer.removeBuffer(binding.buffer)\r\n\r\n binding.buffer.consumers.delete(this.uuid)\r\n if (!binding.buffer.consumers.size) {\r\n binding.buffer.destroy()\r\n }\r\n }\r\n\r\n if ('resultBuffer' in binding) {\r\n this.renderer.removeBuffer(binding.resultBuffer)\r\n\r\n binding.resultBuffer.consumers.delete(this.uuid)\r\n if (!binding.resultBuffer.consumers.size) {\r\n binding.resultBuffer.destroy()\r\n }\r\n }\r\n }\r\n\r\n this.bindings = []\r\n this.bindGroupLayout = null\r\n this.bindGroup = null\r\n this.resetEntries()\r\n }\r\n}\r\n","import { Binding, BindingMemoryAccessType, BindingParams, DOMTextureBindingType } from './Binding'\r\nimport {\r\n getBindGroupLayoutTextureBindingCacheKey,\r\n getBindGroupLayoutTextureBindingType,\r\n getTextureBindingWGSLVarType,\r\n} from './utils'\r\n\r\n/** Defines a {@link TextureBinding} {@link TextureBinding#resource | resource} */\r\nexport type TextureBindingResource = GPUTexture | GPUExternalTexture | null\r\n\r\n/**\r\n * An object defining all possible {@link TextureBinding} class instancing parameters\r\n */\r\nexport interface TextureBindingParams extends BindingParams {\r\n /** The binding type of the {@link TextureBinding} */\r\n bindingType?: DOMTextureBindingType\r\n /** {@link TextureBinding} {@link TextureBinding#resource | resource} */\r\n texture: TextureBindingResource\r\n /** The {@link GPUTexture | texture} format to use */\r\n format?: GPUTextureFormat\r\n /** The storage {@link GPUTexture | texture} binding memory access types (read only, write only or read/write) */\r\n access?: BindingMemoryAccessType\r\n /** The {@link GPUTexture | texture} view dimension to use */\r\n viewDimension?: GPUTextureViewDimension\r\n /** Whethe the {@link GPUTexture | texture} is a multisampled texture. Mainly used internally by depth textures if needed. */\r\n multisampled?: boolean\r\n}\r\n\r\n/**\r\n * Used to handle {@link GPUTexture} and {@link GPUExternalTexture} bindings.\r\n *\r\n * Provide both {@link TextureBinding#resourceLayout | resourceLayout} and {@link TextureBinding#resource | resource} to the {@link GPUBindGroupLayout} and {@link GPUBindGroup}.
\r\n * Also create the appropriate WGSL code snippet to add to the shaders.\r\n */\r\nexport class TextureBinding extends Binding {\r\n /** The binding type of the {@link TextureBinding} */\r\n bindingType: DOMTextureBindingType\r\n /** Our {@link TextureBinding} resource, i.e. a {@link GPUTexture} or {@link GPUExternalTexture} */\r\n texture: TextureBindingResource\r\n /** An array of strings to append to our shaders code declaring all the WGSL variables representing this {@link TextureBinding} */\r\n wgslGroupFragment: string[]\r\n /** Options used to create this {@link TextureBinding} */\r\n options: TextureBindingParams\r\n\r\n /**\r\n * TextureBinding constructor\r\n * @param parameters - {@link TextureBindingParams | parameters} used to create our {@link TextureBinding}\r\n */\r\n constructor({\r\n label = 'Texture',\r\n name = 'texture',\r\n bindingType,\r\n visibility,\r\n texture,\r\n format = 'rgba8unorm',\r\n access = 'write',\r\n viewDimension = '2d',\r\n multisampled = false,\r\n }: TextureBindingParams) {\r\n bindingType = bindingType ?? 'texture'\r\n\r\n if (bindingType === 'storage') {\r\n visibility = ['compute']\r\n }\r\n\r\n super({ label, name, bindingType, visibility })\r\n\r\n this.options = {\r\n ...this.options,\r\n texture,\r\n format,\r\n access,\r\n viewDimension,\r\n multisampled,\r\n }\r\n\r\n this.cacheKey += `${format},${access},${viewDimension},${multisampled},`\r\n\r\n this.resource = texture // should be a texture or an external texture\r\n\r\n this.setWGSLFragment()\r\n }\r\n\r\n /**\r\n * Get bind group layout entry resource, either for {@link GPUBindGroupLayoutEntry#texture | texture} or {@link GPUBindGroupLayoutEntry#externalTexture | external texture}\r\n * @readonly\r\n */\r\n get resourceLayout():\r\n | GPUTextureBindingLayout\r\n | GPUExternalTextureBindingLayout\r\n | GPUStorageTextureBindingLayout\r\n | null {\r\n return getBindGroupLayoutTextureBindingType(this)\r\n }\r\n\r\n /**\r\n * Get the resource cache key\r\n * @readonly\r\n */\r\n get resourceLayoutCacheKey(): string {\r\n return getBindGroupLayoutTextureBindingCacheKey(this)\r\n }\r\n\r\n /**\r\n * Get the {@link GPUBindGroupEntry#resource | bind group resource}\r\n */\r\n get resource(): GPUExternalTexture | GPUTextureView | null {\r\n return this.texture instanceof GPUTexture\r\n ? this.texture.createView({ label: this.options.label + ' view', dimension: this.options.viewDimension })\r\n : this.texture instanceof GPUExternalTexture\r\n ? this.texture\r\n : null\r\n }\r\n\r\n /**\r\n * Set the {@link GPUBindGroupEntry#resource | bind group resource}\r\n * @param value - new bind group resource\r\n */\r\n set resource(value: TextureBindingResource) {\r\n // resource changed, update bind group!\r\n if (value || this.texture) this.shouldResetBindGroup = true\r\n this.texture = value\r\n }\r\n\r\n /**\r\n * Set or update our {@link Binding#bindingType | bindingType} and our WGSL code snippet\r\n * @param bindingType - the new {@link Binding#bindingType | binding type}\r\n */\r\n setBindingType(bindingType: DOMTextureBindingType) {\r\n if (bindingType !== this.bindingType) {\r\n // binding type has changed!\r\n if (bindingType) this.shouldResetBindGroupLayout = true\r\n\r\n this.bindingType = bindingType\r\n this.cacheKey = `${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`\r\n this.setWGSLFragment()\r\n }\r\n }\r\n\r\n /**\r\n * Set or update our texture {@link TextureBindingParams#format | format}. Note that if the texture is a `storage` {@link bindingType} and the `format` value is different from the previous one, the associated {@link core/bindGroups/BindGroup.BindGroup#bindGroupLayout | GPU bind group layout} will be recreated.\r\n * @param format - new texture {@link TextureBindingParams#format | format} value to use\r\n */\r\n setFormat(format: GPUTextureFormat) {\r\n const isNewFormat = format !== this.options.format\r\n this.options.format = format\r\n\r\n if (isNewFormat && this.bindingType === 'storage') {\r\n this.setWGSLFragment()\r\n this.shouldResetBindGroupLayout = true\r\n this.cacheKey = `${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`\r\n }\r\n }\r\n\r\n /**\r\n * Set or update our texture {@link TextureBindingParams#multisampled | multisampled}. Note that if the texture is not a `storage` {@link bindingType} and the `multisampled` value is different from the previous one, the associated {@link core/bindGroups/BindGroup.BindGroup#bindGroupLayout | GPU bind group layout} will be recreated.\r\n * @param multisampled - new texture {@link TextureBindingParams#multisampled | multisampled} value to use\r\n */\r\n setMultisampled(multisampled: boolean) {\r\n const isNewMultisampled = multisampled !== this.options.multisampled\r\n this.options.multisampled = multisampled\r\n\r\n if (isNewMultisampled && this.bindingType !== 'storage') {\r\n this.setWGSLFragment()\r\n this.shouldResetBindGroupLayout = true\r\n this.cacheKey = `${this.bindingType},${this.visibility},${this.options.format},${this.options.access},${this.options.viewDimension},${this.options.multisampled},`\r\n }\r\n }\r\n\r\n /**\r\n * Set the correct WGSL code snippet.\r\n */\r\n setWGSLFragment() {\r\n this.wgslGroupFragment = [`${getTextureBindingWGSLVarType(this)}`]\r\n }\r\n}\r\n","import { Vec3 } from './Vec3'\nimport { Quat } from './Quat'\n\nconst xAxis = new Vec3()\nconst yAxis = new Vec3()\nconst zAxis = new Vec3()\n\n/**\n * Basic 4x4 matrix class used for matrix calculations.\n *\n * Note that like three.js, the constructor and {@link set} method take arguments in row-major order, while internally they are stored in the {@link elements} array in column-major order.\n *\n * @see https://github.com/mrdoob/three.js/blob/dev/src/math/Matrix4.js\n * @see http://glmatrix.net/docs/mat4.js.html\n */\nexport class Mat4 {\n /** The type of the {@link Mat4} */\n type: string\n /** Our matrix array */\n elements: Float32Array\n\n // prettier-ignore\n /**\n * Mat4 constructor\n * @param elements - initial array to use, default to identity matrix\n */\n constructor(elements: Float32Array = new Float32Array([\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n ])) {\n this.type = 'Mat4'\n this.elements = elements\n }\n\n /***\n * Sets the matrix from 16 numbers\n *\n * @param n11 - number\n * @param n12 - number\n * @param n13 - number\n * @param n14 - number\n * @param n21 - number\n * @param n22 - number\n * @param n23 - number\n * @param n24 - number\n * @param n31 - number\n * @param n32 - number\n * @param n33 - number\n * @param n34 - number\n * @param n41 - number\n * @param n42 - number\n * @param n43 - number\n * @param n44 - number\n *\n * @returns - this {@link Mat4} after being set\n */\n set(\n n11: number,\n n12: number,\n n13: number,\n n14: number,\n n21: number,\n n22: number,\n n23: number,\n n24: number,\n n31: number,\n n32: number,\n n33: number,\n n34: number,\n n41: number,\n n42: number,\n n43: number,\n n44: number\n ): Mat4 {\n const te = this.elements\n\n te[0] = n11\n te[1] = n12\n te[2] = n13\n te[3] = n14\n te[4] = n21\n te[5] = n22\n te[6] = n23\n te[7] = n24\n te[8] = n31\n te[9] = n32\n te[10] = n33\n te[11] = n34\n te[12] = n41\n te[13] = n42\n te[14] = n43\n te[15] = n44\n\n return this\n }\n\n /**\n * Sets the {@link Mat4} to an identity matrix\n * @returns - this {@link Mat4} after being set\n */\n identity(): Mat4 {\n // prettier-ignore\n this.set(\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n )\n\n return this\n }\n\n /**\n * Sets the {@link Mat4} values from an array\n * @param array - array to use\n * @returns - this {@link Mat4} after being set\n */\n // prettier-ignore\n setFromArray(array: Float32Array | number[] = new Float32Array([\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n ])): Mat4 {\n for (let i = 0; i < this.elements.length; i++) {\n this.elements[i] = array[i]\n }\n\n return this\n }\n\n /**\n * Copy another {@link Mat4}\n * @param matrix - matrix to copy\n * @returns - this {@link Mat4} after being set\n */\n copy(matrix: Mat4 = new Mat4()): Mat4 {\n const array = matrix.elements\n this.elements[0] = array[0]\n this.elements[1] = array[1]\n this.elements[2] = array[2]\n this.elements[3] = array[3]\n this.elements[4] = array[4]\n this.elements[5] = array[5]\n this.elements[6] = array[6]\n this.elements[7] = array[7]\n this.elements[8] = array[8]\n this.elements[9] = array[9]\n this.elements[10] = array[10]\n this.elements[11] = array[11]\n this.elements[12] = array[12]\n this.elements[13] = array[13]\n this.elements[14] = array[14]\n this.elements[15] = array[15]\n\n return this\n }\n\n /**\n * Clone a {@link Mat4}\n * @returns - cloned {@link Mat4}\n */\n clone(): Mat4 {\n return new Mat4().copy(this)\n }\n\n /**\n * Multiply this {@link Mat4} with another {@link Mat4}\n * @param matrix - {@link Mat4} to multiply with\n * @returns - this {@link Mat4} after multiplication\n */\n multiply(matrix: Mat4 = new Mat4()): Mat4 {\n return this.multiplyMatrices(this, matrix)\n }\n\n /**\n * Multiply another {@link Mat4} with this {@link Mat4}\n * @param matrix - {@link Mat4} to multiply with\n * @returns - this {@link Mat4} after multiplication\n */\n premultiply(matrix: Mat4 = new Mat4()): Mat4 {\n return this.multiplyMatrices(matrix, this)\n }\n\n /**\n * Multiply two {@link Mat4}\n * @param a - first {@link Mat4}\n * @param b - second {@link Mat4}\n * @returns - {@link Mat4} resulting from the multiplication\n */\n multiplyMatrices(a: Mat4 = new Mat4(), b: Mat4 = new Mat4()): Mat4 {\n const ae = a.elements\n const be = b.elements\n const te = this.elements\n\n const a11 = ae[0],\n a12 = ae[4],\n a13 = ae[8],\n a14 = ae[12]\n const a21 = ae[1],\n a22 = ae[5],\n a23 = ae[9],\n a24 = ae[13]\n const a31 = ae[2],\n a32 = ae[6],\n a33 = ae[10],\n a34 = ae[14]\n const a41 = ae[3],\n a42 = ae[7],\n a43 = ae[11],\n a44 = ae[15]\n\n const b11 = be[0],\n b12 = be[4],\n b13 = be[8],\n b14 = be[12]\n const b21 = be[1],\n b22 = be[5],\n b23 = be[9],\n b24 = be[13]\n const b31 = be[2],\n b32 = be[6],\n b33 = be[10],\n b34 = be[14]\n const b41 = be[3],\n b42 = be[7],\n b43 = be[11],\n b44 = be[15]\n\n te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41\n te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42\n te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43\n te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44\n\n te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41\n te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42\n te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43\n te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44\n\n te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41\n te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42\n te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43\n te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44\n\n te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41\n te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42\n te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43\n te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44\n\n return this\n }\n\n /**\n * {@link premultiply} this {@link Mat4} by a translate matrix (i.e. translateMatrix = new Mat4().translate(vector))\n * @param vector - translation {@link Vec3 | vector} to use\n * @returns - this {@link Mat4} after the premultiply translate operation\n */\n premultiplyTranslate(vector: Vec3 = new Vec3()): Mat4 {\n // premultiply by a translateMatrix, ie translateMatrix = new Mat4().translate(vector)\n // where translateMatrix[0] = 1, translateMatrix[5] = 1, scaleMatrix[10] = 1, translateMatrix[15] = 1 from identity\n // and translateMatrix[12] = vector.x, translateMatrix[13] = vector.y, translateMatrix[14] = vector.z from translation\n // equivalent (but faster) to this.multiply(translateMatrix, this)\n\n // from identity matrix\n const a11 = 1\n const a22 = 1\n const a33 = 1\n const a44 = 1\n\n // from translation\n const a14 = vector.x\n const a24 = vector.y\n const a34 = vector.z\n\n const be = this.elements\n const te = this.elements\n\n const b11 = be[0],\n b12 = be[4],\n b13 = be[8],\n b14 = be[12]\n const b21 = be[1],\n b22 = be[5],\n b23 = be[9],\n b24 = be[13]\n const b31 = be[2],\n b32 = be[6],\n b33 = be[10],\n b34 = be[14]\n const b41 = be[3],\n b42 = be[7],\n b43 = be[11],\n b44 = be[15]\n\n te[0] = a11 * b11 + a14 * b41\n te[4] = a11 * b12 + a14 * b42\n te[8] = a11 * b13 + a14 * b43\n te[12] = a11 * b14 + a14 * b44\n\n te[1] = a22 * b21 + a24 * b41\n te[5] = a22 * b22 + a24 * b42\n te[9] = a22 * b23 + a24 * b43\n te[13] = a22 * b24 + a24 * b44\n\n te[2] = a33 * b31 + a34 * b41\n te[6] = a33 * b32 + a34 * b42\n te[10] = a33 * b33 + a34 * b43\n te[14] = a33 * b34 + a34 * b44\n\n te[3] = a44 * b41\n te[7] = a44 * b42\n te[11] = a44 * b43\n te[15] = a44 * b44\n\n return this\n }\n\n /**\n * {@link premultiply} this {@link Mat4} by a scale matrix (i.e. translateMatrix = new Mat4().scale(vector))\n * @param vector - scale {@link Vec3 | vector} to use\n * @returns - this {@link Mat4} after the premultiply scale operation\n */\n premultiplyScale(vector: Vec3 = new Vec3()): Mat4 {\n // premultiply by a scaleMatrix, ie scaleMatrix = new Mat4().scale(vector)\n // where scaleMatrix[0] = vector.x, scaleMatrix[5] = vector.y, scaleMatrix[10] = vector.z, scaleMatrix[15] = 1\n // equivalent (but faster) to this.multiply(scaleMatrix, this)\n\n const be = this.elements\n const te = this.elements\n\n const a11 = vector.x\n const a22 = vector.y\n const a33 = vector.z\n const a44 = 1\n\n const b11 = be[0],\n b12 = be[4],\n b13 = be[8],\n b14 = be[12]\n const b21 = be[1],\n b22 = be[5],\n b23 = be[9],\n b24 = be[13]\n const b31 = be[2],\n b32 = be[6],\n b33 = be[10],\n b34 = be[14]\n const b41 = be[3],\n b42 = be[7],\n b43 = be[11],\n b44 = be[15]\n\n te[0] = a11 * b11\n te[4] = a11 * b12\n te[8] = a11 * b13\n te[12] = a11 * b14\n\n te[1] = a22 * b21\n te[5] = a22 * b22\n te[9] = a22 * b23\n te[13] = a22 * b24\n\n te[2] = a33 * b31\n te[6] = a33 * b32\n te[10] = a33 * b33\n te[14] = a33 * b34\n\n te[3] = a44 * b41\n te[7] = a44 * b42\n te[11] = a44 * b43\n te[15] = a44 * b44\n\n return this\n }\n\n /**\n * Get the {@link Mat4} inverse\n * @returns - the inverted {@link Mat4}\n */\n invert() {\n // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n const te = this.elements,\n n11 = te[0],\n n21 = te[1],\n n31 = te[2],\n n41 = te[3],\n n12 = te[4],\n n22 = te[5],\n n32 = te[6],\n n42 = te[7],\n n13 = te[8],\n n23 = te[9],\n n33 = te[10],\n n43 = te[11],\n n14 = te[12],\n n24 = te[13],\n n34 = te[14],\n n44 = te[15],\n t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,\n t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,\n t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,\n t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34\n\n const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14\n\n if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)\n\n const detInv = 1 / det\n\n te[0] = t11 * detInv\n te[1] =\n (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) *\n detInv\n te[2] =\n (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) *\n detInv\n te[3] =\n (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) *\n detInv\n\n te[4] = t12 * detInv\n te[5] =\n (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) *\n detInv\n te[6] =\n (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) *\n detInv\n te[7] =\n (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) *\n detInv\n\n te[8] = t13 * detInv\n te[9] =\n (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) *\n detInv\n te[10] =\n (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) *\n detInv\n te[11] =\n (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) *\n detInv\n\n te[12] = t14 * detInv\n te[13] =\n (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) *\n detInv\n te[14] =\n (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) *\n detInv\n te[15] =\n (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) *\n detInv\n\n return this\n }\n\n /**\n * Clone and invert the {@link Mat4}\n * @returns - inverted cloned {@link Mat4}\n */\n getInverse(): Mat4 {\n return this.clone().invert()\n }\n\n /**\n * Transpose this {@link Mat4}\n * @returns - the transposed {@link Mat4}\n */\n transpose(): Mat4 {\n let t\n const te = this.elements\n\n t = te[1]\n te[1] = te[4]\n te[4] = t\n\n t = te[2]\n te[2] = te[8]\n te[8] = t\n\n t = te[3]\n te[3] = te[12]\n te[12] = t\n\n t = te[6]\n te[6] = te[9]\n te[9] = t\n\n t = te[7]\n te[7] = te[13]\n te[13] = t\n\n t = te[11]\n te[11] = te[14]\n te[14] = t\n\n return this\n }\n\n /**\n * Translate a {@link Mat4}\n * @param vector - translation {@link Vec3 | vector} to use\n * @returns - translated {@link Mat4}\n */\n translate(vector: Vec3 = new Vec3()): Mat4 {\n const a = this.elements\n\n a[12] = a[0] * vector.x + a[4] * vector.y + a[8] * vector.z + a[12]\n a[13] = a[1] * vector.x + a[5] * vector.y + a[9] * vector.z + a[13]\n a[14] = a[2] * vector.x + a[6] * vector.y + a[10] * vector.z + a[14]\n a[15] = a[3] * vector.x + a[7] * vector.y + a[11] * vector.z + a[15]\n\n return this\n }\n\n /**\n * Get the translation {@link Vec3} component of a {@link Mat4}\n * @param position - {@link Vec3} to set\n * @returns - translation {@link Vec3} component of this {@link Mat4}\n */\n getTranslation(position = new Vec3()): Vec3 {\n return position.set(this.elements[12], this.elements[13], this.elements[14])\n }\n\n /**\n * Scale a {@link Mat4}\n * @param vector - scale {@link Vec3 | vector} to use\n * @returns - scaled {@link Mat4}\n */\n scale(vector: Vec3 = new Vec3()): Mat4 {\n const a = this.elements\n\n a[0] *= vector.x\n a[1] *= vector.x\n a[2] *= vector.x\n a[3] *= vector.x\n a[4] *= vector.y\n a[5] *= vector.y\n a[6] *= vector.y\n a[7] *= vector.y\n a[8] *= vector.z\n a[9] *= vector.z\n a[10] *= vector.z\n a[11] *= vector.z\n\n return this\n }\n\n /**\n * Rotate a {@link Mat4} from a {@link Quat | quaternion}\n * @param quaternion - {@link Quat | quaternion} to use\n * @returns - rotated {@link Mat4}\n */\n rotateFromQuaternion(quaternion: Quat = new Quat()): Mat4 {\n const te = this.elements\n\n const x = quaternion.elements[0],\n y = quaternion.elements[1],\n z = quaternion.elements[2],\n w = quaternion.elements[3]\n\n const x2 = x + x,\n y2 = y + y,\n z2 = z + z\n const xx = x * x2,\n xy = x * y2,\n xz = x * z2\n const yy = y * y2,\n yz = y * z2,\n zz = z * z2\n const wx = w * x2,\n wy = w * y2,\n wz = w * z2\n\n te[0] = 1 - (yy + zz)\n te[4] = xy - wz\n te[8] = xz + wy\n\n te[1] = xy + wz\n te[5] = 1 - (xx + zz)\n te[9] = yz - wx\n\n te[2] = xz - wy\n te[6] = yz + wx\n te[10] = 1 - (xx + yy)\n\n return this\n }\n\n /**\n * Get the maximum scale of the {@link Mat4} on all axes\n * @returns - maximum scale of the {@link Mat4}\n */\n getMaxScaleOnAxis(): number {\n const te = this.elements\n\n const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]\n const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]\n const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]\n\n return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq))\n }\n\n /**\n * Creates a {@link Mat4} from a {@link Quat | quaternion} rotation, {@link Vec3 | vector} translation and {@link Vec3 | vector} scale\n * Equivalent for applying translation, rotation and scale matrices but much faster\n * Source code from: http://glmatrix.net/docs/mat4.js.html\n *\n * @param translation - translation {@link Vec3 | vector} to use\n * @param quaternion - {@link Quat | quaternion} to use\n * @param scale - translation {@link Vec3 | vector} to use\n * @returns - transformed {@link Mat4}\n */\n compose(translation: Vec3 = new Vec3(), quaternion: Quat = new Quat(), scale: Vec3 = new Vec3(1)): Mat4 {\n const matrix = this.elements\n\n // Quaternion math\n const x = quaternion.elements[0],\n y = quaternion.elements[1],\n z = quaternion.elements[2],\n w = quaternion.elements[3]\n\n const x2 = x + x\n const y2 = y + y\n const z2 = z + z\n const xx = x * x2\n const xy = x * y2\n const xz = x * z2\n const yy = y * y2\n const yz = y * z2\n const zz = z * z2\n const wx = w * x2\n const wy = w * y2\n const wz = w * z2\n const sx = scale.x\n const sy = scale.y\n const sz = scale.z\n\n matrix[0] = (1 - (yy + zz)) * sx\n matrix[1] = (xy + wz) * sx\n matrix[2] = (xz - wy) * sx\n matrix[3] = 0\n matrix[4] = (xy - wz) * sy\n matrix[5] = (1 - (xx + zz)) * sy\n matrix[6] = (yz + wx) * sy\n matrix[7] = 0\n matrix[8] = (xz + wy) * sz\n matrix[9] = (yz - wx) * sz\n matrix[10] = (1 - (xx + yy)) * sz\n matrix[11] = 0\n matrix[12] = translation.x\n matrix[13] = translation.y\n matrix[14] = translation.z\n matrix[15] = 1\n\n return this\n }\n\n /**\n * Creates a {@link Mat4} from a {@link Quat | quaternion} rotation, {@link Vec3 | vector} translation and {@link Vec3 | vector} scale, rotating and scaling around the given {@link Vec3 | origin vector}\n * Equivalent for applying translation, rotation and scale matrices but much faster\n * Source code from: http://glmatrix.net/docs/mat4.js.html\n *\n * @param translation - translation {@link Vec3 | vector} to use\n * @param quaternion - {@link Quat | quaternion} to use\n * @param scale - translation {@link Vec3 | vector} to use\n * @param origin - origin {@link Vec3 | vector} around which to scale and rotate\n * @returns - transformed {@link Mat4}\n */\n composeFromOrigin(\n translation: Vec3 = new Vec3(),\n quaternion: Quat = new Quat(),\n scale: Vec3 = new Vec3(1),\n origin: Vec3 = new Vec3()\n ): Mat4 {\n const matrix = this.elements\n\n // Quaternion math\n const x = quaternion.elements[0],\n y = quaternion.elements[1],\n z = quaternion.elements[2],\n w = quaternion.elements[3]\n\n const x2 = x + x\n const y2 = y + y\n const z2 = z + z\n\n const xx = x * x2\n const xy = x * y2\n const xz = x * z2\n const yy = y * y2\n const yz = y * z2\n const zz = z * z2\n\n const wx = w * x2\n const wy = w * y2\n const wz = w * z2\n\n const sx = scale.x\n const sy = scale.y\n const sz = scale.z\n\n const ox = origin.x\n const oy = origin.y\n const oz = origin.z\n\n const out0 = (1 - (yy + zz)) * sx\n const out1 = (xy + wz) * sx\n const out2 = (xz - wy) * sx\n const out4 = (xy - wz) * sy\n const out5 = (1 - (xx + zz)) * sy\n const out6 = (yz + wx) * sy\n const out8 = (xz + wy) * sz\n const out9 = (yz - wx) * sz\n const out10 = (1 - (xx + yy)) * sz\n\n matrix[0] = out0\n matrix[1] = out1\n matrix[2] = out2\n matrix[3] = 0\n matrix[4] = out4\n matrix[5] = out5\n matrix[6] = out6\n matrix[7] = 0\n matrix[8] = out8\n matrix[9] = out9\n matrix[10] = out10\n matrix[11] = 0\n matrix[12] = translation.x + ox - (out0 * ox + out4 * oy + out8 * oz)\n matrix[13] = translation.y + oy - (out1 * ox + out5 * oy + out9 * oz)\n matrix[14] = translation.z + oz - (out2 * ox + out6 * oy + out10 * oz)\n matrix[15] = 1\n\n return this\n }\n\n /**\n * Set this {@link Mat4} as a rotation matrix based on an eye, target and up {@link Vec3 | vectors}\n * @param eye - {@link Vec3 | position vector} of the object that should be rotated\n * @param target - {@link Vec3 | target vector} to look at\n * @param up - up {@link Vec3 | vector}\n * @returns - rotated {@link Mat4}\n */\n lookAt(eye: Vec3 = new Vec3(), target: Vec3 = new Vec3(), up: Vec3 = new Vec3(0, 1, 0)): Mat4 {\n const te = this.elements\n\n zAxis.copy(eye).sub(target)\n\n if (zAxis.lengthSq() === 0) {\n // eye and target are in the same position\n zAxis.z = 1\n }\n\n zAxis.normalize()\n xAxis.crossVectors(up, zAxis)\n\n if (xAxis.lengthSq() === 0) {\n // up and z are parallel\n if (Math.abs(up.z) === 1) {\n zAxis.x += 0.0001\n } else {\n zAxis.z += 0.0001\n }\n\n zAxis.normalize()\n xAxis.crossVectors(up, zAxis)\n }\n\n xAxis.normalize()\n yAxis.crossVectors(zAxis, xAxis)\n\n te[0] = xAxis.x\n te[1] = xAxis.y\n te[2] = xAxis.z\n te[3] = 0\n te[4] = yAxis.x\n te[5] = yAxis.y\n te[6] = yAxis.z\n te[7] = 0\n te[8] = zAxis.x\n te[9] = zAxis.y\n te[10] = zAxis.z\n te[11] = 0\n te[12] = eye.x\n te[13] = eye.y\n te[14] = eye.z\n te[15] = 1\n\n return this\n }\n\n /**\n * Compute a view {@link Mat4} matrix.\n *\n * This is a view matrix which transforms all other objects\n * to be in the space of the view defined by the parameters.\n *\n * @param eye - the position of the object.\n * @param target - the position meant to be aimed at.\n * @param up - a vector pointing up.\n * @returns - the view {@link Mat4} matrix.\n */\n makeView(eye: Vec3 = new Vec3(), target: Vec3 = new Vec3(), up: Vec3 = new Vec3(0, 1, 0)): Mat4 {\n // TODO can easily be confused with lookAt\n\n zAxis.copy(eye).sub(target).normalize()\n xAxis.crossVectors(up, zAxis).normalize()\n yAxis.crossVectors(zAxis, xAxis).normalize()\n\n const te = this.elements\n\n te[0] = xAxis.x\n te[1] = yAxis.x\n te[2] = zAxis.x\n te[3] = 0\n te[4] = xAxis.y\n te[5] = yAxis.y\n te[6] = zAxis.y\n te[7] = 0\n te[8] = xAxis.z\n te[9] = yAxis.z\n te[10] = zAxis.z\n te[11] = 0\n\n te[12] = -(xAxis.x * eye.x + xAxis.y * eye.y + xAxis.z * eye.z)\n te[13] = -(yAxis.x * eye.x + yAxis.y * eye.y + yAxis.z * eye.z)\n te[14] = -(zAxis.x * eye.x + zAxis.y * eye.y + zAxis.z * eye.z)\n te[15] = 1\n\n return this\n }\n\n /**\n * Create an orthographic {@link Mat4} matrix based on the parameters. Transforms from\n * * the given the left, right, bottom, and top dimensions to -1 +1 in x, and y\n * * and 0 to +1 in z.\n *\n * @param parameters - parameters used to create the camera orthographic matrix.\n * @param parameters.left - the left side of the camera near clipping plane viewport.\n * @param parameters.right - the right side of the camera near clipping plane viewport.\n * @param parameters.bottom - the bottom of the camera near clipping plane viewport.\n * @param parameters.top - the top of the camera near clipping plane viewport.\n * @param parameters.near - the camera near plane.\n * @param parameters.far - the camera far plane.\n * @returns - the camera orthographic {@link Mat4} matrix.\n */\n makeOrthographic({\n left,\n right,\n bottom,\n top,\n near,\n far,\n }: {\n left: number\n right: number\n bottom: number\n top: number\n near: number\n far: number\n }): Mat4 {\n const te = this.elements\n\n te[0] = 2 / (right - left)\n te[1] = 0\n te[2] = 0\n te[3] = 0\n\n te[4] = 0\n te[5] = 2 / (top - bottom)\n te[6] = 0\n te[7] = 0\n\n te[8] = 0\n te[9] = 0\n te[10] = 1 / (near - far)\n te[11] = 0\n\n te[12] = (right + left) / (left - right)\n te[13] = (top + bottom) / (bottom - top)\n te[14] = near / (near - far)\n te[15] = 1\n\n return this\n }\n\n /**\n * Create a perspective {@link Mat4} matrix based on the parameters.\n *\n * Note, The matrix generated sends the viewing frustum to the unit box.\n * We assume a unit box extending from -1 to 1 in the x and y dimensions and\n * from -1 to 1 in the z dimension, as three.js and more generally WebGL handles it.\n *\n * @param parameters - parameters used to create the camera perspective matrix.\n * @param parameters.fov - the camera field of view (in radians).\n * @param parameters.aspect - the camera aspect ratio (width / height).\n * @param parameters.near - the camera near plane.\n * @param parameters.far - the camera far plane.\n * @returns - the camera perspective {@link Mat4} matrix.\n */\n makePerspective({ fov, aspect, near, far }: { fov: number; aspect: number; near: number; far: number }): Mat4 {\n const top = near * Math.tan((Math.PI / 180) * 0.5 * fov)\n const height = 2 * top\n const width = aspect * height\n const left = -0.5 * width\n\n const right = left + width\n const bottom = top - height\n\n const x = (2 * near) / (right - left)\n const y = (2 * near) / (top - bottom)\n\n const a = (right + left) / (right - left)\n const b = (top + bottom) / (top - bottom)\n\n // this should handle depth from 0 to 1\n // and correct near / far clipping planes\n // see https://github.com/mrdoob/three.js/blob/master/src/math/Matrix4.js#L777\n const c = -far / (far - near)\n const d = (-far * near) / (far - near)\n\n // prettier-ignore\n this.set(\n x, 0, 0, 0,\n 0, y, 0, 0,\n a, b, c, -1,\n 0, 0, d, 0\n )\n\n return this\n }\n}\n","import { Vec3 } from '../../math/Vec3'\r\nimport { Quat } from '../../math/Quat'\r\nimport { Mat4 } from '../../math/Mat4'\r\n\r\nlet objectIndex = 0\r\nconst tempMatrix = new Mat4()\r\n\r\n/** Defines all kind of possible {@link Object3D} matrix types */\r\nexport type Object3DMatricesType = 'model' | 'world'\r\n\r\n/**\r\n * Defines an {@link Object3D} matrix object\r\n */\r\nexport interface Object3DTransformMatrix {\r\n /** The {@link Mat4 | matrix} used */\r\n matrix: Mat4\r\n /** Whether we should update the {@link Mat4 | matrix} */\r\n shouldUpdate: boolean\r\n /** Function to update our {@link Mat4 | matrix} */\r\n onUpdate: () => void\r\n}\r\n\r\n/** Defines all possible {@link Object3DTransformMatrix | matrix object} used by our {@link Object3D} */\r\nexport type Object3DMatrices = Record\r\n\r\n/**\r\n * Defines all necessary {@link Vec3 | vectors}/{@link Quat | quaternions} to compute a 3D {@link Mat4 | model matrix}\r\n */\r\nexport interface Object3DTransforms {\r\n /** Transformation origin object */\r\n origin: {\r\n /** Transformation origin {@link Vec3 | vector} relative to the {@link Object3D} */\r\n model: Vec3\r\n }\r\n /** Model {@link Quat | quaternion} defining its rotation in 3D space */\r\n quaternion: Quat\r\n /** Model rotation {@link Vec3 | vector} used to compute its {@link Quat | quaternion} */\r\n rotation: Vec3\r\n /** Position object */\r\n position: {\r\n /** Position {@link Vec3 | vector} relative to the 3D world */\r\n world: Vec3\r\n }\r\n /** Model 3D scale {@link Vec3 | vector} */\r\n scale: Vec3\r\n}\r\n\r\n/**\r\n * Used to create an object with transformation properties such as position, scale, rotation and transform origin {@link Vec3 | vectors} and a {@link Quat | quaternion} in order to compute the {@link Object3D#modelMatrix | model matrix} and {@link Object3D#worldMatrix | world matrix}.\r\n *\r\n * If an {@link Object3D} does not have any {@link Object3D#parent | parent}, then its {@link Object3D#modelMatrix | model matrix} and {@link Object3D#worldMatrix | world matrix} are the same.\r\n *\r\n * The transformations {@link Vec3 | vectors} are reactive to changes, which mean that updating one of their components will automatically update the {@link Object3D#modelMatrix | model matrix} and {@link Object3D#worldMatrix | world matrix}.\r\n */\r\nexport class Object3D {\r\n /** {@link Object3DTransforms | Transformation object} of the {@link Object3D} */\r\n transforms: Object3DTransforms\r\n /** {@link Object3DMatrices | Matrices object} of the {@link Object3D} */\r\n matrices: Object3DMatrices\r\n\r\n /** Parent {@link Object3D} in the scene graph, used to compute the {@link worldMatrix | world matrix} */\r\n private _parent: null | Object3D\r\n /** Children {@link Object3D} in the scene graph, used to compute their own {@link worldMatrix | world matrix} */\r\n children: Object3D[]\r\n\r\n /** Index (order of creation) of this {@link Object3D}. Used in the {@link parent} / {@link children} relation. */\r\n object3DIndex: number\r\n\r\n /** Whether at least one of this {@link Object3D} matrix needs an update. */\r\n matricesNeedUpdate: boolean\r\n\r\n /**\r\n * Object3D constructor\r\n */\r\n constructor() {\r\n this._parent = null\r\n this.children = []\r\n\r\n this.matricesNeedUpdate = false\r\n\r\n Object.defineProperty(this as Object3D, 'object3DIndex', { value: objectIndex++ })\r\n\r\n this.setMatrices()\r\n this.setTransforms()\r\n }\r\n\r\n /* PARENT */\r\n\r\n /**\r\n * Get the parent of this {@link Object3D} if any\r\n */\r\n get parent(): Object3D | null {\r\n return this._parent\r\n }\r\n\r\n /**\r\n * Set the parent of this {@link Object3D}\r\n * @param value - new parent to set, could be an {@link Object3D} or null\r\n */\r\n set parent(value: Object3D | null) {\r\n if (this._parent && value && this._parent.object3DIndex === value.object3DIndex) {\r\n return\r\n }\r\n\r\n if (this._parent) {\r\n // if we already have a parent, remove it first\r\n this._parent.children = this._parent.children.filter((child) => child.object3DIndex !== this.object3DIndex)\r\n }\r\n\r\n if (value) {\r\n this.shouldUpdateWorldMatrix()\r\n }\r\n\r\n this._parent = value\r\n this._parent?.children.push(this)\r\n }\r\n\r\n /* TRANSFORMS */\r\n\r\n /**\r\n * Set our transforms properties and {@link Vec3#onChange | vectors onChange} callbacks\r\n */\r\n setTransforms() {\r\n this.transforms = {\r\n origin: {\r\n model: new Vec3(),\r\n },\r\n quaternion: new Quat(),\r\n rotation: new Vec3(),\r\n position: {\r\n world: new Vec3(),\r\n },\r\n scale: new Vec3(1),\r\n }\r\n\r\n this.rotation.onChange(() => this.applyRotation())\r\n this.position.onChange(() => this.applyPosition())\r\n this.scale.onChange(() => this.applyScale())\r\n this.transformOrigin.onChange(() => this.applyTransformOrigin())\r\n }\r\n\r\n /**\r\n * Get our rotation {@link Vec3 | vector}\r\n */\r\n get rotation(): Vec3 {\r\n return this.transforms.rotation\r\n }\r\n\r\n /**\r\n * Set our rotation {@link Vec3 | vector}\r\n * @param value - new rotation {@link Vec3 | vector}\r\n */\r\n set rotation(value: Vec3) {\r\n this.transforms.rotation = value\r\n this.applyRotation()\r\n }\r\n\r\n /**\r\n * Get our {@link Quat | quaternion}\r\n */\r\n get quaternion(): Quat {\r\n return this.transforms.quaternion\r\n }\r\n\r\n /**\r\n * Set our {@link Quat | quaternion}\r\n * @param value - new {@link Quat | quaternion}\r\n */\r\n set quaternion(value: Quat) {\r\n this.transforms.quaternion = value\r\n }\r\n\r\n /**\r\n * Get our position {@link Vec3 | vector}\r\n */\r\n get position(): Vec3 {\r\n return this.transforms.position.world\r\n }\r\n\r\n /**\r\n * Set our position {@link Vec3 | vector}\r\n * @param value - new position {@link Vec3 | vector}\r\n */\r\n set position(value: Vec3) {\r\n this.transforms.position.world = value\r\n }\r\n\r\n /**\r\n * Get our scale {@link Vec3 | vector}\r\n */\r\n get scale(): Vec3 {\r\n return this.transforms.scale\r\n }\r\n\r\n /**\r\n * Set our scale {@link Vec3 | vector}\r\n * @param value - new scale {@link Vec3 | vector}\r\n */\r\n set scale(value: Vec3) {\r\n // force scale to 1 on Z axis\r\n this.transforms.scale = value\r\n this.applyScale()\r\n }\r\n\r\n /**\r\n * Get our transform origin {@link Vec3 | vector}\r\n */\r\n get transformOrigin(): Vec3 {\r\n return this.transforms.origin.model\r\n }\r\n\r\n /**\r\n * Set our transform origin {@link Vec3 | vector}\r\n * @param value - new transform origin {@link Vec3 | vector}\r\n */\r\n set transformOrigin(value: Vec3) {\r\n this.transforms.origin.model = value\r\n }\r\n\r\n /**\r\n * Apply our rotation and tell our {@link modelMatrix | model matrix} to update\r\n */\r\n applyRotation() {\r\n this.quaternion.setFromVec3(this.rotation)\r\n\r\n this.shouldUpdateModelMatrix()\r\n }\r\n\r\n /**\r\n * Tell our {@link modelMatrix | model matrix} to update\r\n */\r\n applyPosition() {\r\n this.shouldUpdateModelMatrix()\r\n }\r\n\r\n /**\r\n * Tell our {@link modelMatrix | model matrix} to update\r\n */\r\n applyScale() {\r\n this.shouldUpdateModelMatrix()\r\n }\r\n\r\n /**\r\n * Tell our {@link modelMatrix | model matrix} to update\r\n */\r\n applyTransformOrigin() {\r\n this.shouldUpdateModelMatrix()\r\n }\r\n\r\n /* MATRICES */\r\n\r\n /**\r\n * Set our {@link modelMatrix | model matrix} and {@link worldMatrix | world matrix}\r\n */\r\n setMatrices() {\r\n this.matrices = {\r\n model: {\r\n matrix: new Mat4(),\r\n shouldUpdate: true,\r\n onUpdate: () => this.updateModelMatrix(),\r\n },\r\n world: {\r\n matrix: new Mat4(),\r\n shouldUpdate: true,\r\n onUpdate: () => this.updateWorldMatrix(),\r\n },\r\n }\r\n }\r\n\r\n /**\r\n * Get our {@link Mat4 | model matrix}\r\n */\r\n get modelMatrix(): Mat4 {\r\n return this.matrices.model.matrix\r\n }\r\n\r\n /**\r\n * Set our {@link Mat4 | model matrix}\r\n * @param value - new {@link Mat4 | model matrix}\r\n */\r\n set modelMatrix(value: Mat4) {\r\n this.matrices.model.matrix = value\r\n this.shouldUpdateModelMatrix()\r\n }\r\n\r\n /**\r\n * Set our {@link modelMatrix | model matrix} shouldUpdate flag to true (tell it to update)\r\n */\r\n shouldUpdateModelMatrix() {\r\n this.matrices.model.shouldUpdate = true\r\n this.shouldUpdateWorldMatrix()\r\n }\r\n\r\n /**\r\n * Get our {@link Mat4 | world matrix}\r\n */\r\n get worldMatrix(): Mat4 {\r\n return this.matrices.world.matrix\r\n }\r\n\r\n /**\r\n * Set our {@link Mat4 | world matrix}\r\n * @param value - new {@link Mat4 | world matrix}\r\n */\r\n set worldMatrix(value: Mat4) {\r\n this.matrices.world.matrix = value\r\n this.shouldUpdateWorldMatrix()\r\n }\r\n\r\n /**\r\n * Set our {@link worldMatrix | world matrix} shouldUpdate flag to true (tell it to update)\r\n */\r\n shouldUpdateWorldMatrix() {\r\n this.matrices.world.shouldUpdate = true\r\n }\r\n\r\n /**\r\n * Rotate this {@link Object3D} so it looks at the {@link Vec3 | target}\r\n * @param target - {@link Vec3 | target} to look at\r\n * @param position - {@link Vec3 | postion} from which to look at\r\n */\r\n lookAt(target: Vec3 = new Vec3(), position = this.position) {\r\n const rotationMatrix = tempMatrix.lookAt(target, position)\r\n this.quaternion.setFromRotationMatrix(rotationMatrix)\r\n this.shouldUpdateModelMatrix()\r\n }\r\n\r\n /**\r\n * Update our {@link modelMatrix | model matrix}\r\n */\r\n updateModelMatrix() {\r\n // compose our model transformation matrix from custom origin\r\n this.modelMatrix = this.modelMatrix.composeFromOrigin(\r\n this.position,\r\n this.quaternion,\r\n this.scale,\r\n this.transformOrigin\r\n )\r\n\r\n // tell our world matrix to update\r\n this.shouldUpdateWorldMatrix()\r\n }\r\n\r\n /**\r\n * Update our {@link worldMatrix | model matrix}\r\n */\r\n updateWorldMatrix() {\r\n if (!this.parent) {\r\n this.worldMatrix.copy(this.modelMatrix)\r\n } else {\r\n this.worldMatrix.multiplyMatrices(this.parent.worldMatrix, this.modelMatrix)\r\n }\r\n\r\n // update the children world matrix as well\r\n for (let i = 0, l = this.children.length; i < l; i++) {\r\n this.children[i].shouldUpdateWorldMatrix()\r\n }\r\n }\r\n\r\n /**\r\n * Check whether at least one of the matrix should be updated\r\n */\r\n shouldUpdateMatrices() {\r\n this.matricesNeedUpdate = !!Object.values(this.matrices).find((matrix) => matrix.shouldUpdate)\r\n }\r\n\r\n /**\r\n * Check at each render whether we should update our matrices, and update them if needed\r\n */\r\n updateMatrixStack() {\r\n this.shouldUpdateMatrices()\r\n\r\n if (this.matricesNeedUpdate) {\r\n for (const matrixName in this.matrices) {\r\n if (this.matrices[matrixName].shouldUpdate) {\r\n this.matrices[matrixName].onUpdate()\r\n this.matrices[matrixName].shouldUpdate = false\r\n }\r\n }\r\n }\r\n\r\n for (let i = 0, l = this.children.length; i < l; i++) {\r\n this.children[i].updateMatrixStack()\r\n }\r\n }\r\n\r\n /**\r\n * Destroy this {@link Object3D}. Removes its parent and set its children free.\r\n */\r\n destroy() {\r\n for (let i = 0, l = this.children.length; i < l; i++) {\r\n if (this.children[i]) this.children[i].parent = null\r\n }\r\n\r\n this.parent = null\r\n }\r\n}\r\n","// texture bitwise flags\r\nimport { TextureBindingType } from '../bindings/Binding'\r\n\r\n/** Defines all kinds of allowed texture usages as camel case strings. */\r\nexport type TextureUsageKeys = 'copySrc' | 'copyDst' | 'renderAttachment' | 'storageBinding' | 'textureBinding'\r\n\r\n/**\r\n * Map {@link TextureUsageKeys | texture usage names} with actual {@link GPUTextureUsageFlags | texture usage bitwise flags}.\r\n */\r\nconst textureUsages: Map = new Map([\r\n ['copySrc', GPUTextureUsage.COPY_SRC],\r\n ['copyDst', GPUTextureUsage.COPY_DST],\r\n ['renderAttachment', GPUTextureUsage.RENDER_ATTACHMENT],\r\n ['storageBinding', GPUTextureUsage.STORAGE_BINDING],\r\n ['textureBinding', GPUTextureUsage.TEXTURE_BINDING],\r\n])\r\n\r\n/**\r\n * Get the corresponding {@link GPUTextureUsageFlags | texture usage bitwise flags} based on an array of {@link TextureUsageKeys | texture usage names}.\r\n * @param usages - array of {@link TextureUsageKeys | texture usage names}.\r\n * @returns - corresponding {@link GPUTextureUsageFlags | texture usage bitwise flags}.\r\n */\r\nexport const getTextureUsages = (usages: TextureUsageKeys[] = []): GPUTextureUsageFlags => {\r\n return usages.reduce((acc, v) => {\r\n return acc | textureUsages.get(v)\r\n }, 0)\r\n}\r\n\r\n/**\r\n * Get the corresponding {@link GPUTextureUsageFlags | texture usage bitwise flags} based on an array of {@link TextureUsageKeys | texture usage names} if specified. If not, will try to fall back to a usage based on the {@link TextureBindingType | texture type}.\r\n * @param usages - array of {@link TextureUsageKeys | texture usage names}.\r\n * @param textureType - the {@link TextureBindingType | texture type}.\r\n * @returns - corresponding {@link GPUTextureUsageFlags | texture usage bitwise flags}.\r\n */\r\nexport const getDefaultTextureUsage = (usages: TextureUsageKeys[] = [], textureType: TextureBindingType) => {\r\n if (usages.length) {\r\n return getTextureUsages(usages)\r\n }\r\n\r\n return textureType !== 'storage'\r\n ? GPUTextureUsage.TEXTURE_BINDING |\r\n GPUTextureUsage.COPY_SRC |\r\n GPUTextureUsage.COPY_DST |\r\n GPUTextureUsage.RENDER_ATTACHMENT\r\n : GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST\r\n}\r\n\r\n/**\r\n * Get the number of mip levels create based on {@link types/Textures.TextureSize | size}\r\n * @param sizes - Array containing our texture width, height and depth\r\n * @returns - number of mip levels\r\n */\r\nexport const getNumMipLevels = (...sizes: number[]): number => {\r\n const maxSize = Math.max(...sizes)\r\n return (1 + Math.log2(maxSize)) | 0\r\n}\r\n","import { Vec3 } from '../../math/Vec3'\r\nimport { isRenderer, Renderer } from '../renderers/utils'\r\nimport { TextureBinding } from '../bindings/TextureBinding'\r\nimport { BufferBinding } from '../bindings/BufferBinding'\r\nimport { Object3D } from '../objects3D/Object3D'\r\nimport { Mat4 } from '../../math/Mat4'\r\nimport { generateUUID, throwWarning } from '../../utils/utils'\r\nimport { BindGroupBindingElement } from '../../types/BindGroups'\r\nimport { DOMTextureOptions, DOMTextureParams, DOMTextureParent, TextureSize, TextureSource } from '../../types/Textures'\r\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\r\nimport { DOMProjectedMesh } from '../renderers/GPURenderer'\r\nimport { getNumMipLevels } from './utils'\r\n\r\n/** @const - default {@link DOMTexture} parameters */\r\nconst defaultDOMTextureParams: DOMTextureParams = {\r\n name: 'texture',\r\n generateMips: false,\r\n flipY: false,\r\n format: 'rgba8unorm',\r\n premultipliedAlpha: false,\r\n placeholderColor: [0, 0, 0, 255], // default to black\r\n useExternalTextures: true,\r\n fromTexture: null,\r\n viewDimension: '2d',\r\n visibility: ['fragment'],\r\n cache: true,\r\n}\r\n\r\n/**\r\n * Used to create {@link GPUTexture} or {@link GPUExternalTexture}, specially made to handle different kinds of DOM elements {@link TextureSource | sources}, like {@link HTMLImageElement}, {@link HTMLVideoElement} or {@link HTMLCanvasElement}.\r\n *\r\n * Handles the various sources loading and uploading, GPU textures creation,{@link BufferBinding | texture model matrix binding} and {@link TextureBinding | GPU texture binding}.\r\n *\r\n * @example\r\n * ```javascript\r\n * // set our main GPUCurtains instance\r\n * const gpuCurtains = new GPUCurtains({\r\n * container: '#canvas' // selector of our WebGPU canvas container\r\n * })\r\n *\r\n * // set the GPU device\r\n * // note this is asynchronous\r\n * await gpuCurtains.setDevice()\r\n *\r\n * // create a DOM texture\r\n * const imageTexture = new DOMTexture(gpuCurtains, {\r\n * label: 'My image texture',\r\n * name: 'imageTexture',\r\n * })\r\n *\r\n * // load an image\r\n * await imageTexture.loadImage(document.querySelector('img'))\r\n * ```\r\n */\r\nexport class DOMTexture extends Object3D {\r\n /** The type of the {@link DOMTexture} */\r\n type: string\r\n /** The universal unique id of this {@link DOMTexture} */\r\n readonly uuid: string\r\n /** {@link Renderer} used by this {@link DOMTexture} */\r\n renderer: Renderer\r\n\r\n /** The {@link GPUTexture} used if any */\r\n texture: null | GPUTexture\r\n /** The {@link GPUExternalTexture} used if any */\r\n externalTexture: null | GPUExternalTexture\r\n\r\n /** The {@link DOMTexture} {@link TextureSource | source} to use */\r\n source: TextureSource\r\n /** The {@link GPUTexture}, matching the {@link TextureSource | source} {@link core/DOM/DOMElement.RectSize | size} (with 1 for depth) */\r\n size: TextureSize\r\n\r\n /** Options used to create this {@link DOMTexture} */\r\n options: DOMTextureOptions\r\n\r\n /** A {@link BufferBinding | buffer binding} that will hold the texture model matrix */\r\n textureMatrix: BufferBinding\r\n /** The bindings used by this {@link DOMTexture}, i.e. its {@link textureMatrix} and its {@link TextureBinding | GPU texture binding} */\r\n bindings: BindGroupBindingElement[]\r\n\r\n /** {@link DOMTexture} parentMesh if any */\r\n private _parentMesh: DOMTextureParent\r\n\r\n /** Whether the source has been loaded */\r\n private _sourceLoaded: boolean\r\n /** Whether the source has been uploaded to the GPU, handled by the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#texturesQueue | GPUDeviceManager texturesQueue array} */\r\n private _sourceUploaded: boolean\r\n /** Whether the texture should be uploaded to the GPU */\r\n shouldUpdate: boolean\r\n\r\n /** {@link HTMLVideoElement.requestVideoFrameCallback | requestVideoFrameCallback} returned id if used */\r\n videoFrameCallbackId: null | number\r\n\r\n /** Private {@link Vec3 | vector} used for {@link#modelMatrix} calculations, based on {@link parentMesh} {@link core/DOM/DOMElement.RectSize | size} */\r\n #parentRatio: Vec3 = new Vec3(1)\r\n /** Private {@link Vec3 | vector} used for {@link modelMatrix} calculations, based on {@link size | source size} */\r\n #sourceRatio: Vec3 = new Vec3(1)\r\n /** Private {@link Vec3 | vector} used for {@link modelMatrix} calculations, based on #parentRatio and #sourceRatio */\r\n #coverScale: Vec3 = new Vec3(1)\r\n /** Private rotation {@link Mat4 | matrix} based on texture {@link quaternion} */\r\n #rotationMatrix: Mat4 = new Mat4()\r\n\r\n // callbacks / events\r\n /** function assigned to the {@link onSourceLoaded} callback */\r\n _onSourceLoadedCallback = () => {\r\n /* allow empty callback */\r\n }\r\n /** function assigned to the {@link onSourceUploaded} callback */\r\n _onSourceUploadedCallback = () => {\r\n /* allow empty callback */\r\n }\r\n\r\n /**\r\n * DOMTexture constructor\r\n * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link DOMTexture}\r\n * @param parameters - {@link DOMTextureParams | parameters} used to create this {@link DOMTexture}\r\n */\r\n constructor(renderer: Renderer | GPUCurtains, parameters = defaultDOMTextureParams) {\r\n super()\r\n\r\n this.type = 'Texture'\r\n\r\n // we could pass our curtains object OR our curtains renderer object\r\n renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as Renderer)\r\n\r\n isRenderer(renderer, parameters.label ? parameters.label + ' ' + this.type : this.type)\r\n\r\n this.renderer = renderer\r\n\r\n this.uuid = generateUUID()\r\n\r\n const defaultOptions = {\r\n ...defaultDOMTextureParams,\r\n source: parameters.fromTexture ? parameters.fromTexture.options.source : null,\r\n sourceType: parameters.fromTexture ? parameters.fromTexture.options.sourceType : null,\r\n }\r\n\r\n this.options = { ...defaultOptions, ...parameters }\r\n // force merge of texture object\r\n //this.options.texture = { ...defaultOptions.texture, ...parameters.texture }\r\n\r\n this.options.label = this.options.label ?? this.options.name\r\n\r\n this.texture = null\r\n this.externalTexture = null\r\n this.source = null\r\n\r\n // sizes\r\n this.size = {\r\n width: 1,\r\n height: 1,\r\n depth: 1,\r\n }\r\n\r\n // we will always declare a texture matrix\r\n this.textureMatrix = new BufferBinding({\r\n label: this.options.label + ': model matrix',\r\n name: this.options.name + 'Matrix',\r\n useStruct: false,\r\n struct: {\r\n [this.options.name + 'Matrix']: {\r\n type: 'mat4x4f',\r\n value: this.modelMatrix,\r\n },\r\n },\r\n })\r\n\r\n this.renderer.deviceManager.bufferBindings.set(this.textureMatrix.cacheKey, this.textureMatrix)\r\n\r\n this.setBindings()\r\n\r\n this._parentMesh = null\r\n\r\n this.sourceLoaded = false\r\n this.sourceUploaded = false\r\n this.shouldUpdate = false\r\n\r\n this.renderer.addDOMTexture(this)\r\n this.createTexture()\r\n }\r\n\r\n /**\r\n * Set our {@link bindings}\r\n */\r\n setBindings() {\r\n this.bindings = [\r\n new TextureBinding({\r\n label: this.options.label + ': texture',\r\n name: this.options.name,\r\n bindingType: this.options.sourceType === 'externalVideo' ? 'externalTexture' : 'texture',\r\n visibility: this.options.visibility,\r\n texture: this.options.sourceType === 'externalVideo' ? this.externalTexture : this.texture,\r\n viewDimension: this.options.viewDimension,\r\n }),\r\n this.textureMatrix,\r\n ]\r\n }\r\n\r\n /**\r\n * Get our {@link TextureBinding | GPU texture binding}\r\n * @readonly\r\n */\r\n get textureBinding(): TextureBinding {\r\n return this.bindings[0] as TextureBinding\r\n }\r\n\r\n /**\r\n * Get our texture {@link parentMesh}\r\n */\r\n get parentMesh(): DOMTextureParent {\r\n return this._parentMesh\r\n }\r\n\r\n /**\r\n * Set our texture {@link parentMesh}\r\n * @param value - texture {@link parentMesh} to set (i.e. any kind of {@link core/renderers/GPURenderer.RenderedMesh | Mesh}\r\n */\r\n set parentMesh(value: DOMTextureParent) {\r\n this._parentMesh = value\r\n this.resize()\r\n }\r\n\r\n /**\r\n * Get whether our {@link source} has been loaded\r\n */\r\n get sourceLoaded(): boolean {\r\n return this._sourceLoaded\r\n }\r\n\r\n /**\r\n * Set whether our {@link source} has been loaded\r\n * @param value - boolean flag indicating if the {@link source} has been loaded\r\n */\r\n set sourceLoaded(value: boolean) {\r\n if (value && !this.sourceLoaded) {\r\n this._onSourceLoadedCallback && this._onSourceLoadedCallback()\r\n }\r\n this._sourceLoaded = value\r\n }\r\n\r\n /**\r\n * Get whether our {@link source} has been uploaded\r\n */\r\n get sourceUploaded(): boolean {\r\n return this._sourceUploaded\r\n }\r\n\r\n /**\r\n * Set whether our {@link source} has been uploaded\r\n * @param value - boolean flag indicating if the {@link source} has been uploaded\r\n */\r\n set sourceUploaded(value: boolean) {\r\n if (value && !this.sourceUploaded) {\r\n this._onSourceUploadedCallback && this._onSourceUploadedCallback()\r\n }\r\n this._sourceUploaded = value\r\n }\r\n\r\n /**\r\n * Set our texture {@link transforms} object\r\n */\r\n setTransforms() {\r\n super.setTransforms()\r\n\r\n this.transforms.quaternion.setAxisOrder('ZXY')\r\n\r\n // reset our model transform origin to reflect CSS transform origins\r\n this.transforms.origin.model.set(0.5, 0.5, 0)\r\n }\r\n\r\n /* TEXTURE MATRIX */\r\n\r\n /**\r\n * Update the {@link modelMatrix}\r\n */\r\n updateModelMatrix() {\r\n if (!this.parentMesh) return\r\n\r\n const parentScale = (this.parentMesh as DOMProjectedMesh).scale\r\n ? (this.parentMesh as DOMProjectedMesh).scale\r\n : new Vec3(1, 1, 1)\r\n\r\n const parentWidth = (this.parentMesh as DOMProjectedMesh).boundingRect\r\n ? (this.parentMesh as DOMProjectedMesh).boundingRect.width * parentScale.x\r\n : this.size.width\r\n const parentHeight = (this.parentMesh as DOMProjectedMesh).boundingRect\r\n ? (this.parentMesh as DOMProjectedMesh).boundingRect.height * parentScale.y\r\n : this.size.height\r\n\r\n const parentRatio = parentWidth / parentHeight\r\n const sourceRatio = this.size.width / this.size.height\r\n\r\n // handle the texture rotation\r\n // huge props to [@grgrdvrt](https://github.com/grgrdvrt) for this solution!\r\n if (parentWidth > parentHeight) {\r\n this.#parentRatio.set(parentRatio, 1, 1)\r\n this.#sourceRatio.set(1 / sourceRatio, 1, 1)\r\n } else {\r\n this.#parentRatio.set(1, 1 / parentRatio, 1)\r\n this.#sourceRatio.set(1, sourceRatio, 1)\r\n }\r\n\r\n // cover ratio is a bit tricky!\r\n const coverRatio =\r\n parentRatio > sourceRatio !== parentWidth > parentHeight\r\n ? 1\r\n : parentWidth > parentHeight\r\n ? this.#parentRatio.x * this.#sourceRatio.x\r\n : this.#sourceRatio.y * this.#parentRatio.y\r\n\r\n this.#coverScale.set(1 / (coverRatio * this.scale.x), 1 / (coverRatio * this.scale.y), 1)\r\n\r\n this.#rotationMatrix.rotateFromQuaternion(this.quaternion)\r\n\r\n // here we could create a matrix for each translations / scales and do:\r\n // this.modelMatrix\r\n // .identity()\r\n // .premultiply(negativeOriginMatrix)\r\n // .premultiply(coverScaleMatrix)\r\n // .premultiply(parentRatioMatrix)\r\n // .premultiply(rotationMatrix)\r\n // .premultiply(textureRatioMatrix)\r\n // .premultiply(originMatrix)\r\n // .translate(this.position)\r\n\r\n // but this is faster!\r\n this.modelMatrix\r\n .identity()\r\n .premultiplyTranslate(this.transformOrigin.clone().multiplyScalar(-1))\r\n .premultiplyScale(this.#coverScale)\r\n .premultiplyScale(this.#parentRatio)\r\n .premultiply(this.#rotationMatrix)\r\n .premultiplyScale(this.#sourceRatio)\r\n .premultiplyTranslate(this.transformOrigin)\r\n .translate(this.position)\r\n }\r\n\r\n /**\r\n * If our {@link modelMatrix} has been updated, tell the {@link textureMatrix | texture matrix binding} to update as well\r\n */\r\n updateMatrixStack() {\r\n super.updateMatrixStack()\r\n\r\n if (this.matricesNeedUpdate) {\r\n this.textureMatrix.shouldUpdateBinding(this.options.name + 'Matrix')\r\n }\r\n }\r\n\r\n /**\r\n * Resize our {@link DOMTexture}\r\n */\r\n resize() {\r\n // this should only happen with canvas textures\r\n if (\r\n this.source &&\r\n this.source instanceof HTMLCanvasElement &&\r\n (this.source.width !== this.size.width || this.source.height !== this.size.height)\r\n ) {\r\n // since the source size has changed, we have to recreate a new texture\r\n this.setSourceSize()\r\n this.createTexture()\r\n }\r\n\r\n // tell our model matrix to update\r\n this.shouldUpdateModelMatrix()\r\n }\r\n\r\n /**\r\n * Tell the {@link Renderer} to upload or texture\r\n */\r\n uploadTexture() {\r\n this.renderer.uploadTexture(this)\r\n this.shouldUpdate = false\r\n }\r\n\r\n /**\r\n * Import a {@link GPUExternalTexture} from the {@link Renderer}, update the {@link textureBinding} and its {@link core/bindGroups/TextureBindGroup.TextureBindGroup | bind group}\r\n */\r\n uploadVideoTexture() {\r\n this.externalTexture = this.renderer.importExternalTexture(this.source as HTMLVideoElement)\r\n this.textureBinding.resource = this.externalTexture\r\n this.textureBinding.setBindingType('externalTexture')\r\n this.shouldUpdate = false\r\n this.sourceUploaded = true\r\n }\r\n\r\n /**\r\n * Copy a {@link DOMTexture}\r\n * @param texture - {@link DOMTexture} to copy\r\n */\r\n copy(texture: DOMTexture) {\r\n if (this.options.sourceType === 'externalVideo' && texture.options.sourceType !== 'externalVideo') {\r\n throwWarning(`${this.options.label}: cannot copy a GPUTexture to a GPUExternalTexture`)\r\n return\r\n } else if (this.options.sourceType !== 'externalVideo' && texture.options.sourceType === 'externalVideo') {\r\n throwWarning(`${this.options.label}: cannot copy a GPUExternalTexture to a GPUTexture`)\r\n return\r\n }\r\n\r\n this.options.fromTexture = texture\r\n\r\n // now copy all desired texture options except source\r\n // const { source, ...optionsToCopy } = texture.options\r\n // this.options = { ...this.options, ...optionsToCopy }\r\n\r\n this.options.sourceType = texture.options.sourceType\r\n\r\n // TODO better way to do that?\r\n this.options.generateMips = texture.options.generateMips\r\n this.options.flipY = texture.options.flipY\r\n this.options.format = texture.options.format\r\n this.options.premultipliedAlpha = texture.options.premultipliedAlpha\r\n this.options.placeholderColor = texture.options.placeholderColor\r\n this.options.useExternalTextures = texture.options.useExternalTextures\r\n\r\n this.sourceLoaded = texture.sourceLoaded\r\n this.sourceUploaded = texture.sourceUploaded\r\n\r\n // TODO external texture?\r\n if (texture.texture) {\r\n if (texture.sourceLoaded) {\r\n this.size = texture.size\r\n this.source = texture.source\r\n\r\n this.resize()\r\n }\r\n\r\n if (texture.sourceUploaded) {\r\n // texture to copy is ready, update our texture and binding\r\n this.texture = texture.texture\r\n this.textureBinding.resource = this.texture\r\n } else {\r\n this.createTexture()\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Set the {@link texture | GPU texture}\r\n */\r\n createTexture() {\r\n const options = {\r\n label: this.options.label,\r\n format: this.options.format,\r\n size: [this.size.width, this.size.height, this.size.depth], // [1, 1] if no source\r\n dimensions: this.options.viewDimension === '1d' ? '1d' : this.options.viewDimension === '3d' ? '3d' : '2d',\r\n //sampleCount: this.source ? this.renderer.sampleCount : 1,\r\n usage: !!this.source\r\n ? GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT\r\n : GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,\r\n } as GPUTextureDescriptor\r\n\r\n if (this.options.sourceType !== 'externalVideo') {\r\n options.mipLevelCount = this.options.generateMips ? getNumMipLevels(this.size.width, this.size.height) : 1\r\n\r\n this.texture?.destroy()\r\n\r\n this.texture = this.renderer.createTexture(options)\r\n\r\n // update texture binding\r\n this.textureBinding.resource = this.texture\r\n }\r\n\r\n this.shouldUpdate = true\r\n }\r\n\r\n /* SOURCES */\r\n\r\n /**\r\n * Set the {@link size} based on the {@link source}\r\n */\r\n setSourceSize() {\r\n this.size = {\r\n width:\r\n (this.source as HTMLImageElement).naturalWidth ||\r\n (this.source as HTMLCanvasElement).width ||\r\n (this.source as HTMLVideoElement).videoWidth,\r\n height:\r\n (this.source as HTMLImageElement).naturalHeight ||\r\n (this.source as HTMLCanvasElement).height ||\r\n (this.source as HTMLVideoElement).videoHeight,\r\n depth: 1,\r\n }\r\n }\r\n\r\n /**\r\n * Load an {@link HTMLImageElement} from a URL and create an {@link ImageBitmap} to use as a {@link source}\r\n * @async\r\n * @param url - URL of the image to load\r\n * @returns - the newly created {@link ImageBitmap}\r\n */\r\n async loadImageBitmap(url: string): Promise {\r\n const res = await fetch(url)\r\n const blob = await res.blob()\r\n return await createImageBitmap(blob, { colorSpaceConversion: 'none' })\r\n }\r\n\r\n /**\r\n * Load and create an {@link ImageBitmap} from a URL or {@link HTMLImageElement}, use it as a {@link source} and create the {@link GPUTexture}\r\n * @async\r\n * @param source - the image URL or {@link HTMLImageElement} to load\r\n * @returns - the newly created {@link ImageBitmap}\r\n */\r\n async loadImage(source: string | HTMLImageElement): Promise {\r\n const url = typeof source === 'string' ? source : source.getAttribute('src')\r\n\r\n this.options.source = url\r\n this.options.sourceType = 'image'\r\n\r\n const cachedTexture = this.renderer.domTextures.find((t) => t.options.source === url)\r\n if (cachedTexture && cachedTexture.texture && cachedTexture.sourceUploaded) {\r\n this.copy(cachedTexture)\r\n return\r\n }\r\n\r\n this.sourceLoaded = false\r\n this.sourceUploaded = false\r\n\r\n this.source = await this.loadImageBitmap(this.options.source)\r\n\r\n this.setSourceSize()\r\n this.resize()\r\n\r\n this.sourceLoaded = true\r\n this.createTexture()\r\n }\r\n\r\n // weirldy enough, we don't have to do anything in that callback\r\n // because the
Index
Constructors
constructor
- new BindGroup(renderer, parameters?): BindGroup
Properties
bindGroupLayout
bindGroupLayout: GPUBindGroupLayout
bindGroup
bindGroup: GPUBindGroup
layoutCacheKey
layoutCacheKey: string
pipelineCacheKey
pipelineCacheKey: string
needsPipelineFlush
needsPipelineFlush: boolean
consumers
consumers: Set<string>
Accessors
shouldCreateBindGroup
- get shouldCreateBindGroup(): boolean
Properties
bindGroupLayout
bindGroupLayout: GPUBindGroupLayout
bindGroup
bindGroup: GPUBindGroup
layoutCacheKey
layoutCacheKey: string
pipelineCacheKey
pipelineCacheKey: string
needsPipelineFlush
needsPipelineFlush: boolean
consumers
consumers: Set<string>
Accessors
shouldCreateBindGroup
- get shouldCreateBindGroup(): boolean
Returns boolean
Methods
setIndex
- setIndex(index): void
Returns boolean
Methods
setIndex
- setIndex(index): void
Returns void
addBindings
- addBindings(bindings?): void
Returns void
addBindings
- addBindings(bindings?): void
Returns void
addBinding
- addBinding(binding): void
Returns void
addBinding
- addBinding(binding): void
Returns void
createInputBindings
Returns void
setInputBindings
- setInputBindings(): void
Returns void
resetEntries
- resetEntries(): void
Returns void
createBindGroup
- createBindGroup(): void
Returns void
resetBindGroup
- resetBindGroup(): void
Returns void
addBindGroupEntry
- addBindGroupEntry(binding): void
Returns void
resetBindGroupLayout
- resetBindGroupLayout(): void
Returns void
addBindGroupLayoutEntry
- addBindGroupLayoutEntry(binding): void
Returns void
loseContext
- loseContext(): void
Returns void
restoreContext
- restoreContext(): void
Returns void
createBindingBuffer
- createBindingBuffer(binding): void
Returns void
loseContext
- loseContext(): void
Returns void
restoreContext
- restoreContext(): void
Returns void
createBindingBuffer
- createBindingBuffer(binding): void
Returns void
fillEntries
- fillEntries(): void
Returns void
setBindGroupLayout
- setBindGroupLayout(): void
Returns void
setBindGroup
- setBindGroup(): void
Returns void
updateBufferBindings
- updateBufferBindings(): void
Returns void
Example
-