From 190408d13f3dcb5f5f4977fc69e4dc929b8f2df0 Mon Sep 17 00:00:00 2001 From: martinlaxenaire Date: Wed, 21 Feb 2024 15:45:39 +0100 Subject: [PATCH] - added a cleanupRenderMaterialParameters method - copy shader pass output to render texture only if needed --- dist/esm/core/meshes/mixins/MeshBaseMixin.mjs | 73 +++-- .../core/meshes/mixins/MeshBaseMixin.mjs.map | 2 +- .../meshes/mixins/ProjectedMeshBaseMixin.mjs | 22 +- .../mixins/ProjectedMeshBaseMixin.mjs.map | 2 +- dist/esm/core/renderPasses/RenderPass.mjs | 6 +- dist/esm/core/renderPasses/RenderPass.mjs.map | 2 +- dist/esm/core/renderPasses/ShaderPass.mjs | 17 +- dist/esm/core/renderPasses/ShaderPass.mjs.map | 2 +- dist/esm/core/scenes/Scene.mjs | 4 +- dist/esm/core/scenes/Scene.mjs.map | 2 +- dist/esm/utils/debug.mjs | 9 +- dist/esm/utils/debug.mjs.map | 2 +- dist/gpu-curtains.umd.js | 131 +++++--- dist/gpu-curtains.umd.js.map | 2 +- dist/gpu-curtains.umd.min.js | 10 +- .../core/meshes/mixins/MeshBaseMixin.d.ts | 16 +- dist/types/core/renderPasses/ShaderPass.d.ts | 21 +- docs/assets/navigation.js | 2 +- docs/assets/search.js | 2 +- .../core_DOM_DOMElement.DOMElement.html | 30 +- .../core_DOM_DOMFrustum.DOMFrustum.html | 30 +- .../core_bindGroups_BindGroup.BindGroup.html | 66 ++-- ...ups_TextureBindGroup.TextureBindGroup.html | 76 ++--- .../core_bindings_Binding.Binding.html | 18 +- ..._bindings_BufferBinding.BufferBinding.html | 54 ++-- ...indings_SamplerBinding.SamplerBinding.html | 30 +- ...indings_TextureBinding.TextureBinding.html | 32 +- ...leBufferBinding.WritableBufferBinding.html | 58 ++-- ...BufferArrayElement.BufferArrayElement.html | 58 ++-- ...rElements_BufferElement.BufferElement.html | 50 +-- ...Element.BufferInterleavedArrayElement.html | 60 ++-- docs/classes/core_camera_Camera.Camera.html | 118 +++---- ...computePasses_ComputePass.ComputePass.html | 92 +++--- .../core_geometries_Geometry.Geometry.html | 46 +-- ...tries_IndexedGeometry.IndexedGeometry.html | 52 ++-- ...eometries_PlaneGeometry.PlaneGeometry.html | 58 ++-- ...rials_ComputeMaterial.ComputeMaterial.html | 110 +++---- .../core_materials_Material.Material.html | 96 +++--- ...terials_RenderMaterial.RenderMaterial.html | 106 +++---- ...eshes_FullscreenPlane.FullscreenPlane.html | 126 ++++---- docs/classes/core_meshes_Mesh.Mesh.html | 222 +++++++------- ...es_mixins_MeshBaseMixin.MeshBaseClass.html | 122 ++++---- ...dMeshBaseMixin.ProjectedMeshBaseClass.html | 142 ++++----- .../core_objects3D_Object3D.Object3D.html | 72 ++--- ...D_ProjectedObject3D.ProjectedObject3D.html | 88 +++--- ...utePipelineEntry.ComputePipelineEntry.html | 50 +-- ...pipelines_PipelineEntry.PipelineEntry.html | 38 +-- ...lines_PipelineManager.PipelineManager.html | 22 +- ...nderPipelineEntry.RenderPipelineEntry.html | 54 ++-- ...re_renderPasses_RenderPass.RenderPass.html | 36 +-- ...enderPasses_RenderTarget.RenderTarget.html | 28 +- ...re_renderPasses_ShaderPass.ShaderPass.html | 136 +++++---- ...s_GPUCameraRenderer.GPUCameraRenderer.html | 196 ++++++------ ...ers_GPUDeviceManager.GPUDeviceManager.html | 82 ++--- ...ore_renderers_GPURenderer.GPURenderer.html | 174 +++++------ .../core_samplers_Sampler.Sampler.html | 24 +- docs/classes/core_scenes_Scene.Scene.html | 40 +-- ..._textures_RenderTexture.RenderTexture.html | 34 +-- .../core_textures_Texture.Texture.html | 164 +++++----- .../curtains_GPUCurtains.GPUCurtains.html | 94 +++--- .../curtains_meshes_DOMMesh.DOMMesh.html | 286 ++++++++--------- ...ns_meshes_PingPongPlane.PingPongPlane.html | 128 ++++---- docs/classes/curtains_meshes_Plane.Plane.html | 288 +++++++++--------- ...ins_objects3D_DOMObject3D.DOMObject3D.html | 138 ++++----- ...UCurtainsRenderer.GPUCurtainsRenderer.html | 198 ++++++------ ...as_geometries_BoxGeometry.BoxGeometry.html | 52 ++-- ...metries_SphereGeometry.SphereGeometry.html | 52 ++-- docs/classes/math_Box3.Box3.html | 18 +- docs/classes/math_Mat4.Mat4.html | 54 ++-- docs/classes/math_Quat.Quat.html | 26 +- docs/classes/math_Vec2.Vec2.html | 62 ++-- docs/classes/math_Vec3.Vec3.html | 82 ++--- .../utils_CacheManager.CacheManager.html | 14 +- .../utils_ResizeManager.ResizeManager.html | 18 +- .../utils_ScrollManager.ScrollManager.html | 18 +- ...s_TasksQueueManager.TasksQueueManager.html | 14 +- ...s_utils.getBindGroupLayoutBindingType.html | 2 +- ....getBindGroupLayoutTextureBindingType.html | 2 +- ..._bindings_utils.getBindingWGSLVarType.html | 2 +- .../core_bindings_utils.getBufferLayout.html | 2 +- ...gs_utils.getTextureBindingWGSLVarType.html | 2 +- ...es_mixins_MeshBaseMixin.MeshBaseMixin.html | 2 +- ...dMeshBaseMixin.ProjectedMeshBaseMixin.html | 2 +- .../core_renderers_utils.generateMips.html | 2 +- ...core_renderers_utils.isCameraRenderer.html | 2 +- ...re_renderers_utils.isCurtainsRenderer.html | 2 +- .../core_renderers_utils.isRenderer.html | 2 +- .../utils_debug.logSceneCommands.html | 2 +- docs/functions/utils_utils.generateUUID.html | 2 +- docs/functions/utils_utils.throwError.html | 2 +- docs/functions/utils_utils.throwWarning.html | 2 +- docs/functions/utils_utils.toCamelCase.html | 2 +- docs/functions/utils_utils.toKebabCase.html | 2 +- docs/hierarchy.html | 2 +- ...DOM_DOMElement.DOMElementBoundingRect.html | 18 +- .../core_DOM_DOMElement.DOMElementParams.html | 10 +- .../core_DOM_DOMElement.DOMPosition.html | 6 +- .../core_DOM_DOMElement.RectBBox.html | 10 +- .../core_DOM_DOMElement.RectCoords.html | 10 +- .../core_DOM_DOMElement.RectSize.html | 6 +- .../core_DOM_DOMFrustum.DOMFrustumParams.html | 14 +- ...xtureBindGroup.TextureBindGroupParams.html | 16 +- .../core_bindings_Binding.BindingParams.html | 10 +- ...BufferBinding.BufferBindingBaseParams.html | 8 +- ...ings_BufferBinding.BufferBindingInput.html | 16 +- ...ngs_BufferBinding.BufferBindingParams.html | 16 +- ...s_SamplerBinding.SamplerBindingParams.html | 14 +- ...s_TextureBinding.TextureBindingParams.html | 20 +- ...erBinding.WritableBufferBindingParams.html | 18 +- ...ArrayElement.BufferArrayElementParams.html | 10 +- ..._BufferElement.BufferElementAlignment.html | 6 +- ...lement.BufferElementAlignmentPosition.html | 6 +- ...nts_BufferElement.BufferElementParams.html | 8 +- ...a_Camera.CameraBasePerspectiveOptions.html | 8 +- .../core_camera_Camera.CameraParams.html | 16 +- ...amera_Camera.CameraPerspectiveOptions.html | 14 +- ...Passes_ComputePass.ComputePassOptions.html | 16 +- ...ePasses_ComputePass.ComputePassParams.html | 30 +- ...eometries_IndexedGeometry.IndexBuffer.html | 10 +- ...try.IndexedGeometryIndexBufferOptions.html | 6 +- ...ies_PlaneGeometry.PlaneGeometryParams.html | 12 +- ..._mixins_MeshBaseMixin.MeshBaseOptions.html | 14 +- ...s_mixins_MeshBaseMixin.MeshBaseParams.html | 62 ++-- ...ns_MeshBaseMixin.MeshBaseRenderParams.html | 60 ++-- ...eshBaseMixin.ProjectedMeshBaseOptions.html | 18 +- ...MeshBaseMixin.ProjectedMeshBaseParams.html | 6 +- ...MeshBaseMixin.ProjectedMeshParameters.html | 66 ++-- ...seMixin.ProjectedRenderMaterialParams.html | 54 ++-- ...ts3D_Object3D.Object3DTransformMatrix.html | 8 +- ...objects3D_Object3D.Object3DTransforms.html | 12 +- ...sses_RenderPass.ColorAttachmentParams.html | 10 +- ...derPasses_RenderPass.RenderPassParams.html | 34 +-- ...asses_RenderTarget.RenderTargetParams.html | 36 +-- ...erPasses_ShaderPass.ShaderPassOptions.html | 16 + ...derPasses_ShaderPass.ShaderPassParams.html | 64 ++-- ...ameraRenderer.GPUCameraRendererParams.html | 16 +- ...UDeviceManager.GPUDeviceManagerParams.html | 10 +- ...nderers_GPURenderer.GPURendererParams.html | 14 +- .../core_samplers_Sampler.SamplerOptions.html | 2 +- .../core_samplers_Sampler.SamplerParams.html | 4 +- .../core_scenes_Scene.ProjectionStack.html | 6 +- .../core_scenes_Scene.RenderPassEntry.html | 14 +- ...RenderTexture.RenderTextureBaseParams.html | 20 +- ...res_RenderTexture.RenderTextureParams.html | 22 +- ...rtains_GPUCurtains.GPUCurtainsOptions.html | 22 +- ...urtains_GPUCurtains.GPUCurtainsParams.html | 22 +- ...ains_meshes_DOMMesh.DOMMeshBaseParams.html | 64 ++-- ...curtains_meshes_DOMMesh.DOMMeshParams.html | 66 ++-- .../curtains_meshes_Plane.PlaneParams.html | 74 ++--- ...jects3D_DOMObject3D.DOMObject3DParams.html | 4 +- ...objects3D_DOMObject3D.DOMObject3DSize.html | 6 +- ...s3D_DOMObject3D.DOMObject3DTransforms.html | 12 +- ...metries_BoxGeometry.BoxGeometryParams.html | 14 +- ...s_SphereGeometry.SphereGeometryParams.html | 20 +- .../types_BindGroups.BindGroupEntries.html | 6 +- .../types_BindGroups.BindGroupInputs.html | 8 +- .../types_BindGroups.BindGroupParams.html | 12 +- docs/interfaces/types_BindGroups.Input.html | 10 +- .../types_BindGroups.InputBase.html | 8 +- .../types_Geometries.GeometryOptions.html | 10 +- .../types_Geometries.VertexBuffer.html | 16 +- ...ypes_Geometries.VertexBufferAttribute.html | 22 +- ...eometries.VertexBufferAttributeParams.html | 16 +- .../types_Geometries.VertexBufferParams.html | 8 +- ...ypes_Materials.ComputeMaterialOptions.html | 24 +- ...types_Materials.ComputeMaterialParams.html | 24 +- .../types_Materials.MaterialBaseParams.html | 8 +- ...Materials.MaterialInputBindingsParams.html | 16 +- .../types_Materials.MaterialOptions.html | 22 +- .../types_Materials.MaterialParams.html | 22 +- .../types_Materials.MaterialShaders.html | 8 +- ...es_Materials.RenderMaterialAttributes.html | 6 +- ...es_Materials.RenderMaterialBaseParams.html | 44 +-- ...ls.RenderMaterialBaseRenderingOptions.html | 26 +- ...types_Materials.RenderMaterialOptions.html | 24 +- .../types_Materials.RenderMaterialParams.html | 50 +-- ...erials.RenderMaterialRenderingOptions.html | 30 +- .../types_Materials.ShaderOptions.html | 6 +- ..._PipelineEntries.PipelineEntryOptions.html | 8 +- ...s_PipelineEntries.PipelineEntryParams.html | 10 +- ...Entries.PipelineEntryPropertiesParams.html | 4 +- ...s_PipelineEntries.PipelineEntryShader.html | 8 +- ..._PipelineEntries.PipelineEntryShaders.html | 10 +- ...s_PipelineEntries.PipelineEntryStatus.html | 8 +- ...Entries.RenderPipelineEntryBaseParams.html | 34 +-- ...ineEntries.RenderPipelineEntryOptions.html | 36 +-- ...lineEntries.RenderPipelineEntryParams.html | 36 +-- ...s.RenderPipelineEntryPropertiesParams.html | 6 +- .../types_Textures.ExternalTextureParams.html | 18 +- .../types_Textures.TextureBaseParams.html | 22 +- .../types_Textures.TextureOptions.html | 28 +- .../types_Textures.TextureParams.html | 24 +- .../types_Textures.TextureSize.html | 8 +- ...tils_ResizeManager.ResizeManagerEntry.html | 8 +- ...ils_ScrollManager.ScrollManagerParams.html | 10 +- ...utils_TasksQueueManager.TaskQueueItem.html | 10 +- docs/modules/core_DOM_DOMElement.html | 2 +- docs/modules/core_DOM_DOMFrustum.html | 2 +- docs/modules/core_bindGroups_BindGroup.html | 2 +- .../core_bindGroups_TextureBindGroup.html | 2 +- docs/modules/core_bindings_Binding.html | 2 +- docs/modules/core_bindings_BufferBinding.html | 2 +- .../modules/core_bindings_SamplerBinding.html | 2 +- .../modules/core_bindings_TextureBinding.html | 2 +- .../core_bindings_WritableBufferBinding.html | 2 +- ...ngs_bufferElements_BufferArrayElement.html | 2 +- ...bindings_bufferElements_BufferElement.html | 2 +- ...lements_BufferInterleavedArrayElement.html | 2 +- docs/modules/core_bindings_utils.html | 2 +- docs/modules/core_camera_Camera.html | 2 +- .../core_computePasses_ComputePass.html | 2 +- docs/modules/core_geometries_Geometry.html | 2 +- .../core_geometries_IndexedGeometry.html | 2 +- .../core_geometries_PlaneGeometry.html | 2 +- .../core_materials_ComputeMaterial.html | 2 +- docs/modules/core_materials_Material.html | 2 +- .../core_materials_RenderMaterial.html | 2 +- docs/modules/core_meshes_FullscreenPlane.html | 2 +- docs/modules/core_meshes_Mesh.html | 2 +- .../core_meshes_mixins_MeshBaseMixin.html | 2 +- ..._meshes_mixins_ProjectedMeshBaseMixin.html | 2 +- docs/modules/core_objects3D_Object3D.html | 2 +- .../core_objects3D_ProjectedObject3D.html | 2 +- .../core_pipelines_ComputePipelineEntry.html | 2 +- .../modules/core_pipelines_PipelineEntry.html | 2 +- .../core_pipelines_PipelineManager.html | 2 +- .../core_pipelines_RenderPipelineEntry.html | 2 +- .../modules/core_renderPasses_RenderPass.html | 2 +- .../core_renderPasses_RenderTarget.html | 2 +- .../modules/core_renderPasses_ShaderPass.html | 3 +- .../core_renderers_GPUCameraRenderer.html | 2 +- .../core_renderers_GPUDeviceManager.html | 2 +- docs/modules/core_renderers_GPURenderer.html | 2 +- docs/modules/core_renderers_utils.html | 2 +- docs/modules/core_samplers_Sampler.html | 2 +- docs/modules/core_scenes_Scene.html | 2 +- docs/modules/core_shaders_ShaderChunks.html | 2 +- docs/modules/core_textures_RenderTexture.html | 2 +- docs/modules/core_textures_Texture.html | 2 +- docs/modules/curtains_GPUCurtains.html | 2 +- docs/modules/curtains_meshes_DOMMesh.html | 2 +- .../curtains_meshes_PingPongPlane.html | 2 +- docs/modules/curtains_meshes_Plane.html | 2 +- .../curtains_objects3D_DOMObject3D.html | 2 +- ...urtains_renderers_GPUCurtainsRenderer.html | 2 +- .../extras_geometries_BoxGeometry.html | 2 +- .../extras_geometries_SphereGeometry.html | 2 +- docs/modules/math_Box3.html | 2 +- docs/modules/math_Mat4.html | 2 +- docs/modules/math_Quat.html | 2 +- docs/modules/math_Vec2.html | 2 +- docs/modules/math_Vec3.html | 2 +- docs/modules/types_BindGroups.html | 2 +- docs/modules/types_Geometries.html | 2 +- docs/modules/types_Materials.html | 2 +- docs/modules/types_PipelineEntries.html | 2 +- docs/modules/types_Textures.html | 2 +- docs/modules/utils_CacheManager.html | 2 +- docs/modules/utils_ResizeManager.html | 2 +- docs/modules/utils_ScrollManager.html | 2 +- docs/modules/utils_TasksQueueManager.html | 2 +- docs/modules/utils_debug.html | 2 +- docs/modules/utils_utils.html | 2 +- ...dings_Binding.BindingMemoryAccessType.html | 2 +- .../core_bindings_Binding.BindingType.html | 2 +- ...Binding.BufferBindingMemoryAccessType.html | 2 +- ...e_bindings_Binding.TextureBindingType.html | 2 +- ...ndings_Binding.TextureSamplerBindings.html | 2 +- ...gs_BufferBinding.AllowedBufferElement.html | 2 +- ...SamplerBinding.SamplerBindingResource.html | 2 +- ...TextureBinding.TextureBindingResource.html | 2 +- .../core_bindings_utils.BufferLayout.html | 2 +- .../types/core_bindings_utils.TypedArray.html | 2 +- ..._bindings_utils.TypedArrayConstructor.html | 2 +- .../core_bindings_utils.WGSLVariableType.html | 2 +- ..._camera_Camera.CameraObject3DMatrices.html | 2 +- ...era_Camera.CameraObject3DMatricesType.html | 2 +- ...mixins_MeshBaseMixin.MixinConstructor.html | 2 +- ...e_objects3D_Object3D.Object3DMatrices.html | 2 +- ...jects3D_Object3D.Object3DMatricesType.html | 2 +- ...tedObject3D.ProjectedObject3DMatrices.html | 2 +- ...bject3D.ProjectedObject3DMatricesType.html | 2 +- ...ipelineManager.AllowedPipelineEntries.html | 2 +- ...enderers_GPURenderer.DOMProjectedMesh.html | 2 +- ...e_renderers_GPURenderer.ProjectedMesh.html | 2 +- ...re_renderers_GPURenderer.RenderedMesh.html | 2 +- ...ore_renderers_GPURenderer.SceneObject.html | 2 +- .../core_renderers_utils.CameraRenderer.html | 2 +- docs/types/core_renderers_utils.Renderer.html | 2 +- .../core_scenes_Scene.ProjectionType.html | 2 +- .../core_scenes_Scene.RenderPassEntries.html | 2 +- ...re_scenes_Scene.RenderPassEntriesType.html | 2 +- docs/types/core_scenes_Scene.Stack.html | 2 +- ..._ShaderChunks.ProjectedShaderChunks-1.html | 2 +- ...e_shaders_ShaderChunks.ShaderChunks-1.html | 2 +- ...enderTexture.RenderTextureBindingType.html | 2 +- docs/types/math_Quat.AxisOrder.html | 2 +- .../types_BindGroups.AllowedBindGroups.html | 2 +- ...es_BindGroups.BindGroupBindingElement.html | 2 +- ...dGroups.BindGroupBufferBindingElement.html | 2 +- ...Groups.BindGroupTextureSamplerElement.html | 2 +- docs/types/types_BindGroups.InputValue.html | 2 +- ...ypes_BindGroups.ReadOnlyInputBindings.html | 2 +- ...pes_BindGroups.ReadWriteInputBindings.html | 2 +- .../types_Geometries.GeometryBaseParams.html | 2 +- .../types_Geometries.GeometryParams.html | 2 +- .../types_Materials.AllowedGeometries.html | 2 +- ..._Materials.ComputeMaterialShadersType.html | 2 +- .../types_Materials.FullShadersType.html | 2 +- .../types_Materials.MaterialBindGroups.html | 2 +- .../types_Materials.MaterialShadersType.html | 2 +- .../types_Materials.MaterialTexture.html | 2 +- ...s_Materials.RenderMaterialShadersType.html | 2 +- ...pelineEntries.PipelineEntryBaseParams.html | 2 +- docs/types/types_Textures.TextureParent.html | 2 +- docs/types/types_Textures.TextureSource.html | 2 +- .../types_Textures.TextureSourceType.html | 2 +- ...TasksQueueManager.TaskQueueItemParams.html | 2 +- ...ferElements_BufferElement.bytesPerRow.html | 2 +- ...erElements_BufferElement.bytesPerSlot.html | 2 +- ...ferElements_BufferElement.slotsPerRow.html | 2 +- ...rs_ShaderChunks.ProjectedShaderChunks.html | 2 +- ...ore_shaders_ShaderChunks.ShaderChunks.html | 2 +- .../utils_CacheManager.cacheManager-1.html | 2 +- .../utils_ResizeManager.resizeManager-1.html | 2 +- src/core/meshes/mixins/MeshBaseMixin.ts | 105 ++++--- .../meshes/mixins/ProjectedMeshBaseMixin.ts | 37 ++- src/core/renderPasses/RenderPass.ts | 6 +- src/core/renderPasses/ShaderPass.ts | 34 ++- src/core/scenes/Scene.ts | 33 +- src/utils/debug.ts | 14 +- tests/orbit-camera-selective-passes/main.js | 76 +++-- tests/ssao/main.js | 2 +- 333 files changed, 3877 insertions(+), 3604 deletions(-) create mode 100644 docs/interfaces/core_renderPasses_ShaderPass.ShaderPassOptions.html diff --git a/dist/esm/core/meshes/mixins/MeshBaseMixin.mjs b/dist/esm/core/meshes/mixins/MeshBaseMixin.mjs index feeaaa922..ef7bf2bb5 100644 --- a/dist/esm/core/meshes/mixins/MeshBaseMixin.mjs +++ b/dist/esm/core/meshes/mixins/MeshBaseMixin.mjs @@ -113,7 +113,7 @@ function MeshBaseMixin(Base) { texturesOptions, ...outputTarget !== void 0 && { outputTarget }, ...autoRender !== void 0 && { autoRender }, - ...meshParameters.useAsyncPipeline !== void 0 && { useAsyncPipeline: meshParameters.useAsyncPipeline } + ...meshParameters }; this.geometry = geometry; if (autoRender !== void 0) { @@ -125,9 +125,9 @@ function MeshBaseMixin(Base) { this.userData = {}; this.computeGeometry(); this.setMaterial({ - label: this.options.label, - shaders: this.options.shaders, - ...{ ...meshParameters, verticesOrder: geometry.verticesOrder, topology: geometry.topology } + ...this.cleanupRenderMaterialParameters({ ...this.options }), + verticesOrder: geometry.verticesOrder, + topology: geometry.topology }); this.addToScene(); } @@ -171,33 +171,6 @@ function MeshBaseMixin(Base) { } this.renderer.meshes = this.renderer.meshes.filter((m) => m.uuid !== this.uuid); } - /** - * Set or update the {@link RenderMaterial} {@link types/Materials.RenderMaterialRenderingOptions | rendering options} to match the {@link RenderPass#descriptor | RenderPass descriptor} used to draw this Mesh. - * @param renderPass - {@link RenderPass | RenderPass} used to draw this Mesh, default to the {@link core/renderers/GPURenderer.GPURenderer#renderPass | renderer renderPass}. - */ - setRenderingOptionsForRenderPass(renderPass) { - const renderingOptions = { - sampleCount: renderPass.options.sampleCount, - // color attachments - ...renderPass.options.colorAttachments.length && { - targetFormat: renderPass.options.colorAttachments[0].targetFormat, - // multiple render targets? - ...renderPass.options.colorAttachments.length > 1 && { - additionalTargets: renderPass.options.colorAttachments.filter((c, i) => i > 0).map((colorAttachment) => { - return { - format: colorAttachment.targetFormat - }; - }) - } - }, - // depth - depth: renderPass.options.useDepth, - ...renderPass.options.useDepth && { - depthFormat: renderPass.options.depthFormat - } - }; - this.material?.setRenderingOptions(renderingOptions); - } /** * Set a new {@link Renderer} for this Mesh * @param renderer - new {@link Renderer} to set @@ -332,6 +305,44 @@ function MeshBaseMixin(Base) { } } /* MATERIAL */ + /** + * Set or update the {@link RenderMaterial} {@link types/Materials.RenderMaterialRenderingOptions | rendering options} to match the {@link RenderPass#descriptor | RenderPass descriptor} used to draw this Mesh. + * @param renderPass - {@link RenderPass | RenderPass} used to draw this Mesh, default to the {@link core/renderers/GPURenderer.GPURenderer#renderPass | renderer renderPass}. + */ + setRenderingOptionsForRenderPass(renderPass) { + const renderingOptions = { + sampleCount: renderPass.options.sampleCount, + // color attachments + ...renderPass.options.colorAttachments.length && { + targetFormat: renderPass.options.colorAttachments[0].targetFormat, + // multiple render targets? + ...renderPass.options.colorAttachments.length > 1 && { + additionalTargets: renderPass.options.colorAttachments.filter((c, i) => i > 0).map((colorAttachment) => { + return { + format: colorAttachment.targetFormat + }; + }) + } + }, + // depth + depth: renderPass.options.useDepth, + ...renderPass.options.useDepth && { + depthFormat: renderPass.options.depthFormat + } + }; + this.material?.setRenderingOptions(renderingOptions); + } + /** + * Hook used to clean up parameters before sending them to the {@link RenderMaterial}. + * @param parameters - parameters to clean before sending them to the {@link RenderMaterial} + * @returns - cleaned parameters + */ + cleanupRenderMaterialParameters(parameters) { + delete parameters.texturesOptions; + delete parameters.outputTarget; + delete parameters.autoRender; + return parameters; + } /** * Set a Mesh transparent property, then set its material * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters} diff --git a/dist/esm/core/meshes/mixins/MeshBaseMixin.mjs.map b/dist/esm/core/meshes/mixins/MeshBaseMixin.mjs.map index d9671bd90..e5a482f04 100644 --- a/dist/esm/core/meshes/mixins/MeshBaseMixin.mjs.map +++ b/dist/esm/core/meshes/mixins/MeshBaseMixin.mjs.map @@ -1 +1 @@ -{"version":3,"file":"MeshBaseMixin.mjs","sources":["../../../../../src/core/meshes/mixins/MeshBaseMixin.ts"],"sourcesContent":["import { generateUUID, throwWarning } from '../../../utils/utils'\nimport { isRenderer, Renderer } from '../../renderers/utils'\nimport { RenderMaterial } from '../../materials/RenderMaterial'\nimport { Texture } from '../../textures/Texture'\nimport { Geometry } from '../../geometries/Geometry'\nimport { RenderTexture, RenderTextureParams } from '../../textures/RenderTexture'\nimport { ExternalTextureParams, TextureParams, TextureParent } from '../../../types/Textures'\nimport { RenderTarget } from '../../renderPasses/RenderTarget'\nimport { GPUCurtains } from '../../../curtains/GPUCurtains'\nimport { ProjectedMesh } from '../../renderers/GPURenderer'\nimport { Material } from '../../materials/Material'\nimport { DOMElementBoundingRect } from '../../DOM/DOMElement'\nimport { AllowedGeometries, RenderMaterialParams, ShaderOptions } from '../../../types/Materials'\nimport { ProjectedMeshBaseClass } from './ProjectedMeshBaseMixin'\nimport default_vsWgsl from '../../shaders/chunks/default_vs.wgsl'\nimport default_fsWgsl from '../../shaders/chunks/default_fs.wgsl'\nimport { RenderPass } from '../../renderPasses/RenderPass'\n\nlet meshIndex = 0\n\nexport interface MeshBaseRenderParams extends RenderMaterialParams {\n /** Whether we should add this Mesh to our {@link core/scenes/Scene.Scene | Scene} to let it handle the rendering process automatically */\n autoRender?: boolean\n /** Flag indicating whether to draw this Mesh or not */\n visible?: boolean\n /** Controls the order in which this Mesh should be rendered by our {@link core/scenes/Scene.Scene | Scene} */\n renderOrder?: number\n /** Optional {@link RenderTarget} to render this Mesh to instead of the canvas context. */\n outputTarget?: RenderTarget\n /** Parameters used by this Mesh to create a {@link Texture} */\n texturesOptions?: ExternalTextureParams\n}\n\n/**\n * Base parameters used to create a Mesh\n */\nexport interface MeshBaseParams extends MeshBaseRenderParams {\n /** Geometry to use */\n geometry: AllowedGeometries\n}\n\n/**\n * Base options used to create this Mesh\n */\nexport interface MeshBaseOptions {\n /** The label of this Mesh, sent to various GPU objects for debugging purpose */\n label?: MeshBaseParams['label']\n /** Shaders to use by this Mesh {@link RenderMaterial} */\n shaders?: MeshBaseParams['shaders']\n /** Parameters used by this Mesh to create a {@link Texture} */\n texturesOptions?: ExternalTextureParams\n /** {@link RenderTarget} to render this Mesh to instead of the canvas context, if any. */\n outputTarget?: RenderTarget | null\n /** Whether we should add this Mesh to our {@link core/scenes/Scene.Scene | Scene} to let it handle the rendering process automatically */\n autoRender?: boolean\n /** Whether to compile this Mesh {@link RenderMaterial} {@link core/pipelines/RenderPipelineEntry.RenderPipelineEntry#pipeline | render pipeline} asynchronously or not */\n useAsyncPipeline?: boolean\n}\n\n/** @const - Default Mesh parameters to merge with user defined parameters */\nconst defaultMeshBaseParams: MeshBaseParams = {\n // geometry\n geometry: new Geometry(),\n // material\n shaders: {},\n autoRender: true,\n useProjection: false,\n useAsyncPipeline: true,\n // rendering\n cullMode: 'back',\n depth: true,\n depthWriteEnabled: true,\n depthCompare: 'less',\n depthFormat: 'depth24plus',\n transparent: false,\n visible: true,\n renderOrder: 0,\n // textures\n texturesOptions: {},\n}\n\n// based on https://stackoverflow.com/a/75673107/13354068\n// we declare first a class, and then the mixin with a return type\n/**\n * This class describes the properties and methods to set up a basic Mesh, implemented in the {@link MeshBaseMixin}:\n * - Set and render the {@link Geometry} and {@link RenderMaterial}\n * - Add helpers to create {@link Texture} and {@link RenderTexture}\n * - Handle resizing, device lost/restoration and destroying the resources\n */\nexport declare class MeshBaseClass {\n /** The type of the {@link MeshBaseClass} */\n type: string\n /** The universal unique id of the {@link MeshBaseClass} */\n readonly uuid: string\n /** Index of this {@link MeshBaseClass}, i.e. creation order */\n readonly index: number\n /** The {@link Renderer} used */\n renderer: Renderer\n\n /** Options used to create this {@link MeshBaseClass} */\n options: MeshBaseOptions\n\n /** {@link RenderMaterial} used by this {@link MeshBaseClass} */\n material: RenderMaterial\n /** {@link AllowedGeometries | Geometry} used by this {@link MeshBaseClass} */\n geometry: MeshBaseParams['geometry']\n\n /** {@link RenderTarget} to render this Mesh to instead of the canvas context, if any. */\n outputTarget: null | RenderTarget\n\n /** Controls the order in which this {@link MeshBaseClass} should be rendered by our {@link core/scenes/Scene.Scene | Scene} */\n renderOrder: number\n /** Whether this {@link MeshBaseClass} should be treated as transparent. Impacts the {@link core/pipelines/RenderPipelineEntry.RenderPipelineEntry#pipeline | render pipeline} blend properties */\n transparent: boolean\n\n /** Flag indicating whether to draw this {@link MeshBaseClass} or not */\n visible: boolean\n /** Flag indicating whether this {@link MeshBaseClass} is ready to be drawn */\n _ready: boolean\n\n /** Empty object to store any additional data or custom properties into your Mesh. */\n userData: Record\n\n // callbacks\n /** function assigned to the {@link onReady} callback */\n _onReadyCallback: () => void\n /** function assigned to the {@link onBeforeRender} callback */\n _onBeforeRenderCallback: () => void\n /** function assigned to the {@link onRender} callback */\n _onRenderCallback: () => void\n /** function assigned to the {@link onAfterRender} callback */\n _onAfterRenderCallback: () => void\n /** function assigned to the {@link onAfterResize} callback */\n _onAfterResizeCallback: () => void\n /**\n * Assign a callback function to _onReadyCallback\n * @param callback - callback to run when {@link MeshBaseClass} is ready\n * @returns - our Mesh\n */\n onReady: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onBeforeRenderCallback\n * @param callback - callback to run just before {@link MeshBaseClass} will be rendered\n * @returns - our Mesh\n */\n onBeforeRender: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onRenderCallback\n * @param callback - callback to run when {@link MeshBaseClass} is rendered\n * @returns - our Mesh\n */\n onRender: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onAfterRenderCallback\n * @param callback - callback to run just after {@link MeshBaseClass} has been rendered\n * @returns - our Mesh\n */\n onAfterRender: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onBeforeRenderCallback\n * @param callback - callback to run just after {@link MeshBaseClass} has been resized\n * @returns - our Mesh\n */\n onAfterResize: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n\n /**\n * {@link MeshBaseClass} constructor\n * @param renderer - our {@link Renderer} class object\n * @param element - a DOM HTML Element that can be bound to a Mesh\n * @param parameters - {@link MeshBaseParams | Mesh base parameters}\n */\n constructor(renderer: Renderer, element: HTMLElement | null, parameters: MeshBaseParams)\n\n /**\n * Get private #autoRender value\n * @readonly\n */\n get autoRender(): boolean // allow to read value from child classes\n\n /**\n * Get/set whether a Mesh is ready or not\n * @readonly\n */\n get ready(): boolean\n set ready(value: boolean)\n\n /**\n * Add a Mesh to the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n addToScene(): void\n\n /**\n * Remove a Mesh from the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n removeFromScene(): void\n\n /**\n * Set or update the {@link RenderMaterial} {@link types/Materials.RenderMaterialRenderingOptions | rendering options} to match the {@link RenderPass#descriptor | RenderPass descriptor} used to draw this Mesh.\n * @param renderPass - {@link RenderPass | RenderPass} used to draw this Mesh, default to the {@link core/renderers/GPURenderer.GPURenderer#renderPass | renderer renderPass}.\n */\n setRenderingOptionsForRenderPass(renderPass: RenderPass): void\n\n /**\n * Set a new {@link Renderer} for this Mesh\n * @param renderer - new {@link Renderer} to set\n */\n setRenderer(renderer: Renderer | GPUCurtains): void\n\n /**\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been lost to prepare everything for restoration.\n * Basically set all the {@link GPUBuffer} to null so they will be reset next time we try to draw the Mesh\n */\n loseContext(): void\n\n /**\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been restored\n */\n restoreContext(): void\n\n /**\n * Set default shaders if one or both of them are missing\n */\n setShaders(): void\n\n /**\n * Compute the Mesh geometry if needed\n */\n computeGeometry(): void\n\n /**\n * Create the Mesh Geometry vertex and index buffers if needed\n */\n createGeometryBuffers(): void\n\n /**\n * Set our Mesh geometry: create buffers and add attributes to material\n */\n setGeometry(): void\n\n /**\n * Set a Mesh transparent property, then set its material\n * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters}\n */\n setMaterial(meshParameters: RenderMaterialParams): void\n\n /**\n * Set Mesh material attributes\n */\n setMaterialGeometryAttributes(): void\n\n /**\n * Get our {@link RenderMaterial#textures | RenderMaterial textures array}\n * @readonly\n */\n get textures(): Texture[]\n\n /**\n * Get our {@link RenderMaterial#renderTextures | RenderMaterial render textures array}\n * @readonly\n */\n get renderTextures(): RenderTexture[]\n\n /**\n * Create a new {@link Texture}\n * @param options - {@link TextureParams | Texture parameters}\n * @returns - newly created Texture\n */\n createTexture(options: TextureParams): Texture\n\n /**\n * Add a {@link Texture}\n * @param texture - {@link Texture} to add\n */\n addTexture(texture: Texture)\n\n /**\n * Callback run when a new {@link Texture} has been created\n * @param texture - newly created Texture\n */\n onTextureAdded(texture: Texture): void\n\n /**\n * Create a new {@link RenderTexture}\n * @param options - {@link RenderTextureParams | RenderTexture parameters}\n * @returns - newly created RenderTexture\n */\n createRenderTexture(options: RenderTextureParams): RenderTexture\n\n /**\n * Add a {@link RenderTexture}\n * @param renderTexture - {@link RenderTexture} to add\n */\n addRenderTexture(renderTexture: RenderTexture)\n\n /**\n * Assign or remove a {@link RenderTarget} to this Mesh\n * Since this manipulates the {@link core/scenes/Scene.Scene | Scene} stacks, it can be used to remove a RenderTarget as well.\n * @param outputTarget - the RenderTarget to assign or null if we want to remove the current RenderTarget\n */\n setOutputTarget(outputTarget: RenderTarget | null): void\n\n /**\n * Get the current {@link RenderMaterial} uniforms\n * @readonly\n */\n get uniforms(): Material['uniforms']\n\n /**\n * Get the current {@link RenderMaterial} storages\n * @readonly\n */\n get storages(): Material['storages']\n\n /**\n * Resize the Mesh's textures\n * @param boundingRect\n */\n resize(boundingRect?: DOMElementBoundingRect): void\n\n /**\n * Called before rendering the Mesh\n * Set the geometry if needed (create buffers and add attributes to the {@link RenderMaterial})\n * Then executes {@link RenderMaterial#onBeforeRender}: create its bind groups and pipeline if needed and eventually update its struct\n */\n onBeforeRenderPass(): void\n\n /**\n * Render our {@link MeshBaseClass} if the {@link RenderMaterial} is ready\n * @param pass - current render pass encoder\n */\n onRenderPass(pass: GPURenderPassEncoder): void\n\n /**\n * Called after having rendered the Mesh\n */\n onAfterRenderPass(): void\n\n /**\n * Render our Mesh\n * - Execute {@link onBeforeRenderPass}\n * - Stop here if {@link Renderer} is not ready or Mesh is not {@link visible}\n * - Execute super render call if it exists\n * - {@link onRenderPass | render} our {@link material} and {@link geometry}\n * - Execute {@link onAfterRenderPass}\n * @param pass - current render pass encoder\n */\n render(pass: GPURenderPassEncoder): void\n\n /**\n * Remove the Mesh from the {@link core/scenes/Scene.Scene | Scene} and destroy it\n */\n remove(): void\n\n /**\n * Destroy the Mesh\n */\n destroy(): void\n}\n\n/**\n * To get started, we need a type which we'll use to extend\n * other classes from. The main responsibility is to declare\n * that the type being passed in is a class.\n * We use a generic version which can apply a constraint on\n * the class which this mixin is applied to\n * @typeParam T - the base constructor\n */\nexport type MixinConstructor = new (...args: any[]) => T\n\n/**\n * Used to mix the basic Mesh properties and methods defined in {@link MeshBaseClass} (basically, set a {@link Geometry} and a {@link RenderMaterial} and render them, add helpers to create {@link Texture} and {@link RenderTexture}) with a given Base of type {@link core/objects3D/Object3D.Object3D | Object3D}, {@link core/objects3D/ProjectedObject3D.ProjectedObject3D | ProjectedObject3D}, {@link curtains/objects3D/DOMObject3D.DOMObject3D | DOMObject3D} or an empty class.\n * @exports MeshBaseMixin\n * @param Base - the class to mix onto\n * @returns - the mixed classes, creating a basic Mesh.\n */\nfunction MeshBaseMixin(Base: TBase): MixinConstructor & TBase {\n /**\n * MeshBase defines our base properties and methods\n */\n return class MeshBase extends Base implements MeshBaseClass {\n /** The type of the {@link MeshBase} */\n type: string\n /** The universal unique id of the {@link MeshBase} */\n readonly uuid: string\n /** Index of this {@link MeshBase}, i.e. creation order */\n readonly index: number\n /** The {@link Renderer} used */\n renderer: Renderer\n\n /** Options used to create this {@link MeshBase} */\n options: MeshBaseOptions\n\n /** {@link RenderMaterial} used by this {@link MeshBase} */\n material: RenderMaterial\n /** {@link AllowedGeometries | Geometry} used by this {@link MeshBase} */\n geometry: MeshBaseParams['geometry']\n\n /** {@link RenderTarget} to render this Mesh to, if any */\n outputTarget: null | RenderTarget\n\n /** Controls the order in which this {@link MeshBase} should be rendered by our {@link core/scenes/Scene.Scene | Scene} */\n renderOrder: number\n /** Whether this {@link MeshBase} should be treated as transparent. Impacts the {@link core/pipelines/RenderPipelineEntry.RenderPipelineEntry#pipeline | render pipeline} blend properties */\n transparent: boolean\n\n /** Flag indicating whether to draw this {@link MeshBase} or not */\n visible: boolean\n /** Flag indicating whether this {@link MeshBase} is ready to be drawn */\n _ready: boolean\n\n /** Empty object to store any additional data or custom properties into your {@link MeshBase}. */\n userData: Record\n\n /** Whether we should add this {@link MeshBase} to our {@link core/scenes/Scene.Scene | Scene} to let it handle the rendering process automatically */\n #autoRender = true\n\n // callbacks / events\n /** function assigned to the {@link onReady} callback */\n _onReadyCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onBeforeRender} callback */\n _onBeforeRenderCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onRender} callback */\n _onRenderCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onAfterRender} callback */\n _onAfterRenderCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onAfterResize} callback */\n _onAfterResizeCallback: () => void = () => {\n /* allow empty callback */\n }\n\n /**\n * MeshBase constructor\n *\n * @typedef MeshBaseArrayParams\n * @type {array}\n * @property {(Renderer|GPUCurtains)} 0 - our {@link Renderer} class object\n * @property {(string|HTMLElement|null)} 1 - a DOM HTML Element that can be bound to a Mesh\n * @property {MeshBaseParams} 2 - {@link MeshBaseParams | Mesh base parameters}\n *\n * @param {MeshBaseArrayParams} params - our MeshBaseMixin parameters\n */\n constructor(...params: any[]) {\n super(\n params[0] as Renderer | GPUCurtains,\n params[1] as HTMLElement | string | null,\n { ...defaultMeshBaseParams, ...params[2] } as MeshBaseParams\n )\n\n let renderer = params[0]\n const parameters = { ...defaultMeshBaseParams, ...params[2] }\n\n this.type = 'MeshBase'\n\n this.uuid = generateUUID()\n Object.defineProperty(this as MeshBase, 'index', { value: meshIndex++ })\n\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 + ' ' + this.type : this.type)\n\n this.renderer = renderer\n\n const {\n label,\n shaders,\n geometry,\n visible,\n renderOrder,\n outputTarget,\n texturesOptions,\n autoRender,\n ...meshParameters\n } = parameters\n\n this.outputTarget = outputTarget ?? null\n\n // set default sample count\n meshParameters.sampleCount = !!meshParameters.sampleCount\n ? meshParameters.sampleCount\n : this.outputTarget\n ? this.outputTarget.renderPass.options.sampleCount\n : this.renderer && this.renderer.renderPass\n ? this.renderer.renderPass.options.sampleCount\n : 1\n\n this.options = {\n ...(this.options ?? {}), // merge possible lower options?\n label: label ?? 'Mesh ' + this.renderer.meshes.length,\n shaders,\n texturesOptions,\n ...(outputTarget !== undefined && { outputTarget }),\n ...(autoRender !== undefined && { autoRender }),\n ...(meshParameters.useAsyncPipeline !== undefined && { useAsyncPipeline: meshParameters.useAsyncPipeline }),\n }\n\n this.geometry = geometry\n\n if (autoRender !== undefined) {\n this.#autoRender = autoRender\n }\n\n this.visible = visible\n this.renderOrder = renderOrder\n this.ready = false\n\n this.userData = {}\n\n this.computeGeometry()\n\n this.setMaterial({\n label: this.options.label,\n shaders: this.options.shaders,\n ...{ ...meshParameters, verticesOrder: geometry.verticesOrder, topology: geometry.topology },\n } as RenderMaterialParams)\n\n this.addToScene()\n }\n\n /**\n * Get private #autoRender value\n * @readonly\n */\n get autoRender(): boolean {\n return this.#autoRender\n }\n\n /**\n * Get/set whether a Mesh is ready or not\n * @readonly\n */\n get ready(): boolean {\n return this._ready\n }\n\n set ready(value: boolean) {\n if (value) {\n this._onReadyCallback && this._onReadyCallback()\n }\n this._ready = value\n }\n\n /* SCENE */\n\n /**\n * Add a Mesh to the renderer and the {@link core/scenes/Scene.Scene | Scene}. Can patch the {@link RenderMaterial} render options to match the {@link RenderPass} used to draw this Mesh.\n */\n addToScene() {\n this.renderer.meshes.push(this as unknown as ProjectedMesh)\n\n this.setRenderingOptionsForRenderPass(this.outputTarget ? this.outputTarget.renderPass : this.renderer.renderPass)\n\n if (this.#autoRender) {\n this.renderer.scene.addMesh(this as unknown as ProjectedMesh)\n }\n }\n\n /**\n * Remove a Mesh from the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n removeFromScene() {\n if (this.#autoRender) {\n this.renderer.scene.removeMesh(this as unknown as ProjectedMesh)\n }\n\n this.renderer.meshes = this.renderer.meshes.filter((m) => m.uuid !== this.uuid)\n }\n\n /**\n * Set or update the {@link RenderMaterial} {@link types/Materials.RenderMaterialRenderingOptions | rendering options} to match the {@link RenderPass#descriptor | RenderPass descriptor} used to draw this Mesh.\n * @param renderPass - {@link RenderPass | RenderPass} used to draw this Mesh, default to the {@link core/renderers/GPURenderer.GPURenderer#renderPass | renderer renderPass}.\n */\n setRenderingOptionsForRenderPass(renderPass: RenderPass) {\n // a Mesh render material rendering options MUST match the render pass descriptor used to draw it!\n const renderingOptions = {\n sampleCount: renderPass.options.sampleCount,\n // color attachments\n ...(renderPass.options.colorAttachments.length && {\n targetFormat: renderPass.options.colorAttachments[0].targetFormat,\n // multiple render targets?\n ...(renderPass.options.colorAttachments.length > 1 && {\n additionalTargets: renderPass.options.colorAttachments\n .filter((c, i) => i > 0)\n .map((colorAttachment) => {\n return {\n format: colorAttachment.targetFormat,\n }\n }),\n }),\n }),\n // depth\n depth: renderPass.options.useDepth,\n ...(renderPass.options.useDepth && {\n depthFormat: renderPass.options.depthFormat,\n }),\n }\n\n this.material?.setRenderingOptions(renderingOptions)\n }\n\n /**\n * Set a new {@link Renderer} for this Mesh\n * @param renderer - new {@link Renderer} to set\n */\n setRenderer(renderer: Renderer | GPUCurtains) {\n // we could pass our curtains object OR our curtains renderer object\n renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as Renderer)\n\n if (\n !renderer ||\n !(\n renderer.type === 'GPURenderer' ||\n renderer.type === 'GPUCameraRenderer' ||\n renderer.type === 'GPUCurtainsRenderer'\n )\n ) {\n throwWarning(\n `${this.options.label}: Cannot set ${renderer} as a renderer because it is not of a valid Renderer type.`\n )\n return\n }\n\n const oldRenderer = this.renderer\n this.removeFromScene()\n this.renderer = renderer\n this.addToScene()\n\n // if old renderer does not contain any meshes any more\n // clear it\n if (!oldRenderer.meshes.length) {\n oldRenderer.onBeforeRenderScene.add(\n (commandEncoder) => {\n oldRenderer.forceClear(commandEncoder)\n },\n { once: true }\n )\n }\n }\n\n /**\n * Assign or remove a {@link RenderTarget} to this Mesh\n * Since this manipulates the {@link core/scenes/Scene.Scene | Scene} stacks, it can be used to remove a RenderTarget as well.\n * @param outputTarget - the RenderTarget to assign or null if we want to remove the current RenderTarget\n */\n setOutputTarget(outputTarget: RenderTarget | null) {\n if (outputTarget && outputTarget.type !== 'RenderTarget') {\n throwWarning(`${this.options.label ?? this.type}: outputTarget is not a RenderTarget: ${outputTarget}`)\n return\n }\n\n // ensure the mesh is in the correct scene stack\n this.removeFromScene()\n this.outputTarget = outputTarget\n this.addToScene()\n }\n\n /**\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been lost to prepare everything for restoration.\n * Basically set all the {@link GPUBuffer} to null so they will be reset next time we try to draw the Mesh\n */\n loseContext() {\n // first the geometry\n this.geometry.vertexBuffers.forEach((vertexBuffer) => {\n vertexBuffer.buffer = null\n })\n\n if ('indexBuffer' in this.geometry) {\n this.geometry.indexBuffer.buffer = null\n }\n\n // then the material\n this.material.loseContext()\n }\n\n /**\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been restored\n */\n restoreContext() {\n this.material.restoreContext()\n }\n\n /* SHADERS */\n\n /**\n * Set default shaders if one or both of them are missing\n */\n setShaders() {\n let { shaders } = this.options\n\n if (!shaders) {\n shaders = {\n vertex: {\n code: default_vsWgsl,\n entryPoint: 'main',\n },\n fragment: {\n code: default_fsWgsl,\n entryPoint: 'main',\n },\n }\n } else {\n if (!shaders.vertex || !shaders.vertex.code) {\n shaders.vertex = {\n code: default_vsWgsl,\n entryPoint: 'main',\n }\n }\n\n if (shaders.fragment === undefined || (shaders.fragment && !(shaders.fragment as ShaderOptions).code)) {\n shaders.fragment = {\n code: default_fsWgsl,\n entryPoint: 'main',\n }\n }\n }\n }\n\n /* GEOMETRY */\n\n /**\n * Compute the Mesh geometry if needed\n */\n computeGeometry() {\n if (this.geometry.shouldCompute) {\n this.geometry.computeGeometry()\n }\n }\n\n /**\n * Create the Mesh Geometry vertex and index buffers if needed\n */\n createGeometryBuffers() {\n if (!this.geometry.ready) {\n this.geometry.vertexBuffers.forEach((vertexBuffer) => {\n if (!vertexBuffer.buffer) {\n vertexBuffer.buffer = this.renderer.createBuffer({\n label: this.options.label + ' geometry: ' + vertexBuffer.name + ' buffer',\n size: vertexBuffer.array.byteLength,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n })\n\n this.renderer.queueWriteBuffer(vertexBuffer.buffer, 0, vertexBuffer.array)\n }\n })\n\n // if it's an indexed geometry, create index GPUBuffer as well\n if ('indexBuffer' in this.geometry && this.geometry.indexBuffer && !this.geometry.indexBuffer.buffer) {\n this.geometry.indexBuffer.buffer = this.renderer.createBuffer({\n label: this.options.label + ' geometry: index buffer',\n size: this.geometry.indexBuffer.array.byteLength,\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\n })\n\n this.renderer.queueWriteBuffer(this.geometry.indexBuffer.buffer, 0, this.geometry.indexBuffer.array)\n }\n }\n }\n\n /**\n * Set our Mesh geometry: create buffers and add attributes to material\n */\n setGeometry() {\n if (this.geometry && this.renderer.ready) {\n this.createGeometryBuffers()\n this.setMaterialGeometryAttributes()\n }\n }\n\n /* MATERIAL */\n\n /**\n * Set a Mesh transparent property, then set its material\n * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters}\n */\n setMaterial(meshParameters: RenderMaterialParams) {\n this.transparent = meshParameters.transparent\n\n this.setShaders()\n\n this.material = new RenderMaterial(this.renderer, meshParameters)\n // add eventual textures passed as parameters\n this.material.options.textures\n ?.filter((texture) => texture instanceof Texture)\n .forEach((texture) => this.onTextureAdded(texture))\n }\n\n /**\n * Set Mesh material attributes\n */\n setMaterialGeometryAttributes() {\n if (this.material && !this.material.attributes) {\n this.material.setAttributesFromGeometry(this.geometry)\n }\n }\n\n /* TEXTURES */\n\n /**\n * Get our {@link RenderMaterial#textures | RenderMaterial textures array}\n * @readonly\n */\n get textures(): Texture[] {\n return this.material?.textures || []\n }\n\n /**\n * Get our {@link RenderMaterial#renderTextures | RenderMaterial render textures array}\n * @readonly\n */\n get renderTextures(): RenderTexture[] {\n return this.material?.renderTextures || []\n }\n\n /**\n * Create a new {@link Texture}\n * @param options - {@link TextureParams | Texture parameters}\n * @returns - newly created {@link Texture}\n */\n createTexture(options: TextureParams): Texture {\n if (!options.name) {\n options.name = 'texture' + this.textures.length\n }\n\n if (!options.label) {\n options.label = this.options.label + ' ' + options.name\n }\n\n const texture = new Texture(this.renderer, { ...options, ...this.options.texturesOptions })\n\n this.addTexture(texture)\n\n return texture\n }\n\n /**\n * Add a {@link Texture}\n * @param texture - {@link Texture} to add\n */\n addTexture(texture: Texture) {\n this.material.addTexture(texture)\n this.onTextureAdded(texture)\n }\n\n /**\n * Callback run when a new {@link Texture} has been added\n * @param texture - newly created Texture\n */\n onTextureAdded(texture: Texture) {\n texture.parentMesh = this as unknown as TextureParent\n }\n\n /**\n * Create a new {@link RenderTexture}\n * @param options - {@link RenderTextureParams | RenderTexture parameters}\n * @returns - newly created {@link RenderTexture}\n */\n createRenderTexture(options: RenderTextureParams): RenderTexture {\n if (!options.name) {\n options.name = 'renderTexture' + this.renderTextures.length\n }\n\n const renderTexture = new RenderTexture(this.renderer, options)\n\n this.addRenderTexture(renderTexture)\n\n return renderTexture\n }\n\n /**\n * Add a {@link RenderTexture}\n * @param renderTexture - {@link RenderTexture} to add\n */\n addRenderTexture(renderTexture: RenderTexture) {\n this.material.addTexture(renderTexture)\n }\n\n /* BINDINGS */\n\n /**\n * Get the current {@link RenderMaterial} uniforms\n * @readonly\n */\n get uniforms(): Material['uniforms'] {\n return this.material?.uniforms\n }\n\n /**\n * Get the current {@link RenderMaterial} storages\n * @readonly\n */\n get storages(): Material['storages'] {\n return this.material?.storages\n }\n\n /* RESIZE */\n\n /**\n * Resize the Mesh's textures\n * @param boundingRect\n */\n resize(boundingRect?: DOMElementBoundingRect | null) {\n // @ts-ignore\n if (super.resize) {\n // @ts-ignore\n super.resize(boundingRect)\n }\n\n this.renderTextures?.forEach((renderTexture) => {\n // copy from original textures again if needed\n if (renderTexture.options.fromTexture) {\n renderTexture.copy(renderTexture.options.fromTexture)\n }\n })\n\n // resize textures\n this.textures?.forEach((texture) => {\n texture.resize()\n })\n\n this._onAfterResizeCallback && this._onAfterResizeCallback()\n }\n\n /* EVENTS */\n\n /**\n * Assign a callback function to _onReadyCallback\n * @param callback - callback to run when {@link MeshBase} is ready\n * @returns - our Mesh\n */\n onReady(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onReadyCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onBeforeRenderCallback\n * @param callback - callback to run just before {@link MeshBase} will be rendered\n * @returns - our Mesh\n */\n onBeforeRender(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onBeforeRenderCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onRenderCallback\n * @param callback - callback to run when {@link MeshBase} is rendered\n * @returns - our Mesh\n */\n onRender(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onRenderCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onAfterRenderCallback\n * @param callback - callback to run just after {@link MeshBase} has been rendered\n * @returns - our Mesh\n */\n onAfterRender(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onAfterRenderCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onAfterResizeCallback\n * @param callback - callback to run just after {@link MeshBase} has been resized\n * @returns - our Mesh\n */\n onAfterResize(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onAfterResizeCallback = callback\n }\n\n return this\n }\n\n /* RENDER */\n\n /**\n * Called before rendering the Mesh\n * Set the geometry if needed (create buffers and add attributes to the {@link RenderMaterial})\n * Then executes {@link RenderMaterial#onBeforeRender}: create its bind groups and pipeline if needed and eventually update its struct\n */\n onBeforeRenderPass() {\n if (!this.renderer.ready) return\n\n if (this.material && this.material.ready && this.geometry && this.geometry.ready && !this.ready) {\n this.ready = true\n }\n\n this.setGeometry()\n\n this._onBeforeRenderCallback && this._onBeforeRenderCallback()\n\n this.material.onBeforeRender()\n }\n\n /**\n * Render our {@link MeshBase} if the {@link RenderMaterial} is ready\n * @param pass - current render pass encoder\n */\n onRenderPass(pass: GPURenderPassEncoder) {\n if (!this.material.ready) return\n\n this._onRenderCallback && this._onRenderCallback()\n\n // render ou material\n this.material.render(pass)\n // then render our geometry\n this.geometry.render(pass)\n }\n\n /**\n * Called after having rendered the Mesh\n */\n onAfterRenderPass() {\n this._onAfterRenderCallback && this._onAfterRenderCallback()\n }\n\n /**\n * Render our Mesh\n * - Execute {@link onBeforeRenderPass}\n * - Stop here if {@link Renderer} is not ready or Mesh is not {@link visible}\n * - Execute super render call if it exists\n * - {@link onRenderPass | render} our {@link material} and {@link geometry}\n * - Execute {@link onAfterRenderPass}\n * @param pass - current render pass encoder\n */\n render(pass: GPURenderPassEncoder) {\n this.onBeforeRenderPass()\n\n // no point to render if the WebGPU device is not ready\n if (!this.renderer.ready || !this.visible) return\n\n // @ts-ignore\n if (super.render) {\n // @ts-ignore\n super.render()\n }\n\n !this.renderer.production && pass.pushDebugGroup(this.options.label)\n\n this.onRenderPass(pass)\n\n !this.renderer.production && pass.popDebugGroup()\n\n this.onAfterRenderPass()\n }\n\n /* DESTROY */\n\n /**\n * Remove the Mesh from the {@link core/scenes/Scene.Scene | Scene} and destroy it\n */\n remove() {\n this.removeFromScene()\n this.destroy()\n\n // if the renderer does not contain any meshes any more\n // clear it\n if (!this.renderer.meshes.length) {\n this.renderer.onBeforeRenderScene.add(\n (commandEncoder) => {\n this.renderer.forceClear(commandEncoder)\n },\n { once: true }\n )\n }\n }\n\n /**\n * Destroy the Mesh\n */\n destroy() {\n // @ts-ignore\n if (super.destroy) {\n // @ts-ignore\n super.destroy()\n }\n\n this.material?.destroy()\n\n // remove geometry buffers from device cache\n this.geometry.vertexBuffers.forEach((vertexBuffer) => {\n // use original vertex buffer label in case it has been swapped (usually by a compute pass)\n this.renderer.removeBuffer(\n vertexBuffer.buffer,\n this.options.label + ' geometry: ' + vertexBuffer.name + ' buffer'\n )\n })\n\n if ('indexBuffer' in this.geometry) {\n this.renderer.removeBuffer(this.geometry.indexBuffer.buffer)\n }\n\n this.geometry?.destroy()\n }\n }\n}\n\nexport { MeshBaseMixin }\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"MeshBaseMixin.mjs","sources":["../../../../../src/core/meshes/mixins/MeshBaseMixin.ts"],"sourcesContent":["import { generateUUID, throwWarning } from '../../../utils/utils'\nimport { isRenderer, Renderer } from '../../renderers/utils'\nimport { RenderMaterial } from '../../materials/RenderMaterial'\nimport { Texture } from '../../textures/Texture'\nimport { Geometry } from '../../geometries/Geometry'\nimport { RenderTexture, RenderTextureParams } from '../../textures/RenderTexture'\nimport { ExternalTextureParams, TextureParams, TextureParent } from '../../../types/Textures'\nimport { RenderTarget } from '../../renderPasses/RenderTarget'\nimport { GPUCurtains } from '../../../curtains/GPUCurtains'\nimport { ProjectedMesh } from '../../renderers/GPURenderer'\nimport { Material } from '../../materials/Material'\nimport { DOMElementBoundingRect } from '../../DOM/DOMElement'\nimport { AllowedGeometries, RenderMaterialParams, ShaderOptions } from '../../../types/Materials'\nimport { ProjectedMeshBaseClass } from './ProjectedMeshBaseMixin'\nimport default_vsWgsl from '../../shaders/chunks/default_vs.wgsl'\nimport default_fsWgsl from '../../shaders/chunks/default_fs.wgsl'\nimport { RenderPass } from '../../renderPasses/RenderPass'\n\nlet meshIndex = 0\n\nexport interface MeshBaseRenderParams extends RenderMaterialParams {\n /** Whether we should add this Mesh to our {@link core/scenes/Scene.Scene | Scene} to let it handle the rendering process automatically */\n autoRender?: boolean\n /** Flag indicating whether to draw this Mesh or not */\n visible?: boolean\n /** Controls the order in which this Mesh should be rendered by our {@link core/scenes/Scene.Scene | Scene} */\n renderOrder?: number\n /** Optional {@link RenderTarget} to render this Mesh to instead of the canvas context. */\n outputTarget?: RenderTarget\n /** Parameters used by this Mesh to create a {@link Texture} */\n texturesOptions?: ExternalTextureParams\n}\n\n/**\n * Base parameters used to create a Mesh\n */\nexport interface MeshBaseParams extends MeshBaseRenderParams {\n /** Geometry to use */\n geometry: AllowedGeometries\n}\n\n/**\n * Base options used to create this Mesh\n */\nexport interface MeshBaseOptions {\n /** The label of this Mesh, sent to various GPU objects for debugging purpose */\n label?: MeshBaseParams['label']\n /** Shaders to use by this Mesh {@link RenderMaterial} */\n shaders?: MeshBaseParams['shaders']\n /** Parameters used by this Mesh to create a {@link Texture} */\n texturesOptions?: ExternalTextureParams\n /** {@link RenderTarget} to render this Mesh to instead of the canvas context, if any. */\n outputTarget?: RenderTarget | null\n /** Whether we should add this Mesh to our {@link core/scenes/Scene.Scene | Scene} to let it handle the rendering process automatically */\n autoRender?: boolean\n /** Whether to compile this Mesh {@link RenderMaterial} {@link core/pipelines/RenderPipelineEntry.RenderPipelineEntry#pipeline | render pipeline} asynchronously or not */\n useAsyncPipeline?: boolean\n}\n\n/** @const - Default Mesh parameters to merge with user defined parameters */\nconst defaultMeshBaseParams: MeshBaseParams = {\n // geometry\n geometry: new Geometry(),\n // material\n shaders: {},\n autoRender: true,\n useProjection: false,\n useAsyncPipeline: true,\n // rendering\n cullMode: 'back',\n depth: true,\n depthWriteEnabled: true,\n depthCompare: 'less',\n depthFormat: 'depth24plus',\n transparent: false,\n visible: true,\n renderOrder: 0,\n // textures\n texturesOptions: {},\n}\n\n// based on https://stackoverflow.com/a/75673107/13354068\n// we declare first a class, and then the mixin with a return type\n/**\n * This class describes the properties and methods to set up a basic Mesh, implemented in the {@link MeshBaseMixin}:\n * - Set and render the {@link Geometry} and {@link RenderMaterial}\n * - Add helpers to create {@link Texture} and {@link RenderTexture}\n * - Handle resizing, device lost/restoration and destroying the resources\n */\nexport declare class MeshBaseClass {\n /** The type of the {@link MeshBaseClass} */\n type: string\n /** The universal unique id of the {@link MeshBaseClass} */\n readonly uuid: string\n /** Index of this {@link MeshBaseClass}, i.e. creation order */\n readonly index: number\n /** The {@link Renderer} used */\n renderer: Renderer\n\n /** Options used to create this {@link MeshBaseClass} */\n options: MeshBaseOptions\n\n /** {@link RenderMaterial} used by this {@link MeshBaseClass} */\n material: RenderMaterial\n /** {@link AllowedGeometries | Geometry} used by this {@link MeshBaseClass} */\n geometry: MeshBaseParams['geometry']\n\n /** {@link RenderTarget} to render this Mesh to instead of the canvas context, if any. */\n outputTarget: null | RenderTarget\n\n /** Controls the order in which this {@link MeshBaseClass} should be rendered by our {@link core/scenes/Scene.Scene | Scene} */\n renderOrder: number\n /** Whether this {@link MeshBaseClass} should be treated as transparent. Impacts the {@link core/pipelines/RenderPipelineEntry.RenderPipelineEntry#pipeline | render pipeline} blend properties */\n transparent: boolean\n\n /** Flag indicating whether to draw this {@link MeshBaseClass} or not */\n visible: boolean\n /** Flag indicating whether this {@link MeshBaseClass} is ready to be drawn */\n _ready: boolean\n\n /** Empty object to store any additional data or custom properties into your Mesh. */\n userData: Record\n\n // callbacks\n /** function assigned to the {@link onReady} callback */\n _onReadyCallback: () => void\n /** function assigned to the {@link onBeforeRender} callback */\n _onBeforeRenderCallback: () => void\n /** function assigned to the {@link onRender} callback */\n _onRenderCallback: () => void\n /** function assigned to the {@link onAfterRender} callback */\n _onAfterRenderCallback: () => void\n /** function assigned to the {@link onAfterResize} callback */\n _onAfterResizeCallback: () => void\n /**\n * Assign a callback function to _onReadyCallback\n * @param callback - callback to run when {@link MeshBaseClass} is ready\n * @returns - our Mesh\n */\n onReady: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onBeforeRenderCallback\n * @param callback - callback to run just before {@link MeshBaseClass} will be rendered\n * @returns - our Mesh\n */\n onBeforeRender: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onRenderCallback\n * @param callback - callback to run when {@link MeshBaseClass} is rendered\n * @returns - our Mesh\n */\n onRender: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onAfterRenderCallback\n * @param callback - callback to run just after {@link MeshBaseClass} has been rendered\n * @returns - our Mesh\n */\n onAfterRender: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onBeforeRenderCallback\n * @param callback - callback to run just after {@link MeshBaseClass} has been resized\n * @returns - our Mesh\n */\n onAfterResize: (callback: () => void) => MeshBaseClass | ProjectedMeshBaseClass\n\n /**\n * {@link MeshBaseClass} constructor\n * @param renderer - our {@link Renderer} class object\n * @param element - a DOM HTML Element that can be bound to a Mesh\n * @param parameters - {@link MeshBaseParams | Mesh base parameters}\n */\n constructor(renderer: Renderer, element: HTMLElement | null, parameters: MeshBaseParams)\n\n /**\n * Get private #autoRender value\n * @readonly\n */\n get autoRender(): boolean // allow to read value from child classes\n\n /**\n * Get/set whether a Mesh is ready or not\n * @readonly\n */\n get ready(): boolean\n set ready(value: boolean)\n\n /**\n * Add a Mesh to the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n addToScene(): void\n\n /**\n * Remove a Mesh from the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n removeFromScene(): void\n\n /**\n * Set a new {@link Renderer} for this Mesh\n * @param renderer - new {@link Renderer} to set\n */\n setRenderer(renderer: Renderer | GPUCurtains): void\n\n /**\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been lost to prepare everything for restoration.\n * Basically set all the {@link GPUBuffer} to null so they will be reset next time we try to draw the Mesh\n */\n loseContext(): void\n\n /**\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been restored\n */\n restoreContext(): void\n\n /**\n * Set default shaders if one or both of them are missing\n */\n setShaders(): void\n\n /**\n * Compute the Mesh geometry if needed\n */\n computeGeometry(): void\n\n /**\n * Create the Mesh Geometry vertex and index buffers if needed\n */\n createGeometryBuffers(): void\n\n /**\n * Set our Mesh geometry: create buffers and add attributes to material\n */\n setGeometry(): void\n\n /**\n * Set or update the {@link RenderMaterial} {@link types/Materials.RenderMaterialRenderingOptions | rendering options} to match the {@link RenderPass#descriptor | RenderPass descriptor} used to draw this Mesh.\n * @param renderPass - {@link RenderPass | RenderPass} used to draw this Mesh, default to the {@link core/renderers/GPURenderer.GPURenderer#renderPass | renderer renderPass}.\n */\n setRenderingOptionsForRenderPass(renderPass: RenderPass): void\n\n /**\n * Hook used to clean up parameters before sending them to the material.\n * @param parameters - parameters to clean before sending them to the {@link RenderMaterial}\n * @returns - cleaned parameters\n */\n cleanupRenderMaterialParameters(parameters: MeshBaseRenderParams): MeshBaseRenderParams\n\n /**\n * Set a Mesh transparent property, then set its material\n * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters}\n */\n setMaterial(meshParameters: RenderMaterialParams): void\n\n /**\n * Set Mesh material attributes\n */\n setMaterialGeometryAttributes(): void\n\n /**\n * Get our {@link RenderMaterial#textures | RenderMaterial textures array}\n * @readonly\n */\n get textures(): Texture[]\n\n /**\n * Get our {@link RenderMaterial#renderTextures | RenderMaterial render textures array}\n * @readonly\n */\n get renderTextures(): RenderTexture[]\n\n /**\n * Create a new {@link Texture}\n * @param options - {@link TextureParams | Texture parameters}\n * @returns - newly created Texture\n */\n createTexture(options: TextureParams): Texture\n\n /**\n * Add a {@link Texture}\n * @param texture - {@link Texture} to add\n */\n addTexture(texture: Texture)\n\n /**\n * Callback run when a new {@link Texture} has been created\n * @param texture - newly created Texture\n */\n onTextureAdded(texture: Texture): void\n\n /**\n * Create a new {@link RenderTexture}\n * @param options - {@link RenderTextureParams | RenderTexture parameters}\n * @returns - newly created RenderTexture\n */\n createRenderTexture(options: RenderTextureParams): RenderTexture\n\n /**\n * Add a {@link RenderTexture}\n * @param renderTexture - {@link RenderTexture} to add\n */\n addRenderTexture(renderTexture: RenderTexture)\n\n /**\n * Assign or remove a {@link RenderTarget} to this Mesh\n * Since this manipulates the {@link core/scenes/Scene.Scene | Scene} stacks, it can be used to remove a RenderTarget as well.\n * @param outputTarget - the RenderTarget to assign or null if we want to remove the current RenderTarget\n */\n setOutputTarget(outputTarget: RenderTarget | null): void\n\n /**\n * Get the current {@link RenderMaterial} uniforms\n * @readonly\n */\n get uniforms(): Material['uniforms']\n\n /**\n * Get the current {@link RenderMaterial} storages\n * @readonly\n */\n get storages(): Material['storages']\n\n /**\n * Resize the Mesh's textures\n * @param boundingRect\n */\n resize(boundingRect?: DOMElementBoundingRect): void\n\n /**\n * Called before rendering the Mesh\n * Set the geometry if needed (create buffers and add attributes to the {@link RenderMaterial})\n * Then executes {@link RenderMaterial#onBeforeRender}: create its bind groups and pipeline if needed and eventually update its struct\n */\n onBeforeRenderPass(): void\n\n /**\n * Render our {@link MeshBaseClass} if the {@link RenderMaterial} is ready\n * @param pass - current render pass encoder\n */\n onRenderPass(pass: GPURenderPassEncoder): void\n\n /**\n * Called after having rendered the Mesh\n */\n onAfterRenderPass(): void\n\n /**\n * Render our Mesh\n * - Execute {@link onBeforeRenderPass}\n * - Stop here if {@link Renderer} is not ready or Mesh is not {@link visible}\n * - Execute super render call if it exists\n * - {@link onRenderPass | render} our {@link material} and {@link geometry}\n * - Execute {@link onAfterRenderPass}\n * @param pass - current render pass encoder\n */\n render(pass: GPURenderPassEncoder): void\n\n /**\n * Remove the Mesh from the {@link core/scenes/Scene.Scene | Scene} and destroy it\n */\n remove(): void\n\n /**\n * Destroy the Mesh\n */\n destroy(): void\n}\n\n/**\n * To get started, we need a type which we'll use to extend\n * other classes from. The main responsibility is to declare\n * that the type being passed in is a class.\n * We use a generic version which can apply a constraint on\n * the class which this mixin is applied to\n * @typeParam T - the base constructor\n */\nexport type MixinConstructor = new (...args: any[]) => T\n\n/**\n * Used to mix the basic Mesh properties and methods defined in {@link MeshBaseClass} (basically, set a {@link Geometry} and a {@link RenderMaterial} and render them, add helpers to create {@link Texture} and {@link RenderTexture}) with a given Base of type {@link core/objects3D/Object3D.Object3D | Object3D}, {@link core/objects3D/ProjectedObject3D.ProjectedObject3D | ProjectedObject3D}, {@link curtains/objects3D/DOMObject3D.DOMObject3D | DOMObject3D} or an empty class.\n * @exports MeshBaseMixin\n * @param Base - the class to mix onto\n * @returns - the mixed classes, creating a basic Mesh.\n */\nfunction MeshBaseMixin(Base: TBase): MixinConstructor & TBase {\n /**\n * MeshBase defines our base properties and methods\n */\n return class MeshBase extends Base implements MeshBaseClass {\n /** The type of the {@link MeshBase} */\n type: string\n /** The universal unique id of the {@link MeshBase} */\n readonly uuid: string\n /** Index of this {@link MeshBase}, i.e. creation order */\n readonly index: number\n /** The {@link Renderer} used */\n renderer: Renderer\n\n /** Options used to create this {@link MeshBase} */\n options: MeshBaseOptions\n\n /** {@link RenderMaterial} used by this {@link MeshBase} */\n material: RenderMaterial\n /** {@link AllowedGeometries | Geometry} used by this {@link MeshBase} */\n geometry: MeshBaseParams['geometry']\n\n /** {@link RenderTarget} to render this Mesh to, if any */\n outputTarget: null | RenderTarget\n\n /** Controls the order in which this {@link MeshBase} should be rendered by our {@link core/scenes/Scene.Scene | Scene} */\n renderOrder: number\n /** Whether this {@link MeshBase} should be treated as transparent. Impacts the {@link core/pipelines/RenderPipelineEntry.RenderPipelineEntry#pipeline | render pipeline} blend properties */\n transparent: boolean\n\n /** Flag indicating whether to draw this {@link MeshBase} or not */\n visible: boolean\n /** Flag indicating whether this {@link MeshBase} is ready to be drawn */\n _ready: boolean\n\n /** Empty object to store any additional data or custom properties into your {@link MeshBase}. */\n userData: Record\n\n /** Whether we should add this {@link MeshBase} to our {@link core/scenes/Scene.Scene | Scene} to let it handle the rendering process automatically */\n #autoRender = true\n\n // callbacks / events\n /** function assigned to the {@link onReady} callback */\n _onReadyCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onBeforeRender} callback */\n _onBeforeRenderCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onRender} callback */\n _onRenderCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onAfterRender} callback */\n _onAfterRenderCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onAfterResize} callback */\n _onAfterResizeCallback: () => void = () => {\n /* allow empty callback */\n }\n\n /**\n * MeshBase constructor\n *\n * @typedef MeshBaseArrayParams\n * @type {array}\n * @property {(Renderer|GPUCurtains)} 0 - our {@link Renderer} class object\n * @property {(string|HTMLElement|null)} 1 - a DOM HTML Element that can be bound to a Mesh\n * @property {MeshBaseParams} 2 - {@link MeshBaseParams | Mesh base parameters}\n *\n * @param {MeshBaseArrayParams} params - our MeshBaseMixin parameters\n */\n constructor(...params: any[]) {\n super(\n params[0] as Renderer | GPUCurtains,\n params[1] as HTMLElement | string | null,\n { ...defaultMeshBaseParams, ...params[2] } as MeshBaseParams\n )\n\n let renderer = params[0]\n const parameters = { ...defaultMeshBaseParams, ...params[2] }\n\n this.type = 'MeshBase'\n\n this.uuid = generateUUID()\n Object.defineProperty(this as MeshBase, 'index', { value: meshIndex++ })\n\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 + ' ' + this.type : this.type)\n\n this.renderer = renderer\n\n const {\n label,\n shaders,\n geometry,\n visible,\n renderOrder,\n outputTarget,\n texturesOptions,\n autoRender,\n ...meshParameters\n } = parameters\n\n this.outputTarget = outputTarget ?? null\n\n // set default sample count\n meshParameters.sampleCount = !!meshParameters.sampleCount\n ? meshParameters.sampleCount\n : this.outputTarget\n ? this.outputTarget.renderPass.options.sampleCount\n : this.renderer && this.renderer.renderPass\n ? this.renderer.renderPass.options.sampleCount\n : 1\n\n this.options = {\n ...(this.options ?? {}), // merge possible lower options?\n label: label ?? 'Mesh ' + this.renderer.meshes.length,\n shaders,\n texturesOptions,\n ...(outputTarget !== undefined && { outputTarget }),\n ...(autoRender !== undefined && { autoRender }),\n ...meshParameters,\n }\n\n this.geometry = geometry\n\n if (autoRender !== undefined) {\n this.#autoRender = autoRender\n }\n\n this.visible = visible\n this.renderOrder = renderOrder\n this.ready = false\n\n this.userData = {}\n\n this.computeGeometry()\n\n this.setMaterial({\n ...this.cleanupRenderMaterialParameters({ ...this.options }),\n verticesOrder: geometry.verticesOrder,\n topology: geometry.topology,\n } as RenderMaterialParams)\n\n this.addToScene()\n }\n\n /**\n * Get private #autoRender value\n * @readonly\n */\n get autoRender(): boolean {\n return this.#autoRender\n }\n\n /**\n * Get/set whether a Mesh is ready or not\n * @readonly\n */\n get ready(): boolean {\n return this._ready\n }\n\n set ready(value: boolean) {\n if (value) {\n this._onReadyCallback && this._onReadyCallback()\n }\n this._ready = value\n }\n\n /* SCENE */\n\n /**\n * Add a Mesh to the renderer and the {@link core/scenes/Scene.Scene | Scene}. Can patch the {@link RenderMaterial} render options to match the {@link RenderPass} used to draw this Mesh.\n */\n addToScene() {\n this.renderer.meshes.push(this as unknown as ProjectedMesh)\n\n this.setRenderingOptionsForRenderPass(this.outputTarget ? this.outputTarget.renderPass : this.renderer.renderPass)\n\n if (this.#autoRender) {\n this.renderer.scene.addMesh(this as unknown as ProjectedMesh)\n }\n }\n\n /**\n * Remove a Mesh from the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n removeFromScene() {\n if (this.#autoRender) {\n this.renderer.scene.removeMesh(this as unknown as ProjectedMesh)\n }\n\n this.renderer.meshes = this.renderer.meshes.filter((m) => m.uuid !== this.uuid)\n }\n\n /**\n * Set a new {@link Renderer} for this Mesh\n * @param renderer - new {@link Renderer} to set\n */\n setRenderer(renderer: Renderer | GPUCurtains) {\n // we could pass our curtains object OR our curtains renderer object\n renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as Renderer)\n\n if (\n !renderer ||\n !(\n renderer.type === 'GPURenderer' ||\n renderer.type === 'GPUCameraRenderer' ||\n renderer.type === 'GPUCurtainsRenderer'\n )\n ) {\n throwWarning(\n `${this.options.label}: Cannot set ${renderer} as a renderer because it is not of a valid Renderer type.`\n )\n return\n }\n\n const oldRenderer = this.renderer\n this.removeFromScene()\n this.renderer = renderer\n this.addToScene()\n\n // if old renderer does not contain any meshes any more\n // clear it\n if (!oldRenderer.meshes.length) {\n oldRenderer.onBeforeRenderScene.add(\n (commandEncoder) => {\n oldRenderer.forceClear(commandEncoder)\n },\n { once: true }\n )\n }\n }\n\n /**\n * Assign or remove a {@link RenderTarget} to this Mesh\n * Since this manipulates the {@link core/scenes/Scene.Scene | Scene} stacks, it can be used to remove a RenderTarget as well.\n * @param outputTarget - the RenderTarget to assign or null if we want to remove the current RenderTarget\n */\n setOutputTarget(outputTarget: RenderTarget | null) {\n if (outputTarget && outputTarget.type !== 'RenderTarget') {\n throwWarning(`${this.options.label ?? this.type}: outputTarget is not a RenderTarget: ${outputTarget}`)\n return\n }\n\n // ensure the mesh is in the correct scene stack\n this.removeFromScene()\n this.outputTarget = outputTarget\n this.addToScene()\n }\n\n /**\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been lost to prepare everything for restoration.\n * Basically set all the {@link GPUBuffer} to null so they will be reset next time we try to draw the Mesh\n */\n loseContext() {\n // first the geometry\n this.geometry.vertexBuffers.forEach((vertexBuffer) => {\n vertexBuffer.buffer = null\n })\n\n if ('indexBuffer' in this.geometry) {\n this.geometry.indexBuffer.buffer = null\n }\n\n // then the material\n this.material.loseContext()\n }\n\n /**\n * Called when the {@link core/renderers/GPUDeviceManager.GPUDeviceManager#device | device} has been restored\n */\n restoreContext() {\n this.material.restoreContext()\n }\n\n /* SHADERS */\n\n /**\n * Set default shaders if one or both of them are missing\n */\n setShaders() {\n let { shaders } = this.options\n\n if (!shaders) {\n shaders = {\n vertex: {\n code: default_vsWgsl,\n entryPoint: 'main',\n },\n fragment: {\n code: default_fsWgsl,\n entryPoint: 'main',\n },\n }\n } else {\n if (!shaders.vertex || !shaders.vertex.code) {\n shaders.vertex = {\n code: default_vsWgsl,\n entryPoint: 'main',\n }\n }\n\n if (shaders.fragment === undefined || (shaders.fragment && !(shaders.fragment as ShaderOptions).code)) {\n shaders.fragment = {\n code: default_fsWgsl,\n entryPoint: 'main',\n }\n }\n }\n }\n\n /* GEOMETRY */\n\n /**\n * Compute the Mesh geometry if needed\n */\n computeGeometry() {\n if (this.geometry.shouldCompute) {\n this.geometry.computeGeometry()\n }\n }\n\n /**\n * Create the Mesh Geometry vertex and index buffers if needed\n */\n createGeometryBuffers() {\n if (!this.geometry.ready) {\n this.geometry.vertexBuffers.forEach((vertexBuffer) => {\n if (!vertexBuffer.buffer) {\n vertexBuffer.buffer = this.renderer.createBuffer({\n label: this.options.label + ' geometry: ' + vertexBuffer.name + ' buffer',\n size: vertexBuffer.array.byteLength,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n })\n\n this.renderer.queueWriteBuffer(vertexBuffer.buffer, 0, vertexBuffer.array)\n }\n })\n\n // if it's an indexed geometry, create index GPUBuffer as well\n if ('indexBuffer' in this.geometry && this.geometry.indexBuffer && !this.geometry.indexBuffer.buffer) {\n this.geometry.indexBuffer.buffer = this.renderer.createBuffer({\n label: this.options.label + ' geometry: index buffer',\n size: this.geometry.indexBuffer.array.byteLength,\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\n })\n\n this.renderer.queueWriteBuffer(this.geometry.indexBuffer.buffer, 0, this.geometry.indexBuffer.array)\n }\n }\n }\n\n /**\n * Set our Mesh geometry: create buffers and add attributes to material\n */\n setGeometry() {\n if (this.geometry && this.renderer.ready) {\n this.createGeometryBuffers()\n this.setMaterialGeometryAttributes()\n }\n }\n\n /* MATERIAL */\n\n /**\n * Set or update the {@link RenderMaterial} {@link types/Materials.RenderMaterialRenderingOptions | rendering options} to match the {@link RenderPass#descriptor | RenderPass descriptor} used to draw this Mesh.\n * @param renderPass - {@link RenderPass | RenderPass} used to draw this Mesh, default to the {@link core/renderers/GPURenderer.GPURenderer#renderPass | renderer renderPass}.\n */\n setRenderingOptionsForRenderPass(renderPass: RenderPass) {\n // a Mesh render material rendering options MUST match the render pass descriptor used to draw it!\n const renderingOptions = {\n sampleCount: renderPass.options.sampleCount,\n // color attachments\n ...(renderPass.options.colorAttachments.length && {\n targetFormat: renderPass.options.colorAttachments[0].targetFormat,\n // multiple render targets?\n ...(renderPass.options.colorAttachments.length > 1 && {\n additionalTargets: renderPass.options.colorAttachments\n .filter((c, i) => i > 0)\n .map((colorAttachment) => {\n return {\n format: colorAttachment.targetFormat,\n }\n }),\n }),\n }),\n // depth\n depth: renderPass.options.useDepth,\n ...(renderPass.options.useDepth && {\n depthFormat: renderPass.options.depthFormat,\n }),\n }\n\n this.material?.setRenderingOptions(renderingOptions)\n }\n\n /**\n * Hook used to clean up parameters before sending them to the {@link RenderMaterial}.\n * @param parameters - parameters to clean before sending them to the {@link RenderMaterial}\n * @returns - cleaned parameters\n */\n cleanupRenderMaterialParameters(parameters: MeshBaseRenderParams): MeshBaseRenderParams {\n // patch and set options, return mesh parameters\n delete parameters.texturesOptions\n delete parameters.outputTarget\n delete parameters.autoRender\n\n return parameters\n }\n\n /**\n * Set a Mesh transparent property, then set its material\n * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters}\n */\n setMaterial(meshParameters: RenderMaterialParams) {\n this.transparent = meshParameters.transparent\n\n this.setShaders()\n\n this.material = new RenderMaterial(this.renderer, meshParameters)\n // add eventual textures passed as parameters\n this.material.options.textures\n ?.filter((texture) => texture instanceof Texture)\n .forEach((texture) => this.onTextureAdded(texture))\n }\n\n /**\n * Set Mesh material attributes\n */\n setMaterialGeometryAttributes() {\n if (this.material && !this.material.attributes) {\n this.material.setAttributesFromGeometry(this.geometry)\n }\n }\n\n /* TEXTURES */\n\n /**\n * Get our {@link RenderMaterial#textures | RenderMaterial textures array}\n * @readonly\n */\n get textures(): Texture[] {\n return this.material?.textures || []\n }\n\n /**\n * Get our {@link RenderMaterial#renderTextures | RenderMaterial render textures array}\n * @readonly\n */\n get renderTextures(): RenderTexture[] {\n return this.material?.renderTextures || []\n }\n\n /**\n * Create a new {@link Texture}\n * @param options - {@link TextureParams | Texture parameters}\n * @returns - newly created {@link Texture}\n */\n createTexture(options: TextureParams): Texture {\n if (!options.name) {\n options.name = 'texture' + this.textures.length\n }\n\n if (!options.label) {\n options.label = this.options.label + ' ' + options.name\n }\n\n const texture = new Texture(this.renderer, { ...options, ...this.options.texturesOptions })\n\n this.addTexture(texture)\n\n return texture\n }\n\n /**\n * Add a {@link Texture}\n * @param texture - {@link Texture} to add\n */\n addTexture(texture: Texture) {\n this.material.addTexture(texture)\n this.onTextureAdded(texture)\n }\n\n /**\n * Callback run when a new {@link Texture} has been added\n * @param texture - newly created Texture\n */\n onTextureAdded(texture: Texture) {\n texture.parentMesh = this as unknown as TextureParent\n }\n\n /**\n * Create a new {@link RenderTexture}\n * @param options - {@link RenderTextureParams | RenderTexture parameters}\n * @returns - newly created {@link RenderTexture}\n */\n createRenderTexture(options: RenderTextureParams): RenderTexture {\n if (!options.name) {\n options.name = 'renderTexture' + this.renderTextures.length\n }\n\n const renderTexture = new RenderTexture(this.renderer, options)\n\n this.addRenderTexture(renderTexture)\n\n return renderTexture\n }\n\n /**\n * Add a {@link RenderTexture}\n * @param renderTexture - {@link RenderTexture} to add\n */\n addRenderTexture(renderTexture: RenderTexture) {\n this.material.addTexture(renderTexture)\n }\n\n /* BINDINGS */\n\n /**\n * Get the current {@link RenderMaterial} uniforms\n * @readonly\n */\n get uniforms(): Material['uniforms'] {\n return this.material?.uniforms\n }\n\n /**\n * Get the current {@link RenderMaterial} storages\n * @readonly\n */\n get storages(): Material['storages'] {\n return this.material?.storages\n }\n\n /* RESIZE */\n\n /**\n * Resize the Mesh's textures\n * @param boundingRect\n */\n resize(boundingRect?: DOMElementBoundingRect | null) {\n // @ts-ignore\n if (super.resize) {\n // @ts-ignore\n super.resize(boundingRect)\n }\n\n this.renderTextures?.forEach((renderTexture) => {\n // copy from original textures again if needed\n if (renderTexture.options.fromTexture) {\n renderTexture.copy(renderTexture.options.fromTexture)\n }\n })\n\n // resize textures\n this.textures?.forEach((texture) => {\n texture.resize()\n })\n\n this._onAfterResizeCallback && this._onAfterResizeCallback()\n }\n\n /* EVENTS */\n\n /**\n * Assign a callback function to _onReadyCallback\n * @param callback - callback to run when {@link MeshBase} is ready\n * @returns - our Mesh\n */\n onReady(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onReadyCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onBeforeRenderCallback\n * @param callback - callback to run just before {@link MeshBase} will be rendered\n * @returns - our Mesh\n */\n onBeforeRender(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onBeforeRenderCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onRenderCallback\n * @param callback - callback to run when {@link MeshBase} is rendered\n * @returns - our Mesh\n */\n onRender(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onRenderCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onAfterRenderCallback\n * @param callback - callback to run just after {@link MeshBase} has been rendered\n * @returns - our Mesh\n */\n onAfterRender(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onAfterRenderCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onAfterResizeCallback\n * @param callback - callback to run just after {@link MeshBase} has been resized\n * @returns - our Mesh\n */\n onAfterResize(callback: () => void): MeshBase | ProjectedMeshBaseClass {\n if (callback) {\n this._onAfterResizeCallback = callback\n }\n\n return this\n }\n\n /* RENDER */\n\n /**\n * Called before rendering the Mesh\n * Set the geometry if needed (create buffers and add attributes to the {@link RenderMaterial})\n * Then executes {@link RenderMaterial#onBeforeRender}: create its bind groups and pipeline if needed and eventually update its struct\n */\n onBeforeRenderPass() {\n if (!this.renderer.ready) return\n\n if (this.material && this.material.ready && this.geometry && this.geometry.ready && !this.ready) {\n this.ready = true\n }\n\n this.setGeometry()\n\n this._onBeforeRenderCallback && this._onBeforeRenderCallback()\n\n this.material.onBeforeRender()\n }\n\n /**\n * Render our {@link MeshBase} if the {@link RenderMaterial} is ready\n * @param pass - current render pass encoder\n */\n onRenderPass(pass: GPURenderPassEncoder) {\n if (!this.material.ready) return\n\n this._onRenderCallback && this._onRenderCallback()\n\n // render ou material\n this.material.render(pass)\n // then render our geometry\n this.geometry.render(pass)\n }\n\n /**\n * Called after having rendered the Mesh\n */\n onAfterRenderPass() {\n this._onAfterRenderCallback && this._onAfterRenderCallback()\n }\n\n /**\n * Render our Mesh\n * - Execute {@link onBeforeRenderPass}\n * - Stop here if {@link Renderer} is not ready or Mesh is not {@link visible}\n * - Execute super render call if it exists\n * - {@link onRenderPass | render} our {@link material} and {@link geometry}\n * - Execute {@link onAfterRenderPass}\n * @param pass - current render pass encoder\n */\n render(pass: GPURenderPassEncoder) {\n this.onBeforeRenderPass()\n\n // no point to render if the WebGPU device is not ready\n if (!this.renderer.ready || !this.visible) return\n\n // @ts-ignore\n if (super.render) {\n // @ts-ignore\n super.render()\n }\n\n !this.renderer.production && pass.pushDebugGroup(this.options.label)\n\n this.onRenderPass(pass)\n\n !this.renderer.production && pass.popDebugGroup()\n\n this.onAfterRenderPass()\n }\n\n /* DESTROY */\n\n /**\n * Remove the Mesh from the {@link core/scenes/Scene.Scene | Scene} and destroy it\n */\n remove() {\n this.removeFromScene()\n this.destroy()\n\n // if the renderer does not contain any meshes any more\n // clear it\n if (!this.renderer.meshes.length) {\n this.renderer.onBeforeRenderScene.add(\n (commandEncoder) => {\n this.renderer.forceClear(commandEncoder)\n },\n { once: true }\n )\n }\n }\n\n /**\n * Destroy the Mesh\n */\n destroy() {\n // @ts-ignore\n if (super.destroy) {\n // @ts-ignore\n super.destroy()\n }\n\n this.material?.destroy()\n\n // remove geometry buffers from device cache\n this.geometry.vertexBuffers.forEach((vertexBuffer) => {\n // use original vertex buffer label in case it has been swapped (usually by a compute pass)\n this.renderer.removeBuffer(\n vertexBuffer.buffer,\n this.options.label + ' geometry: ' + vertexBuffer.name + ' buffer'\n )\n })\n\n if ('indexBuffer' in this.geometry) {\n this.renderer.removeBuffer(this.geometry.indexBuffer.buffer)\n }\n\n this.geometry?.destroy()\n }\n }\n}\n\nexport { MeshBaseMixin }\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/esm/core/meshes/mixins/ProjectedMeshBaseMixin.mjs b/dist/esm/core/meshes/mixins/ProjectedMeshBaseMixin.mjs index 5b928f989..56e769abf 100644 --- a/dist/esm/core/meshes/mixins/ProjectedMeshBaseMixin.mjs +++ b/dist/esm/core/meshes/mixins/ProjectedMeshBaseMixin.mjs @@ -115,12 +115,22 @@ function ProjectedMeshBaseMixin(Base) { this.domFrustum.shouldUpdate = this.frustumCulled; } /* MATERIAL */ + /** + * Hook used to clean up parameters before sending them to the material. + * @param parameters - parameters to clean before sending them to the {@link core/materials/RenderMaterial.RenderMaterial | RenderMaterial} + * @returns - cleaned parameters + */ + cleanupRenderMaterialParameters(parameters) { + delete parameters.frustumCulled; + delete parameters.DOMFrustumMargins; + super.cleanupRenderMaterialParameters(parameters); + return parameters; + } /** * Set a Mesh matrices uniforms inputs then call {@link MeshBaseClass} super method - * @param meshParameters - {@link ProjectedRenderMaterialParams | RenderMaterial parameters} + * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters} */ setMaterial(meshParameters) { - const { frustumCulled, DOMFrustumMargins, ...materialParameters } = meshParameters; const matricesUniforms = { label: "Matrices", struct: { @@ -147,10 +157,10 @@ function ProjectedMeshBaseMixin(Base) { } } }; - if (!materialParameters.uniforms) - materialParameters.uniforms = {}; - materialParameters.uniforms.matrices = matricesUniforms; - super.setMaterial(materialParameters); + if (!meshParameters.uniforms) + meshParameters.uniforms = {}; + meshParameters.uniforms.matrices = matricesUniforms; + super.setMaterial(meshParameters); } /* SIZE & TRANSFORMS */ /** diff --git a/dist/esm/core/meshes/mixins/ProjectedMeshBaseMixin.mjs.map b/dist/esm/core/meshes/mixins/ProjectedMeshBaseMixin.mjs.map index 64bfe285a..6e7d8944b 100644 --- a/dist/esm/core/meshes/mixins/ProjectedMeshBaseMixin.mjs.map +++ b/dist/esm/core/meshes/mixins/ProjectedMeshBaseMixin.mjs.map @@ -1 +1 @@ -{"version":3,"file":"ProjectedMeshBaseMixin.mjs","sources":["../../../../../src/core/meshes/mixins/ProjectedMeshBaseMixin.ts"],"sourcesContent":["import { CameraRenderer, isCameraRenderer } from '../../renderers/utils'\nimport { DOMFrustum } from '../../DOM/DOMFrustum'\nimport { MeshBaseClass, MeshBaseMixin, MeshBaseOptions, MeshBaseParams, MixinConstructor } from './MeshBaseMixin'\nimport { GPUCurtains } from '../../../curtains/GPUCurtains'\nimport { DOMElementBoundingRect, RectCoords } from '../../DOM/DOMElement'\nimport { RenderMaterialParams, ShaderOptions } from '../../../types/Materials'\nimport { ProjectedObject3D } from '../../objects3D/ProjectedObject3D'\nimport { DOMObject3D } from '../../../curtains/objects3D/DOMObject3D'\nimport default_projected_vsWgsl from '../../shaders/chunks/default_projected_vs.wgsl'\nimport default_normal_fsWgsl from '../../shaders/chunks/default_normal_fs.wgsl'\n\n/**\n * Base parameters used to create a ProjectedMesh\n */\nexport interface ProjectedMeshBaseParams {\n /** Whether this ProjectedMesh should be frustum culled (not drawn when outside of {@link CameraRenderer#camera | camera} frustum) */\n frustumCulled?: boolean\n /** Margins (in pixels) to applied to the {@link ProjectedMeshBaseClass#domFrustum | DOM Frustum} to determine if this ProjectedMesh should be frustum culled or not */\n DOMFrustumMargins?: RectCoords\n}\n\n/** Parameters used to create a ProjectedMesh */\nexport interface ProjectedMeshParameters extends MeshBaseParams, ProjectedMeshBaseParams {}\n\nexport interface ProjectedRenderMaterialParams extends RenderMaterialParams, ProjectedMeshBaseParams {}\n\n/** @const - Default ProjectedMesh parameters to merge with user defined parameters */\nconst defaultProjectedMeshParams: ProjectedMeshBaseParams = {\n // frustum culling and visibility\n frustumCulled: true,\n DOMFrustumMargins: {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n },\n}\n\n/** Base options used to create this ProjectedMesh */\nexport interface ProjectedMeshBaseOptions extends MeshBaseOptions, Partial {}\n\n/**\n * This class describes the properties and methods to set up a Projected Mesh (i.e. a basic {@link MeshBaseClass | Mesh} with {@link ProjectedObject3D} transformations matrices and a {@link core/camera/Camera.Camera | Camera} to use for projection), implemented in the {@link ProjectedMeshBaseMixin}:\n * - Handle the frustum culling (check if the {@link ProjectedObject3D} currently lies inside the {@link core/camera/Camera.Camera | Camera} frustum)\n * - Add callbacks for when the Mesh enters or leaves the {@link core/camera/Camera.Camera | Camera} frustum\n */\nexport declare class ProjectedMeshBaseClass extends MeshBaseClass {\n /** The {@link CameraRenderer} used */\n renderer: CameraRenderer\n /** The ProjectedMesh {@link DOMFrustum} class object */\n domFrustum: DOMFrustum\n /** Whether this ProjectedMesh should be frustum culled (not drawn when outside of {@link CameraRenderer#camera | camera} frustum) */\n frustumCulled: boolean\n /** Margins (in pixels) to applied to the {@link ProjectedMeshBaseClass#domFrustum | DOM Frustum} to determine if this ProjectedMesh should be frustum culled or not */\n DOMFrustumMargins: RectCoords\n\n // callbacks\n /** function assigned to the {@link onReEnterView} callback */\n _onReEnterViewCallback: () => void\n /** function assigned to the {@link onLeaveView} callback */\n _onLeaveViewCallback: () => void\n\n /**\n * {@link ProjectedMeshBaseClass} constructor\n * @param renderer - our {@link CameraRenderer} class object\n * @param element - a DOM HTML Element that can be bound to a Mesh\n * @param parameters - {@link ProjectedMeshParameters | Projected Mesh base parameters}\n */\n constructor(renderer: CameraRenderer, element: HTMLElement | null, parameters: ProjectedMeshParameters)\n\n /**\n * Set default shaders if one or both of them are missing\n */\n setShaders(): void\n\n /**\n * Override {@link MeshBaseClass} method to add the domFrustum\n */\n computeGeometry(): void\n\n /**\n * Set a Mesh matrices uniforms inputs then call {@link MeshBaseClass} super method\n * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters}\n */\n setMaterial(meshParameters: ProjectedRenderMaterialParams): void\n\n /**\n * Resize our Mesh\n * @param boundingRect - the new bounding rectangle\n */\n resize(boundingRect: DOMElementBoundingRect | null): void\n\n /**\n * Apply scale and resize textures\n */\n applyScale(): void\n\n /**\n * Get our {@link DOMFrustum} projected bounding rectangle\n * @readonly\n */\n get projectedBoundingRect(): DOMElementBoundingRect\n\n /**\n * At least one of the matrix has been updated, update according uniforms and frustum\n */\n onAfterMatrixStackUpdate(): void\n\n /**\n * Assign a callback function to _onReEnterViewCallback\n * @param callback - callback to run when {@link ProjectedMeshBaseClass} is reentering the view frustum\n * @returns - our Mesh\n */\n onReEnterView: (callback: () => void) => ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onLeaveViewCallback\n * @param callback - callback to run when {@link ProjectedMeshBaseClass} is leaving the view frustum\n * @returns - our Mesh\n */\n onLeaveView: (callback: () => void) => ProjectedMeshBaseClass\n\n /**\n * Called before rendering the Mesh to update matrices and {@link DOMFrustum}.\n * First, we update our matrices to have fresh results. It eventually calls onAfterMatrixStackUpdate() if at least one matrix has been updated.\n * Then we check if we need to update the {@link DOMFrustum} projected bounding rectangle.\n * Finally we call {@link MeshBaseClass#onBeforeRenderPass | Mesh base onBeforeRenderPass} super\n */\n onBeforeRenderPass(): void\n\n /**\n * Only render the Mesh if it is in view frustum.\n * Since render() is actually called before onRenderPass(), we are sure to have fresh frustum bounding rectangle values here.\n * @param pass - current render pass\n */\n onRenderPass(pass: GPURenderPassEncoder): void\n}\n\n/**\n * Used to add the properties and methods defined in {@link ProjectedMeshBaseClass} to the {@link MeshBaseClass} and mix it with a given Base of type {@link ProjectedObject3D} or {@link DOMObject3D}.\n * @exports\n * @param Base - the class to mix onto, should be of {@link ProjectedObject3D} or {@link DOMObject3D} type\n * @returns - the mixed classes, creating a Projected Mesh.\n */\nfunction ProjectedMeshBaseMixin>(\n Base: TBase\n): MixinConstructor & TBase {\n /**\n * ProjectedMeshBase defines our base properties and methods\n */\n return class ProjectedMeshBase extends MeshBaseMixin(Base) {\n /** The {@link CameraRenderer} used */\n renderer: CameraRenderer\n /** The ProjectedMesh {@link DOMFrustum} class object */\n domFrustum: DOMFrustum\n /** Whether this ProjectedMesh should be frustum culled (not drawn when outside of {@link CameraRenderer#camera | camera} frustum) */\n frustumCulled: boolean\n /** Margins (in pixels) to applied to the {@link ProjectedMeshBaseClass#domFrustum | DOM Frustum} to determine if this ProjectedMesh should be frustum culled or not */\n DOMFrustumMargins: RectCoords\n\n /** Options used to create this {@link ProjectedMeshBaseClass} */\n options: ProjectedMeshBaseOptions\n\n // callbacks / events\n /** function assigned to the {@link onReEnterView} callback */\n _onReEnterViewCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onLeaveView} callback */\n _onLeaveViewCallback: () => void = () => {\n /* allow empty callback */\n }\n\n /**\n * ProjectedMeshBase constructor\n *\n * @typedef MeshBaseArrayParams\n * @type {array}\n * @property {(CameraRenderer|GPUCurtains)} 0 - our renderer class object\n * @property {(string|HTMLElement|null)} 1 - the DOM HTML Element that can be bound to a Mesh\n * @property {ProjectedMeshParameters} 2 - Projected Mesh parameters\n *\n * @param {MeshBaseArrayParams} params - our MeshBaseMixin parameters\n */\n constructor(...params: any[]) {\n super(\n params[0] as CameraRenderer | GPUCurtains,\n params[1] as HTMLElement | string,\n { ...defaultProjectedMeshParams, ...params[2], ...{ useProjection: true } } as ProjectedMeshParameters\n )\n\n let renderer = params[0]\n\n // force this mesh to use projection!\n const parameters = {\n ...defaultProjectedMeshParams,\n ...params[2],\n ...{ useProjection: true },\n } as ProjectedMeshParameters\n\n this.type = 'MeshTransformed'\n\n // we could pass our curtains object OR our curtains renderer object\n renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as CameraRenderer)\n\n isCameraRenderer(renderer, parameters.label ? parameters.label + ' ' + this.type : this.type)\n\n this.renderer = renderer\n\n const { geometry, frustumCulled, DOMFrustumMargins } = parameters\n\n this.options = {\n ...(this.options ?? {}), // merge possible lower options?\n frustumCulled,\n DOMFrustumMargins,\n }\n\n this.setDOMFrustum()\n\n // explicitly needed for DOM Frustum\n this.geometry = geometry\n\n // tell the model and projection matrices to update right away\n this.shouldUpdateMatrixStack()\n }\n\n /* SHADERS */\n\n /**\n * Set default shaders if one or both of them are missing\n */\n setShaders() {\n let { shaders } = this.options\n\n if (!shaders) {\n shaders = {\n vertex: {\n code: default_projected_vsWgsl,\n entryPoint: 'main',\n },\n fragment: {\n code: default_normal_fsWgsl,\n entryPoint: 'main',\n },\n }\n } else {\n if (!shaders.vertex || !shaders.vertex.code) {\n shaders.vertex = {\n code: default_projected_vsWgsl,\n entryPoint: 'main',\n }\n }\n\n if (shaders.fragment === undefined || (shaders.fragment && !(shaders.fragment as ShaderOptions).code)) {\n shaders.fragment = {\n code: default_normal_fsWgsl,\n entryPoint: 'main',\n }\n }\n }\n }\n\n /* GEOMETRY */\n\n /**\n * Set the Mesh frustum culling\n */\n setDOMFrustum() {\n this.domFrustum = new DOMFrustum({\n boundingBox: this.geometry.boundingBox,\n modelViewProjectionMatrix: this.modelViewProjectionMatrix,\n containerBoundingRect: this.renderer.boundingRect,\n DOMFrustumMargins: this.options.DOMFrustumMargins,\n onReEnterView: () => {\n this._onReEnterViewCallback && this._onReEnterViewCallback()\n },\n onLeaveView: () => {\n this._onLeaveViewCallback && this._onLeaveViewCallback()\n },\n })\n\n this.DOMFrustumMargins = this.domFrustum.DOMFrustumMargins\n this.frustumCulled = this.options.frustumCulled\n this.domFrustum.shouldUpdate = this.frustumCulled\n }\n\n /* MATERIAL */\n\n /**\n * Set a Mesh matrices uniforms inputs then call {@link MeshBaseClass} super method\n * @param meshParameters - {@link ProjectedRenderMaterialParams | RenderMaterial parameters}\n */\n setMaterial(meshParameters: ProjectedRenderMaterialParams) {\n const { frustumCulled, DOMFrustumMargins, ...materialParameters } = meshParameters\n\n // add matrices uniforms\n const matricesUniforms = {\n label: 'Matrices',\n struct: {\n model: {\n name: 'model',\n type: 'mat4x4f',\n value: this.modelMatrix,\n },\n world: {\n name: 'world',\n type: 'mat4x4f',\n value: this.worldMatrix,\n },\n modelView: {\n // model view matrix (world matrix multiplied by camera view matrix)\n name: 'modelView',\n type: 'mat4x4f',\n value: this.modelViewMatrix,\n },\n modelViewProjection: {\n name: 'modelViewProjection',\n type: 'mat4x4f',\n value: this.modelViewProjectionMatrix,\n },\n },\n }\n\n if (!materialParameters.uniforms) materialParameters.uniforms = {}\n materialParameters.uniforms.matrices = matricesUniforms\n\n super.setMaterial(materialParameters)\n }\n\n /* SIZE & TRANSFORMS */\n\n /**\n * Resize our {@link ProjectedMeshBaseClass}\n * @param boundingRect - the new bounding rectangle\n */\n resize(boundingRect?: DOMElementBoundingRect | null) {\n if (this.domFrustum) this.domFrustum.setContainerBoundingRect(this.renderer.boundingRect)\n\n super.resize(boundingRect)\n }\n\n /**\n * Apply scale and resize textures\n */\n applyScale() {\n super.applyScale()\n\n // resize textures on scale change!\n this.textures.forEach((texture) => texture.resize())\n }\n\n /**\n * Get our {@link DOMFrustum} projected bounding rectangle\n * @readonly\n */\n get projectedBoundingRect(): DOMElementBoundingRect {\n return this.domFrustum?.projectedBoundingRect\n }\n\n /**\n * At least one of the matrix has been updated, update according uniforms and frustum\n */\n onAfterMatrixStackUpdate() {\n if (this.material) {\n this.material.shouldUpdateInputsBindings('matrices')\n }\n\n if (this.domFrustum) this.domFrustum.shouldUpdate = true\n }\n\n /* EVENTS */\n\n /**\n * Assign a callback function to _onReEnterViewCallback\n * @param callback - callback to run when {@link ProjectedMeshBaseClass} is reentering the view frustum\n * @returns - our Mesh\n */\n onReEnterView(callback: () => void): ProjectedMeshBaseClass {\n if (callback) {\n this._onReEnterViewCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onLeaveViewCallback\n * @param callback - callback to run when {@link ProjectedMeshBaseClass} is leaving the view frustum\n * @returns - our Mesh\n */\n onLeaveView(callback: () => void): ProjectedMeshBaseClass {\n if (callback) {\n this._onLeaveViewCallback = callback\n }\n\n return this\n }\n\n /* RENDER */\n\n /**\n * Called before rendering the Mesh to update matrices and {@link DOMFrustum}.\n * First, we update our matrices to have fresh results. It eventually calls onAfterMatrixStackUpdate() if at least one matrix has been updated.\n * Then we check if we need to update the {@link DOMFrustum} projected bounding rectangle.\n * Finally we call {@link MeshBaseClass#onBeforeRenderPass | Mesh base onBeforeRenderPass} super\n */\n onBeforeRenderPass() {\n this.updateMatrixStack()\n\n if (this.domFrustum && this.domFrustum.shouldUpdate && this.frustumCulled) {\n this.domFrustum.computeProjectedToDocumentCoords()\n this.domFrustum.shouldUpdate = false\n }\n\n super.onBeforeRenderPass()\n }\n\n /**\n * Only render the Mesh if it is in view frustum.\n * Since render() is actually called before onRenderPass(), we are sure to have fresh frustum bounding rectangle values here.\n * @param pass - current render pass\n */\n onRenderPass(pass: GPURenderPassEncoder) {\n if (!this.material.ready) return\n\n this._onRenderCallback && this._onRenderCallback()\n\n if ((this.domFrustum && this.domFrustum.isIntersecting) || !this.frustumCulled) {\n // render ou material\n this.material.render(pass)\n // then render our geometry\n this.geometry.render(pass)\n }\n }\n }\n}\n\nexport { ProjectedMeshBaseMixin }\n"],"names":[],"mappings":";;;;;;AA2BA,MAAM,0BAAsD,GAAA;AAAA;AAAA,EAE1D,aAAe,EAAA,IAAA;AAAA,EACf,iBAAmB,EAAA;AAAA,IACjB,GAAK,EAAA,CAAA;AAAA,IACL,KAAO,EAAA,CAAA;AAAA,IACP,MAAQ,EAAA,CAAA;AAAA,IACR,IAAM,EAAA,CAAA;AAAA,GACR;AACF,CAAA,CAAA;AA2GA,SAAS,uBACP,IACkD,EAAA;AAIlD,EAAA,OAAO,MAAM,iBAAA,SAA0B,aAAc,CAAA,IAAI,CAAE,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkCzD,eAAe,MAAe,EAAA;AAC5B,MAAA,KAAA;AAAA,QACE,OAAO,CAAC,CAAA;AAAA,QACR,OAAO,CAAC,CAAA;AAAA,QACR,EAAE,GAAG,0BAAA,EAA4B,GAAG,MAAA,CAAO,CAAC,CAAA,EAAG,GAAG,EAAE,aAAe,EAAA,IAAA,EAAO,EAAA;AAAA,OAC5E,CAAA;AAxBF;AAAA;AAAA,MAAA,IAAA,CAAA,sBAAA,GAAqC,MAAM;AAAA,OAE3C,CAAA;AAEA;AAAA,MAAA,IAAA,CAAA,oBAAA,GAAmC,MAAM;AAAA,OAEzC,CAAA;AAoBE,MAAI,IAAA,QAAA,GAAW,OAAO,CAAC,CAAA,CAAA;AAGvB,MAAA,MAAM,UAAa,GAAA;AAAA,QACjB,GAAG,0BAAA;AAAA,QACH,GAAG,OAAO,CAAC,CAAA;AAAA,QACX,GAAG,EAAE,aAAA,EAAe,IAAK,EAAA;AAAA,OAC3B,CAAA;AAEA,MAAA,IAAA,CAAK,IAAO,GAAA,iBAAA,CAAA;AAGZ,MAAY,QAAA,GAAA,QAAA,IAAa,SAAyB,QAAc,IAAA,QAAA,CAAA;AAEhE,MAAiB,gBAAA,CAAA,QAAA,EAAU,WAAW,KAAQ,GAAA,UAAA,CAAW,QAAQ,GAAM,GAAA,IAAA,CAAK,IAAO,GAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAE5F,MAAA,IAAA,CAAK,QAAW,GAAA,QAAA,CAAA;AAEhB,MAAA,MAAM,EAAE,QAAA,EAAU,aAAe,EAAA,iBAAA,EAAsB,GAAA,UAAA,CAAA;AAEvD,MAAA,IAAA,CAAK,OAAU,GAAA;AAAA,QACb,GAAI,IAAK,CAAA,OAAA,IAAW,EAAC;AAAA;AAAA,QACrB,aAAA;AAAA,QACA,iBAAA;AAAA,OACF,CAAA;AAEA,MAAA,IAAA,CAAK,aAAc,EAAA,CAAA;AAGnB,MAAA,IAAA,CAAK,QAAW,GAAA,QAAA,CAAA;AAGhB,MAAA,IAAA,CAAK,uBAAwB,EAAA,CAAA;AAAA,KAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,UAAa,GAAA;AACX,MAAI,IAAA,EAAE,OAAQ,EAAA,GAAI,IAAK,CAAA,OAAA,CAAA;AAEvB,MAAA,IAAI,CAAC,OAAS,EAAA;AACZ,QAAU,OAAA,GAAA;AAAA,UACR,MAAQ,EAAA;AAAA,YACN,IAAM,EAAA,wBAAA;AAAA,YACN,UAAY,EAAA,MAAA;AAAA,WACd;AAAA,UACA,QAAU,EAAA;AAAA,YACR,IAAM,EAAA,qBAAA;AAAA,YACN,UAAY,EAAA,MAAA;AAAA,WACd;AAAA,SACF,CAAA;AAAA,OACK,MAAA;AACL,QAAA,IAAI,CAAC,OAAQ,CAAA,MAAA,IAAU,CAAC,OAAA,CAAQ,OAAO,IAAM,EAAA;AAC3C,UAAA,OAAA,CAAQ,MAAS,GAAA;AAAA,YACf,IAAM,EAAA,wBAAA;AAAA,YACN,UAAY,EAAA,MAAA;AAAA,WACd,CAAA;AAAA,SACF;AAEA,QAAI,IAAA,OAAA,CAAQ,aAAa,KAAc,CAAA,IAAA,OAAA,CAAQ,YAAY,CAAE,OAAA,CAAQ,SAA2B,IAAO,EAAA;AACrG,UAAA,OAAA,CAAQ,QAAW,GAAA;AAAA,YACjB,IAAM,EAAA,qBAAA;AAAA,YACN,UAAY,EAAA,MAAA;AAAA,WACd,CAAA;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,aAAgB,GAAA;AACd,MAAK,IAAA,CAAA,UAAA,GAAa,IAAI,UAAW,CAAA;AAAA,QAC/B,WAAA,EAAa,KAAK,QAAS,CAAA,WAAA;AAAA,QAC3B,2BAA2B,IAAK,CAAA,yBAAA;AAAA,QAChC,qBAAA,EAAuB,KAAK,QAAS,CAAA,YAAA;AAAA,QACrC,iBAAA,EAAmB,KAAK,OAAQ,CAAA,iBAAA;AAAA,QAChC,eAAe,MAAM;AACnB,UAAK,IAAA,CAAA,sBAAA,IAA0B,KAAK,sBAAuB,EAAA,CAAA;AAAA,SAC7D;AAAA,QACA,aAAa,MAAM;AACjB,UAAK,IAAA,CAAA,oBAAA,IAAwB,KAAK,oBAAqB,EAAA,CAAA;AAAA,SACzD;AAAA,OACD,CAAA,CAAA;AAED,MAAK,IAAA,CAAA,iBAAA,GAAoB,KAAK,UAAW,CAAA,iBAAA,CAAA;AACzC,MAAK,IAAA,CAAA,aAAA,GAAgB,KAAK,OAAQ,CAAA,aAAA,CAAA;AAClC,MAAK,IAAA,CAAA,UAAA,CAAW,eAAe,IAAK,CAAA,aAAA,CAAA;AAAA,KACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,YAAY,cAA+C,EAAA;AACzD,MAAA,MAAM,EAAE,aAAA,EAAe,iBAAmB,EAAA,GAAG,oBAAuB,GAAA,cAAA,CAAA;AAGpE,MAAA,MAAM,gBAAmB,GAAA;AAAA,QACvB,KAAO,EAAA,UAAA;AAAA,QACP,MAAQ,EAAA;AAAA,UACN,KAAO,EAAA;AAAA,YACL,IAAM,EAAA,OAAA;AAAA,YACN,IAAM,EAAA,SAAA;AAAA,YACN,OAAO,IAAK,CAAA,WAAA;AAAA,WACd;AAAA,UACA,KAAO,EAAA;AAAA,YACL,IAAM,EAAA,OAAA;AAAA,YACN,IAAM,EAAA,SAAA;AAAA,YACN,OAAO,IAAK,CAAA,WAAA;AAAA,WACd;AAAA,UACA,SAAW,EAAA;AAAA;AAAA,YAET,IAAM,EAAA,WAAA;AAAA,YACN,IAAM,EAAA,SAAA;AAAA,YACN,OAAO,IAAK,CAAA,eAAA;AAAA,WACd;AAAA,UACA,mBAAqB,EAAA;AAAA,YACnB,IAAM,EAAA,qBAAA;AAAA,YACN,IAAM,EAAA,SAAA;AAAA,YACN,OAAO,IAAK,CAAA,yBAAA;AAAA,WACd;AAAA,SACF;AAAA,OACF,CAAA;AAEA,MAAA,IAAI,CAAC,kBAAmB,CAAA,QAAA;AAAU,QAAA,kBAAA,CAAmB,WAAW,EAAC,CAAA;AACjE,MAAA,kBAAA,CAAmB,SAAS,QAAW,GAAA,gBAAA,CAAA;AAEvC,MAAA,KAAA,CAAM,YAAY,kBAAkB,CAAA,CAAA;AAAA,KACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,OAAO,YAA8C,EAAA;AACnD,MAAA,IAAI,IAAK,CAAA,UAAA;AAAY,QAAA,IAAA,CAAK,UAAW,CAAA,wBAAA,CAAyB,IAAK,CAAA,QAAA,CAAS,YAAY,CAAA,CAAA;AAExF,MAAA,KAAA,CAAM,OAAO,YAAY,CAAA,CAAA;AAAA,KAC3B;AAAA;AAAA;AAAA;AAAA,IAKA,UAAa,GAAA;AACX,MAAA,KAAA,CAAM,UAAW,EAAA,CAAA;AAGjB,MAAA,IAAA,CAAK,SAAS,OAAQ,CAAA,CAAC,OAAY,KAAA,OAAA,CAAQ,QAAQ,CAAA,CAAA;AAAA,KACrD;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,IAAI,qBAAgD,GAAA;AAClD,MAAA,OAAO,KAAK,UAAY,EAAA,qBAAA,CAAA;AAAA,KAC1B;AAAA;AAAA;AAAA;AAAA,IAKA,wBAA2B,GAAA;AACzB,MAAA,IAAI,KAAK,QAAU,EAAA;AACjB,QAAK,IAAA,CAAA,QAAA,CAAS,2BAA2B,UAAU,CAAA,CAAA;AAAA,OACrD;AAEA,MAAA,IAAI,IAAK,CAAA,UAAA;AAAY,QAAA,IAAA,CAAK,WAAW,YAAe,GAAA,IAAA,CAAA;AAAA,KACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,cAAc,QAA8C,EAAA;AAC1D,MAAA,IAAI,QAAU,EAAA;AACZ,QAAA,IAAA,CAAK,sBAAyB,GAAA,QAAA,CAAA;AAAA,OAChC;AAEA,MAAO,OAAA,IAAA,CAAA;AAAA,KACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,YAAY,QAA8C,EAAA;AACxD,MAAA,IAAI,QAAU,EAAA;AACZ,QAAA,IAAA,CAAK,oBAAuB,GAAA,QAAA,CAAA;AAAA,OAC9B;AAEA,MAAO,OAAA,IAAA,CAAA;AAAA,KACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,kBAAqB,GAAA;AACnB,MAAA,IAAA,CAAK,iBAAkB,EAAA,CAAA;AAEvB,MAAA,IAAI,KAAK,UAAc,IAAA,IAAA,CAAK,UAAW,CAAA,YAAA,IAAgB,KAAK,aAAe,EAAA;AACzE,QAAA,IAAA,CAAK,WAAW,gCAAiC,EAAA,CAAA;AACjD,QAAA,IAAA,CAAK,WAAW,YAAe,GAAA,KAAA,CAAA;AAAA,OACjC;AAEA,MAAA,KAAA,CAAM,kBAAmB,EAAA,CAAA;AAAA,KAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,aAAa,IAA4B,EAAA;AACvC,MAAI,IAAA,CAAC,KAAK,QAAS,CAAA,KAAA;AAAO,QAAA,OAAA;AAE1B,MAAK,IAAA,CAAA,iBAAA,IAAqB,KAAK,iBAAkB,EAAA,CAAA;AAEjD,MAAA,IAAK,KAAK,UAAc,IAAA,IAAA,CAAK,WAAW,cAAmB,IAAA,CAAC,KAAK,aAAe,EAAA;AAE9E,QAAK,IAAA,CAAA,QAAA,CAAS,OAAO,IAAI,CAAA,CAAA;AAEzB,QAAK,IAAA,CAAA,QAAA,CAAS,OAAO,IAAI,CAAA,CAAA;AAAA,OAC3B;AAAA,KACF;AAAA,GACF,CAAA;AACF;;;;"} \ No newline at end of file +{"version":3,"file":"ProjectedMeshBaseMixin.mjs","sources":["../../../../../src/core/meshes/mixins/ProjectedMeshBaseMixin.ts"],"sourcesContent":["import { CameraRenderer, isCameraRenderer } from '../../renderers/utils'\nimport { DOMFrustum } from '../../DOM/DOMFrustum'\nimport {\n MeshBaseClass,\n MeshBaseMixin,\n MeshBaseOptions,\n MeshBaseParams,\n MeshBaseRenderParams,\n MixinConstructor,\n} from './MeshBaseMixin'\nimport { GPUCurtains } from '../../../curtains/GPUCurtains'\nimport { DOMElementBoundingRect, RectCoords } from '../../DOM/DOMElement'\nimport { RenderMaterialParams, ShaderOptions } from '../../../types/Materials'\nimport { ProjectedObject3D } from '../../objects3D/ProjectedObject3D'\nimport { DOMObject3D } from '../../../curtains/objects3D/DOMObject3D'\nimport default_projected_vsWgsl from '../../shaders/chunks/default_projected_vs.wgsl'\nimport default_normal_fsWgsl from '../../shaders/chunks/default_normal_fs.wgsl'\nimport { ShaderPassParams } from '../../renderPasses/ShaderPass'\n\n/**\n * Base parameters used to create a ProjectedMesh\n */\nexport interface ProjectedMeshBaseParams {\n /** Whether this ProjectedMesh should be frustum culled (not drawn when outside of {@link CameraRenderer#camera | camera} frustum) */\n frustumCulled?: boolean\n /** Margins (in pixels) to applied to the {@link ProjectedMeshBaseClass#domFrustum | DOM Frustum} to determine if this ProjectedMesh should be frustum culled or not */\n DOMFrustumMargins?: RectCoords\n}\n\n/** Parameters used to create a ProjectedMesh */\nexport interface ProjectedMeshParameters extends MeshBaseParams, ProjectedMeshBaseParams {}\n\nexport interface ProjectedRenderMaterialParams extends RenderMaterialParams, ProjectedMeshBaseParams {}\n\n/** @const - Default ProjectedMesh parameters to merge with user defined parameters */\nconst defaultProjectedMeshParams: ProjectedMeshBaseParams = {\n // frustum culling and visibility\n frustumCulled: true,\n DOMFrustumMargins: {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n },\n}\n\n/** Base options used to create this ProjectedMesh */\nexport interface ProjectedMeshBaseOptions extends MeshBaseOptions, Partial {}\n\n/**\n * This class describes the properties and methods to set up a Projected Mesh (i.e. a basic {@link MeshBaseClass | Mesh} with {@link ProjectedObject3D} transformations matrices and a {@link core/camera/Camera.Camera | Camera} to use for projection), implemented in the {@link ProjectedMeshBaseMixin}:\n * - Handle the frustum culling (check if the {@link ProjectedObject3D} currently lies inside the {@link core/camera/Camera.Camera | Camera} frustum)\n * - Add callbacks for when the Mesh enters or leaves the {@link core/camera/Camera.Camera | Camera} frustum\n */\nexport declare class ProjectedMeshBaseClass extends MeshBaseClass {\n /** The {@link CameraRenderer} used */\n renderer: CameraRenderer\n /** The ProjectedMesh {@link DOMFrustum} class object */\n domFrustum: DOMFrustum\n /** Whether this ProjectedMesh should be frustum culled (not drawn when outside of {@link CameraRenderer#camera | camera} frustum) */\n frustumCulled: boolean\n /** Margins (in pixels) to applied to the {@link ProjectedMeshBaseClass#domFrustum | DOM Frustum} to determine if this ProjectedMesh should be frustum culled or not */\n DOMFrustumMargins: RectCoords\n\n // callbacks\n /** function assigned to the {@link onReEnterView} callback */\n _onReEnterViewCallback: () => void\n /** function assigned to the {@link onLeaveView} callback */\n _onLeaveViewCallback: () => void\n\n /**\n * {@link ProjectedMeshBaseClass} constructor\n * @param renderer - our {@link CameraRenderer} class object\n * @param element - a DOM HTML Element that can be bound to a Mesh\n * @param parameters - {@link ProjectedMeshParameters | Projected Mesh base parameters}\n */\n constructor(renderer: CameraRenderer, element: HTMLElement | null, parameters: ProjectedMeshParameters)\n\n /**\n * Set default shaders if one or both of them are missing\n */\n setShaders(): void\n\n /**\n * Override {@link MeshBaseClass} method to add the domFrustum\n */\n computeGeometry(): void\n\n /**\n * Set a Mesh matrices uniforms inputs then call {@link MeshBaseClass} super method\n * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters}\n */\n setMaterial(meshParameters: ProjectedRenderMaterialParams): void\n\n /**\n * Resize our Mesh\n * @param boundingRect - the new bounding rectangle\n */\n resize(boundingRect: DOMElementBoundingRect | null): void\n\n /**\n * Apply scale and resize textures\n */\n applyScale(): void\n\n /**\n * Get our {@link DOMFrustum} projected bounding rectangle\n * @readonly\n */\n get projectedBoundingRect(): DOMElementBoundingRect\n\n /**\n * At least one of the matrix has been updated, update according uniforms and frustum\n */\n onAfterMatrixStackUpdate(): void\n\n /**\n * Assign a callback function to _onReEnterViewCallback\n * @param callback - callback to run when {@link ProjectedMeshBaseClass} is reentering the view frustum\n * @returns - our Mesh\n */\n onReEnterView: (callback: () => void) => ProjectedMeshBaseClass\n /**\n * Assign a callback function to _onLeaveViewCallback\n * @param callback - callback to run when {@link ProjectedMeshBaseClass} is leaving the view frustum\n * @returns - our Mesh\n */\n onLeaveView: (callback: () => void) => ProjectedMeshBaseClass\n\n /**\n * Called before rendering the Mesh to update matrices and {@link DOMFrustum}.\n * First, we update our matrices to have fresh results. It eventually calls onAfterMatrixStackUpdate() if at least one matrix has been updated.\n * Then we check if we need to update the {@link DOMFrustum} projected bounding rectangle.\n * Finally we call {@link MeshBaseClass#onBeforeRenderPass | Mesh base onBeforeRenderPass} super\n */\n onBeforeRenderPass(): void\n\n /**\n * Only render the Mesh if it is in view frustum.\n * Since render() is actually called before onRenderPass(), we are sure to have fresh frustum bounding rectangle values here.\n * @param pass - current render pass\n */\n onRenderPass(pass: GPURenderPassEncoder): void\n}\n\n/**\n * Used to add the properties and methods defined in {@link ProjectedMeshBaseClass} to the {@link MeshBaseClass} and mix it with a given Base of type {@link ProjectedObject3D} or {@link DOMObject3D}.\n * @exports\n * @param Base - the class to mix onto, should be of {@link ProjectedObject3D} or {@link DOMObject3D} type\n * @returns - the mixed classes, creating a Projected Mesh.\n */\nfunction ProjectedMeshBaseMixin>(\n Base: TBase\n): MixinConstructor & TBase {\n /**\n * ProjectedMeshBase defines our base properties and methods\n */\n return class ProjectedMeshBase extends MeshBaseMixin(Base) {\n /** The {@link CameraRenderer} used */\n renderer: CameraRenderer\n /** The ProjectedMesh {@link DOMFrustum} class object */\n domFrustum: DOMFrustum\n /** Whether this ProjectedMesh should be frustum culled (not drawn when outside of {@link CameraRenderer#camera | camera} frustum) */\n frustumCulled: boolean\n /** Margins (in pixels) to applied to the {@link ProjectedMeshBaseClass#domFrustum | DOM Frustum} to determine if this ProjectedMesh should be frustum culled or not */\n DOMFrustumMargins: RectCoords\n\n /** Options used to create this {@link ProjectedMeshBaseClass} */\n options: ProjectedMeshBaseOptions\n\n // callbacks / events\n /** function assigned to the {@link onReEnterView} callback */\n _onReEnterViewCallback: () => void = () => {\n /* allow empty callback */\n }\n /** function assigned to the {@link onLeaveView} callback */\n _onLeaveViewCallback: () => void = () => {\n /* allow empty callback */\n }\n\n /**\n * ProjectedMeshBase constructor\n *\n * @typedef MeshBaseArrayParams\n * @type {array}\n * @property {(CameraRenderer|GPUCurtains)} 0 - our renderer class object\n * @property {(string|HTMLElement|null)} 1 - the DOM HTML Element that can be bound to a Mesh\n * @property {ProjectedMeshParameters} 2 - Projected Mesh parameters\n *\n * @param {MeshBaseArrayParams} params - our MeshBaseMixin parameters\n */\n constructor(...params: any[]) {\n super(\n params[0] as CameraRenderer | GPUCurtains,\n params[1] as HTMLElement | string,\n { ...defaultProjectedMeshParams, ...params[2], ...{ useProjection: true } } as ProjectedMeshParameters\n )\n\n let renderer = params[0]\n\n // force this mesh to use projection!\n const parameters = {\n ...defaultProjectedMeshParams,\n ...params[2],\n ...{ useProjection: true },\n } as ProjectedMeshParameters\n\n this.type = 'MeshTransformed'\n\n // we could pass our curtains object OR our curtains renderer object\n renderer = (renderer && (renderer as GPUCurtains).renderer) || (renderer as CameraRenderer)\n\n isCameraRenderer(renderer, parameters.label ? parameters.label + ' ' + this.type : this.type)\n\n this.renderer = renderer\n\n const { geometry, frustumCulled, DOMFrustumMargins } = parameters\n\n this.options = {\n ...(this.options ?? {}), // merge possible lower options?\n frustumCulled,\n DOMFrustumMargins,\n }\n\n this.setDOMFrustum()\n\n // explicitly needed for DOM Frustum\n this.geometry = geometry\n\n // tell the model and projection matrices to update right away\n this.shouldUpdateMatrixStack()\n }\n\n /* SHADERS */\n\n /**\n * Set default shaders if one or both of them are missing\n */\n setShaders() {\n let { shaders } = this.options\n\n if (!shaders) {\n shaders = {\n vertex: {\n code: default_projected_vsWgsl,\n entryPoint: 'main',\n },\n fragment: {\n code: default_normal_fsWgsl,\n entryPoint: 'main',\n },\n }\n } else {\n if (!shaders.vertex || !shaders.vertex.code) {\n shaders.vertex = {\n code: default_projected_vsWgsl,\n entryPoint: 'main',\n }\n }\n\n if (shaders.fragment === undefined || (shaders.fragment && !(shaders.fragment as ShaderOptions).code)) {\n shaders.fragment = {\n code: default_normal_fsWgsl,\n entryPoint: 'main',\n }\n }\n }\n }\n\n /* GEOMETRY */\n\n /**\n * Set the Mesh frustum culling\n */\n setDOMFrustum() {\n this.domFrustum = new DOMFrustum({\n boundingBox: this.geometry.boundingBox,\n modelViewProjectionMatrix: this.modelViewProjectionMatrix,\n containerBoundingRect: this.renderer.boundingRect,\n DOMFrustumMargins: this.options.DOMFrustumMargins,\n onReEnterView: () => {\n this._onReEnterViewCallback && this._onReEnterViewCallback()\n },\n onLeaveView: () => {\n this._onLeaveViewCallback && this._onLeaveViewCallback()\n },\n })\n\n this.DOMFrustumMargins = this.domFrustum.DOMFrustumMargins\n this.frustumCulled = this.options.frustumCulled\n this.domFrustum.shouldUpdate = this.frustumCulled\n }\n\n /* MATERIAL */\n\n /**\n * Hook used to clean up parameters before sending them to the material.\n * @param parameters - parameters to clean before sending them to the {@link core/materials/RenderMaterial.RenderMaterial | RenderMaterial}\n * @returns - cleaned parameters\n */\n cleanupRenderMaterialParameters(parameters: ProjectedRenderMaterialParams): MeshBaseRenderParams {\n // patch mesh parameters\n delete parameters.frustumCulled\n delete parameters.DOMFrustumMargins\n\n super.cleanupRenderMaterialParameters(parameters)\n\n return parameters\n }\n\n /**\n * Set a Mesh matrices uniforms inputs then call {@link MeshBaseClass} super method\n * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters}\n */\n setMaterial(meshParameters: RenderMaterialParams) {\n // add matrices uniforms\n const matricesUniforms = {\n label: 'Matrices',\n struct: {\n model: {\n name: 'model',\n type: 'mat4x4f',\n value: this.modelMatrix,\n },\n world: {\n name: 'world',\n type: 'mat4x4f',\n value: this.worldMatrix,\n },\n modelView: {\n // model view matrix (world matrix multiplied by camera view matrix)\n name: 'modelView',\n type: 'mat4x4f',\n value: this.modelViewMatrix,\n },\n modelViewProjection: {\n name: 'modelViewProjection',\n type: 'mat4x4f',\n value: this.modelViewProjectionMatrix,\n },\n },\n }\n\n if (!meshParameters.uniforms) meshParameters.uniforms = {}\n meshParameters.uniforms.matrices = matricesUniforms\n\n super.setMaterial(meshParameters)\n }\n\n /* SIZE & TRANSFORMS */\n\n /**\n * Resize our {@link ProjectedMeshBaseClass}\n * @param boundingRect - the new bounding rectangle\n */\n resize(boundingRect?: DOMElementBoundingRect | null) {\n if (this.domFrustum) this.domFrustum.setContainerBoundingRect(this.renderer.boundingRect)\n\n super.resize(boundingRect)\n }\n\n /**\n * Apply scale and resize textures\n */\n applyScale() {\n super.applyScale()\n\n // resize textures on scale change!\n this.textures.forEach((texture) => texture.resize())\n }\n\n /**\n * Get our {@link DOMFrustum} projected bounding rectangle\n * @readonly\n */\n get projectedBoundingRect(): DOMElementBoundingRect {\n return this.domFrustum?.projectedBoundingRect\n }\n\n /**\n * At least one of the matrix has been updated, update according uniforms and frustum\n */\n onAfterMatrixStackUpdate() {\n if (this.material) {\n this.material.shouldUpdateInputsBindings('matrices')\n }\n\n if (this.domFrustum) this.domFrustum.shouldUpdate = true\n }\n\n /* EVENTS */\n\n /**\n * Assign a callback function to _onReEnterViewCallback\n * @param callback - callback to run when {@link ProjectedMeshBaseClass} is reentering the view frustum\n * @returns - our Mesh\n */\n onReEnterView(callback: () => void): ProjectedMeshBaseClass {\n if (callback) {\n this._onReEnterViewCallback = callback\n }\n\n return this\n }\n\n /**\n * Assign a callback function to _onLeaveViewCallback\n * @param callback - callback to run when {@link ProjectedMeshBaseClass} is leaving the view frustum\n * @returns - our Mesh\n */\n onLeaveView(callback: () => void): ProjectedMeshBaseClass {\n if (callback) {\n this._onLeaveViewCallback = callback\n }\n\n return this\n }\n\n /* RENDER */\n\n /**\n * Called before rendering the Mesh to update matrices and {@link DOMFrustum}.\n * First, we update our matrices to have fresh results. It eventually calls onAfterMatrixStackUpdate() if at least one matrix has been updated.\n * Then we check if we need to update the {@link DOMFrustum} projected bounding rectangle.\n * Finally we call {@link MeshBaseClass#onBeforeRenderPass | Mesh base onBeforeRenderPass} super\n */\n onBeforeRenderPass() {\n this.updateMatrixStack()\n\n if (this.domFrustum && this.domFrustum.shouldUpdate && this.frustumCulled) {\n this.domFrustum.computeProjectedToDocumentCoords()\n this.domFrustum.shouldUpdate = false\n }\n\n super.onBeforeRenderPass()\n }\n\n /**\n * Only render the Mesh if it is in view frustum.\n * Since render() is actually called before onRenderPass(), we are sure to have fresh frustum bounding rectangle values here.\n * @param pass - current render pass\n */\n onRenderPass(pass: GPURenderPassEncoder) {\n if (!this.material.ready) return\n\n this._onRenderCallback && this._onRenderCallback()\n\n if ((this.domFrustum && this.domFrustum.isIntersecting) || !this.frustumCulled) {\n // render ou material\n this.material.render(pass)\n // then render our geometry\n this.geometry.render(pass)\n }\n }\n }\n}\n\nexport { ProjectedMeshBaseMixin }\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/esm/core/renderPasses/RenderPass.mjs b/dist/esm/core/renderPasses/RenderPass.mjs index 3d4fc2ef6..c1619ac5f 100644 --- a/dist/esm/core/renderPasses/RenderPass.mjs +++ b/dist/esm/core/renderPasses/RenderPass.mjs @@ -73,7 +73,7 @@ class RenderPass { this.createDepthTexture(); } this.viewTextures = []; - if (this.options.useColorAttachments) { + if (this.options.useColorAttachments && (!this.options.shouldUpdateView || this.options.sampleCount > 1)) { this.createViewTextures(); } this.setRenderPassDescriptor(); @@ -121,8 +121,8 @@ class RenderPass { colorAttachments: this.options.colorAttachments.map((colorAttachment, index) => { return { // view - view: this.viewTextures[index].texture.createView({ - label: this.viewTextures[index].texture.label + " view" + view: this.viewTextures[index]?.texture.createView({ + label: this.viewTextures[index]?.texture.label + " view" }), // clear values clearValue: colorAttachment.clearValue, diff --git a/dist/esm/core/renderPasses/RenderPass.mjs.map b/dist/esm/core/renderPasses/RenderPass.mjs.map index b945a3f11..d703c8a67 100644 --- a/dist/esm/core/renderPasses/RenderPass.mjs.map +++ b/dist/esm/core/renderPasses/RenderPass.mjs.map @@ -1 +1 @@ -{"version":3,"file":"RenderPass.mjs","sources":["../../../../src/core/renderPasses/RenderPass.ts"],"sourcesContent":["import { isRenderer, Renderer } from '../renderers/utils'\nimport { generateUUID } from '../../utils/utils'\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\nimport { RenderTexture } from '../textures/RenderTexture'\n\n/** Define the parameters of a color attachment */\nexport interface ColorAttachmentParams {\n /** The {@link GPULoadOp | load operation} to perform while drawing this {@link RenderPass} */\n loadOp?: GPULoadOp\n /** The {@link GPUStoreOp | store operation} to perform while drawing this {@link RenderPass} */\n storeOp?: GPUStoreOp\n /** The {@link GPUColor | color values} to clear to before drawing this {@link RenderPass} */\n clearValue?: GPUColor\n /** Optional format of the color attachment texture */\n targetFormat: GPUTextureFormat\n}\n\n/**\n * Parameters used to create this {@link RenderPass}\n */\nexport interface RenderPassParams {\n /** The label of the {@link RenderPass}, sent to various GPU objects for debugging purpose */\n label?: string\n\n /** Whether the {@link RenderPass | view and depth textures} should use multisampling or not */\n sampleCount?: GPUSize32\n\n /** Force all the {@link RenderPass} textures size to be set to the given ratio of the {@link core/renderers/GPURenderer.GPURenderer#displayBoundingRect | renderer display bounding rectangle}. Used mainly to shrink the rendered definition. */\n qualityRatio?: number\n\n /** Whether this {@link RenderPass} should handle a view texture */\n useColorAttachments?: boolean\n /** Whether the main (first {@link colorAttachments}) view texture should be updated each frame */\n shouldUpdateView?: boolean\n /** The {@link GPULoadOp | load operation} to perform while drawing this {@link RenderPass} */\n loadOp?: GPULoadOp\n /** The {@link GPUStoreOp | store operation} to perform while drawing this {@link RenderPass} */\n storeOp?: GPUStoreOp\n /** The {@link GPUColor | color values} to clear to before drawing this {@link RenderPass} */\n clearValue?: GPUColor\n /** Optional format of the color attachment texture */\n targetFormat: GPUTextureFormat\n /** Define all the color attachments parameters to use here in case this {@link RenderPass} should output to multiple color attachments (Multiple Render Targets) */\n colorAttachments?: ColorAttachmentParams[]\n\n /** Whether this {@link RenderPass} should handle a depth texture */\n useDepth?: boolean\n /** Whether this {@link RenderPass} should use an already created depth texture */\n depthTexture?: RenderTexture\n /** The {@link GPULoadOp | depth load operation} to perform while drawing this {@link RenderPass} */\n depthLoadOp?: GPULoadOp\n /** The {@link GPUStoreOp | depth store operation} to perform while drawing this {@link RenderPass} */\n depthStoreOp?: GPUStoreOp\n /** The depth clear value to clear to before drawing this {@link RenderPass} */\n depthClearValue?: number\n /** Optional format of the depth texture */\n depthFormat?: GPUTextureFormat\n}\n\n/**\n * Used by {@link core/renderPasses/RenderTarget.RenderTarget | RenderTarget} and the {@link Renderer} to render to one or multiple {@link RenderPass#viewTextures | view textures} (and optionally a {@link RenderPass#depthTexture | depth texture}), using a specific {@link GPURenderPassDescriptor | render pass descriptor}.\n */\nexport class RenderPass {\n /** {@link Renderer} used by this {@link RenderPass} */\n renderer: Renderer\n /** The type of the {@link RenderPass} */\n type: string\n /** The universal unique id of this {@link RenderPass} */\n readonly uuid: string\n\n /** Options used to create this {@link RenderPass} */\n options: RenderPassParams\n\n /** Depth {@link RenderTexture} to use with this {@link RenderPass} if it should handle depth */\n depthTexture: RenderTexture | undefined\n\n /** Array of {@link RenderTexture} used for this {@link RenderPass} color attachments view textures */\n viewTextures: RenderTexture[]\n\n /** The {@link RenderPass} {@link GPURenderPassDescriptor | descriptor} */\n descriptor: GPURenderPassDescriptor\n\n /**\n * RenderPass constructor\n * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link RenderPass}\n * @param parameters - {@link RenderPassParams | parameters} used to create this {@link RenderPass}\n */\n constructor(\n renderer: Renderer | GPUCurtains,\n {\n label = 'Render Pass',\n sampleCount = 4,\n qualityRatio = 1,\n // color\n useColorAttachments = true,\n shouldUpdateView = true,\n loadOp = 'clear' as GPULoadOp,\n storeOp = 'store' as GPUStoreOp,\n clearValue = [0, 0, 0, 0],\n targetFormat,\n colorAttachments = [],\n // depth\n useDepth = true,\n depthTexture = null,\n depthLoadOp = 'clear' as GPULoadOp,\n depthStoreOp = 'store' as GPUStoreOp,\n depthClearValue = 1,\n depthFormat = 'depth24plus' as GPUTextureFormat,\n } = {} as RenderPassParams\n ) {\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, 'RenderPass')\n\n this.type = 'RenderPass'\n this.uuid = generateUUID()\n\n this.renderer = renderer\n\n if (useColorAttachments) {\n const defaultColorAttachment = {\n loadOp,\n storeOp,\n clearValue,\n targetFormat: targetFormat ?? this.renderer.options.preferredFormat,\n }\n\n if (!colorAttachments.length) {\n colorAttachments = [defaultColorAttachment]\n } else {\n colorAttachments = colorAttachments.map((colorAttachment) => {\n return { ...defaultColorAttachment, ...colorAttachment }\n })\n }\n }\n\n this.options = {\n label,\n sampleCount,\n qualityRatio,\n // color\n useColorAttachments,\n shouldUpdateView,\n loadOp,\n storeOp,\n clearValue,\n targetFormat: targetFormat ?? this.renderer.options.preferredFormat,\n colorAttachments,\n // depth\n useDepth,\n ...(depthTexture !== undefined && { depthTexture }),\n depthLoadOp,\n depthStoreOp,\n depthClearValue,\n depthFormat,\n }\n\n this.setClearValue(clearValue)\n\n // if needed, create a depth texture before our descriptor\n if (this.options.useDepth) {\n this.createDepthTexture()\n }\n\n // if needed, create a view texture before our descriptor\n this.viewTextures = []\n if (this.options.useColorAttachments) {\n this.createViewTextures()\n }\n\n this.setRenderPassDescriptor()\n }\n\n /**\n * Create and set our {@link depthTexture | depth texture}\n */\n createDepthTexture() {\n if (this.options.depthTexture) {\n this.depthTexture = this.options.depthTexture\n // adjust depth format as well\n this.options.depthFormat = this.options.depthTexture.options.format\n } else {\n this.depthTexture = new RenderTexture(this.renderer, {\n label: this.options.label + ' depth texture',\n name: 'depthTexture',\n usage: 'depth',\n format: this.options.depthFormat,\n sampleCount: this.options.sampleCount,\n qualityRatio: this.options.qualityRatio,\n })\n }\n }\n\n /**\n * Create and set our {@link viewTextures | view textures}\n */\n createViewTextures() {\n this.options.colorAttachments.forEach((colorAttachment, index) => {\n this.viewTextures.push(\n new RenderTexture(this.renderer, {\n label: `${this.options.label} colorAttachment[${index}] view texture`,\n name: `colorAttachment${index}ViewTexture`,\n format: colorAttachment.targetFormat,\n sampleCount: this.options.sampleCount,\n qualityRatio: this.options.qualityRatio,\n })\n )\n })\n }\n\n /**\n * Set our render pass {@link descriptor}\n */\n setRenderPassDescriptor() {\n this.descriptor = {\n label: this.options.label + ' descriptor',\n colorAttachments: this.options.colorAttachments.map((colorAttachment, index) => {\n return {\n // view\n view: this.viewTextures[index].texture.createView({\n label: this.viewTextures[index].texture.label + ' view',\n }),\n // clear values\n clearValue: colorAttachment.clearValue,\n // loadOp: 'clear' specifies to clear the texture to the clear value before drawing\n // The other option is 'load' which means load the existing contents of the texture into the GPU so we can draw over what's already there.\n loadOp: colorAttachment.loadOp,\n // storeOp: 'store' means store the result of what we draw.\n // We could also pass 'discard' which would throw away what we draw.\n // see https://webgpufundamentals.org/webgpu/lessons/webgpu-multisampling.html\n storeOp: colorAttachment.storeOp,\n }\n }),\n\n ...(this.options.useDepth && {\n depthStencilAttachment: {\n view: this.depthTexture.texture.createView({\n label: this.depthTexture.texture.label + ' view',\n }),\n depthClearValue: this.options.depthClearValue,\n // the same way loadOp is working, we can specify if we want to clear or load the previous depth buffer result\n depthLoadOp: this.options.depthLoadOp,\n depthStoreOp: this.options.depthStoreOp,\n },\n }),\n } as GPURenderPassDescriptor\n }\n\n /**\n * Resize our {@link RenderPass}: reset its {@link RenderTexture}\n */\n resize() {\n // reassign textures\n if (this.options.useDepth) {\n this.descriptor.depthStencilAttachment.view = this.depthTexture.texture.createView({\n label: this.depthTexture.options.label + ' view',\n })\n }\n\n this.viewTextures.forEach((viewTexture, index) => {\n this.descriptor.colorAttachments[index].view = viewTexture.texture.createView({\n label: viewTexture.options.label + ' view',\n })\n })\n }\n\n /**\n * Set the {@link descriptor} {@link GPULoadOp | load operation}\n * @param loadOp - new {@link GPULoadOp | load operation} to use\n * @param colorAttachmentIndex - index of the color attachment for which to use this load operation\n */\n setLoadOp(loadOp: GPULoadOp = 'clear', colorAttachmentIndex = 0) {\n this.options.loadOp = loadOp\n if (this.options.useColorAttachments && this.descriptor) {\n if (this.descriptor.colorAttachments && this.descriptor.colorAttachments[colorAttachmentIndex]) {\n this.descriptor.colorAttachments[colorAttachmentIndex].loadOp = loadOp\n }\n }\n }\n\n /**\n * Set the {@link descriptor} {@link GPULoadOp | depth load operation}\n * @param depthLoadOp - new {@link GPULoadOp | depth load operation} to use\n */\n setDepthLoadOp(depthLoadOp: GPULoadOp = 'clear') {\n this.options.depthLoadOp = depthLoadOp\n if (this.options.useDepth && this.descriptor.depthStencilAttachment) {\n this.descriptor.depthStencilAttachment.depthLoadOp = depthLoadOp\n }\n }\n\n /**\n * Set our {@link GPUColor | clear colors value}.
\n * Beware that if the {@link renderer} is using {@link core/renderers/GPURenderer.GPURenderer#alphaMode | premultiplied alpha mode}, your R, G and B channels should be premultiplied by your alpha channel.\n * @param clearValue - new {@link GPUColor | clear colors value} to use\n * @param colorAttachmentIndex - index of the color attachment for which to use this clear value\n */\n setClearValue(clearValue: GPUColor = [0, 0, 0, 0], colorAttachmentIndex = 0) {\n if (this.renderer.alphaMode === 'premultiplied') {\n const alpha = clearValue[3]\n clearValue[0] = Math.min(clearValue[0], alpha)\n clearValue[1] = Math.min(clearValue[1], alpha)\n clearValue[2] = Math.min(clearValue[2], alpha)\n } else {\n this.options.clearValue = clearValue\n }\n\n if (this.descriptor && this.descriptor.colorAttachments && this.descriptor.colorAttachments[colorAttachmentIndex]) {\n this.descriptor.colorAttachments[colorAttachmentIndex].clearValue = clearValue\n }\n }\n\n /**\n * Set the current {@link descriptor} texture {@link GPURenderPassColorAttachment#view | view} and {@link GPURenderPassColorAttachment#resolveTarget | resolveTarget} (depending on whether we're using multisampling)\n * @param renderTexture - {@link GPUTexture} to use, or the {@link core/renderers/GPURenderer.GPURenderer#context | context} {@link GPUTexture | current texture} if null.\n * @returns - the {@link GPUTexture | texture} to render to.\n */\n updateView(renderTexture: GPUTexture | null = null): GPUTexture | null {\n if (!this.options.colorAttachments.length || !this.options.shouldUpdateView) {\n return renderTexture\n }\n\n if (!renderTexture) {\n renderTexture = this.renderer.context.getCurrentTexture()\n renderTexture.label = `${this.renderer.type} context current texture`\n }\n\n if (this.options.sampleCount > 1) {\n this.descriptor.colorAttachments[0].view = this.viewTextures[0].texture.createView({\n label: this.viewTextures[0].options.label + ' view',\n })\n this.descriptor.colorAttachments[0].resolveTarget = renderTexture.createView({\n label: renderTexture.label + ' resolve target view',\n })\n } else {\n this.descriptor.colorAttachments[0].view = renderTexture.createView({\n label: renderTexture.label + ' view',\n })\n }\n\n return renderTexture\n }\n\n /**\n * Destroy our {@link RenderPass}\n */\n destroy() {\n this.viewTextures.forEach((viewTexture) => viewTexture.destroy())\n\n if (!this.options.depthTexture && this.depthTexture) {\n this.depthTexture.destroy()\n }\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"RenderPass.mjs","sources":["../../../../src/core/renderPasses/RenderPass.ts"],"sourcesContent":["import { isRenderer, Renderer } from '../renderers/utils'\nimport { generateUUID } from '../../utils/utils'\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\nimport { RenderTexture } from '../textures/RenderTexture'\n\n/** Define the parameters of a color attachment */\nexport interface ColorAttachmentParams {\n /** The {@link GPULoadOp | load operation} to perform while drawing this {@link RenderPass} */\n loadOp?: GPULoadOp\n /** The {@link GPUStoreOp | store operation} to perform while drawing this {@link RenderPass} */\n storeOp?: GPUStoreOp\n /** The {@link GPUColor | color values} to clear to before drawing this {@link RenderPass} */\n clearValue?: GPUColor\n /** Optional format of the color attachment texture */\n targetFormat: GPUTextureFormat\n}\n\n/**\n * Parameters used to create this {@link RenderPass}\n */\nexport interface RenderPassParams {\n /** The label of the {@link RenderPass}, sent to various GPU objects for debugging purpose */\n label?: string\n\n /** Whether the {@link RenderPass | view and depth textures} should use multisampling or not */\n sampleCount?: GPUSize32\n\n /** Force all the {@link RenderPass} textures size to be set to the given ratio of the {@link core/renderers/GPURenderer.GPURenderer#displayBoundingRect | renderer display bounding rectangle}. Used mainly to shrink the rendered definition. */\n qualityRatio?: number\n\n /** Whether this {@link RenderPass} should handle a view texture */\n useColorAttachments?: boolean\n /** Whether the main (first {@link colorAttachments}) view texture should be updated each frame */\n shouldUpdateView?: boolean\n /** The {@link GPULoadOp | load operation} to perform while drawing this {@link RenderPass} */\n loadOp?: GPULoadOp\n /** The {@link GPUStoreOp | store operation} to perform while drawing this {@link RenderPass} */\n storeOp?: GPUStoreOp\n /** The {@link GPUColor | color values} to clear to before drawing this {@link RenderPass} */\n clearValue?: GPUColor\n /** Optional format of the color attachment texture */\n targetFormat: GPUTextureFormat\n /** Define all the color attachments parameters to use here in case this {@link RenderPass} should output to multiple color attachments (Multiple Render Targets) */\n colorAttachments?: ColorAttachmentParams[]\n\n /** Whether this {@link RenderPass} should handle a depth texture */\n useDepth?: boolean\n /** Whether this {@link RenderPass} should use an already created depth texture */\n depthTexture?: RenderTexture\n /** The {@link GPULoadOp | depth load operation} to perform while drawing this {@link RenderPass} */\n depthLoadOp?: GPULoadOp\n /** The {@link GPUStoreOp | depth store operation} to perform while drawing this {@link RenderPass} */\n depthStoreOp?: GPUStoreOp\n /** The depth clear value to clear to before drawing this {@link RenderPass} */\n depthClearValue?: number\n /** Optional format of the depth texture */\n depthFormat?: GPUTextureFormat\n}\n\n/**\n * Used by {@link core/renderPasses/RenderTarget.RenderTarget | RenderTarget} and the {@link Renderer} to render to one or multiple {@link RenderPass#viewTextures | view textures} (and optionally a {@link RenderPass#depthTexture | depth texture}), using a specific {@link GPURenderPassDescriptor | render pass descriptor}.\n */\nexport class RenderPass {\n /** {@link Renderer} used by this {@link RenderPass} */\n renderer: Renderer\n /** The type of the {@link RenderPass} */\n type: string\n /** The universal unique id of this {@link RenderPass} */\n readonly uuid: string\n\n /** Options used to create this {@link RenderPass} */\n options: RenderPassParams\n\n /** Depth {@link RenderTexture} to use with this {@link RenderPass} if it should handle depth */\n depthTexture: RenderTexture | undefined\n\n /** Array of {@link RenderTexture} used for this {@link RenderPass} color attachments view textures */\n viewTextures: RenderTexture[]\n\n /** The {@link RenderPass} {@link GPURenderPassDescriptor | descriptor} */\n descriptor: GPURenderPassDescriptor\n\n /**\n * RenderPass constructor\n * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link RenderPass}\n * @param parameters - {@link RenderPassParams | parameters} used to create this {@link RenderPass}\n */\n constructor(\n renderer: Renderer | GPUCurtains,\n {\n label = 'Render Pass',\n sampleCount = 4,\n qualityRatio = 1,\n // color\n useColorAttachments = true,\n shouldUpdateView = true,\n loadOp = 'clear' as GPULoadOp,\n storeOp = 'store' as GPUStoreOp,\n clearValue = [0, 0, 0, 0],\n targetFormat,\n colorAttachments = [],\n // depth\n useDepth = true,\n depthTexture = null,\n depthLoadOp = 'clear' as GPULoadOp,\n depthStoreOp = 'store' as GPUStoreOp,\n depthClearValue = 1,\n depthFormat = 'depth24plus' as GPUTextureFormat,\n } = {} as RenderPassParams\n ) {\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, 'RenderPass')\n\n this.type = 'RenderPass'\n this.uuid = generateUUID()\n\n this.renderer = renderer\n\n if (useColorAttachments) {\n const defaultColorAttachment = {\n loadOp,\n storeOp,\n clearValue,\n targetFormat: targetFormat ?? this.renderer.options.preferredFormat,\n }\n\n if (!colorAttachments.length) {\n colorAttachments = [defaultColorAttachment]\n } else {\n colorAttachments = colorAttachments.map((colorAttachment) => {\n return { ...defaultColorAttachment, ...colorAttachment }\n })\n }\n }\n\n this.options = {\n label,\n sampleCount,\n qualityRatio,\n // color\n useColorAttachments,\n shouldUpdateView,\n loadOp,\n storeOp,\n clearValue,\n targetFormat: targetFormat ?? this.renderer.options.preferredFormat,\n colorAttachments,\n // depth\n useDepth,\n ...(depthTexture !== undefined && { depthTexture }),\n depthLoadOp,\n depthStoreOp,\n depthClearValue,\n depthFormat,\n }\n\n this.setClearValue(clearValue)\n\n // if needed, create a depth texture before our descriptor\n if (this.options.useDepth) {\n this.createDepthTexture()\n }\n\n // if needed, create a view texture before our descriptor\n this.viewTextures = []\n if (this.options.useColorAttachments && (!this.options.shouldUpdateView || this.options.sampleCount > 1)) {\n this.createViewTextures()\n }\n\n this.setRenderPassDescriptor()\n }\n\n /**\n * Create and set our {@link depthTexture | depth texture}\n */\n createDepthTexture() {\n if (this.options.depthTexture) {\n this.depthTexture = this.options.depthTexture\n // adjust depth format as well\n this.options.depthFormat = this.options.depthTexture.options.format\n } else {\n this.depthTexture = new RenderTexture(this.renderer, {\n label: this.options.label + ' depth texture',\n name: 'depthTexture',\n usage: 'depth',\n format: this.options.depthFormat,\n sampleCount: this.options.sampleCount,\n qualityRatio: this.options.qualityRatio,\n })\n }\n }\n\n /**\n * Create and set our {@link viewTextures | view textures}\n */\n createViewTextures() {\n this.options.colorAttachments.forEach((colorAttachment, index) => {\n this.viewTextures.push(\n new RenderTexture(this.renderer, {\n label: `${this.options.label} colorAttachment[${index}] view texture`,\n name: `colorAttachment${index}ViewTexture`,\n format: colorAttachment.targetFormat,\n sampleCount: this.options.sampleCount,\n qualityRatio: this.options.qualityRatio,\n })\n )\n })\n }\n\n /**\n * Set our render pass {@link descriptor}\n */\n setRenderPassDescriptor() {\n this.descriptor = {\n label: this.options.label + ' descriptor',\n colorAttachments: this.options.colorAttachments.map((colorAttachment, index) => {\n return {\n // view\n view: this.viewTextures[index]?.texture.createView({\n label: this.viewTextures[index]?.texture.label + ' view',\n }),\n // clear values\n clearValue: colorAttachment.clearValue,\n // loadOp: 'clear' specifies to clear the texture to the clear value before drawing\n // The other option is 'load' which means load the existing contents of the texture into the GPU so we can draw over what's already there.\n loadOp: colorAttachment.loadOp,\n // storeOp: 'store' means store the result of what we draw.\n // We could also pass 'discard' which would throw away what we draw.\n // see https://webgpufundamentals.org/webgpu/lessons/webgpu-multisampling.html\n storeOp: colorAttachment.storeOp,\n }\n }),\n\n ...(this.options.useDepth && {\n depthStencilAttachment: {\n view: this.depthTexture.texture.createView({\n label: this.depthTexture.texture.label + ' view',\n }),\n depthClearValue: this.options.depthClearValue,\n // the same way loadOp is working, we can specify if we want to clear or load the previous depth buffer result\n depthLoadOp: this.options.depthLoadOp,\n depthStoreOp: this.options.depthStoreOp,\n },\n }),\n } as GPURenderPassDescriptor\n }\n\n /**\n * Resize our {@link RenderPass}: reset its {@link RenderTexture}\n */\n resize() {\n // reassign textures\n if (this.options.useDepth) {\n this.descriptor.depthStencilAttachment.view = this.depthTexture.texture.createView({\n label: this.depthTexture.options.label + ' view',\n })\n }\n\n this.viewTextures.forEach((viewTexture, index) => {\n this.descriptor.colorAttachments[index].view = viewTexture.texture.createView({\n label: viewTexture.options.label + ' view',\n })\n })\n }\n\n /**\n * Set the {@link descriptor} {@link GPULoadOp | load operation}\n * @param loadOp - new {@link GPULoadOp | load operation} to use\n * @param colorAttachmentIndex - index of the color attachment for which to use this load operation\n */\n setLoadOp(loadOp: GPULoadOp = 'clear', colorAttachmentIndex = 0) {\n this.options.loadOp = loadOp\n if (this.options.useColorAttachments && this.descriptor) {\n if (this.descriptor.colorAttachments && this.descriptor.colorAttachments[colorAttachmentIndex]) {\n this.descriptor.colorAttachments[colorAttachmentIndex].loadOp = loadOp\n }\n }\n }\n\n /**\n * Set the {@link descriptor} {@link GPULoadOp | depth load operation}\n * @param depthLoadOp - new {@link GPULoadOp | depth load operation} to use\n */\n setDepthLoadOp(depthLoadOp: GPULoadOp = 'clear') {\n this.options.depthLoadOp = depthLoadOp\n if (this.options.useDepth && this.descriptor.depthStencilAttachment) {\n this.descriptor.depthStencilAttachment.depthLoadOp = depthLoadOp\n }\n }\n\n /**\n * Set our {@link GPUColor | clear colors value}.
\n * Beware that if the {@link renderer} is using {@link core/renderers/GPURenderer.GPURenderer#alphaMode | premultiplied alpha mode}, your R, G and B channels should be premultiplied by your alpha channel.\n * @param clearValue - new {@link GPUColor | clear colors value} to use\n * @param colorAttachmentIndex - index of the color attachment for which to use this clear value\n */\n setClearValue(clearValue: GPUColor = [0, 0, 0, 0], colorAttachmentIndex = 0) {\n if (this.renderer.alphaMode === 'premultiplied') {\n const alpha = clearValue[3]\n clearValue[0] = Math.min(clearValue[0], alpha)\n clearValue[1] = Math.min(clearValue[1], alpha)\n clearValue[2] = Math.min(clearValue[2], alpha)\n } else {\n this.options.clearValue = clearValue\n }\n\n if (this.descriptor && this.descriptor.colorAttachments && this.descriptor.colorAttachments[colorAttachmentIndex]) {\n this.descriptor.colorAttachments[colorAttachmentIndex].clearValue = clearValue\n }\n }\n\n /**\n * Set the current {@link descriptor} texture {@link GPURenderPassColorAttachment#view | view} and {@link GPURenderPassColorAttachment#resolveTarget | resolveTarget} (depending on whether we're using multisampling)\n * @param renderTexture - {@link GPUTexture} to use, or the {@link core/renderers/GPURenderer.GPURenderer#context | context} {@link GPUTexture | current texture} if null.\n * @returns - the {@link GPUTexture | texture} to render to.\n */\n updateView(renderTexture: GPUTexture | null = null): GPUTexture | null {\n if (!this.options.colorAttachments.length || !this.options.shouldUpdateView) {\n return renderTexture\n }\n\n if (!renderTexture) {\n renderTexture = this.renderer.context.getCurrentTexture()\n renderTexture.label = `${this.renderer.type} context current texture`\n }\n\n if (this.options.sampleCount > 1) {\n this.descriptor.colorAttachments[0].view = this.viewTextures[0].texture.createView({\n label: this.viewTextures[0].options.label + ' view',\n })\n this.descriptor.colorAttachments[0].resolveTarget = renderTexture.createView({\n label: renderTexture.label + ' resolve target view',\n })\n } else {\n this.descriptor.colorAttachments[0].view = renderTexture.createView({\n label: renderTexture.label + ' view',\n })\n }\n\n return renderTexture\n }\n\n /**\n * Destroy our {@link RenderPass}\n */\n destroy() {\n this.viewTextures.forEach((viewTexture) => viewTexture.destroy())\n\n if (!this.options.depthTexture && this.depthTexture) {\n this.depthTexture.destroy()\n }\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/esm/core/renderPasses/ShaderPass.mjs b/dist/esm/core/renderPasses/ShaderPass.mjs index ee6f1f83b..238728d9a 100644 --- a/dist/esm/core/renderPasses/ShaderPass.mjs +++ b/dist/esm/core/renderPasses/ShaderPass.mjs @@ -41,15 +41,26 @@ class ShaderPass extends FullscreenPlane { }); } /** - * Get our main {@link RenderTexture}, the one that contains our post processed content + * Hook used to clean up parameters before sending them to the material. + * @param parameters - parameters to clean before sending them to the {@link core/materials/RenderMaterial.RenderMaterial | RenderMaterial} + * @returns - cleaned parameters + */ + cleanupRenderMaterialParameters(parameters) { + delete parameters.copyOutputToRenderTexture; + delete parameters.inputTarget; + super.cleanupRenderMaterialParameters(parameters); + return parameters; + } + /** + * Get our main {@link RenderTexture} that contains the input content to be used by the {@link ShaderPass}. Can also contain the ouputted content if {@link ShaderPassOptions#copyOutputToRenderTexture | copyOutputToRenderTexture} is set to true. * @readonly */ get renderTexture() { return this.renderTextures.find((texture) => texture.options.name === "renderTexture"); } - // TODO /** - * Assign or remove a {@link RenderTarget} to this {@link ShaderPass} + * Assign or remove an input {@link RenderTarget} to this {@link ShaderPass}, which can be different from what has just been drawn to the {@link core/renderers/GPURenderer.GPURenderer#context | context} current texture. + * * Since this manipulates the {@link core/scenes/Scene.Scene | Scene} stacks, it can be used to remove a RenderTarget as well. * Also copy or remove the {@link RenderTarget#renderTexture | render target render texture} into the {@link ShaderPass} {@link renderTexture} * @param inputTarget - the {@link RenderTarget} to assign or null if we want to remove the current {@link RenderTarget} diff --git a/dist/esm/core/renderPasses/ShaderPass.mjs.map b/dist/esm/core/renderPasses/ShaderPass.mjs.map index 15fd2b9e9..2f2d7ce90 100644 --- a/dist/esm/core/renderPasses/ShaderPass.mjs.map +++ b/dist/esm/core/renderPasses/ShaderPass.mjs.map @@ -1 +1 @@ -{"version":3,"file":"ShaderPass.mjs","sources":["../../../../src/core/renderPasses/ShaderPass.ts"],"sourcesContent":["import { FullscreenPlane } from '../meshes/FullscreenPlane'\nimport { isRenderer, Renderer } from '../renderers/utils'\nimport { RenderTarget } from './RenderTarget'\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\nimport { MeshBaseRenderParams } from '../meshes/mixins/MeshBaseMixin'\nimport { RenderTexture } from '../textures/RenderTexture'\nimport default_pass_fsWGSl from '../shaders/chunks/default_pass_fs.wgsl'\nimport { throwWarning } from '../../utils/utils'\n\n/**\n * Parameters used to create a {@link ShaderPass}\n */\nexport interface ShaderPassParams extends MeshBaseRenderParams {\n /** Optional input {@link RenderTarget} to assign to the {@link ShaderPass}. Used to automatically copy the content of the given {@link RenderTarget} texture into the {@link ShaderPass#renderTexture | ShaderPass renderTexture}. */\n inputTarget?: RenderTarget\n}\n\n/**\n * Used to apply postprocessing, i.e. draw meshes to a {@link RenderTexture} and then draw a {@link FullscreenPlane} using that texture as an input.\n *\n * A ShaderPass could either post process the whole scene or just a bunch of meshes using a specific {@link RenderTarget}.\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 ShaderPass\n * const shaderPass = new ShaderPass(gpuCurtain, {\n * label: 'My shader pass',\n * shaders: {\n * fragment: {\n * code: shaderPassCode, // assume it is a valid WGSL fragment shader\n * },\n * },\n * })\n * ```\n */\nexport class ShaderPass extends FullscreenPlane {\n /** Optional input {@link RenderTarget} to assign to the {@link ShaderPass}. Used to automatically copy the content of the given {@link RenderTarget} texture into the {@link ShaderPass#renderTexture | ShaderPass renderTexture}. */\n inputTarget: RenderTarget | undefined\n\n /**\n * ShaderPass constructor\n * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link ShaderPass}\n * @param parameters - {@link ShaderPassParams | parameters} use to create this {@link ShaderPass}\n */\n constructor(renderer: Renderer | GPUCurtains, parameters: ShaderPassParams = {}) {\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 + ' ShaderPass' : 'ShaderPass')\n\n // force transparency to allow for correct blending between successive passes\n parameters.transparent = true\n parameters.label = parameters.label ?? 'ShaderPass ' + renderer.shaderPasses?.length\n\n // set default sample count to post processing render pass\n parameters.sampleCount = !!parameters.sampleCount\n ? parameters.sampleCount\n : renderer && renderer.postProcessingPass\n ? renderer && renderer.postProcessingPass.options.sampleCount\n : 1\n\n if (!parameters.shaders) {\n parameters.shaders = {}\n }\n\n if (!parameters.shaders.fragment) {\n parameters.shaders.fragment = {\n code: default_pass_fsWGSl,\n entryPoint: 'main',\n }\n }\n\n // force the postprocessing passes to not use depth\n parameters.depth = false\n\n super(renderer, parameters)\n\n if (parameters.inputTarget) {\n this.setInputTarget(parameters.inputTarget)\n }\n\n if (this.outputTarget) {\n // patch to match outputTarget if needed\n this.setRenderingOptionsForRenderPass(this.outputTarget.renderPass)\n }\n\n this.type = 'ShaderPass'\n\n this.createRenderTexture({\n label: parameters.label ? `${parameters.label} render texture` : 'Shader pass render texture',\n name: 'renderTexture',\n fromTexture: this.inputTarget ? this.inputTarget.renderTexture : null,\n ...(this.outputTarget &&\n this.outputTarget.options.qualityRatio && { qualityRatio: this.outputTarget.options.qualityRatio }),\n })\n }\n\n /**\n * Get our main {@link RenderTexture}, the one that contains our post processed content\n * @readonly\n */\n get renderTexture(): RenderTexture | undefined {\n return this.renderTextures.find((texture) => texture.options.name === 'renderTexture')\n }\n\n // TODO\n /**\n * Assign or remove a {@link RenderTarget} to this {@link ShaderPass}\n * Since this manipulates the {@link core/scenes/Scene.Scene | Scene} stacks, it can be used to remove a RenderTarget as well.\n * Also copy or remove the {@link RenderTarget#renderTexture | render target render texture} into the {@link ShaderPass} {@link renderTexture}\n * @param inputTarget - the {@link RenderTarget} to assign or null if we want to remove the current {@link RenderTarget}\n */\n setInputTarget(inputTarget: RenderTarget | null) {\n if (inputTarget && inputTarget.type !== 'RenderTarget') {\n throwWarning(`${this.options.label ?? this.type}: inputTarget is not a RenderTarget: ${inputTarget}`)\n return\n }\n\n // ensure the mesh is in the correct scene stack\n this.removeFromScene()\n this.inputTarget = inputTarget\n this.addToScene()\n\n // it might not have been created yet\n if (this.renderTexture) {\n if (inputTarget) {\n this.renderTexture.copy(this.inputTarget.renderTexture)\n } else {\n this.renderTexture.options.fromTexture = null\n this.renderTexture.createTexture()\n }\n }\n }\n\n /**\n * Add the {@link ShaderPass} to the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n addToScene() {\n this.renderer.shaderPasses.push(this)\n\n this.setRenderingOptionsForRenderPass(\n this.outputTarget ? this.outputTarget.renderPass : this.renderer.postProcessingPass\n )\n\n if (this.autoRender) {\n this.renderer.scene.addShaderPass(this)\n }\n }\n\n /**\n * Remove the {@link ShaderPass} from the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n removeFromScene() {\n if (this.outputTarget) {\n this.outputTarget.destroy()\n }\n\n if (this.autoRender) {\n this.renderer.scene.removeShaderPass(this)\n }\n\n this.renderer.shaderPasses = this.renderer.shaderPasses.filter((sP) => sP.uuid !== this.uuid)\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"ShaderPass.mjs","sources":["../../../../src/core/renderPasses/ShaderPass.ts"],"sourcesContent":["import { FullscreenPlane } from '../meshes/FullscreenPlane'\nimport { isRenderer, Renderer } from '../renderers/utils'\nimport { RenderTarget } from './RenderTarget'\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\nimport { MeshBaseOptions, MeshBaseRenderParams } from '../meshes/mixins/MeshBaseMixin'\nimport { RenderTexture } from '../textures/RenderTexture'\nimport default_pass_fsWGSl from '../shaders/chunks/default_pass_fs.wgsl'\nimport { throwWarning } from '../../utils/utils'\n\n/**\n * Parameters used to create a {@link ShaderPass}\n */\nexport interface ShaderPassParams extends MeshBaseRenderParams {\n /** Optional input {@link RenderTarget} to assign to the {@link ShaderPass}. Used to automatically copy the content of the given {@link RenderTarget} texture into the {@link ShaderPass#renderTexture | ShaderPass renderTexture}. */\n inputTarget?: RenderTarget\n\n /** Whether the result of this {@link ShaderPass} should be copied to the {@link ShaderPass#renderTexture | renderTexture} after each render. Default to false. */\n copyOutputToRenderTexture?: boolean\n}\n\nexport interface ShaderPassOptions extends MeshBaseOptions {\n /** Whether the result of this {@link ShaderPass} should be copied to the {@link ShaderPass#renderTexture | renderTexture} after each render. Default to false. */\n copyOutputToRenderTexture?: boolean\n}\n\n/**\n * Used to apply postprocessing, i.e. draw meshes to a {@link RenderTexture} and then draw a {@link FullscreenPlane} using that texture as an input.\n *\n * A ShaderPass could either post process the whole scene or just a bunch of meshes using a specific {@link RenderTarget}.\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 ShaderPass\n * const shaderPass = new ShaderPass(gpuCurtain, {\n * label: 'My shader pass',\n * shaders: {\n * fragment: {\n * code: shaderPassCode, // assume it is a valid WGSL fragment shader\n * },\n * },\n * })\n * ```\n */\nexport class ShaderPass extends FullscreenPlane {\n /** Optional input {@link RenderTarget} to assign to the {@link ShaderPass}. Used to automatically copy the content of the given {@link RenderTarget} texture into the {@link ShaderPass#renderTexture | ShaderPass renderTexture}. */\n inputTarget: RenderTarget | undefined\n\n /** Options used to create this {@link ShaderPass} */\n options: ShaderPassOptions\n\n /**\n * ShaderPass constructor\n * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link ShaderPass}\n * @param parameters - {@link ShaderPassParams | parameters} use to create this {@link ShaderPass}\n */\n constructor(renderer: Renderer | GPUCurtains, parameters: ShaderPassParams = {}) {\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 + ' ShaderPass' : 'ShaderPass')\n\n // force transparency to allow for correct blending between successive passes\n parameters.transparent = true\n parameters.label = parameters.label ?? 'ShaderPass ' + renderer.shaderPasses?.length\n\n // set default sample count to post processing render pass\n parameters.sampleCount = !!parameters.sampleCount\n ? parameters.sampleCount\n : renderer && renderer.postProcessingPass\n ? renderer && renderer.postProcessingPass.options.sampleCount\n : 1\n\n if (!parameters.shaders) {\n parameters.shaders = {}\n }\n\n if (!parameters.shaders.fragment) {\n parameters.shaders.fragment = {\n code: default_pass_fsWGSl,\n entryPoint: 'main',\n }\n }\n\n // force the postprocessing passes to not use depth\n parameters.depth = false\n\n super(renderer, parameters)\n\n if (parameters.inputTarget) {\n this.setInputTarget(parameters.inputTarget)\n }\n\n if (this.outputTarget) {\n // patch to match outputTarget if needed\n this.setRenderingOptionsForRenderPass(this.outputTarget.renderPass)\n }\n\n this.type = 'ShaderPass'\n\n this.createRenderTexture({\n label: parameters.label ? `${parameters.label} render texture` : 'Shader pass render texture',\n name: 'renderTexture',\n fromTexture: this.inputTarget ? this.inputTarget.renderTexture : null,\n ...(this.outputTarget &&\n this.outputTarget.options.qualityRatio && { qualityRatio: this.outputTarget.options.qualityRatio }),\n })\n }\n\n /**\n * Hook used to clean up parameters before sending them to the material.\n * @param parameters - parameters to clean before sending them to the {@link core/materials/RenderMaterial.RenderMaterial | RenderMaterial}\n * @returns - cleaned parameters\n */\n cleanupRenderMaterialParameters(parameters: ShaderPassParams): MeshBaseRenderParams {\n // patch mesh parameters\n delete parameters.copyOutputToRenderTexture\n delete parameters.inputTarget\n\n super.cleanupRenderMaterialParameters(parameters)\n\n return parameters\n }\n\n /**\n * Get our main {@link RenderTexture} that contains the input content to be used by the {@link ShaderPass}. Can also contain the ouputted content if {@link ShaderPassOptions#copyOutputToRenderTexture | copyOutputToRenderTexture} is set to true.\n * @readonly\n */\n get renderTexture(): RenderTexture | undefined {\n return this.renderTextures.find((texture) => texture.options.name === 'renderTexture')\n }\n\n /**\n * Assign or remove an input {@link RenderTarget} to this {@link ShaderPass}, which can be different from what has just been drawn to the {@link core/renderers/GPURenderer.GPURenderer#context | context} current texture.\n *\n * Since this manipulates the {@link core/scenes/Scene.Scene | Scene} stacks, it can be used to remove a RenderTarget as well.\n * Also copy or remove the {@link RenderTarget#renderTexture | render target render texture} into the {@link ShaderPass} {@link renderTexture}\n * @param inputTarget - the {@link RenderTarget} to assign or null if we want to remove the current {@link RenderTarget}\n */\n setInputTarget(inputTarget: RenderTarget | null) {\n if (inputTarget && inputTarget.type !== 'RenderTarget') {\n throwWarning(`${this.options.label ?? this.type}: inputTarget is not a RenderTarget: ${inputTarget}`)\n return\n }\n\n // ensure the mesh is in the correct scene stack\n this.removeFromScene()\n this.inputTarget = inputTarget\n this.addToScene()\n\n // it might not have been created yet\n if (this.renderTexture) {\n if (inputTarget) {\n this.renderTexture.copy(this.inputTarget.renderTexture)\n } else {\n this.renderTexture.options.fromTexture = null\n this.renderTexture.createTexture()\n }\n }\n }\n\n /**\n * Add the {@link ShaderPass} to the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n addToScene() {\n this.renderer.shaderPasses.push(this)\n\n this.setRenderingOptionsForRenderPass(\n this.outputTarget ? this.outputTarget.renderPass : this.renderer.postProcessingPass\n )\n\n if (this.autoRender) {\n this.renderer.scene.addShaderPass(this)\n }\n }\n\n /**\n * Remove the {@link ShaderPass} from the renderer and the {@link core/scenes/Scene.Scene | Scene}\n */\n removeFromScene() {\n if (this.outputTarget) {\n this.outputTarget.destroy()\n }\n\n if (this.autoRender) {\n this.renderer.scene.removeShaderPass(this)\n }\n\n this.renderer.shaderPasses = this.renderer.shaderPasses.filter((sP) => sP.uuid !== this.uuid)\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/esm/core/scenes/Scene.mjs b/dist/esm/core/scenes/Scene.mjs index abd8941e5..01358ff75 100644 --- a/dist/esm/core/scenes/Scene.mjs +++ b/dist/esm/core/scenes/Scene.mjs @@ -183,7 +183,7 @@ class Scene { } this.renderer.postProcessingPass.setLoadOp("clear"); }; - const onAfterRenderPass = shaderPass.outputTarget ? null : (commandEncoder, swapChainTexture) => { + const onAfterRenderPass = !shaderPass.outputTarget && shaderPass.options.copyOutputToRenderTexture ? (commandEncoder, swapChainTexture) => { if (shaderPass.renderTexture && swapChainTexture) { commandEncoder.copyTextureToTexture( { @@ -195,7 +195,7 @@ class Scene { [shaderPass.renderTexture.size.width, shaderPass.renderTexture.size.height] ); } - }; + } : null; const shaderPassEntry = { // use output target or postprocessing render pass renderPass: shaderPass.outputTarget ? shaderPass.outputTarget.renderPass : this.renderer.postProcessingPass, diff --git a/dist/esm/core/scenes/Scene.mjs.map b/dist/esm/core/scenes/Scene.mjs.map index 9a375826a..ba580b75b 100644 --- a/dist/esm/core/scenes/Scene.mjs.map +++ b/dist/esm/core/scenes/Scene.mjs.map @@ -1 +1 @@ -{"version":3,"file":"Scene.mjs","sources":["../../../../src/core/scenes/Scene.ts"],"sourcesContent":["import { CameraRenderer, isRenderer, Renderer } from '../renderers/utils'\nimport { DOMProjectedMesh, ProjectedMesh, RenderedMesh } from '../renderers/GPURenderer'\nimport { ShaderPass } from '../renderPasses/ShaderPass'\nimport { PingPongPlane } from '../../curtains/meshes/PingPongPlane'\nimport { ComputePass } from '../computePasses/ComputePass'\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\nimport { RenderTarget } from '../renderPasses/RenderTarget'\nimport { DOMMesh } from '../../curtains/meshes/DOMMesh'\nimport { Plane } from '../../curtains/meshes/Plane'\nimport { RenderPass } from '../renderPasses/RenderPass'\nimport { RenderTexture } from '../textures/RenderTexture'\n\n/**\n * Meshes rendering order is dependant of their transparency setting\n */\nexport interface ProjectionStack {\n /** opaque Meshes will be drawn first */\n opaque: ProjectedMesh[]\n /** transparent Meshes will be drawn last */\n transparent: ProjectedMesh[]\n}\n\n/** Meshes will be stacked in 2 different objects whether they are projected (use a {@link core/camera/Camera.Camera | Camera}) or not */\nexport type ProjectionType = 'unProjected' | 'projected'\n\n/**\n * Meshes will be put into two stacks of projected/unprojected transparent and opaques meshes arrays\n */\nexport type Stack = Record\n\n/**\n * A RenderPassEntry object is used to group Meshes based on their rendering target\n */\nexport interface RenderPassEntry {\n /** {@link RenderPass} target used onto which render */\n renderPass: RenderPass\n /** {@link RenderTexture} to render to if any (if not specified then this {@link RenderPassEntry} Meshes will be rendered directly to screen) */\n renderTexture: RenderTexture | null\n /** Optional function to execute just before rendering the Meshes, useful for eventual texture copy */\n onBeforeRenderPass: ((commandEncoder?: GPUCommandEncoder, swapChainTexture?: GPUTexture) => void) | null\n /** Optional function to execute just after rendering the Meshes, useful for eventual texture copy */\n onAfterRenderPass: ((commandEncoder?: GPUCommandEncoder, swapChainTexture?: GPUTexture) => void) | null\n /** If this {@link RenderPassEntry} needs to render only one Mesh */\n element: PingPongPlane | ShaderPass | null\n /** If this {@link RenderPassEntry} needs to render multiple Meshes, then use a {@link Stack} object */\n stack: Stack | null\n}\n\n/** Defines all our possible render targets */\nexport type RenderPassEntriesType = 'pingPong' | 'renderTarget' | 'screen'\n/** Defines our render pass entries object */\nexport type RenderPassEntries = Record\n\n/**\n * Used to by the {@link Renderer} render everything that needs to be rendered (compute passes and meshes) in the right order with the right pass descriptors and target textures, perform textures copy at the right time, etc.\n *\n * ## Render order\n *\n * - Run all the {@link ComputePass} first, sorted by their {@link ComputePass#renderOrder | renderOrder}\n * - Then render all {@link renderPassEntries} pingPong entries Meshes, sorted by their {@link PingPongPlane#renderOrder | renderOrder}\n * - Then all Meshes that need to be rendered into specific {@link renderPassEntries} outputTarget entries:\n * - First, the opaque unprojected Meshes (i.e. opaque {@link core/meshes/FullscreenPlane.FullscreenPlane | FullscreenPlane}, if any), sorted by their {@link core/meshes/FullscreenPlane.FullscreenPlane#renderOrder | renderOrder}\n * - Then, the transparent unprojected Meshes (i.e. transparent {@link core/meshes/FullscreenPlane.FullscreenPlane | FullscreenPlane}, if any), sorted by their {@link core/meshes/FullscreenPlane.FullscreenPlane#renderOrder | renderOrder}\n * - Then, the opaque projected Meshes (i.e. opaque {@link core/meshes/Mesh.Mesh | Mesh}, {@link DOMMesh} or {@link Plane}), sorted by their {@link core/meshes/Mesh.Mesh#renderOrder | renderOrder}\n * - Finally, the transparent projected Meshes (i.e. transparent {@link core/meshes/Mesh.Mesh | Mesh}, {@link DOMMesh} or {@link Plane}), sorted by their Z position and then their {@link core/meshes/Mesh.Mesh#renderOrder | renderOrder}\n * - Finally all Meshes that need to be rendered directly to the {@link renderPassEntries} screen (the {@link Renderer} current texture), in the same order than above.\n */\nexport class Scene {\n /** {@link Renderer} used by this {@link Scene} */\n renderer: Renderer\n /** Array of {@link ComputePass} to render, ordered by {@link ComputePass#renderOrder | renderOrder} */\n computePassEntries: ComputePass[]\n /**\n * A {@link RenderPassEntries} object that will contain every Meshes that need to be drawn, put inside each one of our three entries type arrays: 'pingPong', 'outputTarget' and 'screen'.\n * - The {@link Scene} will first render all {@link renderPassEntries} pingPong entries Meshes\n * - Then all Meshes that need to be rendered into specific {@link renderPassEntries} outputTarget entries\n * - Finally all Meshes that need to be rendered to the {@link renderPassEntries} screen\n */\n renderPassEntries: RenderPassEntries\n\n /**\n * Scene constructor\n * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link Scene}\n */\n constructor({ renderer }: { renderer: Renderer | GPUCurtains }) {\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, 'Scene')\n\n this.renderer = renderer\n\n this.computePassEntries = []\n\n this.renderPassEntries = {\n /** Array of {@link RenderPassEntry} that will handle {@link PingPongPlane}. Each {@link PingPongPlane} will be added as a distinct {@link RenderPassEntry} here */\n pingPong: [] as RenderPassEntry[],\n /** Array of {@link RenderPassEntry} that will render to a specific {@link RenderTarget}. Each {@link RenderTarget} will be added as a distinct {@link RenderPassEntry} here */\n renderTarget: [] as RenderPassEntry[],\n /** Array of {@link RenderPassEntry} that will render directly to the screen. Our first entry will contain all the Meshes that do not have any {@link RenderTarget} assigned. Following entries will be created for every global {@link ShaderPass} */\n screen: [\n // add our basic scene entry\n {\n renderPass: this.renderer.renderPass,\n renderTexture: null,\n onBeforeRenderPass: null,\n onAfterRenderPass: null,\n element: null, // explicitly set to null\n stack: {\n unProjected: {\n opaque: [],\n transparent: [],\n },\n projected: {\n opaque: [],\n transparent: [],\n },\n },\n },\n ] as RenderPassEntry[],\n }\n }\n\n /**\n * Get the number of meshes a {@link RenderPassEntry | render pass entry} should draw.\n * @param renderPassEntry - The {@link RenderPassEntry | render pass entry} to test\n */\n getRenderPassEntryLength(renderPassEntry: RenderPassEntry): number {\n if (!renderPassEntry) {\n return 0\n } else {\n return renderPassEntry.element\n ? renderPassEntry.element.visible\n ? 1\n : 0\n : renderPassEntry.stack.unProjected.opaque.length +\n renderPassEntry.stack.unProjected.transparent.length +\n renderPassEntry.stack.projected.opaque.length +\n renderPassEntry.stack.projected.transparent.length\n }\n }\n\n /**\n * Add a {@link ComputePass} to our scene {@link computePassEntries} array\n * @param computePass - {@link ComputePass} to add\n */\n addComputePass(computePass: ComputePass) {\n this.computePassEntries.push(computePass)\n this.computePassEntries.sort((a, b) => {\n if (a.renderOrder !== b.renderOrder) {\n return a.renderOrder - b.renderOrder\n } else {\n return a.index - b.index\n }\n })\n }\n\n /**\n * Remove a {@link ComputePass} from our scene {@link computePassEntries} array\n * @param computePass - {@link ComputePass} to remove\n */\n removeComputePass(computePass: ComputePass) {\n this.computePassEntries = this.computePassEntries.filter((cP) => cP.uuid !== computePass.uuid)\n }\n\n /**\n * Add a {@link RenderTarget} to our scene {@link renderPassEntries} outputTarget array.\n * Every Meshes later added to this {@link RenderTarget} will be rendered to the {@link RenderTarget#renderTexture | RenderTarget RenderTexture} using the {@link RenderTarget#renderPass.descriptor | RenderTarget RenderPass descriptor}\n * @param renderTarget - {@link RenderTarget} to add\n */\n addRenderTarget(renderTarget: RenderTarget) {\n // if RT is not already in the render pass entries\n if (!this.renderPassEntries.renderTarget.find((entry) => entry.renderPass.uuid === renderTarget.renderPass.uuid))\n this.renderPassEntries.renderTarget.push({\n renderPass: renderTarget.renderPass,\n renderTexture: renderTarget.renderTexture,\n onBeforeRenderPass: null,\n onAfterRenderPass: null,\n element: null, // explicitly set to null\n stack: {\n unProjected: {\n opaque: [],\n transparent: [],\n },\n projected: {\n opaque: [],\n transparent: [],\n },\n },\n } as RenderPassEntry)\n }\n\n /**\n * Remove a {@link RenderTarget} from our scene {@link renderPassEntries} outputTarget array.\n * @param renderTarget - {@link RenderTarget} to add\n */\n removeRenderTarget(renderTarget: RenderTarget) {\n this.renderPassEntries.renderTarget = this.renderPassEntries.renderTarget.filter(\n (entry) => entry.renderPass.uuid !== renderTarget.renderPass.uuid\n )\n }\n\n /**\n * Get the correct {@link renderPassEntries | render pass entry} (either {@link renderPassEntries} outputTarget or {@link renderPassEntries} screen) {@link Stack} onto which this Mesh should be added, depending on whether it's projected or not\n * @param mesh - Mesh to check\n * @returns - the corresponding render pass entry {@link Stack}\n */\n getMeshProjectionStack(mesh: ProjectedMesh): ProjectionStack {\n // first get correct render pass enty and stack\n const renderPassEntry = mesh.outputTarget\n ? this.renderPassEntries.renderTarget.find(\n (passEntry) => passEntry.renderPass.uuid === mesh.outputTarget.renderPass.uuid\n )\n : this.renderPassEntries.screen[0]\n\n const { stack } = renderPassEntry\n\n return mesh.material.options.rendering.useProjection ? stack.projected : stack.unProjected\n }\n\n /**\n * Add a Mesh to the correct {@link renderPassEntries | render pass entry} {@link Stack} array.\n * Meshes are then ordered by their {@link core/meshes/mixins/MeshBaseMixin.MeshBaseClass#index | indexes (order of creation]}, position along the Z axis in case they are transparent and then {@link core/meshes/mixins/MeshBaseMixin.MeshBaseClass#renderOrder | renderOrder}\n * @param mesh - Mesh to add\n */\n addMesh(mesh: ProjectedMesh) {\n const projectionStack = this.getMeshProjectionStack(mesh)\n\n // rebuild stack\n const similarMeshes = mesh.transparent ? [...projectionStack.transparent] : [...projectionStack.opaque]\n\n // find if there's already a plane with the same pipeline with a findLastIndex function\n let siblingMeshIndex = -1\n\n for (let i = similarMeshes.length - 1; i >= 0; i--) {\n if (similarMeshes[i].material.pipelineEntry.index === mesh.material.pipelineEntry.index) {\n siblingMeshIndex = i + 1\n break\n }\n }\n\n // if findIndex returned -1 (no matching pipeline)\n siblingMeshIndex = Math.max(0, siblingMeshIndex)\n\n // add it to our stack plane array\n similarMeshes.splice(siblingMeshIndex, 0, mesh)\n similarMeshes.sort((a, b) => a.index - b.index)\n\n // sort by Z pos if transparent\n if ((mesh instanceof DOMMesh || mesh instanceof Plane) && mesh.transparent) {\n similarMeshes.sort(\n (a, b) => (b as DOMProjectedMesh).documentPosition.z - (a as DOMProjectedMesh).documentPosition.z\n )\n }\n\n // then sort by their render order\n similarMeshes.sort((a, b) => a.renderOrder - b.renderOrder)\n\n mesh.transparent ? (projectionStack.transparent = similarMeshes) : (projectionStack.opaque = similarMeshes)\n }\n\n /**\n * Remove a Mesh from our {@link Scene}\n * @param mesh - Mesh to remove\n */\n removeMesh(mesh: ProjectedMesh) {\n const projectionStack = this.getMeshProjectionStack(mesh)\n\n if (mesh.transparent) {\n projectionStack.transparent = projectionStack.transparent.filter((m) => m.uuid !== mesh.uuid)\n } else {\n projectionStack.opaque = projectionStack.opaque.filter((m) => m.uuid !== mesh.uuid)\n }\n }\n\n /**\n * Add a {@link ShaderPass} to our scene {@link renderPassEntries} screen array.\n * Before rendering the {@link ShaderPass}, we will copy the correct input texture into its {@link ShaderPass#renderTexture | renderTexture}\n * This also handles the {@link renderPassEntries} screen array entries order: We will first draw selective passes, then our main screen pass and finally global post processing passes.\n * @see {@link https://codesandbox.io/p/sandbox/webgpu-render-to-2-textures-without-texture-copy-c4sx4s?file=%2Fsrc%2Findex.js%3A10%2C4 | minimal code example}\n * @param shaderPass - {@link ShaderPass} to add\n */\n addShaderPass(shaderPass: ShaderPass) {\n const onBeforeRenderPass =\n shaderPass.inputTarget || shaderPass.outputTarget\n ? null\n : (commandEncoder, swapChainTexture) => {\n // draw the content into our render texture\n // if it's a global postprocessing pass, copy the context current texture into its renderTexture\n // we don't need to do that if it has an inputTarget\n // because in this case its renderTexture is already a copy of the render target content\n if (shaderPass.renderTexture && swapChainTexture) {\n commandEncoder.copyTextureToTexture(\n {\n texture: swapChainTexture,\n },\n {\n texture: shaderPass.renderTexture.texture,\n },\n [shaderPass.renderTexture.size.width, shaderPass.renderTexture.size.height]\n )\n }\n\n // if we want to post process the whole scene, clear render pass content\n this.renderer.postProcessingPass.setLoadOp('clear')\n }\n\n const onAfterRenderPass = shaderPass.outputTarget\n ? null\n : (commandEncoder, swapChainTexture) => {\n // if we rendered to the screen,\n // copy the context current texture result back into the shaderPass renderTexture\n if (shaderPass.renderTexture && swapChainTexture) {\n commandEncoder.copyTextureToTexture(\n {\n texture: swapChainTexture,\n },\n {\n texture: shaderPass.renderTexture.texture,\n },\n [shaderPass.renderTexture.size.width, shaderPass.renderTexture.size.height]\n )\n }\n }\n\n const shaderPassEntry = {\n // use output target or postprocessing render pass\n renderPass: shaderPass.outputTarget ? shaderPass.outputTarget.renderPass : this.renderer.postProcessingPass,\n // render to output target renderTexture or directly to screen\n renderTexture: shaderPass.outputTarget ? shaderPass.outputTarget.renderTexture : null,\n onBeforeRenderPass,\n onAfterRenderPass,\n element: shaderPass,\n stack: null, // explicitly set to null\n }\n\n this.renderPassEntries.screen.push(shaderPassEntry)\n\n // screen passes are sorted by 2 criteria\n // first we draw render passes that have an output target OR our main render pass, ordered by renderOrder\n // then we draw our full postprocessing pass, ordered by renderOrder\n this.renderPassEntries.screen.sort((a, b) => {\n const isPostProA = a.element && !a.element.outputTarget\n const renderOrderA = a.element ? a.element.renderOrder : 0\n const indexA = a.element ? a.element.index : 0\n\n const isPostProB = b.element && !b.element.outputTarget\n const renderOrderB = b.element ? b.element.renderOrder : 0\n const indexB = b.element ? b.element.index : 0\n\n if (isPostProA && !isPostProB) {\n return 1\n } else if (!isPostProA && isPostProB) {\n return -1\n } else if (renderOrderA !== renderOrderB) {\n return renderOrderA - renderOrderB\n } else {\n return indexA - indexB\n }\n })\n }\n\n /**\n * Remove a {@link ShaderPass} from our scene {@link renderPassEntries} screen array\n * @param shaderPass - {@link ShaderPass} to remove\n */\n removeShaderPass(shaderPass: ShaderPass) {\n this.renderPassEntries.screen = this.renderPassEntries.screen.filter(\n (entry) => !entry.element || entry.element.uuid !== shaderPass.uuid\n )\n }\n\n /**\n * Add a {@link PingPongPlane} to our scene {@link renderPassEntries} pingPong array.\n * After rendering the {@link PingPongPlane}, we will copy the context current texture into its {@link PingPongPlane#renderTexture | renderTexture} so we'll be able to use it as an input for the next pass\n * @see {@link https://codesandbox.io/p/sandbox/webgpu-render-ping-pong-to-texture-use-in-quad-gwjx9p | minimal code example}\n * @param pingPongPlane\n */\n addPingPongPlane(pingPongPlane: PingPongPlane) {\n this.renderPassEntries.pingPong.push({\n renderPass: pingPongPlane.outputTarget.renderPass,\n renderTexture: pingPongPlane.outputTarget.renderTexture,\n onBeforeRenderPass: null,\n onAfterRenderPass: (commandEncoder, swapChainTexture) => {\n // Copy the rendering results from the swapChainTexture into our |pingPongPlane texture|.\n commandEncoder.copyTextureToTexture(\n {\n texture: swapChainTexture,\n },\n {\n texture: pingPongPlane.renderTexture.texture,\n },\n [pingPongPlane.renderTexture.size.width, pingPongPlane.renderTexture.size.height]\n )\n },\n element: pingPongPlane,\n stack: null, // explicitly set to null\n } as RenderPassEntry)\n\n // sort by their render order\n this.renderPassEntries.pingPong.sort((a, b) => a.element.renderOrder - b.element.renderOrder)\n }\n\n /**\n * Remove a {@link PingPongPlane} from our scene {@link renderPassEntries} pingPong array.\n * @param pingPongPlane - {@link PingPongPlane} to remove\n */\n removePingPongPlane(pingPongPlane: PingPongPlane) {\n this.renderPassEntries.pingPong = this.renderPassEntries.pingPong.filter(\n (entry) => entry.element.uuid !== pingPongPlane.uuid\n )\n }\n\n /**\n * Get any rendered object or {@link RenderTarget} {@link RenderPassEntry}. Useful to override a {@link RenderPassEntry#onBeforeRenderPass | RenderPassEntry onBeforeRenderPass} or {@link RenderPassEntry#onAfterRenderPass | RenderPassEntry onAfterRenderPass} default behavior.\n * @param object - The object from which we want to get the parentMesh {@link RenderPassEntry}\n * @returns - the {@link RenderPassEntry} if found\n */\n getObjectRenderPassEntry(object: RenderedMesh | RenderTarget): RenderPassEntry | undefined {\n if (object instanceof RenderTarget) {\n return this.renderPassEntries.renderTarget.find((entry) => entry.renderPass.uuid === object.renderPass.uuid)\n } else if (object instanceof PingPongPlane) {\n return this.renderPassEntries.pingPong.find((entry) => entry.element.uuid === object.uuid)\n } else if (object instanceof ShaderPass) {\n return this.renderPassEntries.screen.find((entry) => entry.element?.uuid === object.uuid)\n } else {\n const entryType = object.outputTarget ? 'renderTarget' : 'screen'\n return this.renderPassEntries[entryType].find((entry) => {\n return [\n ...entry.stack.unProjected.opaque,\n ...entry.stack.unProjected.transparent,\n ...entry.stack.projected.opaque,\n ...entry.stack.projected.transparent,\n ].some((mesh) => mesh.uuid === object.uuid)\n })\n }\n }\n\n /**\n * Here we render a {@link RenderPassEntry}:\n * - Set its {@link RenderPass#descriptor | renderPass descriptor} view or resolveTarget and get it at as swap chain texture\n * - Execute {@link RenderPassEntry#onBeforeRenderPass | onBeforeRenderPass} callback if specified\n * - Begin the {@link GPURenderPassEncoder | GPU render pass encoder} using our {@link RenderPass#descriptor | renderPass descriptor}\n * - Render the single element if specified or the render pass entry {@link Stack}: draw unprojected opaque / transparent meshes first, then set the {@link CameraRenderer#cameraBindGroup | camera bind group} and draw projected opaque / transparent meshes\n * - End the {@link GPURenderPassEncoder | GPU render pass encoder}\n * - Execute {@link RenderPassEntry#onAfterRenderPass | onAfterRenderPass} callback if specified\n * - Reset {@link core/pipelines/PipelineManager.PipelineManager#currentPipelineIndex | pipeline manager current pipeline}\n * @param commandEncoder - current {@link GPUCommandEncoder}\n * @param renderPassEntry - {@link RenderPassEntry} to render\n */\n renderSinglePassEntry(commandEncoder: GPUCommandEncoder, renderPassEntry: RenderPassEntry) {\n // set the pass texture to render to\n const swapChainTexture = renderPassEntry.renderPass.updateView(renderPassEntry.renderTexture?.texture)\n\n renderPassEntry.onBeforeRenderPass && renderPassEntry.onBeforeRenderPass(commandEncoder, swapChainTexture)\n\n // now begin our actual render pass\n const pass = commandEncoder.beginRenderPass(renderPassEntry.renderPass.descriptor)\n !this.renderer.production &&\n pass.pushDebugGroup(\n renderPassEntry.element\n ? `${renderPassEntry.element.options.label} render pass using ${renderPassEntry.renderPass.options.label} descriptor`\n : `Render stack pass using ${renderPassEntry.renderPass.options.label}${\n renderPassEntry.renderTexture ? ' onto ' + renderPassEntry.renderTexture.options.label : ''\n }`\n )\n\n // pass entries can have a single element or a stack\n if (renderPassEntry.element) {\n renderPassEntry.element.render(pass)\n } else if (renderPassEntry.stack) {\n // draw unProjected regular meshes\n renderPassEntry.stack.unProjected.opaque.forEach((mesh) => mesh.render(pass))\n renderPassEntry.stack.unProjected.transparent.forEach((mesh) => mesh.render(pass))\n\n // then draw projected meshes\n if (renderPassEntry.stack.projected.opaque.length || renderPassEntry.stack.projected.transparent.length) {\n if ((this.renderer as CameraRenderer).cameraBindGroup) {\n // set camera bind group once\n pass.setBindGroup(\n (this.renderer as CameraRenderer).cameraBindGroup.index,\n (this.renderer as CameraRenderer).cameraBindGroup.bindGroup\n )\n }\n\n renderPassEntry.stack.projected.opaque.forEach((mesh) => mesh.render(pass))\n renderPassEntry.stack.projected.transparent.forEach((mesh) => mesh.render(pass))\n }\n }\n\n !this.renderer.production && pass.popDebugGroup()\n pass.end()\n\n renderPassEntry.onAfterRenderPass && renderPassEntry.onAfterRenderPass(commandEncoder, swapChainTexture)\n\n this.renderer.pipelineManager.resetCurrentPipeline()\n }\n\n /**\n * Render our {@link Scene}\n * - Render {@link computePassEntries} first\n * - Then our {@link renderPassEntries}\n * @param commandEncoder - current {@link GPUCommandEncoder}\n */\n render(commandEncoder: GPUCommandEncoder) {\n this.computePassEntries.forEach((computePass) => {\n const pass = commandEncoder.beginComputePass()\n computePass.render(pass)\n pass.end()\n\n computePass.copyBufferToResult(commandEncoder)\n\n this.renderer.pipelineManager.resetCurrentPipeline()\n })\n\n for (const renderPassEntryType in this.renderPassEntries) {\n let passDrawnCount = 0\n\n this.renderPassEntries[renderPassEntryType].forEach((renderPassEntry) => {\n // early bail if there's nothing to draw\n if (!this.getRenderPassEntryLength(renderPassEntry)) return\n\n // if we're drawing to screen and it's not our first pass, load result from previous passes\n // post processing scene pass will clear content inside onBeforeRenderPass anyway\n renderPassEntry.renderPass.setLoadOp(\n renderPassEntryType === 'screen' && passDrawnCount !== 0 ? 'load' : 'clear'\n )\n\n passDrawnCount++\n\n this.renderSinglePassEntry(commandEncoder, renderPassEntry)\n })\n }\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Scene.mjs","sources":["../../../../src/core/scenes/Scene.ts"],"sourcesContent":["import { CameraRenderer, isRenderer, Renderer } from '../renderers/utils'\nimport { DOMProjectedMesh, ProjectedMesh, RenderedMesh } from '../renderers/GPURenderer'\nimport { ShaderPass } from '../renderPasses/ShaderPass'\nimport { PingPongPlane } from '../../curtains/meshes/PingPongPlane'\nimport { ComputePass } from '../computePasses/ComputePass'\nimport { GPUCurtains } from '../../curtains/GPUCurtains'\nimport { RenderTarget } from '../renderPasses/RenderTarget'\nimport { DOMMesh } from '../../curtains/meshes/DOMMesh'\nimport { Plane } from '../../curtains/meshes/Plane'\nimport { RenderPass } from '../renderPasses/RenderPass'\nimport { RenderTexture } from '../textures/RenderTexture'\n\n/**\n * Meshes rendering order is dependant of their transparency setting\n */\nexport interface ProjectionStack {\n /** opaque Meshes will be drawn first */\n opaque: ProjectedMesh[]\n /** transparent Meshes will be drawn last */\n transparent: ProjectedMesh[]\n}\n\n/** Meshes will be stacked in 2 different objects whether they are projected (use a {@link core/camera/Camera.Camera | Camera}) or not */\nexport type ProjectionType = 'unProjected' | 'projected'\n\n/**\n * Meshes will be put into two stacks of projected/unprojected transparent and opaques meshes arrays\n */\nexport type Stack = Record\n\n/**\n * A RenderPassEntry object is used to group Meshes based on their rendering target\n */\nexport interface RenderPassEntry {\n /** {@link RenderPass} target used onto which render */\n renderPass: RenderPass\n /** {@link RenderTexture} to render to if any (if not specified then this {@link RenderPassEntry} Meshes will be rendered directly to screen) */\n renderTexture: RenderTexture | null\n /** Optional function to execute just before rendering the Meshes, useful for eventual texture copy */\n onBeforeRenderPass: ((commandEncoder?: GPUCommandEncoder, swapChainTexture?: GPUTexture) => void) | null\n /** Optional function to execute just after rendering the Meshes, useful for eventual texture copy */\n onAfterRenderPass: ((commandEncoder?: GPUCommandEncoder, swapChainTexture?: GPUTexture) => void) | null\n /** If this {@link RenderPassEntry} needs to render only one Mesh */\n element: PingPongPlane | ShaderPass | null\n /** If this {@link RenderPassEntry} needs to render multiple Meshes, then use a {@link Stack} object */\n stack: Stack | null\n}\n\n/** Defines all our possible render targets */\nexport type RenderPassEntriesType = 'pingPong' | 'renderTarget' | 'screen'\n/** Defines our render pass entries object */\nexport type RenderPassEntries = Record\n\n/**\n * Used to by the {@link Renderer} render everything that needs to be rendered (compute passes and meshes) in the right order with the right pass descriptors and target textures, perform textures copy at the right time, etc.\n *\n * ## Render order\n *\n * - Run all the {@link ComputePass} first, sorted by their {@link ComputePass#renderOrder | renderOrder}\n * - Then render all {@link renderPassEntries} pingPong entries Meshes, sorted by their {@link PingPongPlane#renderOrder | renderOrder}\n * - Then all Meshes that need to be rendered into specific {@link renderPassEntries} outputTarget entries:\n * - First, the opaque unprojected Meshes (i.e. opaque {@link core/meshes/FullscreenPlane.FullscreenPlane | FullscreenPlane}, if any), sorted by their {@link core/meshes/FullscreenPlane.FullscreenPlane#renderOrder | renderOrder}\n * - Then, the transparent unprojected Meshes (i.e. transparent {@link core/meshes/FullscreenPlane.FullscreenPlane | FullscreenPlane}, if any), sorted by their {@link core/meshes/FullscreenPlane.FullscreenPlane#renderOrder | renderOrder}\n * - Then, the opaque projected Meshes (i.e. opaque {@link core/meshes/Mesh.Mesh | Mesh}, {@link DOMMesh} or {@link Plane}), sorted by their {@link core/meshes/Mesh.Mesh#renderOrder | renderOrder}\n * - Finally, the transparent projected Meshes (i.e. transparent {@link core/meshes/Mesh.Mesh | Mesh}, {@link DOMMesh} or {@link Plane}), sorted by their Z position and then their {@link core/meshes/Mesh.Mesh#renderOrder | renderOrder}\n * - Finally all Meshes that need to be rendered directly to the {@link renderPassEntries} screen (the {@link Renderer} current texture), in the same order than above.\n */\nexport class Scene {\n /** {@link Renderer} used by this {@link Scene} */\n renderer: Renderer\n /** Array of {@link ComputePass} to render, ordered by {@link ComputePass#renderOrder | renderOrder} */\n computePassEntries: ComputePass[]\n /**\n * A {@link RenderPassEntries} object that will contain every Meshes that need to be drawn, put inside each one of our three entries type arrays: 'pingPong', 'outputTarget' and 'screen'.\n * - The {@link Scene} will first render all {@link renderPassEntries} pingPong entries Meshes\n * - Then all Meshes that need to be rendered into specific {@link renderPassEntries} outputTarget entries\n * - Finally all Meshes that need to be rendered to the {@link renderPassEntries} screen\n */\n renderPassEntries: RenderPassEntries\n\n /**\n * Scene constructor\n * @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link Scene}\n */\n constructor({ renderer }: { renderer: Renderer | GPUCurtains }) {\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, 'Scene')\n\n this.renderer = renderer\n\n this.computePassEntries = []\n\n this.renderPassEntries = {\n /** Array of {@link RenderPassEntry} that will handle {@link PingPongPlane}. Each {@link PingPongPlane} will be added as a distinct {@link RenderPassEntry} here */\n pingPong: [] as RenderPassEntry[],\n /** Array of {@link RenderPassEntry} that will render to a specific {@link RenderTarget}. Each {@link RenderTarget} will be added as a distinct {@link RenderPassEntry} here */\n renderTarget: [] as RenderPassEntry[],\n /** Array of {@link RenderPassEntry} that will render directly to the screen. Our first entry will contain all the Meshes that do not have any {@link RenderTarget} assigned. Following entries will be created for every global {@link ShaderPass} */\n screen: [\n // add our basic scene entry\n {\n renderPass: this.renderer.renderPass,\n renderTexture: null,\n onBeforeRenderPass: null,\n onAfterRenderPass: null,\n element: null, // explicitly set to null\n stack: {\n unProjected: {\n opaque: [],\n transparent: [],\n },\n projected: {\n opaque: [],\n transparent: [],\n },\n },\n },\n ] as RenderPassEntry[],\n }\n }\n\n /**\n * Get the number of meshes a {@link RenderPassEntry | render pass entry} should draw.\n * @param renderPassEntry - The {@link RenderPassEntry | render pass entry} to test\n */\n getRenderPassEntryLength(renderPassEntry: RenderPassEntry): number {\n if (!renderPassEntry) {\n return 0\n } else {\n return renderPassEntry.element\n ? renderPassEntry.element.visible\n ? 1\n : 0\n : renderPassEntry.stack.unProjected.opaque.length +\n renderPassEntry.stack.unProjected.transparent.length +\n renderPassEntry.stack.projected.opaque.length +\n renderPassEntry.stack.projected.transparent.length\n }\n }\n\n /**\n * Add a {@link ComputePass} to our scene {@link computePassEntries} array\n * @param computePass - {@link ComputePass} to add\n */\n addComputePass(computePass: ComputePass) {\n this.computePassEntries.push(computePass)\n this.computePassEntries.sort((a, b) => {\n if (a.renderOrder !== b.renderOrder) {\n return a.renderOrder - b.renderOrder\n } else {\n return a.index - b.index\n }\n })\n }\n\n /**\n * Remove a {@link ComputePass} from our scene {@link computePassEntries} array\n * @param computePass - {@link ComputePass} to remove\n */\n removeComputePass(computePass: ComputePass) {\n this.computePassEntries = this.computePassEntries.filter((cP) => cP.uuid !== computePass.uuid)\n }\n\n /**\n * Add a {@link RenderTarget} to our scene {@link renderPassEntries} outputTarget array.\n * Every Meshes later added to this {@link RenderTarget} will be rendered to the {@link RenderTarget#renderTexture | RenderTarget RenderTexture} using the {@link RenderTarget#renderPass.descriptor | RenderTarget RenderPass descriptor}\n * @param renderTarget - {@link RenderTarget} to add\n */\n addRenderTarget(renderTarget: RenderTarget) {\n // if RT is not already in the render pass entries\n if (!this.renderPassEntries.renderTarget.find((entry) => entry.renderPass.uuid === renderTarget.renderPass.uuid))\n this.renderPassEntries.renderTarget.push({\n renderPass: renderTarget.renderPass,\n renderTexture: renderTarget.renderTexture,\n onBeforeRenderPass: null,\n onAfterRenderPass: null,\n element: null, // explicitly set to null\n stack: {\n unProjected: {\n opaque: [],\n transparent: [],\n },\n projected: {\n opaque: [],\n transparent: [],\n },\n },\n } as RenderPassEntry)\n }\n\n /**\n * Remove a {@link RenderTarget} from our scene {@link renderPassEntries} outputTarget array.\n * @param renderTarget - {@link RenderTarget} to add\n */\n removeRenderTarget(renderTarget: RenderTarget) {\n this.renderPassEntries.renderTarget = this.renderPassEntries.renderTarget.filter(\n (entry) => entry.renderPass.uuid !== renderTarget.renderPass.uuid\n )\n }\n\n /**\n * Get the correct {@link renderPassEntries | render pass entry} (either {@link renderPassEntries} outputTarget or {@link renderPassEntries} screen) {@link Stack} onto which this Mesh should be added, depending on whether it's projected or not\n * @param mesh - Mesh to check\n * @returns - the corresponding render pass entry {@link Stack}\n */\n getMeshProjectionStack(mesh: ProjectedMesh): ProjectionStack {\n // first get correct render pass enty and stack\n const renderPassEntry = mesh.outputTarget\n ? this.renderPassEntries.renderTarget.find(\n (passEntry) => passEntry.renderPass.uuid === mesh.outputTarget.renderPass.uuid\n )\n : this.renderPassEntries.screen[0]\n\n const { stack } = renderPassEntry\n\n return mesh.material.options.rendering.useProjection ? stack.projected : stack.unProjected\n }\n\n /**\n * Add a Mesh to the correct {@link renderPassEntries | render pass entry} {@link Stack} array.\n * Meshes are then ordered by their {@link core/meshes/mixins/MeshBaseMixin.MeshBaseClass#index | indexes (order of creation]}, position along the Z axis in case they are transparent and then {@link core/meshes/mixins/MeshBaseMixin.MeshBaseClass#renderOrder | renderOrder}\n * @param mesh - Mesh to add\n */\n addMesh(mesh: ProjectedMesh) {\n const projectionStack = this.getMeshProjectionStack(mesh)\n\n // rebuild stack\n const similarMeshes = mesh.transparent ? [...projectionStack.transparent] : [...projectionStack.opaque]\n\n // find if there's already a plane with the same pipeline with a findLastIndex function\n let siblingMeshIndex = -1\n\n for (let i = similarMeshes.length - 1; i >= 0; i--) {\n if (similarMeshes[i].material.pipelineEntry.index === mesh.material.pipelineEntry.index) {\n siblingMeshIndex = i + 1\n break\n }\n }\n\n // if findIndex returned -1 (no matching pipeline)\n siblingMeshIndex = Math.max(0, siblingMeshIndex)\n\n // add it to our stack plane array\n similarMeshes.splice(siblingMeshIndex, 0, mesh)\n similarMeshes.sort((a, b) => a.index - b.index)\n\n // sort by Z pos if transparent\n if ((mesh instanceof DOMMesh || mesh instanceof Plane) && mesh.transparent) {\n similarMeshes.sort(\n (a, b) => (b as DOMProjectedMesh).documentPosition.z - (a as DOMProjectedMesh).documentPosition.z\n )\n }\n\n // then sort by their render order\n similarMeshes.sort((a, b) => a.renderOrder - b.renderOrder)\n\n mesh.transparent ? (projectionStack.transparent = similarMeshes) : (projectionStack.opaque = similarMeshes)\n }\n\n /**\n * Remove a Mesh from our {@link Scene}\n * @param mesh - Mesh to remove\n */\n removeMesh(mesh: ProjectedMesh) {\n const projectionStack = this.getMeshProjectionStack(mesh)\n\n if (mesh.transparent) {\n projectionStack.transparent = projectionStack.transparent.filter((m) => m.uuid !== mesh.uuid)\n } else {\n projectionStack.opaque = projectionStack.opaque.filter((m) => m.uuid !== mesh.uuid)\n }\n }\n\n /**\n * Add a {@link ShaderPass} to our scene {@link renderPassEntries} screen array.\n * Before rendering the {@link ShaderPass}, we will copy the correct input texture into its {@link ShaderPass#renderTexture | renderTexture}\n * This also handles the {@link renderPassEntries} screen array entries order: We will first draw selective passes, then our main screen pass and finally global post processing passes.\n * @see {@link https://codesandbox.io/p/sandbox/webgpu-render-to-2-textures-without-texture-copy-c4sx4s?file=%2Fsrc%2Findex.js%3A10%2C4 | minimal code example}\n * @param shaderPass - {@link ShaderPass} to add\n */\n addShaderPass(shaderPass: ShaderPass) {\n const onBeforeRenderPass =\n shaderPass.inputTarget || shaderPass.outputTarget\n ? null\n : (commandEncoder, swapChainTexture) => {\n // draw the content into our render texture\n // if it's a global postprocessing pass, copy the context current texture into its renderTexture\n // we don't need to do that if it has an inputTarget\n // because in this case its renderTexture is already a copy of the render target content\n if (shaderPass.renderTexture && swapChainTexture) {\n commandEncoder.copyTextureToTexture(\n {\n texture: swapChainTexture,\n },\n {\n texture: shaderPass.renderTexture.texture,\n },\n [shaderPass.renderTexture.size.width, shaderPass.renderTexture.size.height]\n )\n }\n\n // if we want to post process the whole scene, clear render pass content\n this.renderer.postProcessingPass.setLoadOp('clear')\n }\n\n const onAfterRenderPass =\n !shaderPass.outputTarget && shaderPass.options.copyOutputToRenderTexture\n ? (commandEncoder, swapChainTexture) => {\n // if we rendered to the screen,\n // copy the context current texture result back into the shaderPass renderTexture\n if (shaderPass.renderTexture && swapChainTexture) {\n commandEncoder.copyTextureToTexture(\n {\n texture: swapChainTexture,\n },\n {\n texture: shaderPass.renderTexture.texture,\n },\n [shaderPass.renderTexture.size.width, shaderPass.renderTexture.size.height]\n )\n }\n }\n : null\n\n const shaderPassEntry = {\n // use output target or postprocessing render pass\n renderPass: shaderPass.outputTarget ? shaderPass.outputTarget.renderPass : this.renderer.postProcessingPass,\n // render to output target renderTexture or directly to screen\n renderTexture: shaderPass.outputTarget ? shaderPass.outputTarget.renderTexture : null,\n onBeforeRenderPass,\n onAfterRenderPass,\n element: shaderPass,\n stack: null, // explicitly set to null\n }\n\n this.renderPassEntries.screen.push(shaderPassEntry)\n\n // screen passes are sorted by 2 criteria\n // first we draw render passes that have an output target OR our main render pass, ordered by renderOrder\n // then we draw our full postprocessing pass, ordered by renderOrder\n this.renderPassEntries.screen.sort((a, b) => {\n const isPostProA = a.element && !a.element.outputTarget\n const renderOrderA = a.element ? a.element.renderOrder : 0\n const indexA = a.element ? a.element.index : 0\n\n const isPostProB = b.element && !b.element.outputTarget\n const renderOrderB = b.element ? b.element.renderOrder : 0\n const indexB = b.element ? b.element.index : 0\n\n if (isPostProA && !isPostProB) {\n return 1\n } else if (!isPostProA && isPostProB) {\n return -1\n } else if (renderOrderA !== renderOrderB) {\n return renderOrderA - renderOrderB\n } else {\n return indexA - indexB\n }\n })\n }\n\n /**\n * Remove a {@link ShaderPass} from our scene {@link renderPassEntries} screen array\n * @param shaderPass - {@link ShaderPass} to remove\n */\n removeShaderPass(shaderPass: ShaderPass) {\n this.renderPassEntries.screen = this.renderPassEntries.screen.filter(\n (entry) => !entry.element || entry.element.uuid !== shaderPass.uuid\n )\n }\n\n /**\n * Add a {@link PingPongPlane} to our scene {@link renderPassEntries} pingPong array.\n * After rendering the {@link PingPongPlane}, we will copy the context current texture into its {@link PingPongPlane#renderTexture | renderTexture} so we'll be able to use it as an input for the next pass\n * @see {@link https://codesandbox.io/p/sandbox/webgpu-render-ping-pong-to-texture-use-in-quad-gwjx9p | minimal code example}\n * @param pingPongPlane\n */\n addPingPongPlane(pingPongPlane: PingPongPlane) {\n this.renderPassEntries.pingPong.push({\n renderPass: pingPongPlane.outputTarget.renderPass,\n renderTexture: pingPongPlane.outputTarget.renderTexture,\n onBeforeRenderPass: null,\n onAfterRenderPass: (commandEncoder, swapChainTexture) => {\n // Copy the rendering results from the swapChainTexture into our |pingPongPlane texture|.\n commandEncoder.copyTextureToTexture(\n {\n texture: swapChainTexture,\n },\n {\n texture: pingPongPlane.renderTexture.texture,\n },\n [pingPongPlane.renderTexture.size.width, pingPongPlane.renderTexture.size.height]\n )\n },\n element: pingPongPlane,\n stack: null, // explicitly set to null\n } as RenderPassEntry)\n\n // sort by their render order\n this.renderPassEntries.pingPong.sort((a, b) => a.element.renderOrder - b.element.renderOrder)\n }\n\n /**\n * Remove a {@link PingPongPlane} from our scene {@link renderPassEntries} pingPong array.\n * @param pingPongPlane - {@link PingPongPlane} to remove\n */\n removePingPongPlane(pingPongPlane: PingPongPlane) {\n this.renderPassEntries.pingPong = this.renderPassEntries.pingPong.filter(\n (entry) => entry.element.uuid !== pingPongPlane.uuid\n )\n }\n\n /**\n * Get any rendered object or {@link RenderTarget} {@link RenderPassEntry}. Useful to override a {@link RenderPassEntry#onBeforeRenderPass | RenderPassEntry onBeforeRenderPass} or {@link RenderPassEntry#onAfterRenderPass | RenderPassEntry onAfterRenderPass} default behavior.\n * @param object - The object from which we want to get the parentMesh {@link RenderPassEntry}\n * @returns - the {@link RenderPassEntry} if found\n */\n getObjectRenderPassEntry(object: RenderedMesh | RenderTarget): RenderPassEntry | undefined {\n if (object instanceof RenderTarget) {\n return this.renderPassEntries.renderTarget.find((entry) => entry.renderPass.uuid === object.renderPass.uuid)\n } else if (object instanceof PingPongPlane) {\n return this.renderPassEntries.pingPong.find((entry) => entry.element.uuid === object.uuid)\n } else if (object instanceof ShaderPass) {\n return this.renderPassEntries.screen.find((entry) => entry.element?.uuid === object.uuid)\n } else {\n const entryType = object.outputTarget ? 'renderTarget' : 'screen'\n return this.renderPassEntries[entryType].find((entry) => {\n return [\n ...entry.stack.unProjected.opaque,\n ...entry.stack.unProjected.transparent,\n ...entry.stack.projected.opaque,\n ...entry.stack.projected.transparent,\n ].some((mesh) => mesh.uuid === object.uuid)\n })\n }\n }\n\n /**\n * Here we render a {@link RenderPassEntry}:\n * - Set its {@link RenderPass#descriptor | renderPass descriptor} view or resolveTarget and get it at as swap chain texture\n * - Execute {@link RenderPassEntry#onBeforeRenderPass | onBeforeRenderPass} callback if specified\n * - Begin the {@link GPURenderPassEncoder | GPU render pass encoder} using our {@link RenderPass#descriptor | renderPass descriptor}\n * - Render the single element if specified or the render pass entry {@link Stack}: draw unprojected opaque / transparent meshes first, then set the {@link CameraRenderer#cameraBindGroup | camera bind group} and draw projected opaque / transparent meshes\n * - End the {@link GPURenderPassEncoder | GPU render pass encoder}\n * - Execute {@link RenderPassEntry#onAfterRenderPass | onAfterRenderPass} callback if specified\n * - Reset {@link core/pipelines/PipelineManager.PipelineManager#currentPipelineIndex | pipeline manager current pipeline}\n * @param commandEncoder - current {@link GPUCommandEncoder}\n * @param renderPassEntry - {@link RenderPassEntry} to render\n */\n renderSinglePassEntry(commandEncoder: GPUCommandEncoder, renderPassEntry: RenderPassEntry) {\n // set the pass texture to render to\n const swapChainTexture = renderPassEntry.renderPass.updateView(renderPassEntry.renderTexture?.texture)\n\n renderPassEntry.onBeforeRenderPass && renderPassEntry.onBeforeRenderPass(commandEncoder, swapChainTexture)\n\n // now begin our actual render pass\n const pass = commandEncoder.beginRenderPass(renderPassEntry.renderPass.descriptor)\n !this.renderer.production &&\n pass.pushDebugGroup(\n renderPassEntry.element\n ? `${renderPassEntry.element.options.label} render pass using ${renderPassEntry.renderPass.options.label} descriptor`\n : `Render stack pass using ${renderPassEntry.renderPass.options.label}${\n renderPassEntry.renderTexture ? ' onto ' + renderPassEntry.renderTexture.options.label : ''\n }`\n )\n\n // pass entries can have a single element or a stack\n if (renderPassEntry.element) {\n renderPassEntry.element.render(pass)\n } else if (renderPassEntry.stack) {\n // draw unProjected regular meshes\n renderPassEntry.stack.unProjected.opaque.forEach((mesh) => mesh.render(pass))\n renderPassEntry.stack.unProjected.transparent.forEach((mesh) => mesh.render(pass))\n\n // then draw projected meshes\n if (renderPassEntry.stack.projected.opaque.length || renderPassEntry.stack.projected.transparent.length) {\n if ((this.renderer as CameraRenderer).cameraBindGroup) {\n // set camera bind group once\n pass.setBindGroup(\n (this.renderer as CameraRenderer).cameraBindGroup.index,\n (this.renderer as CameraRenderer).cameraBindGroup.bindGroup\n )\n }\n\n renderPassEntry.stack.projected.opaque.forEach((mesh) => mesh.render(pass))\n renderPassEntry.stack.projected.transparent.forEach((mesh) => mesh.render(pass))\n }\n }\n\n !this.renderer.production && pass.popDebugGroup()\n pass.end()\n\n renderPassEntry.onAfterRenderPass && renderPassEntry.onAfterRenderPass(commandEncoder, swapChainTexture)\n\n this.renderer.pipelineManager.resetCurrentPipeline()\n }\n\n /**\n * Render our {@link Scene}\n * - Render {@link computePassEntries} first\n * - Then our {@link renderPassEntries}\n * @param commandEncoder - current {@link GPUCommandEncoder}\n */\n render(commandEncoder: GPUCommandEncoder) {\n this.computePassEntries.forEach((computePass) => {\n const pass = commandEncoder.beginComputePass()\n computePass.render(pass)\n pass.end()\n\n computePass.copyBufferToResult(commandEncoder)\n\n this.renderer.pipelineManager.resetCurrentPipeline()\n })\n\n for (const renderPassEntryType in this.renderPassEntries) {\n let passDrawnCount = 0\n\n this.renderPassEntries[renderPassEntryType].forEach((renderPassEntry) => {\n // early bail if there's nothing to draw\n if (!this.getRenderPassEntryLength(renderPassEntry)) return\n\n // if we're drawing to screen and it's not our first pass, load result from previous passes\n // post processing scene pass will clear content inside onBeforeRenderPass anyway\n renderPassEntry.renderPass.setLoadOp(\n renderPassEntryType === 'screen' && passDrawnCount !== 0 ? 'load' : 'clear'\n )\n\n passDrawnCount++\n\n this.renderSinglePassEntry(commandEncoder, renderPassEntry)\n })\n }\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/esm/utils/debug.mjs b/dist/esm/utils/debug.mjs index 74681bbcc..86143c5c4 100644 --- a/dist/esm/utils/debug.mjs +++ b/dist/esm/utils/debug.mjs @@ -29,14 +29,15 @@ const logSceneCommands = (renderer) => { let descriptor = renderPassEntry.renderPass.options.label; const operations = { loadOp: renderPassEntry.renderPass.options.useColorAttachments ? renderPassEntryType === "screen" && passDrawnCount > 0 ? "load" : renderPassEntry.renderPass.options.loadOp : void 0, - depthLoadOp: void 0 + depthLoadOp: void 0, + sampleCount: renderPassEntry.renderPass.options.sampleCount }; if (renderPassEntry.renderPass.options.useDepth) { operations.depthLoadOp = renderPassEntry.renderPass.options.depthLoadOp; } passDrawnCount++; if (renderPassEntry.element) { - if (renderPassEntry.element.type === "ShaderPass" && !renderPassEntry.element.renderTarget) { + if (renderPassEntry.element.type === "ShaderPass" && !(renderPassEntry.element.inputTarget || renderPassEntry.element.outputTarget)) { renderCommands.push({ command: `Copy texture to texture`, source: destination, @@ -51,11 +52,11 @@ const logSceneCommands = (renderer) => { destination, descriptor }); - if (renderPassEntry.element.type === "ShaderPass" && renderPassEntry.element.renderTarget) { + if (renderPassEntry.element.type === "ShaderPass" && !renderPassEntry.element.outputTarget && renderPassEntry.element.options.copyOutputToRenderTexture) { renderCommands.push({ command: `Copy texture to texture`, source: destination, - destination: `${renderPassEntry.element.renderTarget.options.label} renderTexture` + destination: `${renderPassEntry.element.options.label} renderTexture` }); } else if (renderPassEntry.element.type === "PingPongPlane") { renderCommands.push({ diff --git a/dist/esm/utils/debug.mjs.map b/dist/esm/utils/debug.mjs.map index 4d1ef64ff..2a822a7df 100644 --- a/dist/esm/utils/debug.mjs.map +++ b/dist/esm/utils/debug.mjs.map @@ -1 +1 @@ -{"version":3,"file":"debug.mjs","sources":["../../../src/utils/debug.ts"],"sourcesContent":["import { WritableBufferBinding } from '../core/bindings/WritableBufferBinding'\nimport { Renderer } from '../core/renderers/utils'\n\n/**\n * Logs all the main commands executed during each {@link core/scenes/Scene.Scene#render | Scene render} calls.\n */\nexport const logSceneCommands = (renderer: Renderer) => {\n const { scene } = renderer\n\n if (!scene) return\n\n const renderCommands = []\n\n scene.computePassEntries.forEach((computePass) => {\n renderCommands.push({\n command: 'Render ComputePass',\n content: computePass.options.label,\n })\n\n computePass.material.bindGroups.forEach((bindGroup) => {\n bindGroup.bufferBindings.forEach((binding: WritableBufferBinding) => {\n if (binding.shouldCopyResult) {\n renderCommands.push({\n command: `Copy buffer to buffer`,\n source: `${binding.name} buffer`,\n destination: `${binding.name} result buffer`,\n })\n }\n })\n })\n })\n\n for (const renderPassEntryType in scene.renderPassEntries) {\n let passDrawnCount = 0\n\n scene.renderPassEntries[renderPassEntryType].forEach((renderPassEntry) => {\n // early bail if there's nothing to draw\n if (!scene.getRenderPassEntryLength(renderPassEntry)) return\n\n const destination = !renderPassEntry.renderPass.options.useColorAttachments\n ? undefined\n : renderPassEntry.renderTexture\n ? `${renderPassEntry.renderTexture.options.label}`\n : renderPassEntry.renderPass.options.colorAttachments.length > 1\n ? 'Multiple render target'\n : 'Context current texture'\n\n let descriptor = renderPassEntry.renderPass.options.label\n\n const operations = {\n loadOp: renderPassEntry.renderPass.options.useColorAttachments\n ? renderPassEntryType === 'screen' && passDrawnCount > 0\n ? 'load'\n : renderPassEntry.renderPass.options.loadOp\n : undefined,\n depthLoadOp: undefined,\n }\n\n if (renderPassEntry.renderPass.options.useDepth) {\n operations.depthLoadOp = renderPassEntry.renderPass.options.depthLoadOp\n }\n\n passDrawnCount++\n\n if (renderPassEntry.element) {\n if (renderPassEntry.element.type === 'ShaderPass' && !renderPassEntry.element.renderTarget) {\n renderCommands.push({\n command: `Copy texture to texture`,\n source: destination,\n destination: `${renderPassEntry.element.options.label} renderTexture`,\n })\n\n operations.loadOp = 'clear'\n }\n\n descriptor += ' ' + JSON.stringify(operations)\n\n renderCommands.push({\n command: `Render ${renderPassEntry.element.type}`,\n source: renderPassEntry.element.options.label,\n destination,\n descriptor,\n })\n\n if (renderPassEntry.element.type === 'ShaderPass' && renderPassEntry.element.renderTarget) {\n renderCommands.push({\n command: `Copy texture to texture`,\n source: destination,\n destination: `${renderPassEntry.element.renderTarget.options.label} renderTexture`,\n })\n } else if (renderPassEntry.element.type === 'PingPongPlane') {\n renderCommands.push({\n command: `Copy texture to texture`,\n source: destination,\n destination: `${renderPassEntry.element.renderTexture.options.label}`,\n })\n }\n } else if (renderPassEntry.stack) {\n descriptor += ' ' + JSON.stringify(operations)\n\n for (const stackType in renderPassEntry.stack) {\n for (const objectType in renderPassEntry.stack[stackType]) {\n if (renderPassEntry.stack[stackType][objectType].length) {\n renderCommands.push({\n command: `Render stack (${stackType} ${objectType} objects)`,\n source: renderPassEntry.stack[stackType][objectType],\n destination,\n descriptor,\n })\n }\n }\n }\n }\n })\n }\n\n console.table(renderCommands)\n}\n"],"names":[],"mappings":"AAMa,MAAA,gBAAA,GAAmB,CAAC,QAAuB,KAAA;AACtD,EAAM,MAAA,EAAE,OAAU,GAAA,QAAA,CAAA;AAElB,EAAA,IAAI,CAAC,KAAA;AAAO,IAAA,OAAA;AAEZ,EAAA,MAAM,iBAAiB,EAAC,CAAA;AAExB,EAAM,KAAA,CAAA,kBAAA,CAAmB,OAAQ,CAAA,CAAC,WAAgB,KAAA;AAChD,IAAA,cAAA,CAAe,IAAK,CAAA;AAAA,MAClB,OAAS,EAAA,oBAAA;AAAA,MACT,OAAA,EAAS,YAAY,OAAQ,CAAA,KAAA;AAAA,KAC9B,CAAA,CAAA;AAED,IAAA,WAAA,CAAY,QAAS,CAAA,UAAA,CAAW,OAAQ,CAAA,CAAC,SAAc,KAAA;AACrD,MAAU,SAAA,CAAA,cAAA,CAAe,OAAQ,CAAA,CAAC,OAAmC,KAAA;AACnE,QAAA,IAAI,QAAQ,gBAAkB,EAAA;AAC5B,UAAA,cAAA,CAAe,IAAK,CAAA;AAAA,YAClB,OAAS,EAAA,CAAA,qBAAA,CAAA;AAAA,YACT,MAAA,EAAQ,CAAG,EAAA,OAAA,CAAQ,IAAI,CAAA,OAAA,CAAA;AAAA,YACvB,WAAA,EAAa,CAAG,EAAA,OAAA,CAAQ,IAAI,CAAA,cAAA,CAAA;AAAA,WAC7B,CAAA,CAAA;AAAA,SACH;AAAA,OACD,CAAA,CAAA;AAAA,KACF,CAAA,CAAA;AAAA,GACF,CAAA,CAAA;AAED,EAAW,KAAA,MAAA,mBAAA,IAAuB,MAAM,iBAAmB,EAAA;AACzD,IAAA,IAAI,cAAiB,GAAA,CAAA,CAAA;AAErB,IAAA,KAAA,CAAM,iBAAkB,CAAA,mBAAmB,CAAE,CAAA,OAAA,CAAQ,CAAC,eAAoB,KAAA;AAExE,MAAI,IAAA,CAAC,KAAM,CAAA,wBAAA,CAAyB,eAAe,CAAA;AAAG,QAAA,OAAA;AAEtD,MAAM,MAAA,WAAA,GAAc,CAAC,eAAgB,CAAA,UAAA,CAAW,QAAQ,mBACpD,GAAA,KAAA,CAAA,GACA,gBAAgB,aAChB,GAAA,CAAA,EAAG,gBAAgB,aAAc,CAAA,OAAA,CAAQ,KAAK,CAC9C,CAAA,GAAA,eAAA,CAAgB,WAAW,OAAQ,CAAA,gBAAA,CAAiB,MAAS,GAAA,CAAA,GAC7D,wBACA,GAAA,yBAAA,CAAA;AAEJ,MAAI,IAAA,UAAA,GAAa,eAAgB,CAAA,UAAA,CAAW,OAAQ,CAAA,KAAA,CAAA;AAEpD,MAAA,MAAM,UAAa,GAAA;AAAA,QACjB,MAAQ,EAAA,eAAA,CAAgB,UAAW,CAAA,OAAA,CAAQ,mBACvC,GAAA,mBAAA,KAAwB,QAAY,IAAA,cAAA,GAAiB,CACnD,GAAA,MAAA,GACA,eAAgB,CAAA,UAAA,CAAW,QAAQ,MACrC,GAAA,KAAA,CAAA;AAAA,QACJ,WAAa,EAAA,KAAA,CAAA;AAAA,OACf,CAAA;AAEA,MAAI,IAAA,eAAA,CAAgB,UAAW,CAAA,OAAA,CAAQ,QAAU,EAAA;AAC/C,QAAW,UAAA,CAAA,WAAA,GAAc,eAAgB,CAAA,UAAA,CAAW,OAAQ,CAAA,WAAA,CAAA;AAAA,OAC9D;AAEA,MAAA,cAAA,EAAA,CAAA;AAEA,MAAA,IAAI,gBAAgB,OAAS,EAAA;AAC3B,QAAA,IAAI,gBAAgB,OAAQ,CAAA,IAAA,KAAS,gBAAgB,CAAC,eAAA,CAAgB,QAAQ,YAAc,EAAA;AAC1F,UAAA,cAAA,CAAe,IAAK,CAAA;AAAA,YAClB,OAAS,EAAA,CAAA,uBAAA,CAAA;AAAA,YACT,MAAQ,EAAA,WAAA;AAAA,YACR,WAAa,EAAA,CAAA,EAAG,eAAgB,CAAA,OAAA,CAAQ,QAAQ,KAAK,CAAA,cAAA,CAAA;AAAA,WACtD,CAAA,CAAA;AAED,UAAA,UAAA,CAAW,MAAS,GAAA,OAAA,CAAA;AAAA,SACtB;AAEA,QAAc,UAAA,IAAA,GAAA,GAAM,IAAK,CAAA,SAAA,CAAU,UAAU,CAAA,CAAA;AAE7C,QAAA,cAAA,CAAe,IAAK,CAAA;AAAA,UAClB,OAAS,EAAA,CAAA,OAAA,EAAU,eAAgB,CAAA,OAAA,CAAQ,IAAI,CAAA,CAAA;AAAA,UAC/C,MAAA,EAAQ,eAAgB,CAAA,OAAA,CAAQ,OAAQ,CAAA,KAAA;AAAA,UACxC,WAAA;AAAA,UACA,UAAA;AAAA,SACD,CAAA,CAAA;AAED,QAAA,IAAI,gBAAgB,OAAQ,CAAA,IAAA,KAAS,YAAgB,IAAA,eAAA,CAAgB,QAAQ,YAAc,EAAA;AACzF,UAAA,cAAA,CAAe,IAAK,CAAA;AAAA,YAClB,OAAS,EAAA,CAAA,uBAAA,CAAA;AAAA,YACT,MAAQ,EAAA,WAAA;AAAA,YACR,aAAa,CAAG,EAAA,eAAA,CAAgB,OAAQ,CAAA,YAAA,CAAa,QAAQ,KAAK,CAAA,cAAA,CAAA;AAAA,WACnE,CAAA,CAAA;AAAA,SACQ,MAAA,IAAA,eAAA,CAAgB,OAAQ,CAAA,IAAA,KAAS,eAAiB,EAAA;AAC3D,UAAA,cAAA,CAAe,IAAK,CAAA;AAAA,YAClB,OAAS,EAAA,CAAA,uBAAA,CAAA;AAAA,YACT,MAAQ,EAAA,WAAA;AAAA,YACR,aAAa,CAAG,EAAA,eAAA,CAAgB,OAAQ,CAAA,aAAA,CAAc,QAAQ,KAAK,CAAA,CAAA;AAAA,WACpE,CAAA,CAAA;AAAA,SACH;AAAA,OACF,MAAA,IAAW,gBAAgB,KAAO,EAAA;AAChC,QAAc,UAAA,IAAA,GAAA,GAAM,IAAK,CAAA,SAAA,CAAU,UAAU,CAAA,CAAA;AAE7C,QAAW,KAAA,MAAA,SAAA,IAAa,gBAAgB,KAAO,EAAA;AAC7C,UAAA,KAAA,MAAW,UAAc,IAAA,eAAA,CAAgB,KAAM,CAAA,SAAS,CAAG,EAAA;AACzD,YAAA,IAAI,gBAAgB,KAAM,CAAA,SAAS,CAAE,CAAA,UAAU,EAAE,MAAQ,EAAA;AACvD,cAAA,cAAA,CAAe,IAAK,CAAA;AAAA,gBAClB,OAAS,EAAA,CAAA,cAAA,EAAiB,SAAS,CAAA,CAAA,EAAI,UAAU,CAAA,SAAA,CAAA;AAAA,gBACjD,MAAQ,EAAA,eAAA,CAAgB,KAAM,CAAA,SAAS,EAAE,UAAU,CAAA;AAAA,gBACnD,WAAA;AAAA,gBACA,UAAA;AAAA,eACD,CAAA,CAAA;AAAA,aACH;AAAA,WACF;AAAA,SACF;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAEA,EAAA,OAAA,CAAQ,MAAM,cAAc,CAAA,CAAA;AAC9B;;;;"} \ No newline at end of file +{"version":3,"file":"debug.mjs","sources":["../../../src/utils/debug.ts"],"sourcesContent":["import { WritableBufferBinding } from '../core/bindings/WritableBufferBinding'\nimport { Renderer } from '../core/renderers/utils'\n\n/**\n * Logs all the main commands executed during each {@link core/scenes/Scene.Scene#render | Scene render} calls.\n */\nexport const logSceneCommands = (renderer: Renderer) => {\n const { scene } = renderer\n\n if (!scene) return\n\n const renderCommands = []\n\n scene.computePassEntries.forEach((computePass) => {\n renderCommands.push({\n command: 'Render ComputePass',\n content: computePass.options.label,\n })\n\n computePass.material.bindGroups.forEach((bindGroup) => {\n bindGroup.bufferBindings.forEach((binding: WritableBufferBinding) => {\n if (binding.shouldCopyResult) {\n renderCommands.push({\n command: `Copy buffer to buffer`,\n source: `${binding.name} buffer`,\n destination: `${binding.name} result buffer`,\n })\n }\n })\n })\n })\n\n for (const renderPassEntryType in scene.renderPassEntries) {\n let passDrawnCount = 0\n\n scene.renderPassEntries[renderPassEntryType].forEach((renderPassEntry) => {\n // early bail if there's nothing to draw\n if (!scene.getRenderPassEntryLength(renderPassEntry)) return\n\n const destination = !renderPassEntry.renderPass.options.useColorAttachments\n ? undefined\n : renderPassEntry.renderTexture\n ? `${renderPassEntry.renderTexture.options.label}`\n : renderPassEntry.renderPass.options.colorAttachments.length > 1\n ? 'Multiple render target'\n : 'Context current texture'\n\n let descriptor = renderPassEntry.renderPass.options.label\n\n const operations = {\n loadOp: renderPassEntry.renderPass.options.useColorAttachments\n ? renderPassEntryType === 'screen' && passDrawnCount > 0\n ? 'load'\n : renderPassEntry.renderPass.options.loadOp\n : undefined,\n depthLoadOp: undefined,\n sampleCount: renderPassEntry.renderPass.options.sampleCount,\n }\n\n if (renderPassEntry.renderPass.options.useDepth) {\n operations.depthLoadOp = renderPassEntry.renderPass.options.depthLoadOp\n }\n\n passDrawnCount++\n\n if (renderPassEntry.element) {\n if (\n renderPassEntry.element.type === 'ShaderPass' &&\n !(renderPassEntry.element.inputTarget || renderPassEntry.element.outputTarget)\n ) {\n renderCommands.push({\n command: `Copy texture to texture`,\n source: destination,\n destination: `${renderPassEntry.element.options.label} renderTexture`,\n })\n\n operations.loadOp = 'clear'\n }\n\n descriptor += ' ' + JSON.stringify(operations)\n\n renderCommands.push({\n command: `Render ${renderPassEntry.element.type}`,\n source: renderPassEntry.element.options.label,\n destination,\n descriptor,\n })\n\n if (\n renderPassEntry.element.type === 'ShaderPass' &&\n !renderPassEntry.element.outputTarget &&\n renderPassEntry.element.options.copyOutputToRenderTexture\n ) {\n renderCommands.push({\n command: `Copy texture to texture`,\n source: destination,\n destination: `${renderPassEntry.element.options.label} renderTexture`,\n })\n } else if (renderPassEntry.element.type === 'PingPongPlane') {\n renderCommands.push({\n command: `Copy texture to texture`,\n source: destination,\n destination: `${renderPassEntry.element.renderTexture.options.label}`,\n })\n }\n } else if (renderPassEntry.stack) {\n descriptor += ' ' + JSON.stringify(operations)\n\n for (const stackType in renderPassEntry.stack) {\n for (const objectType in renderPassEntry.stack[stackType]) {\n if (renderPassEntry.stack[stackType][objectType].length) {\n renderCommands.push({\n command: `Render stack (${stackType} ${objectType} objects)`,\n source: renderPassEntry.stack[stackType][objectType],\n destination,\n descriptor,\n })\n }\n }\n }\n }\n })\n }\n\n console.table(renderCommands)\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 a50ee8d48..2a4a0ce02 100644 --- a/dist/gpu-curtains.umd.js +++ b/dist/gpu-curtains.umd.js @@ -6139,7 +6139,7 @@ struct VertexOutput { texturesOptions, ...outputTarget !== void 0 && { outputTarget }, ...autoRender !== void 0 && { autoRender }, - ...meshParameters.useAsyncPipeline !== void 0 && { useAsyncPipeline: meshParameters.useAsyncPipeline } + ...meshParameters }; this.geometry = geometry; if (autoRender !== void 0) { @@ -6151,9 +6151,9 @@ struct VertexOutput { this.userData = {}; this.computeGeometry(); this.setMaterial({ - label: this.options.label, - shaders: this.options.shaders, - ...{ ...meshParameters, verticesOrder: geometry.verticesOrder, topology: geometry.topology } + ...this.cleanupRenderMaterialParameters({ ...this.options }), + verticesOrder: geometry.verticesOrder, + topology: geometry.topology }); this.addToScene(); } @@ -6197,33 +6197,6 @@ struct VertexOutput { } this.renderer.meshes = this.renderer.meshes.filter((m) => m.uuid !== this.uuid); } - /** - * Set or update the {@link RenderMaterial} {@link types/Materials.RenderMaterialRenderingOptions | rendering options} to match the {@link RenderPass#descriptor | RenderPass descriptor} used to draw this Mesh. - * @param renderPass - {@link RenderPass | RenderPass} used to draw this Mesh, default to the {@link core/renderers/GPURenderer.GPURenderer#renderPass | renderer renderPass}. - */ - setRenderingOptionsForRenderPass(renderPass) { - const renderingOptions = { - sampleCount: renderPass.options.sampleCount, - // color attachments - ...renderPass.options.colorAttachments.length && { - targetFormat: renderPass.options.colorAttachments[0].targetFormat, - // multiple render targets? - ...renderPass.options.colorAttachments.length > 1 && { - additionalTargets: renderPass.options.colorAttachments.filter((c, i) => i > 0).map((colorAttachment) => { - return { - format: colorAttachment.targetFormat - }; - }) - } - }, - // depth - depth: renderPass.options.useDepth, - ...renderPass.options.useDepth && { - depthFormat: renderPass.options.depthFormat - } - }; - this.material?.setRenderingOptions(renderingOptions); - } /** * Set a new {@link Renderer} for this Mesh * @param renderer - new {@link Renderer} to set @@ -6358,6 +6331,44 @@ struct VertexOutput { } } /* MATERIAL */ + /** + * Set or update the {@link RenderMaterial} {@link types/Materials.RenderMaterialRenderingOptions | rendering options} to match the {@link RenderPass#descriptor | RenderPass descriptor} used to draw this Mesh. + * @param renderPass - {@link RenderPass | RenderPass} used to draw this Mesh, default to the {@link core/renderers/GPURenderer.GPURenderer#renderPass | renderer renderPass}. + */ + setRenderingOptionsForRenderPass(renderPass) { + const renderingOptions = { + sampleCount: renderPass.options.sampleCount, + // color attachments + ...renderPass.options.colorAttachments.length && { + targetFormat: renderPass.options.colorAttachments[0].targetFormat, + // multiple render targets? + ...renderPass.options.colorAttachments.length > 1 && { + additionalTargets: renderPass.options.colorAttachments.filter((c, i) => i > 0).map((colorAttachment) => { + return { + format: colorAttachment.targetFormat + }; + }) + } + }, + // depth + depth: renderPass.options.useDepth, + ...renderPass.options.useDepth && { + depthFormat: renderPass.options.depthFormat + } + }; + this.material?.setRenderingOptions(renderingOptions); + } + /** + * Hook used to clean up parameters before sending them to the {@link RenderMaterial}. + * @param parameters - parameters to clean before sending them to the {@link RenderMaterial} + * @returns - cleaned parameters + */ + cleanupRenderMaterialParameters(parameters) { + delete parameters.texturesOptions; + delete parameters.outputTarget; + delete parameters.autoRender; + return parameters; + } /** * Set a Mesh transparent property, then set its material * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters} @@ -6986,12 +6997,22 @@ struct VSOutput { this.domFrustum.shouldUpdate = this.frustumCulled; } /* MATERIAL */ + /** + * Hook used to clean up parameters before sending them to the material. + * @param parameters - parameters to clean before sending them to the {@link core/materials/RenderMaterial.RenderMaterial | RenderMaterial} + * @returns - cleaned parameters + */ + cleanupRenderMaterialParameters(parameters) { + delete parameters.frustumCulled; + delete parameters.DOMFrustumMargins; + super.cleanupRenderMaterialParameters(parameters); + return parameters; + } /** * Set a Mesh matrices uniforms inputs then call {@link MeshBaseClass} super method - * @param meshParameters - {@link ProjectedRenderMaterialParams | RenderMaterial parameters} + * @param meshParameters - {@link RenderMaterialParams | RenderMaterial parameters} */ setMaterial(meshParameters) { - const { frustumCulled, DOMFrustumMargins, ...materialParameters } = meshParameters; const matricesUniforms = { label: "Matrices", struct: { @@ -7018,10 +7039,10 @@ struct VSOutput { } } }; - if (!materialParameters.uniforms) - materialParameters.uniforms = {}; - materialParameters.uniforms.matrices = matricesUniforms; - super.setMaterial(materialParameters); + if (!meshParameters.uniforms) + meshParameters.uniforms = {}; + meshParameters.uniforms.matrices = matricesUniforms; + super.setMaterial(meshParameters); } /* SIZE & TRANSFORMS */ /** @@ -8087,15 +8108,26 @@ struct VSOutput { }); } /** - * Get our main {@link RenderTexture}, the one that contains our post processed content + * Hook used to clean up parameters before sending them to the material. + * @param parameters - parameters to clean before sending them to the {@link core/materials/RenderMaterial.RenderMaterial | RenderMaterial} + * @returns - cleaned parameters + */ + cleanupRenderMaterialParameters(parameters) { + delete parameters.copyOutputToRenderTexture; + delete parameters.inputTarget; + super.cleanupRenderMaterialParameters(parameters); + return parameters; + } + /** + * Get our main {@link RenderTexture} that contains the input content to be used by the {@link ShaderPass}. Can also contain the ouputted content if {@link ShaderPassOptions#copyOutputToRenderTexture | copyOutputToRenderTexture} is set to true. * @readonly */ get renderTexture() { return this.renderTextures.find((texture) => texture.options.name === "renderTexture"); } - // TODO /** - * Assign or remove a {@link RenderTarget} to this {@link ShaderPass} + * Assign or remove an input {@link RenderTarget} to this {@link ShaderPass}, which can be different from what has just been drawn to the {@link core/renderers/GPURenderer.GPURenderer#context | context} current texture. + * * Since this manipulates the {@link core/scenes/Scene.Scene | Scene} stacks, it can be used to remove a RenderTarget as well. * Also copy or remove the {@link RenderTarget#renderTexture | render target render texture} into the {@link ShaderPass} {@link renderTexture} * @param inputTarget - the {@link RenderTarget} to assign or null if we want to remove the current {@link RenderTarget} @@ -8214,7 +8246,7 @@ struct VSOutput { this.createDepthTexture(); } this.viewTextures = []; - if (this.options.useColorAttachments) { + if (this.options.useColorAttachments && (!this.options.shouldUpdateView || this.options.sampleCount > 1)) { this.createViewTextures(); } this.setRenderPassDescriptor(); @@ -8262,8 +8294,8 @@ struct VSOutput { colorAttachments: this.options.colorAttachments.map((colorAttachment, index) => { return { // view - view: this.viewTextures[index].texture.createView({ - label: this.viewTextures[index].texture.label + " view" + view: this.viewTextures[index]?.texture.createView({ + label: this.viewTextures[index]?.texture.label + " view" }), // clear values clearValue: colorAttachment.clearValue, @@ -9273,7 +9305,7 @@ struct VSOutput { } this.renderer.postProcessingPass.setLoadOp("clear"); }; - const onAfterRenderPass = shaderPass.outputTarget ? null : (commandEncoder, swapChainTexture) => { + const onAfterRenderPass = !shaderPass.outputTarget && shaderPass.options.copyOutputToRenderTexture ? (commandEncoder, swapChainTexture) => { if (shaderPass.renderTexture && swapChainTexture) { commandEncoder.copyTextureToTexture( { @@ -9285,7 +9317,7 @@ struct VSOutput { [shaderPass.renderTexture.size.width, shaderPass.renderTexture.size.height] ); } - }; + } : null; const shaderPassEntry = { // use output target or postprocessing render pass renderPass: shaderPass.outputTarget ? shaderPass.outputTarget.renderPass : this.renderer.postProcessingPass, @@ -11442,14 +11474,15 @@ struct VSOutput { let descriptor = renderPassEntry.renderPass.options.label; const operations = { loadOp: renderPassEntry.renderPass.options.useColorAttachments ? renderPassEntryType === "screen" && passDrawnCount > 0 ? "load" : renderPassEntry.renderPass.options.loadOp : void 0, - depthLoadOp: void 0 + depthLoadOp: void 0, + sampleCount: renderPassEntry.renderPass.options.sampleCount }; if (renderPassEntry.renderPass.options.useDepth) { operations.depthLoadOp = renderPassEntry.renderPass.options.depthLoadOp; } passDrawnCount++; if (renderPassEntry.element) { - if (renderPassEntry.element.type === "ShaderPass" && !renderPassEntry.element.renderTarget) { + if (renderPassEntry.element.type === "ShaderPass" && !(renderPassEntry.element.inputTarget || renderPassEntry.element.outputTarget)) { renderCommands.push({ command: `Copy texture to texture`, source: destination, @@ -11464,11 +11497,11 @@ struct VSOutput { destination, descriptor }); - if (renderPassEntry.element.type === "ShaderPass" && renderPassEntry.element.renderTarget) { + if (renderPassEntry.element.type === "ShaderPass" && !renderPassEntry.element.outputTarget && renderPassEntry.element.options.copyOutputToRenderTexture) { renderCommands.push({ command: `Copy texture to texture`, source: destination, - destination: `${renderPassEntry.element.renderTarget.options.label} renderTexture` + destination: `${renderPassEntry.element.options.label} renderTexture` }); } else if (renderPassEntry.element.type === "PingPongPlane") { renderCommands.push({ diff --git a/dist/gpu-curtains.umd.js.map b/dist/gpu-curtains.umd.js.map index 74bc58942..4f5fb0e4e 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