diff --git a/dist/esm/core/bindings/TextureBinding.mjs b/dist/esm/core/bindings/TextureBinding.mjs index 7a6964768..d87edfdf0 100644 --- a/dist/esm/core/bindings/TextureBinding.mjs +++ b/dist/esm/core/bindings/TextureBinding.mjs @@ -44,7 +44,7 @@ class TextureBinding extends Binding { * Get the {@link GPUBindGroupEntry#resource | bind group resource} */ get resource() { - return this.texture instanceof GPUTexture ? this.texture.createView({ label: this.options.label + " view" }) : this.texture instanceof GPUExternalTexture ? this.texture : null; + 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 the {@link GPUBindGroupEntry#resource | bind group resource} diff --git a/dist/esm/core/bindings/TextureBinding.mjs.map b/dist/esm/core/bindings/TextureBinding.mjs.map index 2cedfdf27..6040b6caa 100644 --- a/dist/esm/core/bindings/TextureBinding.mjs.map +++ b/dist/esm/core/bindings/TextureBinding.mjs.map @@ -1 +1 @@ -{"version":3,"file":"TextureBinding.mjs","sources":["../../../../src/core/bindings/TextureBinding.ts"],"sourcesContent":["import { Binding, BindingMemoryAccessType, BindingParams, BindingType } from './Binding'\nimport { getBindGroupLayoutTextureBindingType, getTextureBindingWGSLVarType } from './utils'\n\n/** Defines a {@link TextureBinding} {@link TextureBinding#resource | resource} */\nexport type TextureBindingResource = GPUTexture | GPUExternalTexture | null\n\n/**\n * An object defining all possible {@link TextureBinding} class instancing parameters\n */\nexport interface TextureBindingParams extends BindingParams {\n /** {@link TextureBinding} {@link TextureBinding#resource | resource} */\n texture: TextureBindingResource\n /** The {@link GPUTexture | texture} format to use */\n format?: GPUTextureFormat\n /** The storage {@link GPUTexture | texture} binding memory access types (read only, write only or read/write) */\n access?: BindingMemoryAccessType\n /** The {@link GPUTexture | texture} view dimension to use */\n viewDimension?: GPUTextureViewDimension\n /** Whethe the {@link GPUTexture | texture} is a multisampled texture. Mainly used internally by depth textures if needed. */\n multisampled?: boolean\n}\n\n/**\n * Used to handle {@link GPUTexture} and {@link GPUExternalTexture} bindings.\n *\n * Provide both {@link TextureBinding#resourceLayout | resourceLayout} and {@link TextureBinding#resource | resource} to the {@link GPUBindGroupLayout} and {@link GPUBindGroup}.
\n * Also create the appropriate WGSL code snippet to add to the shaders.\n */\nexport class TextureBinding extends Binding {\n /** Our {@link TextureBinding} resource, i.e. a {@link GPUTexture} or {@link GPUExternalTexture} */\n texture: TextureBindingResource\n /** An array of strings to append to our shaders code declaring all the WGSL variables representing this {@link TextureBinding} */\n wgslGroupFragment: string[]\n /** Options used to create this {@link TextureBinding} */\n options: TextureBindingParams\n\n /**\n * TextureBinding constructor\n * @param parameters - {@link TextureBindingParams | parameters} used to create our {@link TextureBinding}\n */\n constructor({\n label = 'Texture',\n name = 'texture',\n bindingType,\n visibility,\n texture,\n format = 'rgba8unorm',\n access = 'write',\n viewDimension = '2d',\n multisampled = false,\n }: TextureBindingParams) {\n bindingType = bindingType ?? 'texture'\n\n if (bindingType === 'storage') {\n visibility = 'compute'\n }\n\n super({ label, name, bindingType, visibility })\n\n this.options = {\n ...this.options,\n texture,\n format,\n access,\n viewDimension,\n multisampled,\n }\n\n this.resource = texture // should be a texture or an external texture\n\n this.setWGSLFragment()\n }\n\n /**\n * Get bind group layout entry resource, either for {@link GPUBindGroupLayoutEntry#texture | texture} or {@link GPUBindGroupLayoutEntry#externalTexture | external texture}\n * @readonly\n */\n get resourceLayout():\n | GPUTextureBindingLayout\n | GPUExternalTextureBindingLayout\n | GPUStorageTextureBindingLayout\n | null {\n return getBindGroupLayoutTextureBindingType(this)\n }\n\n /**\n * Get the {@link GPUBindGroupEntry#resource | bind group resource}\n */\n get resource(): GPUExternalTexture | GPUTextureView | null {\n return this.texture instanceof GPUTexture\n ? this.texture.createView({ label: this.options.label + ' view' })\n : this.texture instanceof GPUExternalTexture\n ? this.texture\n : null\n }\n\n /**\n * Set the {@link GPUBindGroupEntry#resource | bind group resource}\n * @param value - new bind group resource\n */\n set resource(value: TextureBindingResource) {\n // resource changed, update bind group!\n if (value || this.texture) this.shouldResetBindGroup = true\n this.texture = value\n }\n\n /**\n * Set or update our {@link Binding#bindingType | bindingType} and our WGSL code snippet\n * @param bindingType - the new {@link Binding#bindingType | binding type}\n */\n setBindingType(bindingType: BindingType) {\n if (bindingType !== this.bindingType) {\n // binding type has changed!\n if (bindingType) this.shouldResetBindGroupLayout = true\n\n this.bindingType = bindingType\n this.setWGSLFragment()\n }\n }\n\n /**\n * Set the correct WGSL code snippet.\n */\n setWGSLFragment() {\n this.wgslGroupFragment = [`${getTextureBindingWGSLVarType(this)}`]\n }\n}\n"],"names":[],"mappings":";;;AA4BO,MAAM,uBAAuB,OAAQ,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY1C,WAAY,CAAA;AAAA,IACV,KAAQ,GAAA,SAAA;AAAA,IACR,IAAO,GAAA,SAAA;AAAA,IACP,WAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAS,GAAA,YAAA;AAAA,IACT,MAAS,GAAA,OAAA;AAAA,IACT,aAAgB,GAAA,IAAA;AAAA,IAChB,YAAe,GAAA,KAAA;AAAA,GACQ,EAAA;AACvB,IAAA,WAAA,GAAc,WAAe,IAAA,SAAA,CAAA;AAE7B,IAAA,IAAI,gBAAgB,SAAW,EAAA;AAC7B,MAAa,UAAA,GAAA,SAAA,CAAA;AAAA,KACf;AAEA,IAAA,KAAA,CAAM,EAAE,KAAA,EAAO,IAAM,EAAA,WAAA,EAAa,YAAY,CAAA,CAAA;AAE9C,IAAA,IAAA,CAAK,OAAU,GAAA;AAAA,MACb,GAAG,IAAK,CAAA,OAAA;AAAA,MACR,OAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA;AAAA,MACA,aAAA;AAAA,MACA,YAAA;AAAA,KACF,CAAA;AAEA,IAAA,IAAA,CAAK,QAAW,GAAA,OAAA,CAAA;AAEhB,IAAA,IAAA,CAAK,eAAgB,EAAA,CAAA;AAAA,GACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,cAIK,GAAA;AACP,IAAA,OAAO,qCAAqC,IAAI,CAAA,CAAA;AAAA,GAClD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAuD,GAAA;AACzD,IAAA,OAAO,KAAK,OAAmB,YAAA,UAAA,GAC3B,KAAK,OAAQ,CAAA,UAAA,CAAW,EAAE,KAAO,EAAA,IAAA,CAAK,OAAQ,CAAA,KAAA,GAAQ,SAAS,CAAA,GAC/D,KAAK,OAAmB,YAAA,kBAAA,GACxB,KAAK,OACL,GAAA,IAAA,CAAA;AAAA,GACN;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,SAAS,KAA+B,EAAA;AAE1C,IAAA,IAAI,SAAS,IAAK,CAAA,OAAA;AAAS,MAAA,IAAA,CAAK,oBAAuB,GAAA,IAAA,CAAA;AACvD,IAAA,IAAA,CAAK,OAAU,GAAA,KAAA,CAAA;AAAA,GACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAe,WAA0B,EAAA;AACvC,IAAI,IAAA,WAAA,KAAgB,KAAK,WAAa,EAAA;AAEpC,MAAI,IAAA,WAAA;AAAa,QAAA,IAAA,CAAK,0BAA6B,GAAA,IAAA,CAAA;AAEnD,MAAA,IAAA,CAAK,WAAc,GAAA,WAAA,CAAA;AACnB,MAAA,IAAA,CAAK,eAAgB,EAAA,CAAA;AAAA,KACvB;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAkB,GAAA;AAChB,IAAA,IAAA,CAAK,oBAAoB,CAAC,CAAA,EAAG,4BAA6B,CAAA,IAAI,CAAC,CAAE,CAAA,CAAA,CAAA;AAAA,GACnE;AACF;;;;"} \ No newline at end of file +{"version":3,"file":"TextureBinding.mjs","sources":["../../../../src/core/bindings/TextureBinding.ts"],"sourcesContent":["import { Binding, BindingMemoryAccessType, BindingParams, BindingType } from './Binding'\nimport { getBindGroupLayoutTextureBindingType, getTextureBindingWGSLVarType } from './utils'\n\n/** Defines a {@link TextureBinding} {@link TextureBinding#resource | resource} */\nexport type TextureBindingResource = GPUTexture | GPUExternalTexture | null\n\n/**\n * An object defining all possible {@link TextureBinding} class instancing parameters\n */\nexport interface TextureBindingParams extends BindingParams {\n /** {@link TextureBinding} {@link TextureBinding#resource | resource} */\n texture: TextureBindingResource\n /** The {@link GPUTexture | texture} format to use */\n format?: GPUTextureFormat\n /** The storage {@link GPUTexture | texture} binding memory access types (read only, write only or read/write) */\n access?: BindingMemoryAccessType\n /** The {@link GPUTexture | texture} view dimension to use */\n viewDimension?: GPUTextureViewDimension\n /** Whethe the {@link GPUTexture | texture} is a multisampled texture. Mainly used internally by depth textures if needed. */\n multisampled?: boolean\n}\n\n/**\n * Used to handle {@link GPUTexture} and {@link GPUExternalTexture} bindings.\n *\n * Provide both {@link TextureBinding#resourceLayout | resourceLayout} and {@link TextureBinding#resource | resource} to the {@link GPUBindGroupLayout} and {@link GPUBindGroup}.
\n * Also create the appropriate WGSL code snippet to add to the shaders.\n */\nexport class TextureBinding extends Binding {\n /** Our {@link TextureBinding} resource, i.e. a {@link GPUTexture} or {@link GPUExternalTexture} */\n texture: TextureBindingResource\n /** An array of strings to append to our shaders code declaring all the WGSL variables representing this {@link TextureBinding} */\n wgslGroupFragment: string[]\n /** Options used to create this {@link TextureBinding} */\n options: TextureBindingParams\n\n /**\n * TextureBinding constructor\n * @param parameters - {@link TextureBindingParams | parameters} used to create our {@link TextureBinding}\n */\n constructor({\n label = 'Texture',\n name = 'texture',\n bindingType,\n visibility,\n texture,\n format = 'rgba8unorm',\n access = 'write',\n viewDimension = '2d',\n multisampled = false,\n }: TextureBindingParams) {\n bindingType = bindingType ?? 'texture'\n\n if (bindingType === 'storage') {\n visibility = 'compute'\n }\n\n super({ label, name, bindingType, visibility })\n\n this.options = {\n ...this.options,\n texture,\n format,\n access,\n viewDimension,\n multisampled,\n }\n\n this.resource = texture // should be a texture or an external texture\n\n this.setWGSLFragment()\n }\n\n /**\n * Get bind group layout entry resource, either for {@link GPUBindGroupLayoutEntry#texture | texture} or {@link GPUBindGroupLayoutEntry#externalTexture | external texture}\n * @readonly\n */\n get resourceLayout():\n | GPUTextureBindingLayout\n | GPUExternalTextureBindingLayout\n | GPUStorageTextureBindingLayout\n | null {\n return getBindGroupLayoutTextureBindingType(this)\n }\n\n /**\n * Get the {@link GPUBindGroupEntry#resource | bind group resource}\n */\n get resource(): GPUExternalTexture | GPUTextureView | null {\n return this.texture instanceof GPUTexture\n ? this.texture.createView({ label: this.options.label + ' view', dimension: this.options.viewDimension })\n : this.texture instanceof GPUExternalTexture\n ? this.texture\n : null\n }\n\n /**\n * Set the {@link GPUBindGroupEntry#resource | bind group resource}\n * @param value - new bind group resource\n */\n set resource(value: TextureBindingResource) {\n // resource changed, update bind group!\n if (value || this.texture) this.shouldResetBindGroup = true\n this.texture = value\n }\n\n /**\n * Set or update our {@link Binding#bindingType | bindingType} and our WGSL code snippet\n * @param bindingType - the new {@link Binding#bindingType | binding type}\n */\n setBindingType(bindingType: BindingType) {\n if (bindingType !== this.bindingType) {\n // binding type has changed!\n if (bindingType) this.shouldResetBindGroupLayout = true\n\n this.bindingType = bindingType\n this.setWGSLFragment()\n }\n }\n\n /**\n * Set the correct WGSL code snippet.\n */\n setWGSLFragment() {\n this.wgslGroupFragment = [`${getTextureBindingWGSLVarType(this)}`]\n }\n}\n"],"names":[],"mappings":";;;AA4BO,MAAM,uBAAuB,OAAQ,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY1C,WAAY,CAAA;AAAA,IACV,KAAQ,GAAA,SAAA;AAAA,IACR,IAAO,GAAA,SAAA;AAAA,IACP,WAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAS,GAAA,YAAA;AAAA,IACT,MAAS,GAAA,OAAA;AAAA,IACT,aAAgB,GAAA,IAAA;AAAA,IAChB,YAAe,GAAA,KAAA;AAAA,GACQ,EAAA;AACvB,IAAA,WAAA,GAAc,WAAe,IAAA,SAAA,CAAA;AAE7B,IAAA,IAAI,gBAAgB,SAAW,EAAA;AAC7B,MAAa,UAAA,GAAA,SAAA,CAAA;AAAA,KACf;AAEA,IAAA,KAAA,CAAM,EAAE,KAAA,EAAO,IAAM,EAAA,WAAA,EAAa,YAAY,CAAA,CAAA;AAE9C,IAAA,IAAA,CAAK,OAAU,GAAA;AAAA,MACb,GAAG,IAAK,CAAA,OAAA;AAAA,MACR,OAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA;AAAA,MACA,aAAA;AAAA,MACA,YAAA;AAAA,KACF,CAAA;AAEA,IAAA,IAAA,CAAK,QAAW,GAAA,OAAA,CAAA;AAEhB,IAAA,IAAA,CAAK,eAAgB,EAAA,CAAA;AAAA,GACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,cAIK,GAAA;AACP,IAAA,OAAO,qCAAqC,IAAI,CAAA,CAAA;AAAA,GAClD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAuD,GAAA;AACzD,IAAO,OAAA,IAAA,CAAK,mBAAmB,UAC3B,GAAA,IAAA,CAAK,QAAQ,UAAW,CAAA,EAAE,KAAO,EAAA,IAAA,CAAK,OAAQ,CAAA,KAAA,GAAQ,SAAS,SAAW,EAAA,IAAA,CAAK,QAAQ,aAAc,EAAC,IACtG,IAAK,CAAA,OAAA,YAAmB,kBACxB,GAAA,IAAA,CAAK,OACL,GAAA,IAAA,CAAA;AAAA,GACN;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,SAAS,KAA+B,EAAA;AAE1C,IAAA,IAAI,SAAS,IAAK,CAAA,OAAA;AAAS,MAAA,IAAA,CAAK,oBAAuB,GAAA,IAAA,CAAA;AACvD,IAAA,IAAA,CAAK,OAAU,GAAA,KAAA,CAAA;AAAA,GACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAe,WAA0B,EAAA;AACvC,IAAI,IAAA,WAAA,KAAgB,KAAK,WAAa,EAAA;AAEpC,MAAI,IAAA,WAAA;AAAa,QAAA,IAAA,CAAK,0BAA6B,GAAA,IAAA,CAAA;AAEnD,MAAA,IAAA,CAAK,WAAc,GAAA,WAAA,CAAA;AACnB,MAAA,IAAA,CAAK,eAAgB,EAAA,CAAA;AAAA,KACvB;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAkB,GAAA;AAChB,IAAA,IAAA,CAAK,oBAAoB,CAAC,CAAA,EAAG,4BAA6B,CAAA,IAAI,CAAC,CAAE,CAAA,CAAA,CAAA;AAAA,GACnE;AACF;;;;"} \ No newline at end of file diff --git a/dist/esm/core/textures/RenderTexture.mjs b/dist/esm/core/textures/RenderTexture.mjs index bf45e0b68..bbb607f83 100644 --- a/dist/esm/core/textures/RenderTexture.mjs +++ b/dist/esm/core/textures/RenderTexture.mjs @@ -52,11 +52,11 @@ class RenderTexture { 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 + depth: this.options.fixedSize.depth ?? this.options.viewDimension.indexOf("cube") !== -1 ? 6 : 1 } : { width: Math.floor(this.renderer.displayBoundingRect.width * this.options.qualityRatio), height: Math.floor(this.renderer.displayBoundingRect.height * this.options.qualityRatio), - depth: 1 + depth: this.options.viewDimension.indexOf("cube") !== -1 ? 6 : 1 }; if (this.options.fixedSize) { __privateSet(this, _autoResize, false); @@ -100,7 +100,7 @@ class RenderTexture { label: this.options.label, format: this.options.format, size: [this.size.width, this.size.height, this.size.depth ?? 1], - dimensions: this.options.viewDimension === "1d" ? "1d" : this.options.viewDimension === "3d" ? "3d" : "2d", + dimensions: this.options.viewDimension, sampleCount: this.options.sampleCount, usage: ( // TODO let user chose? diff --git a/dist/esm/core/textures/RenderTexture.mjs.map b/dist/esm/core/textures/RenderTexture.mjs.map index 0c1f32130..a8931a8d7 100644 --- a/dist/esm/core/textures/RenderTexture.mjs.map +++ b/dist/esm/core/textures/RenderTexture.mjs.map @@ -1 +1 @@ -{"version":3,"file":"RenderTexture.mjs","sources":["../../../../src/core/textures/RenderTexture.ts"],"sourcesContent":["import { isRenderer, Renderer } from '../renderers/utils'\r\nimport { TextureBinding, TextureBindingParams } from '../bindings/TextureBinding'\r\nimport { BindGroupBindingElement } from '../../types/BindGroups'\r\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\r\nimport { BindingMemoryAccessType, TextureBindingType } from '../bindings/Binding'\r\nimport { generateUUID } from '../../utils/utils'\r\nimport { Texture } from './Texture'\r\nimport { TextureSize } from '../../types/Textures'\r\n\r\n/**\r\n * Define the possible binding types of a {@link RenderTexture}\r\n */\r\nexport type RenderTextureBindingType = Exclude\r\n\r\n/**\r\n * Base parameters used to create a {@link RenderTexture}\r\n */\r\nexport interface RenderTextureBaseParams {\r\n /** The label of the {@link RenderTexture}, used to create various GPU objects for debugging purpose */\r\n label?: string\r\n /** Name of the {@link RenderTexture} to use in the {@link TextureBinding | texture binding} */\r\n name?: string\r\n\r\n /** Optional fixed size of the {@link RenderTexture#texture | texture}. If set, the {@link RenderTexture} will never be resized and always keep that size. */\r\n fixedSize?: TextureSize\r\n\r\n /** Force the texture size to be set to the given ratio of the {@link core/renderers/GPURenderer.GPURenderer#displayBoundingRect | renderer display bounding rectangle} or {@link fixedSize}. Used mainly to shrink render target definition. */\r\n qualityRatio?: number\r\n\r\n /** Whether to use this {@link RenderTexture} as a regular, storage or depth texture */\r\n usage?: RenderTextureBindingType\r\n /** Optional format of the {@link RenderTexture#texture | texture}, mainly used for storage textures */\r\n format?: GPUTextureFormat\r\n /** Optional texture binding memory access type, mainly used for storage textures */\r\n access?: BindingMemoryAccessType\r\n /** Optional {@link RenderTexture#texture | texture} view dimension to use */\r\n viewDimension?: GPUTextureViewDimension\r\n /** Sample count of the {@link RenderTexture#texture | texture}, used for multisampling */\r\n sampleCount?: GPUSize32\r\n}\r\n\r\n/**\r\n * Parameters used to create a {@link RenderTexture}\r\n */\r\nexport interface RenderTextureParams extends RenderTextureBaseParams {\r\n /** Optional texture to use as a copy source input. Could be a {@link RenderTexture} or {@link Texture} */\r\n fromTexture?: RenderTexture | Texture | null\r\n}\r\n\r\n/** @const - default {@link RenderTexture} parameters */\r\nconst defaultRenderTextureParams: RenderTextureParams = {\r\n label: 'RenderTexture',\r\n name: 'renderTexture',\r\n usage: 'texture',\r\n access: 'write',\r\n fromTexture: null,\r\n viewDimension: '2d',\r\n sampleCount: 1,\r\n qualityRatio: 1,\r\n}\r\n\r\n/**\r\n * Used to create {@link GPUTexture | texture} that can be used as copy source/destination for {@link core/renderPasses/RenderPass.RenderPass | RenderPass} and {@link core/renderPasses/RenderTarget.RenderTarget | RenderTarget}.
\r\n * Basically useful for copying anything outputted to the screen at one point or another.\r\n *\r\n * Will create a {@link GPUTexture} and its associated {@link TextureBinding}.\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 render texture\r\n * const renderTexture = new RenderTexture(gpuCurtains, {\r\n * label: 'My render texture',\r\n * name: 'renderTexture',\r\n * })\r\n * ```\r\n */\r\nexport class RenderTexture {\r\n /** {@link Renderer | renderer} used by this {@link RenderTexture} */\r\n renderer: Renderer\r\n /** The type of the {@link RenderTexture} */\r\n type: string\r\n /** The universal unique id of this {@link RenderTexture} */\r\n readonly uuid: string\r\n\r\n /** The {@link GPUTexture} used */\r\n texture: GPUTexture\r\n\r\n /** Size of the {@link RenderTexture#texture | texture} source, usually our {@link Renderer#displayBoundingRect | renderer display bounding rectangle size} */\r\n size: TextureSize\r\n\r\n /** Options used to create this {@link RenderTexture} */\r\n options: RenderTextureParams\r\n\r\n /** Array of {@link core/bindings/Binding.Binding | bindings} that will actually only hold one {@link TextureBinding | texture binding} */\r\n bindings: BindGroupBindingElement[]\r\n\r\n /** Whether this texture should be automatically resized when the {@link Renderer renderer} size changes. Default to true. */\r\n #autoResize = true\r\n\r\n /**\r\n * RenderTexture constructor\r\n * @param renderer - {@link Renderer | renderer} object or {@link GPUCurtains} class object used to create this {@link RenderTexture}\r\n * @param parameters - {@link RenderTextureParams | parameters} used to create this {@link RenderTexture}\r\n */\r\n constructor(renderer: Renderer | GPUCurtains, parameters = defaultRenderTextureParams) {\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 + ' RenderTexture' : 'RenderTexture')\r\n\r\n this.type = 'RenderTexture'\r\n\r\n this.renderer = renderer\r\n\r\n this.uuid = generateUUID()\r\n\r\n this.options = { ...defaultRenderTextureParams, ...parameters }\r\n\r\n if (!this.options.format) {\r\n this.options.format = this.renderer.options.preferredFormat\r\n }\r\n\r\n // sizes\r\n this.size = this.options.fixedSize\r\n ? {\r\n width: this.options.fixedSize.width * this.options.qualityRatio,\r\n height: this.options.fixedSize.height * this.options.qualityRatio,\r\n depth: this.options.fixedSize.depth,\r\n }\r\n : {\r\n width: Math.floor(this.renderer.displayBoundingRect.width * this.options.qualityRatio),\r\n height: Math.floor(this.renderer.displayBoundingRect.height * this.options.qualityRatio),\r\n depth: 1,\r\n }\r\n\r\n if (this.options.fixedSize) {\r\n this.#autoResize = false\r\n }\r\n\r\n // struct\r\n this.setBindings()\r\n\r\n // texture\r\n this.renderer.addRenderTexture(this)\r\n this.createTexture()\r\n }\r\n\r\n /**\r\n * Copy another {@link RenderTexture} into this {@link RenderTexture}\r\n * @param texture - {@link RenderTexture} to copy\r\n */\r\n copy(texture: RenderTexture | Texture) {\r\n this.options.fromTexture = texture\r\n this.createTexture()\r\n }\r\n\r\n /**\r\n * Copy a {@link GPUTexture} directly into this {@link RenderTexture}. Mainly used for depth textures.\r\n * @param texture - {@link GPUTexture} to copy\r\n */\r\n copyGPUTexture(texture: GPUTexture) {\r\n this.size = {\r\n width: texture.width,\r\n height: texture.height,\r\n depth: texture.depthOrArrayLayers,\r\n }\r\n\r\n this.texture = texture\r\n this.textureBinding.resource = this.texture\r\n }\r\n\r\n /**\r\n * Create the {@link GPUTexture | texture} (or copy it from source) and update the {@link TextureBinding#resource | binding resource}\r\n */\r\n createTexture() {\r\n if (this.options.fromTexture) {\r\n // copy the GPU texture\r\n this.options.format = this.options.fromTexture.options.format\r\n this.copyGPUTexture(this.options.fromTexture.texture)\r\n return\r\n }\r\n\r\n this.texture?.destroy()\r\n\r\n this.texture = this.renderer.createTexture({\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],\r\n dimensions: this.options.viewDimension === '1d' ? '1d' : this.options.viewDimension === '3d' ? '3d' : '2d',\r\n sampleCount: this.options.sampleCount,\r\n usage:\r\n // TODO let user chose?\r\n // see https://matrix.to/#/!MFogdGJfnZLrDmgkBN:matrix.org/$vESU70SeCkcsrJQdyQGMWBtCgVd3XqnHcBxFDKTKKSQ?via=matrix.org&via=mozilla.org&via=hej.im\r\n this.options.usage !== '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 } as GPUTextureDescriptor)\r\n\r\n // update texture binding\r\n this.textureBinding.resource = this.texture\r\n }\r\n\r\n /**\r\n * Set our {@link RenderTexture#bindings | bindings}\r\n */\r\n setBindings() {\r\n this.bindings = [\r\n new TextureBinding({\r\n label: this.options.label + ': ' + this.options.name + ' render texture',\r\n name: this.options.name,\r\n texture: this.texture,\r\n bindingType: this.options.usage,\r\n format: this.options.format,\r\n viewDimension: this.options.viewDimension,\r\n multisampled: this.options.sampleCount > 1,\r\n } as TextureBindingParams),\r\n ]\r\n }\r\n\r\n /**\r\n * Get our {@link TextureBinding | 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 * Resize our {@link RenderTexture}, which means recreate it/copy it again and tell the {@link core/bindGroups/TextureBindGroup.TextureBindGroup | texture bind group} to update\r\n * @param size - the optional new {@link TextureSize | size} to set\r\n */\r\n resize(size: TextureSize | null = null) {\r\n if (!this.#autoResize) return\r\n\r\n if (!size) {\r\n size = {\r\n width: Math.floor(this.renderer.displayBoundingRect.width * this.options.qualityRatio),\r\n height: Math.floor(this.renderer.displayBoundingRect.height * this.options.qualityRatio),\r\n depth: 1,\r\n }\r\n }\r\n\r\n // no real resize, bail!\r\n if (size.width === this.size.width && size.height === this.size.height && size.depth === this.size.depth) {\r\n return\r\n }\r\n\r\n this.size = size\r\n this.createTexture()\r\n }\r\n\r\n /**\r\n * Destroy our {@link RenderTexture}\r\n */\r\n destroy() {\r\n this.renderer.removeRenderTexture(this)\r\n\r\n // destroy the GPU texture only if it's not a copy of another texture\r\n if (!this.options.fromTexture) {\r\n this.texture?.destroy()\r\n }\r\n\r\n this.texture = null\r\n }\r\n}\r\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"RenderTexture.mjs","sources":["../../../../src/core/textures/RenderTexture.ts"],"sourcesContent":["import { isRenderer, Renderer } from '../renderers/utils'\nimport { TextureBinding, TextureBindingParams } from '../bindings/TextureBinding'\nimport { BindGroupBindingElement } from '../../types/BindGroups'\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\nimport { BindingMemoryAccessType, TextureBindingType } from '../bindings/Binding'\nimport { generateUUID } from '../../utils/utils'\nimport { Texture } from './Texture'\nimport { TextureSize } from '../../types/Textures'\n\n/**\n * Define the possible binding types of a {@link RenderTexture}\n */\nexport type RenderTextureBindingType = Exclude\n\n/**\n * Base parameters used to create a {@link RenderTexture}\n */\nexport interface RenderTextureBaseParams {\n /** The label of the {@link RenderTexture}, used to create various GPU objects for debugging purpose */\n label?: string\n /** Name of the {@link RenderTexture} to use in the {@link TextureBinding | texture binding} */\n name?: string\n\n /** Optional fixed size of the {@link RenderTexture#texture | texture}. If set, the {@link RenderTexture} will never be resized and always keep that size. */\n fixedSize?: TextureSize\n\n /** Force the texture size to be set to the given ratio of the {@link core/renderers/GPURenderer.GPURenderer#displayBoundingRect | renderer display bounding rectangle} or {@link fixedSize}. Used mainly to shrink render target definition. */\n qualityRatio?: number\n\n /** Whether to use this {@link RenderTexture} as a regular, storage or depth texture */\n usage?: RenderTextureBindingType\n /** Optional format of the {@link RenderTexture#texture | texture}, mainly used for storage textures */\n format?: GPUTextureFormat\n /** Optional texture binding memory access type, mainly used for storage textures */\n access?: BindingMemoryAccessType\n /** Optional {@link RenderTexture#texture | texture} view dimension to use */\n viewDimension?: GPUTextureViewDimension\n /** Sample count of the {@link RenderTexture#texture | texture}, used for multisampling */\n sampleCount?: GPUSize32\n}\n\n/**\n * Parameters used to create a {@link RenderTexture}\n */\nexport interface RenderTextureParams extends RenderTextureBaseParams {\n /** Optional texture to use as a copy source input. Could be a {@link RenderTexture} or {@link Texture} */\n fromTexture?: RenderTexture | Texture | null\n}\n\n/** @const - default {@link RenderTexture} parameters */\nconst defaultRenderTextureParams: RenderTextureParams = {\n label: 'RenderTexture',\n name: 'renderTexture',\n usage: 'texture',\n access: 'write',\n fromTexture: null,\n viewDimension: '2d',\n sampleCount: 1,\n qualityRatio: 1,\n}\n\n/**\n * Used to create {@link GPUTexture | texture} that can be used as copy source/destination for {@link core/renderPasses/RenderPass.RenderPass | RenderPass} and {@link core/renderPasses/RenderTarget.RenderTarget | RenderTarget}.
\n * Basically useful for copying anything outputted to the screen at one point or another.\n *\n * Will create a {@link GPUTexture} and its associated {@link TextureBinding}.\n *\n * @example\n * ```javascript\n * // set our main GPUCurtains instance\n * const gpuCurtains = new GPUCurtains({\n * container: '#canvas' // selector of our WebGPU canvas container\n * })\n *\n * // set the GPU device\n * // note this is asynchronous\n * await gpuCurtains.setDevice()\n *\n * // create a render texture\n * const renderTexture = new RenderTexture(gpuCurtains, {\n * label: 'My render texture',\n * name: 'renderTexture',\n * })\n * ```\n */\nexport class RenderTexture {\n /** {@link Renderer | renderer} used by this {@link RenderTexture} */\n renderer: Renderer\n /** The type of the {@link RenderTexture} */\n type: string\n /** The universal unique id of this {@link RenderTexture} */\n readonly uuid: string\n\n /** The {@link GPUTexture} used */\n texture: GPUTexture\n\n /** Size of the {@link RenderTexture#texture | texture} source, usually our {@link Renderer#displayBoundingRect | renderer display bounding rectangle size} */\n size: TextureSize\n\n /** Options used to create this {@link RenderTexture} */\n options: RenderTextureParams\n\n /** Array of {@link core/bindings/Binding.Binding | bindings} that will actually only hold one {@link TextureBinding | texture binding} */\n bindings: BindGroupBindingElement[]\n\n /** Whether this texture should be automatically resized when the {@link Renderer renderer} size changes. Default to true. */\n #autoResize = true\n\n /**\n * RenderTexture constructor\n * @param renderer - {@link Renderer | renderer} object or {@link GPUCurtains} class object used to create this {@link RenderTexture}\n * @param parameters - {@link RenderTextureParams | parameters} used to create this {@link RenderTexture}\n */\n constructor(renderer: Renderer | GPUCurtains, parameters = defaultRenderTextureParams) {\n // we could pass our curtains object OR our curtains renderer object\n renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as Renderer)\n\n isRenderer(renderer, parameters.label ? parameters.label + ' RenderTexture' : 'RenderTexture')\n\n this.type = 'RenderTexture'\n\n this.renderer = renderer\n\n this.uuid = generateUUID()\n\n this.options = { ...defaultRenderTextureParams, ...parameters }\n\n if (!this.options.format) {\n this.options.format = this.renderer.options.preferredFormat\n }\n\n // sizes\n this.size = this.options.fixedSize\n ? {\n width: this.options.fixedSize.width * this.options.qualityRatio,\n height: this.options.fixedSize.height * this.options.qualityRatio,\n depth: this.options.fixedSize.depth ?? this.options.viewDimension.indexOf('cube') !== -1 ? 6 : 1,\n }\n : {\n width: Math.floor(this.renderer.displayBoundingRect.width * this.options.qualityRatio),\n height: Math.floor(this.renderer.displayBoundingRect.height * this.options.qualityRatio),\n depth: this.options.viewDimension.indexOf('cube') !== -1 ? 6 : 1,\n }\n\n if (this.options.fixedSize) {\n this.#autoResize = false\n }\n\n // struct\n this.setBindings()\n\n // texture\n this.renderer.addRenderTexture(this)\n this.createTexture()\n }\n\n /**\n * Copy another {@link RenderTexture} into this {@link RenderTexture}\n * @param texture - {@link RenderTexture} to copy\n */\n copy(texture: RenderTexture | Texture) {\n this.options.fromTexture = texture\n this.createTexture()\n }\n\n /**\n * Copy a {@link GPUTexture} directly into this {@link RenderTexture}. Mainly used for depth textures.\n * @param texture - {@link GPUTexture} to copy\n */\n copyGPUTexture(texture: GPUTexture) {\n this.size = {\n width: texture.width,\n height: texture.height,\n depth: texture.depthOrArrayLayers,\n }\n\n this.texture = texture\n this.textureBinding.resource = this.texture\n }\n\n /**\n * Create the {@link GPUTexture | texture} (or copy it from source) and update the {@link TextureBinding#resource | binding resource}\n */\n createTexture() {\n if (this.options.fromTexture) {\n // copy the GPU texture\n this.options.format = this.options.fromTexture.options.format\n this.copyGPUTexture(this.options.fromTexture.texture)\n return\n }\n\n this.texture?.destroy()\n\n this.texture = this.renderer.createTexture({\n label: this.options.label,\n format: this.options.format,\n size: [this.size.width, this.size.height, this.size.depth ?? 1],\n dimensions: this.options.viewDimension,\n sampleCount: this.options.sampleCount,\n usage:\n // TODO let user chose?\n // see https://matrix.to/#/!MFogdGJfnZLrDmgkBN:matrix.org/$vESU70SeCkcsrJQdyQGMWBtCgVd3XqnHcBxFDKTKKSQ?via=matrix.org&via=mozilla.org&via=hej.im\n this.options.usage !== 'storage'\n ? GPUTextureUsage.TEXTURE_BINDING |\n GPUTextureUsage.COPY_SRC |\n GPUTextureUsage.COPY_DST |\n GPUTextureUsage.RENDER_ATTACHMENT\n : GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,\n } as GPUTextureDescriptor)\n\n // update texture binding\n this.textureBinding.resource = this.texture\n }\n\n /**\n * Set our {@link RenderTexture#bindings | bindings}\n */\n setBindings() {\n this.bindings = [\n new TextureBinding({\n label: this.options.label + ': ' + this.options.name + ' render texture',\n name: this.options.name,\n texture: this.texture,\n bindingType: this.options.usage,\n format: this.options.format,\n viewDimension: this.options.viewDimension,\n multisampled: this.options.sampleCount > 1,\n } as TextureBindingParams),\n ]\n }\n\n /**\n * Get our {@link TextureBinding | texture binding}\n * @readonly\n */\n get textureBinding(): TextureBinding {\n return this.bindings[0] as TextureBinding\n }\n\n /**\n * Resize our {@link RenderTexture}, which means recreate it/copy it again and tell the {@link core/bindGroups/TextureBindGroup.TextureBindGroup | texture bind group} to update\n * @param size - the optional new {@link TextureSize | size} to set\n */\n resize(size: TextureSize | null = null) {\n if (!this.#autoResize) return\n\n if (!size) {\n size = {\n width: Math.floor(this.renderer.displayBoundingRect.width * this.options.qualityRatio),\n height: Math.floor(this.renderer.displayBoundingRect.height * this.options.qualityRatio),\n depth: 1,\n }\n }\n\n // no real resize, bail!\n if (size.width === this.size.width && size.height === this.size.height && size.depth === this.size.depth) {\n return\n }\n\n this.size = size\n this.createTexture()\n }\n\n /**\n * Destroy our {@link RenderTexture}\n */\n destroy() {\n this.renderer.removeRenderTexture(this)\n\n // destroy the GPU texture only if it's not a copy of another texture\n if (!this.options.fromTexture) {\n this.texture?.destroy()\n }\n\n this.texture = null\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/gpu-curtains.umd.js b/dist/gpu-curtains.umd.js index 16cf1a9ac..342bbd803 100644 --- a/dist/gpu-curtains.umd.js +++ b/dist/gpu-curtains.umd.js @@ -2098,7 +2098,7 @@ * Get the {@link GPUBindGroupEntry#resource | bind group resource} */ get resource() { - return this.texture instanceof GPUTexture ? this.texture.createView({ label: this.options.label + " view" }) : this.texture instanceof GPUExternalTexture ? this.texture : null; + 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 the {@link GPUBindGroupEntry#resource | bind group resource} @@ -4113,11 +4113,11 @@ 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 + depth: this.options.fixedSize.depth ?? this.options.viewDimension.indexOf("cube") !== -1 ? 6 : 1 } : { width: Math.floor(this.renderer.displayBoundingRect.width * this.options.qualityRatio), height: Math.floor(this.renderer.displayBoundingRect.height * this.options.qualityRatio), - depth: 1 + depth: this.options.viewDimension.indexOf("cube") !== -1 ? 6 : 1 }; if (this.options.fixedSize) { __privateSet$4(this, _autoResize, false); @@ -4161,7 +4161,7 @@ label: this.options.label, format: this.options.format, size: [this.size.width, this.size.height, this.size.depth ?? 1], - dimensions: this.options.viewDimension === "1d" ? "1d" : this.options.viewDimension === "3d" ? "3d" : "2d", + dimensions: this.options.viewDimension, sampleCount: this.options.sampleCount, usage: ( // TODO let user chose? diff --git a/dist/gpu-curtains.umd.js.map b/dist/gpu-curtains.umd.js.map index 753a7f7bb..4c2b1b8a3 100644 --- a/dist/gpu-curtains.umd.js.map +++ b/dist/gpu-curtains.umd.js.map @@ -1 +1 @@ -{"version":3,"file":"gpu-curtains.umd.js","sources":["../src/utils/utils.ts","../src/core/renderers/utils.ts","../src/core/bindings/Binding.ts","../src/core/bindings/utils.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/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/Texture.ts","../src/core/bindGroups/TextureBindGroup.ts","../src/core/bindings/SamplerBinding.ts","../src/core/camera/Camera.ts","../src/core/samplers/Sampler.ts","../src/core/textures/RenderTexture.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/RenderMaterial.ts","../src/core/shaders/chunks/default_vs.wgsl.js","../src/core/shaders/chunks/default_fs.wgsl.js","../src/core/meshes/mixins/MeshBaseMixin.ts","../src/utils/CacheManager.ts","../src/core/meshes/FullscreenPlane.ts","../src/core/objects3D/ProjectedObject3D.ts","../src/core/shaders/chunks/default_projected_vs.wgsl.js","../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_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/shaders/chunks/default_pass_fs.wgsl.js","../src/core/renderPasses/ShaderPass.ts","../src/core/renderPasses/RenderPass.ts","../src/core/renderPasses/RenderTarget.ts","../src/curtains/meshes/PingPongPlane.ts","../src/curtains/objects3D/DOMObject3D.ts","../src/curtains/meshes/DOMMesh.ts","../src/curtains/meshes/Plane.ts","../src/core/scenes/Scene.ts","../src/utils/TasksQueueManager.ts","../src/core/renderers/GPURenderer.ts","../src/core/renderers/GPUCameraRenderer.ts","../src/core/renderers/GPUDeviceManager.ts","../src/curtains/renderers/GPUCurtainsRenderer.ts","../src/utils/ScrollManager.ts","../src/curtains/GPUCurtains.ts","../src/extras/geometries/BoxGeometry.ts","../src/extras/geometries/SphereGeometry.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'\nimport { GPURenderer } from './GPURenderer'\nimport { GPUCameraRenderer } from './GPUCameraRenderer'\nimport { GPUCurtainsRenderer } from '../../curtains/renderers/GPUCurtainsRenderer'\nimport { WritableBufferBinding } from '../bindings/WritableBufferBinding'\n\n/**\n * A Renderer could be either a {@link GPURenderer}, a {@link GPUCameraRenderer} or a {@link GPUCurtainsRenderer}\n * @type {Renderer}\n */\nexport type Renderer = GPUCurtainsRenderer | GPUCameraRenderer | GPURenderer\n/**\n * A CameraRenderer could be either a {@link GPUCameraRenderer} or a {@link GPUCurtainsRenderer}\n * @type {CameraRenderer}\n */\nexport type CameraRenderer = GPUCurtainsRenderer | GPUCameraRenderer\n\n/**\n * Format a renderer error based on given renderer, renderer type and object type\n * @param renderer - renderer that failed the test\n * @param rendererType - expected renderer type\n * @param type - object type\n */\nconst formatRendererError = (renderer: Renderer, rendererType = 'GPURenderer', type: string | null): void => {\n const error = type\n ? `Unable to create ${type} because the ${rendererType} is not defined: ${renderer}`\n : `The ${rendererType} is not defined: ${renderer}`\n throwError(error)\n}\n\n/**\n * Check if the given renderer is a {@link Renderer}\n * @param renderer - renderer to test\n * @param type - object type used to format the error if needed\n * @returns - whether the given renderer is a {@link Renderer}\n */\nexport const isRenderer = (renderer: Renderer | undefined, type: string | null): boolean => {\n const isRenderer =\n renderer &&\n (renderer.type === 'GPURenderer' ||\n renderer.type === 'GPUCameraRenderer' ||\n renderer.type === 'GPUCurtainsRenderer')\n\n if (!isRenderer) {\n formatRendererError(renderer, 'GPURenderer', type)\n }\n\n return isRenderer\n}\n\n/**\n * Check if the given renderer is a {@link CameraRenderer}\n * @param renderer - renderer to test\n * @param type - object type used to format the error if needed\n * @returns - whether the given renderer is a {@link CameraRenderer}\n */\nexport const isCameraRenderer = (renderer: CameraRenderer | undefined, type: string | null): boolean => {\n const isCameraRenderer =\n renderer && (renderer.type === 'GPUCameraRenderer' || renderer.type === 'GPUCurtainsRenderer')\n\n if (!isCameraRenderer) {\n formatRendererError(renderer, 'GPUCameraRenderer', type)\n }\n\n return isCameraRenderer\n}\n\n/**\n * Check if the given renderer is a {@link GPUCurtainsRenderer}\n * @param renderer - renderer to test\n * @param type - object type used to format the error if needed\n * @returns - whether the given renderer is a {@link GPUCurtainsRenderer}\n */\nexport const isCurtainsRenderer = (renderer: GPUCurtainsRenderer | undefined, type: string | null): boolean => {\n const isCurtainsRenderer = renderer && renderer.type === 'GPUCurtainsRenderer'\n\n if (!isCurtainsRenderer) {\n formatRendererError(renderer, 'GPUCurtainsRenderer', type)\n }\n\n return isCurtainsRenderer\n}\n\n/**\n * Helper to generate mips on the GPU\n * Taken from https://webgpufundamentals.org/webgpu/lessons/webgpu-importing-textures.html\n */\nexport const generateMips = (() => {\n let sampler\n let module\n const pipelineByFormat = {}\n\n return function generateMips(device: GPUDevice, texture: GPUTexture) {\n if (!module) {\n module = device.createShaderModule({\n label: 'textured quad shaders for mip level generation',\n code: `\n struct VSOutput {\n @builtin(position) position: vec4f,\n @location(0) texcoord: vec2f,\n };\n\n @vertex fn vs(\n @builtin(vertex_index) vertexIndex : u32\n ) -> VSOutput {\n var pos = array(\n\n vec2f( 0.0, 0.0), // center\n vec2f( 1.0, 0.0), // right, center\n vec2f( 0.0, 1.0), // center, top\n\n // 2st triangle\n vec2f( 0.0, 1.0), // center, top\n vec2f( 1.0, 0.0), // right, center\n vec2f( 1.0, 1.0), // right, top\n );\n\n var vsOutput: VSOutput;\n let xy = pos[vertexIndex];\n vsOutput.position = vec4f(xy * 2.0 - 1.0, 0.0, 1.0);\n vsOutput.texcoord = vec2f(xy.x, 1.0 - xy.y);\n return vsOutput;\n }\n\n @group(0) @binding(0) var ourSampler: sampler;\n @group(0) @binding(1) var ourTexture: texture_2d;\n\n @fragment fn fs(fsInput: VSOutput) -> @location(0) vec4f {\n return textureSample(ourTexture, ourSampler, fsInput.texcoord);\n }\n `,\n })\n\n sampler = device.createSampler({\n minFilter: 'linear',\n })\n }\n\n if (!pipelineByFormat[texture.format]) {\n pipelineByFormat[texture.format] = device.createRenderPipeline({\n label: 'mip level generator pipeline',\n layout: 'auto',\n vertex: {\n module,\n entryPoint: 'vs',\n },\n fragment: {\n module,\n entryPoint: 'fs',\n targets: [{ format: texture.format }],\n },\n })\n }\n const pipeline = pipelineByFormat[texture.format]\n\n const encoder = device.createCommandEncoder({\n label: 'mip gen encoder',\n })\n\n let width = texture.width\n let height = texture.height\n let baseMipLevel = 0\n while (width > 1 || height > 1) {\n width = Math.max(1, (width / 2) | 0)\n height = Math.max(1, (height / 2) | 0)\n\n const bindGroup = device.createBindGroup({\n layout: pipeline.getBindGroupLayout(0),\n entries: [\n { binding: 0, resource: sampler },\n {\n binding: 1,\n resource: texture.createView({\n baseMipLevel,\n mipLevelCount: 1,\n }),\n },\n ],\n })\n\n ++baseMipLevel\n\n const renderPassDescriptor = {\n label: 'our basic canvas renderPass',\n colorAttachments: [\n {\n view: texture.createView({ baseMipLevel, mipLevelCount: 1 }),\n loadOp: 'clear',\n storeOp: 'store',\n },\n ],\n }\n\n const pass = encoder.beginRenderPass(renderPassDescriptor as GPURenderPassDescriptor)\n pass.setPipeline(pipeline)\n pass.setBindGroup(0, bindGroup)\n pass.draw(6) // call our vertex shader 6 times\n pass.end()\n }\n\n const commandBuffer = encoder.finish()\n device.queue.submit([commandBuffer])\n }\n})()\n","import { toCamelCase } from '../../utils/utils'\nimport { MaterialShadersType } from '../../types/Materials'\nimport { TextureBinding } from './TextureBinding'\nimport { SamplerBinding } from './SamplerBinding'\n\n/** Defines all kind of texture binding types */\nexport type TextureBindingType = 'texture' | 'externalTexture' | 'storage' | 'depth'\n/** Defines all kind of binding types */\nexport type BindingType = 'uniform' | 'storage' | TextureBindingType | 'sampler'\n\n// see https://www.w3.org/TR/WGSL/#memory-access-mode\n/** Defines buffer binding memory access types (read only or read/write) */\nexport type BufferBindingMemoryAccessType = 'read' | 'read_write'\n/** Defines texture binding memory access types (read only, write only or read/write) */\nexport type BindingMemoryAccessType = BufferBindingMemoryAccessType | 'write'\n\n/**\n * Defines all kind of {@link Binding} that are related to textures or samplers\n */\nexport type TextureSamplerBindings = TextureBinding | SamplerBinding\n\n/**\n * An object defining all possible {@link Binding} class instancing parameters\n */\nexport interface BindingParams {\n /** {@link Binding} label */\n label?: string\n /** {@link Binding} name/key */\n name?: string\n /** {@link BindingType | binding type} to use with this {@link Binding} */\n bindingType?: BindingType\n /** {@link Binding} variables shaders visibility */\n visibility?: MaterialShadersType | null\n}\n\n/**\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}.\n *\n * Ultimately the goal of a {@link Binding} element is to provide correct resources for {@link GPUBindGroupLayoutEntry} and {@link GPUBindGroupEntry}\n *\n * ## WGSL\n *\n * Each {@link Binding} creates its own WGSL code snippet variable declaration, using structured types or not.\n */\nexport class Binding {\n /** The label of the {@link Binding} */\n label: string\n /** The name/key of the {@link Binding} */\n name: string\n /** The binding type of the {@link Binding} */\n bindingType: BindingType\n /** The visibility of the {@link Binding} in the shaders */\n visibility: GPUShaderStageFlags\n /** Options used to create this {@link Binding} */\n options: BindingParams\n\n /** Flag indicating whether we should recreate the parentMesh {@link core/bindGroups/BindGroup.BindGroup#bindGroup | bind group}, usually when a resource has changed */\n shouldResetBindGroup: boolean\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 */\n shouldResetBindGroupLayout: boolean\n\n /**\n * Binding constructor\n * @param parameters - {@link BindingParams | parameters} used to create our {@link Binding}\n */\n constructor({ label = 'Uniform', name = 'uniform', bindingType = 'uniform', visibility }: BindingParams) {\n this.label = label\n this.name = toCamelCase(name)\n this.bindingType = bindingType\n\n this.visibility = visibility\n ? (() => {\n switch (visibility) {\n case 'vertex':\n return GPUShaderStage.VERTEX\n case 'fragment':\n return GPUShaderStage.FRAGMENT\n case 'compute':\n return GPUShaderStage.COMPUTE\n default:\n return GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE\n }\n })()\n : GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE\n\n this.options = {\n label,\n name,\n bindingType,\n visibility,\n }\n\n this.shouldResetBindGroup = false\n this.shouldResetBindGroupLayout = false\n }\n}\n","import { BindingType } from './Binding'\nimport { BufferBinding } from './BufferBinding'\nimport { TextureBinding } from './TextureBinding'\n\n/** Defines a typed array */\nexport type TypedArray =\n | Int8Array\n | Uint8Array\n | Uint8ClampedArray\n | Int16Array\n | Uint16Array\n | Int32Array\n | Uint32Array\n | Float32Array\n | Float64Array\n\n/** Defines a typed array constructor */\nexport type TypedArrayConstructor =\n | Int8ArrayConstructor\n | Uint8ArrayConstructor\n | Int16ArrayConstructor\n | Uint16ArrayConstructor\n | Int32ArrayConstructor\n | Uint32ArrayConstructor\n | Float32ArrayConstructor\n | Float64ArrayConstructor\n\n/** Defines the possible WGSL variable types */\nexport type WGSLVariableType = string // TODO 'mat4x4f', 'mat3x3f', 'vec3f', 'vec2f', 'f32' etc\n\n/**\n * Defines a {@link BufferLayout} object used to pad our {@link GPUBuffer} arrays\n */\nexport type BufferLayout = {\n /** Number of elements hold by this variable type */\n numElements: number\n /** Required alignment by this variable type */\n align: number\n /** Size in bytes of this variable type */\n size: number\n /** Variable type */\n type: WGSLVariableType\n /** Typed array constructor required by this variable type */\n View: TypedArrayConstructor\n /** Pad values required by this variable type */\n pad?: number[]\n}\n\n// from https://github.com/greggman/webgpu-utils/blob/main/src/buffer-views.ts\n/**\n * Get the correct [buffer layout]{@link BufferLayout} for given [variable type]{@link WGSLVariableType}\n * @param bufferType - [variable type]{@link WGSLVariableType} to use\n * @returns - the [buffer layout]{@link BufferLayout}\n */\nexport const getBufferLayout = (bufferType: WGSLVariableType): BufferLayout => {\n const bufferLayouts = {\n i32: { numElements: 1, align: 4, size: 4, type: 'i32', View: Int32Array },\n u32: { numElements: 1, align: 4, size: 4, type: 'u32', View: Uint32Array },\n f32: { numElements: 1, align: 4, size: 4, type: 'f32', View: Float32Array },\n f16: { numElements: 1, align: 2, size: 2, type: 'u16', View: Uint16Array },\n\n vec2f: { numElements: 2, align: 8, size: 8, type: 'f32', View: Float32Array },\n vec2i: { numElements: 2, align: 8, size: 8, type: 'i32', View: Int32Array },\n vec2u: { numElements: 2, align: 8, size: 8, type: 'u32', View: Uint32Array },\n vec2h: { numElements: 2, align: 4, size: 4, type: 'u16', View: Uint16Array },\n vec3i: { numElements: 3, align: 16, size: 12, type: 'i32', View: Int32Array },\n vec3u: { numElements: 3, align: 16, size: 12, type: 'u32', View: Uint32Array },\n vec3f: { numElements: 3, align: 16, size: 12, type: 'f32', View: Float32Array },\n vec3h: { numElements: 3, align: 8, size: 6, type: 'u16', View: Uint16Array },\n vec4i: { numElements: 4, align: 16, size: 16, type: 'i32', View: Int32Array },\n vec4u: { numElements: 4, align: 16, size: 16, type: 'u32', View: Uint32Array },\n vec4f: { numElements: 4, align: 16, size: 16, type: 'f32', View: Float32Array },\n vec4h: { numElements: 4, align: 8, size: 8, type: 'u16', View: Uint16Array },\n\n // AlignOf(vecR)\tSizeOf(array)\n mat2x2f: { numElements: 4, align: 8, size: 16, type: 'f32', View: Float32Array },\n mat2x2h: { numElements: 4, align: 4, size: 8, type: 'u16', View: Uint16Array },\n mat3x2f: { numElements: 6, align: 8, size: 24, type: 'f32', View: Float32Array },\n mat3x2h: { numElements: 6, align: 4, size: 12, type: 'u16', View: Uint16Array },\n mat4x2f: { numElements: 8, align: 8, size: 32, type: 'f32', View: Float32Array },\n mat4x2h: { numElements: 8, align: 4, size: 16, type: 'u16', View: Uint16Array },\n mat2x3f: { numElements: 8, align: 16, size: 32, pad: [3, 1], type: 'f32', View: Float32Array },\n mat2x3h: { numElements: 8, align: 8, size: 16, pad: [3, 1], type: 'u16', View: Uint16Array },\n mat3x3f: { numElements: 12, align: 16, size: 48, pad: [3, 1], type: 'f32', View: Float32Array },\n mat3x3h: { numElements: 12, align: 8, size: 24, pad: [3, 1], type: 'u16', View: Uint16Array },\n mat4x3f: { numElements: 16, align: 16, size: 64, pad: [3, 1], type: 'f32', View: Float32Array },\n mat4x3h: { numElements: 16, align: 8, size: 32, pad: [3, 1], type: 'u16', View: Uint16Array },\n mat2x4f: { numElements: 8, align: 16, size: 32, type: 'f32', View: Float32Array },\n mat2x4h: { numElements: 8, align: 8, size: 16, type: 'u16', View: Uint16Array },\n mat3x4f: { numElements: 12, align: 16, size: 48, pad: [3, 1], type: 'f32', View: Float32Array },\n mat3x4h: { numElements: 12, align: 8, size: 24, pad: [3, 1], type: 'u16', View: Uint16Array },\n mat4x4f: { numElements: 16, align: 16, size: 64, type: 'f32', View: Float32Array },\n mat4x4h: { numElements: 16, align: 8, size: 32, type: 'u16', View: Uint16Array },\n }\n\n return bufferLayouts[bufferType]\n}\n\n/**\n * Get the correct WGSL variable declaration code fragment based on the given [buffer binding]{@link BufferBinding}\n * @param binding - [buffer binding]{@link BufferBinding} to use\n * @returns - WGSL variable declaration code fragment\n */\nexport const getBindingWGSLVarType = (binding: BufferBinding): string => {\n return (() => {\n switch (binding.bindingType) {\n case 'storage':\n return `var<${binding.bindingType}, ${binding.options.access}>`\n case 'uniform':\n default:\n return 'var'\n }\n })()\n}\n\n/**\n * Get the correct WGSL variable declaration code fragment based on the given [texture binding]{@link TextureBinding}\n * @param binding - [texture binding]{@link TextureBinding} to use\n * @returns - WGSL variable declaration code fragment\n */\nexport const getTextureBindingWGSLVarType = (binding: TextureBinding): string => {\n if (binding.bindingType === 'externalTexture') {\n return `var ${binding.name}: texture_external;`\n }\n\n return binding.bindingType === 'storage'\n ? `var ${binding.name}: texture_storage_${binding.options.viewDimension}<${binding.options.format}, ${binding.options.access}>;`\n : binding.bindingType === 'depth'\n ? `var ${binding.name}: texture_depth${binding.options.multisampled ? '_multisampled' : ''}_${\n binding.options.viewDimension\n };`\n : `var ${binding.name}: texture${binding.options.multisampled ? '_multisampled' : ''}_${\n binding.options.viewDimension\n };`\n}\n\n/**\n * Get the correct [bind group layout]{@link GPUBindGroupLayout} resource type based on the given [binding type]{@link BindingType}\n * @param binding - [buffer binding]{@link BufferBinding} to use\n * @returns - {@link GPUBindGroupLayout | bind group layout} resource type\n */\nexport const getBindGroupLayoutBindingType = (binding: BufferBinding): GPUBufferBindingType => {\n if (binding.bindingType === 'storage' && binding.options.access === 'read_write') {\n return 'storage'\n } else if (binding.bindingType === 'storage') {\n return 'read-only-storage'\n } else {\n return 'uniform'\n }\n}\n\n/**\n * Get the correct [bind group layout]{@link GPUBindGroupLayout} resource type based on the given [texture binding type]{@link BindingType}\n * @param binding - [texture binding]{@link TextureBinding} to use\n * @returns - [bind group layout]{@link GPUBindGroupLayout} resource type\n */\nexport const getBindGroupLayoutTextureBindingType = (\n binding: TextureBinding\n): GPUTextureBindingLayout | GPUExternalTextureBindingLayout | GPUStorageTextureBindingLayout | null => {\n return (() => {\n switch (binding.bindingType) {\n case 'externalTexture':\n return { externalTexture: {} }\n case 'storage':\n return {\n storageTexture: {\n format: binding.options.format,\n viewDimension: binding.options.viewDimension,\n } as GPUStorageTextureBindingLayout,\n }\n case 'texture':\n return {\n texture: {\n multisampled: binding.options.multisampled,\n viewDimension: binding.options.viewDimension,\n sampleType: binding.options.multisampled ? 'unfilterable-float' : 'float',\n } as GPUTextureBindingLayout,\n }\n case 'depth':\n return {\n texture: {\n multisampled: binding.options.multisampled,\n format: binding.options.format,\n viewDimension: binding.options.viewDimension,\n sampleType: 'depth',\n } as GPUTextureBindingLayout,\n }\n default:\n return null\n }\n })()\n}\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 * 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 * 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\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 /**\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\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 // 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 if (size <= bytesPerRow && nextPositionAvailable.byte + size > bytesPerRow) {\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 * Update the {@link view} based on the new value\r\n * @param value - new value to use\r\n */\r\n update(value) {\r\n if (this.type === 'f32' || this.type === 'u32' || this.type === 'i32') {\r\n this.view[0] = value as number\r\n } else if (this.type === 'vec2f') {\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 } else if (this.type === 'vec3f') {\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 } else if ((value as Quat | Mat4).elements) {\r\n this.view.set((value as Quat | Mat4).elements)\r\n } else if (ArrayBuffer.isView(value) || Array.isArray(value)) {\r\n this.view.set(value as number[])\r\n }\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'\nimport { throwWarning } from '../../../utils/utils'\n\n/**\n * Parameters used to create a {@link BufferArrayElement}\n */\nexport interface BufferArrayElementParams extends BufferElementParams {\n /** Initial length of the input {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array} */\n arrayLength: number\n}\n\n/**\n * Used to handle specific array {@link core/bindings/BufferBinding.BufferBinding | BufferBinding} types\n */\nexport class BufferArrayElement extends BufferElement {\n /** Initial length of the input {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array} */\n arrayLength: number\n /** Total number of elements (i.e. {@link arrayLength} divided by {@link core/bindings/utils.BufferLayout | buffer layout} number of elements */\n numElements: number\n /** Number of bytes in the {@link ArrayBuffer} between two elements {@link startOffset} */\n arrayStride: number\n\n /**\n * BufferArrayElement constructor\n * @param parameters - {@link BufferArrayElementParams | parameters} used to create our {@link BufferArrayElement}\n */\n constructor({ name, key, type = 'f32', arrayLength = 1 }: BufferArrayElementParams) {\n super({ name, key, type })\n\n this.arrayLength = arrayLength\n this.numElements = this.arrayLength / this.bufferLayout.numElements\n }\n\n /**\n * Get the array stride between two elements of the array, in indices\n * @readonly\n */\n get arrayStrideToIndex(): number {\n return this.arrayStride / bytesPerSlot\n }\n\n /**\n * Set the {@link core/bindings/bufferElements/BufferElement.BufferElementAlignment | alignment}\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.\n * @param startOffset - offset at which to start inserting the values in the {@link core/bindings/BufferBinding.BufferBinding#arrayBuffer | buffer binding array buffer}\n */\n setAlignment(startOffset = 0) {\n super.setAlignment(startOffset)\n\n // repeat for a second element to know how things are laid out\n const nextAlignment = this.getElementAlignment(this.getPositionAtOffset(this.endOffset + 1))\n this.arrayStride = this.getByteCountBetweenPositions(this.alignment.end, nextAlignment.end)\n\n this.alignment.end = this.getPositionAtOffset(this.endOffset + this.arrayStride * (this.numElements - 1))\n }\n\n /**\n * Update the {@link view} based on the new value\n * @param value - new value to use\n */\n update(value) {\n if (ArrayBuffer.isView(value) || Array.isArray(value)) {\n let valueIndex = 0\n\n const viewLength = this.byteCount / this.bufferLayout.View.BYTES_PER_ELEMENT\n // arrayStride is our view length divided by the number of elements in our array\n const stride = Math.ceil(viewLength / this.numElements)\n\n for (let i = 0; i < this.numElements; i++) {\n for (let j = 0; j < this.bufferLayout.numElements; j++) {\n this.view[j + i * stride] = value[valueIndex]\n\n valueIndex++\n }\n }\n } else {\n throwWarning(`BufferArrayElement: value passed to ${this.name} is not an array: ${value}`)\n }\n }\n}\n","import { BufferArrayElement, BufferArrayElementParams } from './BufferArrayElement'\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 = 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) {\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","import { Binding, BindingParams, BufferBindingMemoryAccessType } 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'\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\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}\n\n/**\n * Parameters used to create a {@link BufferBinding}\n */\nexport interface BufferBindingParams extends BindingParams, BufferBindingBaseParams {}\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 /** 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 GPUBuffer */\n buffer: GPUBuffer | null\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 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 struct: struct,\n }\n\n this.arrayBufferSize = 0\n\n this.shouldUpdate = false\n this.useStruct = useStruct\n\n this.bufferElements = []\n this.inputs = {}\n this.buffer = null\n\n this.setBindings(struct)\n this.setBufferAttributes()\n this.setWGSLFragment()\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 {@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 }\n }\n\n /**\n * Format bindings struct and set our {@link inputs}\n * @param bindings - bindings inputs\n */\n setBindings(bindings: Record) {\n Object.keys(bindings).forEach((bindingKey) => {\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 // force the binding to have a name\n binding.name = bindings[bindingKey].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 }\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 const arrayBindings = Object.keys(this.inputs).filter(\n (bindingKey) => this.inputs[bindingKey].type.indexOf('array') !== -1\n )\n\n // put the array struct at the end\n let orderedBindings = Object.keys(this.inputs).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 // handle buffer (non interleaved) elements\n orderedBindings.forEach((bindingKey) => {\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.indexOf('array') !== -1 && (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 (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.bufferElements.forEach((bufferElement) => {\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 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 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 const bindingKey = Object.keys(this.inputs).find((bindingKey) => this.inputs[bindingKey].name === bindingName)\n\n if (bindingKey) this.inputs[bindingKey].shouldUpdate = true\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 Object.keys(this.inputs).forEach((bindingKey) => {\n const binding = this.inputs[bindingKey]\n const bufferElement = this.bufferElements.find((bufferEl) => bufferEl.key === bindingKey)\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'\n\n/**\n * Parameters used to create a {@link WritableBufferBinding}\n */\nexport interface WritableBufferBindingParams extends BufferBindingParams {\n /** Whether whe should automatically copy the {@link WritableBufferBinding#buffer | GPU buffer} content into our {@link WritableBufferBinding#resultBuffer | result GPU buffer} */\n shouldCopyResult?: boolean\n}\n\n/**\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.\n *\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\"`.\n */\nexport class WritableBufferBinding extends BufferBinding {\n /** Flag indicating whether whe should automatically copy the {@link buffer | GPU buffer} content into our {@link resultBuffer | result GPU buffer} */\n shouldCopyResult: boolean\n /** The result GPUBuffer */\n resultBuffer: GPUBuffer | null\n /** Options used to create this {@link WritableBufferBinding} */\n options: WritableBufferBindingParams\n\n /**\n * WritableBufferBinding constructor\n * @param parameters - {@link WritableBufferBindingParams | parameters} used to create our {@link WritableBufferBinding}\n */\n constructor({\n label = 'Work',\n name = 'work',\n bindingType,\n useStruct = true,\n struct = {},\n visibility,\n access = 'read_write',\n shouldCopyResult = false,\n }: WritableBufferBindingParams) {\n bindingType = 'storage'\n visibility = 'compute'\n\n super({ label, name, bindingType, useStruct, struct: struct, visibility, access })\n\n this.options = {\n ...this.options,\n shouldCopyResult,\n }\n\n this.shouldCopyResult = shouldCopyResult\n\n // can be used as our buffer copy destination\n this.resultBuffer = null\n }\n}\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 { BindingType } from '../bindings/Binding'\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 * struct: {\r\n * opacity: {\r\n * type: 'f32',\r\n * value: 1,\r\n * },\r\n * mousePosition: {\r\n * type: '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 /** Flag indicating whether we need to flush and recreate the pipeline using this {@link BindGroup} s*/\r\n needsPipelineFlush: boolean\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.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.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 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: BindingType = 'uniform',\r\n inputs: ReadOnlyInputBindings = {}\r\n ): BindGroupBindingElement[] {\r\n return [\r\n ...Object.keys(inputs).map((inputKey) => {\r\n const binding = inputs[inputKey] as WritableBufferBindingParams\r\n\r\n const bindingParams: WritableBufferBindingParams = {\r\n label: toKebabCase(binding.label || inputKey),\r\n name: inputKey,\r\n bindingType,\r\n useStruct: true, // by default\r\n visibility: binding.access === 'read_write' ? 'compute' : binding.visibility,\r\n access: binding.access ?? 'read', // read by default\r\n struct: binding.struct,\r\n ...(binding.shouldCopyResult !== undefined && { shouldCopyResult: binding.shouldCopyResult }),\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\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.bindings.forEach((binding) => {\r\n this.entries.bindGroup.push({\r\n binding: this.entries.bindGroup.length,\r\n resource: binding.resource,\r\n })\r\n })\r\n\r\n this.setBindGroup()\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.bindings.forEach((binding) => {\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\r\n this.setBindGroupLayout()\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 this.bufferBindings.forEach((binding) => {\r\n binding.buffer = null\r\n\r\n if ('resultBuffer' in binding) {\r\n binding.resultBuffer = null\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 * 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 // TODO user defined usage?\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 = this.renderer.createBuffer({\r\n label: this.options.label + ': ' + binding.bindingType + ' buffer from: ' + binding.label,\r\n size: binding.arrayBuffer.byteLength,\r\n usage:\r\n binding.bindingType === 'uniform'\r\n ? GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC | GPUBufferUsage.VERTEX\r\n : GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC | GPUBufferUsage.VERTEX,\r\n })\r\n\r\n if ('resultBuffer' in binding) {\r\n binding.resultBuffer = this.renderer.createBuffer({\r\n label: this.options.label + ': Result buffer from: ' + binding.label,\r\n size: binding.arrayBuffer.byteLength,\r\n usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,\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 this.bindings.forEach((binding) => {\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 && !binding.buffer) {\r\n this.createBindingBuffer(binding)\r\n }\r\n\r\n // now that everything is ready, fill our entries\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.entries.bindGroup.push({\r\n binding: this.entries.bindGroup.length,\r\n resource: binding.resource,\r\n })\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 this.bindGroupLayout = this.renderer.createBindGroupLayout({\r\n label: this.options.label + ' layout',\r\n entries: this.entries.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.bufferBindings.forEach((binding, index) => {\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, 0, binding.bufferElements[index].view)\r\n } else {\r\n this.renderer.queueWriteBuffer(binding.buffer, 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 * 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 this.bindings.forEach((binding) => {\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 bindingsRef.forEach((binding, index) => {\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 && !binding.buffer) {\r\n bindGroupCopy.createBindingBuffer(binding)\r\n }\r\n\r\n // if we should create a new bind group layout, fill it\r\n if (!keepLayout) {\r\n bindGroupCopy.entries.bindGroupLayout.push({\r\n binding: bindGroupCopy.entries.bindGroupLayout.length,\r\n ...binding.resourceLayout,\r\n visibility: binding.visibility,\r\n })\r\n }\r\n\r\n bindGroupCopy.entries.bindGroup.push({\r\n binding: bindGroupCopy.entries.bindGroup.length,\r\n resource: binding.resource,\r\n } as GPUBindGroupEntry)\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 }\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 this.bufferBindings.forEach((binding) => {\r\n if ('buffer' in binding) {\r\n this.renderer.removeBuffer(binding.buffer)\r\n binding.buffer?.destroy()\r\n binding.buffer = null\r\n }\r\n\r\n if ('resultBuffer' in binding) {\r\n this.renderer.removeBuffer(binding.resultBuffer)\r\n binding.resultBuffer?.destroy()\r\n binding.resultBuffer = null\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, BindingType } from './Binding'\nimport { getBindGroupLayoutTextureBindingType, getTextureBindingWGSLVarType } from './utils'\n\n/** Defines a {@link TextureBinding} {@link TextureBinding#resource | resource} */\nexport type TextureBindingResource = GPUTexture | GPUExternalTexture | null\n\n/**\n * An object defining all possible {@link TextureBinding} class instancing parameters\n */\nexport interface TextureBindingParams extends BindingParams {\n /** {@link TextureBinding} {@link TextureBinding#resource | resource} */\n texture: TextureBindingResource\n /** The {@link GPUTexture | texture} format to use */\n format?: GPUTextureFormat\n /** The storage {@link GPUTexture | texture} binding memory access types (read only, write only or read/write) */\n access?: BindingMemoryAccessType\n /** The {@link GPUTexture | texture} view dimension to use */\n viewDimension?: GPUTextureViewDimension\n /** Whethe the {@link GPUTexture | texture} is a multisampled texture. Mainly used internally by depth textures if needed. */\n multisampled?: boolean\n}\n\n/**\n * Used to handle {@link GPUTexture} and {@link GPUExternalTexture} bindings.\n *\n * Provide both {@link TextureBinding#resourceLayout | resourceLayout} and {@link TextureBinding#resource | resource} to the {@link GPUBindGroupLayout} and {@link GPUBindGroup}.
\n * Also create the appropriate WGSL code snippet to add to the shaders.\n */\nexport class TextureBinding extends Binding {\n /** Our {@link TextureBinding} resource, i.e. a {@link GPUTexture} or {@link GPUExternalTexture} */\n texture: TextureBindingResource\n /** An array of strings to append to our shaders code declaring all the WGSL variables representing this {@link TextureBinding} */\n wgslGroupFragment: string[]\n /** Options used to create this {@link TextureBinding} */\n options: TextureBindingParams\n\n /**\n * TextureBinding constructor\n * @param parameters - {@link TextureBindingParams | parameters} used to create our {@link TextureBinding}\n */\n constructor({\n label = 'Texture',\n name = 'texture',\n bindingType,\n visibility,\n texture,\n format = 'rgba8unorm',\n access = 'write',\n viewDimension = '2d',\n multisampled = false,\n }: TextureBindingParams) {\n bindingType = bindingType ?? 'texture'\n\n if (bindingType === 'storage') {\n visibility = 'compute'\n }\n\n super({ label, name, bindingType, visibility })\n\n this.options = {\n ...this.options,\n texture,\n format,\n access,\n viewDimension,\n multisampled,\n }\n\n this.resource = texture // should be a texture or an external texture\n\n this.setWGSLFragment()\n }\n\n /**\n * Get bind group layout entry resource, either for {@link GPUBindGroupLayoutEntry#texture | texture} or {@link GPUBindGroupLayoutEntry#externalTexture | external texture}\n * @readonly\n */\n get resourceLayout():\n | GPUTextureBindingLayout\n | GPUExternalTextureBindingLayout\n | GPUStorageTextureBindingLayout\n | null {\n return getBindGroupLayoutTextureBindingType(this)\n }\n\n /**\n * Get the {@link GPUBindGroupEntry#resource | bind group resource}\n */\n get resource(): GPUExternalTexture | GPUTextureView | null {\n return this.texture instanceof GPUTexture\n ? this.texture.createView({ label: this.options.label + ' view' })\n : this.texture instanceof GPUExternalTexture\n ? this.texture\n : null\n }\n\n /**\n * Set the {@link GPUBindGroupEntry#resource | bind group resource}\n * @param value - new bind group resource\n */\n set resource(value: TextureBindingResource) {\n // resource changed, update bind group!\n if (value || this.texture) this.shouldResetBindGroup = true\n this.texture = value\n }\n\n /**\n * Set or update our {@link Binding#bindingType | bindingType} and our WGSL code snippet\n * @param bindingType - the new {@link Binding#bindingType | binding type}\n */\n setBindingType(bindingType: BindingType) {\n if (bindingType !== this.bindingType) {\n // binding type has changed!\n if (bindingType) this.shouldResetBindGroupLayout = true\n\n this.bindingType = bindingType\n this.setWGSLFragment()\n }\n }\n\n /**\n * Set the correct WGSL code snippet.\n */\n setWGSLFragment() {\n this.wgslGroupFragment = [`${getTextureBindingWGSLVarType(this)}`]\n }\n}\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\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 * 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 // TODO handle z from 0 to 1 like https://github.com/greggman/wgpu-matrix/blob/main/src/mat4-impl.ts#L756 does it?\n // It would be aligned with the WebGPU specs but it breaks the Camera screenRatio and DOMObject3D position calcs\n\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 const c = -(far + near) / (far - near)\n const d = (-2 * 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'\nimport { Quat } from '../../math/Quat'\nimport { Mat4 } from '../../math/Mat4'\n\nlet objectIndex = 0\n\n/** Defines all kind of possible {@link Object3D} matrix types */\nexport type Object3DMatricesType = 'model' | 'world'\n\n/**\n * Defines an {@link Object3D} matrix object\n */\nexport interface Object3DTransformMatrix {\n /** The {@link Mat4 | matrix} used */\n matrix: Mat4\n /** Whether we should update the {@link Mat4 | matrix} */\n shouldUpdate: boolean\n /** Function to update our {@link Mat4 | matrix} */\n onUpdate: () => void\n}\n\n/** Defines all possible {@link Object3DTransformMatrix | matrix object} used by our {@link Object3D} */\nexport type Object3DMatrices = Record\n\n/**\n * Defines all necessary {@link Vec3 | vectors}/{@link Quat | quaternions} to compute a 3D {@link Mat4 | model matrix}\n */\nexport interface Object3DTransforms {\n /** Transformation origin object */\n origin: {\n /** Transformation origin {@link Vec3 | vector} relative to the {@link Object3D} */\n model: Vec3\n }\n /** Model {@link Quat | quaternion} defining its rotation in 3D space */\n quaternion: Quat\n /** Model rotation {@link Vec3 | vector} used to compute its {@link Quat | quaternion} */\n rotation: Vec3\n /** Position object */\n position: {\n /** Position {@link Vec3 | vector} relative to the 3D world */\n world: Vec3\n }\n /** Model 3D scale {@link Vec3 | vector} */\n scale: Vec3\n}\n\n/**\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}.\n *\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.\n *\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}.\n */\nexport class Object3D {\n /** {@link Object3DTransforms | Transformation object} of the {@link Object3D} */\n transforms: Object3DTransforms\n /** {@link Object3DMatrices | Matrices object} of the {@link Object3D} */\n matrices: Object3DMatrices\n\n /** Parent {@link Object3D} in the scene graph, used to compute the {@link worldMatrix | world matrix} */\n private _parent: null | Object3D\n /** Children {@link Object3D} in the scene graph, used to compute their own {@link worldMatrix | world matrix} */\n children: Object3D[]\n\n /** Index (order of creation) of this {@link Object3D}. Used in the {@link parent} / {@link children} relation. */\n object3DIndex: number\n\n /**\n * Object3D constructor\n */\n constructor() {\n this.parent = null\n this.children = []\n\n Object.defineProperty(this as Object3D, 'object3DIndex', { value: objectIndex++ })\n\n this.setMatrices()\n this.setTransforms()\n }\n\n /* PARENT */\n\n /**\n * Get the parent of this {@link Object3D} if any\n */\n get parent(): Object3D | null {\n return this._parent\n }\n\n /**\n * Set the parent of this {@link Object3D}\n * @param value - new parent to set, could be an {@link Object3D} or null\n */\n set parent(value: Object3D | null) {\n if (this.parent) {\n this.parent.children = this.parent.children.filter((child) => child.object3DIndex !== this.object3DIndex)\n }\n this._parent = value\n this._parent?.children.push(this)\n }\n\n /* TRANSFORMS */\n\n /**\n * Set our transforms properties and {@link Vec3#onChange | vectors onChange} callbacks\n */\n setTransforms() {\n this.transforms = {\n origin: {\n model: new Vec3(),\n },\n quaternion: new Quat(),\n rotation: new Vec3(),\n position: {\n world: new Vec3(),\n },\n scale: new Vec3(1),\n }\n\n this.rotation.onChange(() => this.applyRotation())\n this.position.onChange(() => this.applyPosition())\n this.scale.onChange(() => this.applyScale())\n this.transformOrigin.onChange(() => this.applyTransformOrigin())\n }\n\n /**\n * Get our rotation {@link Vec3 | vector}\n */\n get rotation(): Vec3 {\n return this.transforms.rotation\n }\n\n /**\n * Set our rotation {@link Vec3 | vector}\n * @param value - new rotation {@link Vec3 | vector}\n */\n set rotation(value: Vec3) {\n this.transforms.rotation = value\n this.applyRotation()\n }\n\n /**\n * Get our {@link Quat | quaternion}\n */\n get quaternion(): Quat {\n return this.transforms.quaternion\n }\n\n /**\n * Set our {@link Quat | quaternion}\n * @param value - new {@link Quat | quaternion}\n */\n set quaternion(value: Quat) {\n this.transforms.quaternion = value\n }\n\n /**\n * Get our position {@link Vec3 | vector}\n */\n get position(): Vec3 {\n return this.transforms.position.world\n }\n\n /**\n * Set our position {@link Vec3 | vector}\n * @param value - new position {@link Vec3 | vector}\n */\n set position(value: Vec3) {\n this.transforms.position.world = value\n }\n\n /**\n * Get our scale {@link Vec3 | vector}\n */\n get scale(): Vec3 {\n return this.transforms.scale\n }\n\n /**\n * Set our scale {@link Vec3 | vector}\n * @param value - new scale {@link Vec3 | vector}\n */\n set scale(value: Vec3) {\n // force scale to 1 on Z axis\n this.transforms.scale = value\n this.applyScale()\n }\n\n /**\n * Get our transform origin {@link Vec3 | vector}\n */\n get transformOrigin(): Vec3 {\n return this.transforms.origin.model\n }\n\n /**\n * Set our transform origin {@link Vec3 | vector}\n * @param value - new transform origin {@link Vec3 | vector}\n */\n set transformOrigin(value: Vec3) {\n this.transforms.origin.model = value\n }\n\n /**\n * Apply our rotation and tell our {@link modelMatrix | model matrix} to update\n */\n applyRotation() {\n this.quaternion.setFromVec3(this.rotation)\n\n this.shouldUpdateModelMatrix()\n }\n\n /**\n * Tell our {@link modelMatrix | model matrix} to update\n */\n applyPosition() {\n this.shouldUpdateModelMatrix()\n }\n\n /**\n * Tell our {@link modelMatrix | model matrix} to update\n */\n applyScale() {\n this.shouldUpdateModelMatrix()\n }\n\n /**\n * Tell our {@link modelMatrix | model matrix} to update\n */\n applyTransformOrigin() {\n this.shouldUpdateModelMatrix()\n }\n\n /* MATRICES */\n\n /**\n * Set our {@link modelMatrix | model matrix} and {@link worldMatrix | world matrix}\n */\n setMatrices() {\n this.matrices = {\n model: {\n matrix: new Mat4(),\n shouldUpdate: false,\n onUpdate: () => this.updateModelMatrix(),\n },\n world: {\n matrix: new Mat4(),\n shouldUpdate: false,\n onUpdate: () => this.updateWorldMatrix(),\n },\n }\n }\n\n /**\n * Get our {@link Mat4 | model matrix}\n */\n get modelMatrix(): Mat4 {\n return this.matrices.model.matrix\n }\n\n /**\n * Set our {@link Mat4 | model matrix}\n * @param value - new {@link Mat4 | model matrix}\n */\n set modelMatrix(value: Mat4) {\n this.matrices.model.matrix = value\n this.shouldUpdateModelMatrix()\n }\n\n /**\n * Set our {@link modelMatrix | model matrix} shouldUpdate flag to true (tell it to update)\n */\n shouldUpdateModelMatrix() {\n this.matrices.model.shouldUpdate = true\n this.shouldUpdateWorldMatrix()\n }\n\n /**\n * Get our {@link Mat4 | world matrix}\n */\n get worldMatrix(): Mat4 {\n return this.matrices.world.matrix\n }\n\n /**\n * Set our {@link Mat4 | world matrix}\n * @param value - new {@link Mat4 | world matrix}\n */\n set worldMatrix(value: Mat4) {\n this.matrices.world.matrix = value\n this.shouldUpdateWorldMatrix()\n }\n\n /**\n * Set our {@link worldMatrix | world matrix} shouldUpdate flag to true (tell it to update)\n */\n shouldUpdateWorldMatrix() {\n this.matrices.world.shouldUpdate = true\n }\n\n /**\n * Rotate this {@link Object3D} so it looks at the {@link Vec3 | target}\n * @param target - {@link Vec3 | target} to look at\n */\n lookAt(target: Vec3 = new Vec3()) {\n const rotationMatrix = new Mat4().lookAt(target, this.position)\n this.quaternion.setFromRotationMatrix(rotationMatrix)\n this.shouldUpdateModelMatrix()\n }\n\n /**\n * Update our {@link modelMatrix | model matrix}\n */\n updateModelMatrix() {\n // compose our model transformation matrix from custom origin\n this.modelMatrix = this.modelMatrix.composeFromOrigin(\n this.position,\n this.quaternion,\n this.scale,\n this.transformOrigin\n )\n\n // tell our world matrix to update\n this.shouldUpdateWorldMatrix()\n }\n\n /**\n * Update our {@link worldMatrix | model matrix}\n */\n updateWorldMatrix() {\n if (!this.parent) {\n this.worldMatrix.copy(this.modelMatrix)\n } else {\n this.worldMatrix.multiplyMatrices(this.parent.worldMatrix, this.modelMatrix)\n }\n\n // update the children world matrix as well\n this.children.forEach((child) => {\n child.shouldUpdateWorldMatrix()\n })\n }\n\n /**\n * Callback to run if at least one matrix of the stack has been updated\n */\n onAfterMatrixStackUpdate() {\n /* will be used by the classes extending Object3D */\n }\n\n /**\n * Check at each render whether we should update our matrices, and update them if needed\n */\n updateMatrixStack() {\n // if it has a parent and it is an Object3D\n // it means nothing updates it in the render loop, so do it from here\n if (this.parent && this.parent.constructor.name === 'Object3D') {\n this.parent.updateMatrixStack()\n }\n\n // check if at least one matrix should update\n const matrixShouldUpdate = !!Object.keys(this.matrices).find((matrixName) => this.matrices[matrixName].shouldUpdate)\n\n if (matrixShouldUpdate) {\n for (const matrixName in this.matrices) {\n if (this.matrices[matrixName].shouldUpdate) {\n this.matrices[matrixName].onUpdate()\n this.matrices[matrixName].shouldUpdate = false\n }\n }\n\n // callback to run if at least one matrix of the stack has been updated\n this.onAfterMatrixStackUpdate()\n }\n }\n}\n","import { Vec3 } from '../../math/Vec3'\r\nimport { isRenderer, Renderer } from '../renderers/utils'\r\nimport { TextureBinding, TextureBindingParams } 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 { TextureOptions, TextureParams, TextureParent, TextureSize, TextureSource } from '../../types/Textures'\r\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\r\nimport { DOMProjectedMesh } from '../renderers/GPURenderer'\r\nimport { RectSize } from '../DOM/DOMElement'\r\n\r\n/** @const - default {@link Texture} parameters */\r\nconst defaultTextureParams: TextureParams = {\r\n name: 'texture',\r\n generateMips: false,\r\n flipY: false,\r\n format: 'rgba8unorm',\r\n premultipliedAlpha: true,\r\n placeholderColor: [0, 0, 0, 255], // default to black\r\n useExternalTextures: true,\r\n fromTexture: null,\r\n viewDimension: '2d',\r\n cache: true,\r\n}\r\n\r\n/**\r\n * Used to create {@link GPUTexture} or {@link GPUExternalTexture} from different kinds of {@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 render texture\r\n * const imageTexture = new Texture(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 Texture extends Object3D {\r\n /** The type of the {@link Texture} */\r\n type: string\r\n /** The universal unique id of this {@link Texture} */\r\n readonly uuid: string\r\n /** {@link Renderer} used by this {@link Texture} */\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 Texture} {@link TextureSource | source} to use */\r\n source: TextureSource\r\n /** The {@link GPUTexture}, matching the {@link TextureSource | source} {@link RectSize | size} (with 1 for depth) */\r\n size: TextureSize\r\n\r\n /** Options used to create this {@link Texture} */\r\n options: TextureOptions\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 Texture}, i.e. its {@link textureMatrix} and its {@link TextureBinding | GPU texture binding} */\r\n bindings: BindGroupBindingElement[]\r\n\r\n /** {@link Texture} parentMesh if any */\r\n private _parentMesh: TextureParent\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 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 * Texture constructor\r\n * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link Texture}\r\n * @param parameters - {@link TextureParams | parameters} used to create this {@link Texture}\r\n */\r\n constructor(renderer: Renderer | GPUCurtains, parameters = defaultTextureParams) {\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 ...defaultTextureParams,\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 matrix: {\r\n name: this.options.name + 'Matrix',\r\n type: 'mat4x4f',\r\n value: this.modelMatrix,\r\n },\r\n },\r\n })\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.addTexture(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 texture: this.options.sourceType === 'externalVideo' ? this.externalTexture : this.texture,\r\n bindingType: this.options.sourceType === 'externalVideo' ? 'externalTexture' : 'texture',\r\n viewDimension: this.options.viewDimension,\r\n } as TextureBindingParams),\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(): TextureParent {\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: TextureParent) {\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 onAfterMatrixStackUpdate() {\r\n this.textureMatrix.shouldUpdateBinding(this.options.name + 'Matrix')\r\n }\r\n\r\n /**\r\n * Resize our {@link Texture}\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 * Get the number of mip levels create based on {@link size}\r\n * @param sizes - Array containing our texture width, height and depth\r\n * @returns - number of mip levels\r\n */\r\n getNumMipLevels(...sizes: number[]): number {\r\n const maxSize = Math.max(...sizes)\r\n return (1 + Math.log2(maxSize)) | 0\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 Texture}\r\n * @param texture - {@link Texture} to copy\r\n */\r\n copy(texture: Texture) {\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 ? this.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.textures.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