UNPKG

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.

366 lines (365 loc) 11.6 kB
import { GPUDeviceManager } from "../core/renderers/GPUDeviceManager.mjs"; import { GPURenderer } from "../core/renderers/GPURenderer.mjs"; import { GPUCameraRenderer } from "../core/renderers/GPUCameraRenderer.mjs"; import { Plane } from "./meshes/Plane.mjs"; import { GPUCurtainsRenderer } from "./renderers/GPUCurtainsRenderer.mjs"; import { ScrollManager } from "../utils/ScrollManager.mjs"; //#region src/curtains/GPUCurtains.ts /** * Used as a global class to create a {@link GPUCurtainsRenderer}, create all objects that need a reference to a renderer, listen to various events such as scroll and resize and render. * * @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() * ``` */ var GPUCurtains = class { /** * GPUCurtains constructor * @param parameters - {@link GPUCurtainsParams | parameters} used to create this {@link GPUCurtains}. */ constructor({ container, label, pixelRatio = window.devicePixelRatio ?? 1, context = {}, production = false, adapterOptions = {}, requiredFeatures = [], requestAdapterLimits = [], renderPass, camera, lights, autoRender = true, autoResize = true, watchScroll = true } = {}) { this._onScrollCallback = () => {}; this._onErrorCallback = (message) => {}; this._onContextLostCallback = () => {}; this._onContextDestroyedCallback = () => {}; this.type = "CurtainsGPU"; this.options = { container, label, pixelRatio, camera, lights, production, adapterOptions, requiredFeatures, requestAdapterLimits, context, renderPass, autoRender, autoResize, watchScroll }; this.setDeviceManager(); if (container) this.setContainer(container); this.initScroll(); } /** * Set the {@link GPUCurtains.container | container}. * @param container - {@link HTMLElement} or string representing an {@link HTMLElement} selector to use. */ setContainer(container) { if (!container) { const container = document.createElement("div"); container.setAttribute("id", "curtains-gpu-canvas"); document.body.appendChild(container); this.options.container = container; } else if (typeof container === "string") { container = document.querySelector(container); if (!container) { const container = document.createElement("div"); container.setAttribute("id", "curtains-gpu-canvas"); document.body.appendChild(container); this.options.container = container; } else this.options.container = container; } else if (container instanceof Element) this.options.container = container; this.container = this.options.container; this.setMainRenderer(); } /** * Set the default {@link GPUCurtainsRenderer | renderer}. */ setMainRenderer() { if (this.options.adapterOptions.featureLevel === "compatibility") { if (this.options.lights === void 0 || this.options.lights && !this.options.lights.useUniformsForShadows) this.options.lights = { ...this.options.lights, useUniformsForShadows: true }; } this.createCurtainsRenderer({ label: this.options.label || "GPUCurtains main GPUCurtainsRenderer", container: this.options.container, pixelRatio: this.options.pixelRatio, autoResize: this.options.autoResize, context: this.options.context, renderPass: this.options.renderPass, camera: this.options.camera, lights: this.options.lights }); } /** * Patch the options with default values before creating a {@link Renderer}. * @param parameters - Parameters to patch. */ patchRendererOptions(parameters) { if (parameters.pixelRatio === void 0) parameters.pixelRatio = this.options.pixelRatio; if (parameters.autoResize === void 0) parameters.autoResize = this.options.autoResize; return parameters; } /** * Create a new {@link GPURenderer} instance. * @param parameters - {@link GPURendererParams | parameters} to use. */ createRenderer(parameters) { parameters = this.patchRendererOptions(parameters); return new GPURenderer({ ...parameters, deviceManager: this.deviceManager }); } /** * Create a new {@link GPUCameraRenderer} instance. * @param parameters - {@link GPUCameraRendererParams | parameters} to use. */ createCameraRenderer(parameters) { parameters = this.patchRendererOptions(parameters); return new GPUCameraRenderer({ ...parameters, deviceManager: this.deviceManager }); } /** * Create a new {@link GPUCurtainsRenderer} instance. * @param parameters - {@link GPUCameraRendererParams | parameters} to use. */ createCurtainsRenderer(parameters) { parameters = this.patchRendererOptions(parameters); return new GPUCurtainsRenderer({ ...parameters, deviceManager: this.deviceManager }); } /** * Set our {@link GPUDeviceManager}. */ setDeviceManager() { this.deviceManager = new GPUDeviceManager({ label: "GPUCurtains default device", production: this.options.production, adapterOptions: this.options.adapterOptions, requiredFeatures: this.options.requiredFeatures, requestAdapterLimits: this.options.requestAdapterLimits, autoRender: this.options.autoRender, onError: (message) => setTimeout(() => { this._onErrorCallback && this._onErrorCallback(message); }, 0), onDeviceLost: (info) => this._onContextLostCallback && this._onContextLostCallback(info), onDeviceDestroyed: (info) => this._onContextDestroyedCallback && this._onContextDestroyedCallback(info) }); } /** * Get all created {@link Renderer}. * @readonly */ get renderers() { return this.deviceManager.renderers; } /** * Get the first created {@link Renderer} if any. * @readonly */ get renderer() { return this.renderers[0]; } /** * Set the {@link GPUDeviceManager} {@link GPUDeviceManager#adapter | adapter} and {@link GPUDeviceManager#device | device} if possible, then set all created {@link Renderer} contexts. * @param parameters - {@link GPUAdapter} and/or {@link GPUDevice} to use if set. */ async setDevice({ adapter = null, device = null } = {}) { await this.deviceManager.init({ adapter, device }); } /** * Restore the {@link GPUDeviceManager#adapter | adapter} and {@link GPUDeviceManager#device | device}. */ async restoreContext() { await this.deviceManager.restoreDevice(); } /** * Get all the created {@link PingPongPlane}. * @readonly */ get pingPongPlanes() { return this.renderers?.map((renderer) => renderer.pingPongPlanes).flat(); } /** * Get all the created {@link ShaderPass}. * @readonly */ get shaderPasses() { return this.renderers?.map((renderer) => renderer.shaderPasses).flat(); } /** * Get all the created {@link SceneStackedMesh | meshes}. * @readonly */ get meshes() { return this.renderers?.map((renderer) => renderer.meshes).flat(); } /** * Get all the created {@link DOMMesh | DOM Meshes} (including {@link Plane | planes}). * @readonly */ get domMeshes() { return this.renderers?.filter((renderer) => renderer instanceof GPUCurtainsRenderer).map((renderer) => renderer.domMeshes).flat(); } /** * Get all created {@link curtains/objects3D/DOMObject3D.DOMObject3D | DOMObject3D} which position should be updated on scroll. * @readonly */ get domObjects() { return this.renderers?.filter((renderer) => renderer instanceof GPUCurtainsRenderer).map((renderer) => renderer.domObjects).flat(); } /** * Get all the created {@link Plane | planes}. * @readonly */ get planes() { return this.domMeshes.filter((domMesh) => domMesh instanceof Plane); } /** * Get all the created {@link ComputePass | compute passes}. * @readonly */ get computePasses() { return this.renderers?.map((renderer) => renderer.computePasses).flat(); } /** * Get our {@link GPUCurtainsRenderer#boundingRect | default GPUCurtainsRenderer bounding rectangle}. */ get boundingRect() { return this.renderer?.boundingRect; } /** * Set the {@link scrollManager}. */ initScroll() { this.scrollManager = new ScrollManager({ scroll: { x: window.pageXOffset, y: window.pageYOffset }, delta: { x: 0, y: 0 }, shouldWatch: this.options.watchScroll, onScroll: (delta) => this.updateScroll(delta) }); } /** * Update all {@link DOMMesh#updateScrollPosition | DOMMesh scroll positions}. * @param delta - Last {@link ScrollManager#delta | scroll delta values}. */ updateScroll(delta = { x: 0, y: 0 }) { this.domObjects.forEach((domObject) => { if (domObject.domElement && domObject.watchScroll) domObject.updateScrollPosition(delta); }); this._onScrollCallback && this._onScrollCallback(); } /** * Update our {@link ScrollManager#scroll | scrollManager scroll values}. Called each time the scroll has changed if {@link GPUCurtains#options.watchScroll | watchScroll option} is set to true. Could be called externally as well. * @param scroll - New {@link DOMPosition | scroll values}. */ updateScrollValues(scroll = { x: 0, y: 0 }) { this.scrollManager.updateScrollValues(scroll); } /** * Get our {@link ScrollManager#delta | scrollManager delta values}. * @readonly */ get scrollDelta() { return this.scrollManager.delta; } /** * Get our {@link ScrollManager#scroll | scrollManager scroll values}. * @readonly */ get scrollValues() { return this.scrollManager.scroll; } /** * Called each frame before rendering. * @param callback - callback to run at each render. * @returns - our {@link GPUCurtains}. */ onBeforeRender(callback) { this.deviceManager.onBeforeRender(callback); return this; } /** * Called each frame after rendering. * @param callback - callback to run at each render. * @returns - our {@link GPUCurtains}. */ onAfterRender(callback) { this.deviceManager.onAfterRender(callback); return this; } /** * Called each time the {@link ScrollManager#scroll | scrollManager scroll values} changed. * @param callback - callback to run each time the {@link ScrollManager#scroll | scrollManager scroll values} changed. * @returns - our {@link GPUCurtains}. */ onScroll(callback) { if (callback) this._onScrollCallback = callback; return this; } /** * Called if there's been an error while trying to create the {@link GPUDeviceManager#device | device}. * @param callback - callback to run if there's been an error while trying to create the {@link GPUDeviceManager#device | device}. * @returns - our {@link GPUCurtains}. */ onError(callback) { if (callback) this._onErrorCallback = callback; return this; } /** * Called whenever the {@link GPUDeviceManager#device | device} is lost. * @param callback - callback to run whenever the {@link GPUDeviceManager#device | device} is lost. * @returns - our {@link GPUCurtains}. */ onContextLost(callback) { if (callback) this._onContextLostCallback = callback; return this; } /** * Called whenever the {@link GPUDeviceManager#device | device} has been intentionally destroyed. * @param callback - callback to run whenever the {@link GPUDeviceManager#device | device} has been destroyed. * @returns - our {@link GPUCurtains}. */ onContextDestroyed(callback) { if (callback) this._onContextDestroyedCallback = callback; return this; } /** * Render our {@link GPUDeviceManager}. */ render() { this.deviceManager.render(); } /** * Destroy our {@link GPUCurtains} and {@link GPUDeviceManager}. */ destroy() { this.deviceManager.destroy(); this.scrollManager?.destroy(); } }; //#endregion export { GPUCurtains };