gpu-curtains
Version:
gpu-curtains is a 3D WebGPU rendering engine. It can be used as a standalone 3D engine, but also includes extra classes focused on mapping 3d objects to DOM elements; It allows users to synchronize values such as position, sizing, or scale between them.
158 lines (157 loc) • 7.12 kB
JavaScript
import { generateUUID } from "../../utils/utils.mjs";
import { isRenderer } from "../renderers/utils.mjs";
import { Texture } from "../textures/Texture.mjs";
import { RenderPass } from "./RenderPass.mjs";
//#region src/core/renderPasses/RenderTarget.ts
/**
* Used to draw to {@link RenderPass#viewTextures | RenderPass view textures} (and eventually {@link RenderPass#depthTexture | depth texture}) instead of directly to screen.
*
* The meshes assigned to a {@link RenderTarget} will be drawn before the other objects in the {@link core/scenes/Scene.Scene | Scene} rendering loop.
*
* Can also be assigned as ShaderPass {@link core/renderPasses/ShaderPass.ShaderPass#inputTarget | input} or {@link core/renderPasses/ShaderPass.ShaderPass#outputTarget | output} targets.
*
* If the {@link RenderPass} created handle color attachments, then a {@link RenderTarget#renderTexture | Texture} will be created to update and/or resolve the content of the current view. This {@link RenderTarget#renderTexture | Texture} could therefore usually be used to access the current content of this {@link RenderTarget}.
*
* @example
* ```javascript
* // set our main GPUCurtains instance
* const gpuCurtains = new GPUCurtains({
* container: '#canvas' // selector of our WebGPU canvas container
* })
*
* // set the GPU device
* // note this is asynchronous
* await gpuCurtains.setDevice()
*
* const outputTarget = new RenderTarget(gpuCurtains, {
* label: 'My render target',
* })
* ```
*/
var RenderTarget = class {
/** Whether we should add this {@link RenderTarget} to our {@link core/scenes/Scene.Scene | Scene} to let it handle the rendering process automatically. */
#autoRender = true;
/**
* RenderTarget constructor
* @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link RenderTarget}.
* @param parameters - {@link RenderTargetParams | parameters} use to create this {@link RenderTarget}.
*/
constructor(renderer, parameters = {}) {
this.type = "RenderTarget";
renderer = isRenderer(renderer, this.type);
this.renderer = renderer;
this.uuid = generateUUID();
const { label, colorAttachments, depthTexture, autoRender, renderTextureName, isPostTarget, ...renderPassParams } = parameters;
const depthTextureToUse = !!depthTexture ? depthTexture : this.renderer.renderPass.options.sampleCount === (parameters.sampleCount ?? 4) && (!renderPassParams.qualityRatio || renderPassParams.qualityRatio === 1) && !renderPassParams.fixedSize && (!parameters.depthFormat || parameters.depthFormat === this.renderer.renderPass.depthTexture.options.format) ? this.renderer.renderPass.depthTexture : null;
this.options = {
label,
...renderPassParams,
...depthTextureToUse && { depthTexture: depthTextureToUse },
...colorAttachments && { colorAttachments },
renderTextureName: renderTextureName ?? "renderTexture",
autoRender: autoRender === void 0 ? true : autoRender,
isPostTarget: !!isPostTarget
};
if (autoRender !== void 0) this.#autoRender = autoRender;
this.renderPass = new RenderPass(this.renderer, {
label: this.options.label ? `${this.options.label} Render Pass` : "Render Target Render Pass",
...colorAttachments && { colorAttachments },
depthTexture: this.options.depthTexture,
...renderPassParams
});
if (renderPassParams.useColorAttachments !== false) this.renderTexture = new Texture(this.renderer, {
label: this.options.label ? `${this.options.label} Render Texture` : "Render Target render texture",
name: this.options.renderTextureName,
format: colorAttachments && colorAttachments.length && colorAttachments[0].targetFormat ? colorAttachments[0].targetFormat : this.renderer.options.context.format,
...this.options.qualityRatio !== void 0 && { qualityRatio: this.options.qualityRatio },
...this.options.fixedSize !== void 0 && { fixedSize: this.options.fixedSize },
usage: [
"copySrc",
"copyDst",
"renderAttachment",
"textureBinding"
]
});
this.addToScene();
}
/**
* Reset this {@link RenderTarget} {@link RenderTarget.renderer | renderer}. Also set the {@link renderPass} renderer.
* @param renderer - New {@link Renderer} or {@link GPUCurtains} instance to use.
*/
setRenderer(renderer) {
if (this.renderer) this.removeFromScene();
renderer = isRenderer(renderer, this.type);
this.renderer = renderer;
if (this.options.depthTexture) this.options.depthTexture.setRenderer(this.renderer);
this.renderPass.setRenderer(this.renderer);
if (this.renderTexture) this.renderTexture.setRenderer(this.renderer);
this.addToScene();
}
/**
* Get the textures outputted by the {@link renderPass} if any, which means its {@link RenderPass.viewTextures | viewTextures} if not multisampled, or the {@link RenderPass.resolveTargets | resolveTargets} else.
*
* Since some {@link RenderPass} might not have any view textures (or in case the first resolve target is `null`), the first element can be the {@link RenderTarget.renderTexture | RenderTarget renderTexture} itself.
*
* @readonly
*/
get outputTextures() {
return !this.renderPass.outputTextures.length ? !this.renderTexture ? [] : [this.renderTexture] : this.renderPass.outputTextures.map((texture, index) => {
return index === 0 && this.renderPass.options.renderToSwapChain ? this.renderTexture : texture;
});
}
/**
* Add the {@link RenderTarget} to the renderer and the {@link core/scenes/Scene.Scene | Scene}
*/
addToScene() {
this.renderer.renderTargets.push(this);
if (this.#autoRender) this.renderer.scene.addRenderTarget(this);
}
/**
* Remove the {@link RenderTarget} from the renderer and the {@link core/scenes/Scene.Scene | Scene}
*/
removeFromScene() {
if (this.#autoRender) this.renderer.scene.removeRenderTarget(this);
this.renderer.renderTargets = this.renderer.renderTargets.filter((renderTarget) => renderTarget.uuid !== this.uuid);
}
/**
* Update our {@link RenderTarget} {@link renderTexture} and {@link renderPass} quality ratio.
* @param qualityRatio - New quality ratio to use.
*/
setQualityRatio(qualityRatio = 1) {
this.options.qualityRatio = qualityRatio;
this.renderTexture?.setQualityRatio(qualityRatio);
this.renderPass?.setQualityRatio(qualityRatio);
}
/**
* Resize our {@link renderPass}.
*/
resize() {
if (this.options.depthTexture) this.renderPass.options.depthTexture.texture = this.options.depthTexture.texture;
this.renderPass?.resize();
}
/**
* Remove our {@link RenderTarget}. Alias of {@link RenderTarget#destroy}.
*/
remove() {
this.destroy();
}
/**
* Destroy our {@link RenderTarget}.
*/
destroy() {
this.renderer.meshes.forEach((mesh) => {
if (mesh.outputTarget && mesh.outputTarget.uuid === this.uuid) mesh.setOutputTarget(null);
});
this.renderer.shaderPasses.forEach((shaderPass) => {
if (shaderPass.outputTarget && shaderPass.outputTarget.uuid === this.uuid) {
shaderPass.outputTarget = null;
shaderPass.setOutputTarget(null);
}
});
this.removeFromScene();
this.renderPass?.destroy();
this.renderTexture?.destroy();
}
};
//#endregion
export { RenderTarget };