diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e82e44d3c0..fed3a60fcf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -167,8 +167,8 @@ jobs: - name: Install dependencies run: npm clean-install --progress=false --no-fund - - name: Build PlayCanvas (ES5-only) - run: npm run build:es5 + - name: Build PlayCanvas (ES6-only) + run: npm run build:es6 - name: Build PlayCanvas Extras run: npm run build:extras diff --git a/examples/assets/models/pbr-house.glb b/examples/assets/models/pbr-house.glb new file mode 100644 index 00000000000..37b9f6d9769 Binary files /dev/null and b/examples/assets/models/pbr-house.glb differ diff --git a/examples/assets/models/pbr-house.txt b/examples/assets/models/pbr-house.txt new file mode 100644 index 00000000000..816f34cc504 --- /dev/null +++ b/examples/assets/models/pbr-house.txt @@ -0,0 +1,5 @@ +The house model has been obtained from this address: +https://sketchfab.com/3d-models/house-03-pbr-c56521b89188460a99235dec8bcd0ed3 + +It's distributed under CC license: +https://creativecommons.org/licenses/by/4.0/ \ No newline at end of file diff --git a/examples/src/examples/graphics/post-processing/example.mjs b/examples/src/examples/graphics/post-processing/example.mjs index ec3a7f5c74f..5c086387af8 100644 --- a/examples/src/examples/graphics/post-processing/example.mjs +++ b/examples/src/examples/graphics/post-processing/example.mjs @@ -44,6 +44,7 @@ const createOptions = new pc.AppOptions(); createOptions.graphicsDevice = device; createOptions.mouse = new pc.Mouse(document.body); createOptions.touch = new pc.TouchDevice(document.body); +createOptions.keyboard = new pc.Keyboard(window); createOptions.componentSystems = [ pc.RenderComponentSystem, @@ -81,7 +82,8 @@ assetListLoader.load(() => { // disable skydome rendering itself, we don't need it as we use camera clear color app.scene.layers.getLayerByName('Skybox').enabled = false; - // render in HDR mode + // the render passes render in HDR format, and so disable output tone mapping and gamma correction, + // as that is applied in the final compose pass app.scene.toneMapping = pc.TONEMAP_LINEAR; app.scene.gammaCorrection = pc.GAMMA_NONE; @@ -143,7 +145,9 @@ assetListLoader.load(() => { // add orbit camera script with a mouse and a touch support cameraEntity.addComponent('script'); - cameraEntity.script.create('orbitCamera', { + + // add orbit camera script with a mouse and a touch support + cameraEntity.script.create("orbitCamera", { attributes: { inertiaFactor: 0.2, focusEntity: mosquitoEntity, @@ -154,7 +158,6 @@ assetListLoader.load(() => { cameraEntity.script.create('orbitCameraInputMouse'); cameraEntity.script.create('orbitCameraInputTouch'); - // position the camera in the world cameraEntity.setLocalPosition(0, 40, -220); cameraEntity.lookAt(0, 0, 100); app.root.addChild(cameraEntity); @@ -218,9 +221,9 @@ assetListLoader.load(() => { samples: 0, // number of samples for multi-sampling sceneColorMap: true, // true if the scene color should be captured - // disabled by default as this is WIP + // disabled TAA as it currently does not handle dynamic objects prepassEnabled: false, - taaEnabled: false // true if temporal anti-aliasing should be used + taaEnabled: false }; const setupRenderPass = () => { @@ -245,6 +248,7 @@ assetListLoader.load(() => { const taaEnabled = data.get('data.taa.enabled'); if (noPasses || taaEnabled !== currentOptions.taaEnabled) { currentOptions.taaEnabled = taaEnabled; + currentOptions.prepassEnabled = taaEnabled; // create new pass setupRenderPass(); diff --git a/examples/src/examples/graphics/taa/config.mjs b/examples/src/examples/graphics/taa/config.mjs new file mode 100644 index 00000000000..b7fc3987320 --- /dev/null +++ b/examples/src/examples/graphics/taa/config.mjs @@ -0,0 +1,6 @@ +/** + * @type {import('../../../../types.mjs').ExampleConfig} + */ +export default { + WEBGPU_ENABLED: true +}; diff --git a/examples/src/examples/graphics/taa/controls.mjs b/examples/src/examples/graphics/taa/controls.mjs new file mode 100644 index 00000000000..49524627bc4 --- /dev/null +++ b/examples/src/examples/graphics/taa/controls.mjs @@ -0,0 +1,59 @@ +import * as pc from 'playcanvas'; + +/** + * @param {import('../../../app/components/Example.mjs').ControlOptions} options - The options. + * @returns {JSX.Element} The returned JSX Element. + */ +export function controls({ observer, ReactPCUI, React, jsx, fragment }) { + const { BindingTwoWay, BooleanInput, LabelGroup, Panel, SliderInput } = ReactPCUI; + return fragment( + jsx( + Panel, + { headerText: 'Scene Rendering' }, + jsx( + LabelGroup, + { text: 'resolution' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'data.scene.scale' }, + min: 0.5, + max: 1, + precision: 1 + }) + ), + jsx( + LabelGroup, + { text: 'Bloom' }, + jsx(BooleanInput, { + type: 'toggle', + binding: new BindingTwoWay(), + link: { observer, path: 'data.scene.bloom' } + }) + ) + ), + jsx( + Panel, + { headerText: 'TAA' }, + jsx( + LabelGroup, + { text: 'enabled' }, + jsx(BooleanInput, { + type: 'toggle', + binding: new BindingTwoWay(), + link: { observer, path: 'data.taa.enabled' } + }) + ), + jsx( + LabelGroup, + { text: 'jitter' }, + jsx(SliderInput, { + binding: new BindingTwoWay(), + link: { observer, path: 'data.taa.jitter' }, + min: 0, + max: 1, + precision: 2 + }) + ) + ) + ); +} diff --git a/examples/src/examples/graphics/taa/example.mjs b/examples/src/examples/graphics/taa/example.mjs new file mode 100644 index 00000000000..e11be3b5c70 --- /dev/null +++ b/examples/src/examples/graphics/taa/example.mjs @@ -0,0 +1,204 @@ +import * as pc from 'playcanvas'; +import * as pcx from 'playcanvas-extras'; +import { data } from '@examples/observer'; +import { deviceType, rootPath } from '@examples/utils'; + +const canvas = document.getElementById('application-canvas'); +if (!(canvas instanceof HTMLCanvasElement)) { + throw new Error('No canvas found'); +} + +const assets = { + orbit: new pc.Asset('script', 'script', { url: rootPath + '/static/scripts/camera/orbit-camera.js' }), + house: new pc.Asset('house', 'container', { url: rootPath + '/static/assets/models/pbr-house.glb' }), + envatlas: new pc.Asset( + 'env-atlas', + 'texture', + { url: rootPath + '/static/assets/cubemaps/table-mountain-env-atlas.png' }, + { type: pc.TEXTURETYPE_RGBP, mipmaps: false } + ) +}; + +const gfxOptions = { + deviceTypes: [deviceType], + glslangUrl: rootPath + '/static/lib/glslang/glslang.js', + twgslUrl: rootPath + '/static/lib/twgsl/twgsl.js', + + // disable anti-aliasing as TAA is used to smooth edges + antialias: false +}; + +const device = await pc.createGraphicsDevice(canvas, gfxOptions); +const createOptions = new pc.AppOptions(); +createOptions.graphicsDevice = device; +createOptions.mouse = new pc.Mouse(document.body); +createOptions.touch = new pc.TouchDevice(document.body); + +createOptions.componentSystems = [ + pc.RenderComponentSystem, + pc.CameraComponentSystem, + pc.LightComponentSystem, + pc.ScriptComponentSystem +]; +createOptions.resourceHandlers = [ + pc.TextureHandler, + pc.ContainerHandler, + pc.ScriptHandler +]; + +const app = new pc.AppBase(canvas); +app.init(createOptions); + +// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size +app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW); +app.setCanvasResolution(pc.RESOLUTION_AUTO); + +// Ensure canvas is resized when window changes size +const resize = () => app.resizeCanvas(); +window.addEventListener('resize', resize); +app.on('destroy', () => { + window.removeEventListener('resize', resize); +}); + +const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets); +assetListLoader.load(() => { + app.start(); + + // setup skydome with low intensity + app.scene.envAtlas = assets.envatlas.resource; + app.scene.skyboxMip = 0; + app.scene.exposure = 1.0; + + // the render passes render in HDR format, and so disable output tone mapping and gamma correction, + // as that is applied in the final compose pass + app.scene.toneMapping = pc.TONEMAP_LINEAR; + app.scene.gammaCorrection = pc.GAMMA_NONE; + + // create an instance of the house and add it to the scene + const houseEntity = assets.house.resource.instantiateRenderEntity(); + houseEntity.setLocalScale(100, 100, 100); + app.root.addChild(houseEntity); + + // Create an Entity with a camera component + const cameraEntity = new pc.Entity(); + cameraEntity.addComponent('camera', { + nearClip: 10, + farClip: 600, + fov: 80 + }); + + // add orbit camera script with a mouse and a touch support + cameraEntity.addComponent('script'); + cameraEntity.script.create("orbitCamera", { + attributes: { + inertiaFactor: 0.2, + focusEntity: houseEntity, + distanceMax: 400, + frameOnStart: true + } + }); + cameraEntity.script.create("orbitCameraInputMouse"); + cameraEntity.script.create("orbitCameraInputTouch"); + cameraEntity.setLocalPosition(0, 40, -220); + cameraEntity.lookAt(0, 0, 100); + app.root.addChild(cameraEntity); + + // add a shadow casting directional light + const lightColor = new pc.Color(1, 1, 1); + const light = new pc.Entity(); + light.addComponent('light', { + type: 'directional', + color: lightColor, + intensity: 0.2, + range: 700, + shadowResolution: 4096, + shadowDistance: 600, + castShadows: true, + shadowBias: 0.2, + normalOffsetBias: 0.05 + }); + app.root.addChild(light); + light.setLocalEulerAngles(40, 10, 0); + + // ------ Custom render passes set up ------ + + const currentOptions = { + camera: cameraEntity.camera, // camera used to render those passes + samples: 0, // number of samples for multi-sampling + // sceneColorMap: true, // true if the scene color should be captured + sceneColorMap: false, + + // enable the pre-pass to generate the depth buffer, which is needed by the TAA + prepassEnabled: true, + + // enable temporal anti-aliasing + taaEnabled: true + }; + + const setupRenderPass = () => { + // destroy existing pass if any + if (cameraEntity.camera.renderPasses.length > 0) { + cameraEntity.camera.renderPasses[0].destroy(); + } + + // Use a render pass camera frame, which is a render pass that implements typical rendering of a camera. + // Internally this sets up additional passes it needs, based on the options passed to it. + const renderPassCamera = new pcx.RenderPassCameraFrame(app, currentOptions); + + const composePass = renderPassCamera.composePass; + composePass.toneMapping = data.get('data.scene.tonemapping'); + composePass.bloomIntensity = 0.02; + + // and set up these rendering passes to be used by the camera, instead of its default rendering + cameraEntity.camera.renderPasses = [renderPassCamera]; + }; + + // ------ + + const applySettings = () => { + + // if settings require render passes to be re-created + const noPasses = cameraEntity.camera.renderPasses.length === 0; + const taaEnabled = data.get('data.taa.enabled'); + if (noPasses || taaEnabled !== currentOptions.taaEnabled) { + currentOptions.taaEnabled = taaEnabled; + + // create new pass + setupRenderPass(); + } + + + // apply all runtime settings + const renderPassCamera = cameraEntity.camera.renderPasses[0]; + renderPassCamera.renderTargetScale = data.get('data.scene.scale'); + renderPassCamera.bloomEnabled = data.get('data.scene.bloom'); + + // taa - enable camera jitter if taa is enabled + cameraEntity.camera.jitter = taaEnabled ? data.get('data.taa.jitter') : 0; + }; + + // apply UI changes + let initialValuesSetup = false; + data.on('*:set', () => { + if (initialValuesSetup) applySettings(); + }); + + // set initial values + data.set('data', { + scene: { + scale: 1, + bloom: true, + tonemapping: pc.TONEMAP_ACES + }, + taa: { + enabled: currentOptions.taaEnabled, + jitter: 1 + } + }); + + // apply initial settings after all values are set + initialValuesSetup = true; + applySettings(); +}); + +export { app }; diff --git a/examples/thumbnails/graphics_taa_large.webp b/examples/thumbnails/graphics_taa_large.webp new file mode 100644 index 00000000000..a5badeb02dd Binary files /dev/null and b/examples/thumbnails/graphics_taa_large.webp differ diff --git a/examples/thumbnails/graphics_taa_small.webp b/examples/thumbnails/graphics_taa_small.webp new file mode 100644 index 00000000000..3ea7e6a8f7c Binary files /dev/null and b/examples/thumbnails/graphics_taa_small.webp differ diff --git a/examples/thumbnails/loaders_bundle_large.webp b/examples/thumbnails/loaders_bundle_large.webp new file mode 100644 index 00000000000..dc8e3cf1fc4 Binary files /dev/null and b/examples/thumbnails/loaders_bundle_large.webp differ diff --git a/examples/thumbnails/loaders_bundle_small.webp b/examples/thumbnails/loaders_bundle_small.webp new file mode 100644 index 00000000000..cfe1697e314 Binary files /dev/null and b/examples/thumbnails/loaders_bundle_small.webp differ diff --git a/extras/render-passes/render-pass-camera-frame.js b/extras/render-passes/render-pass-camera-frame.js index 92735acd1c2..ee3164eb4a0 100644 --- a/extras/render-passes/render-pass-camera-frame.js +++ b/extras/render-passes/render-pass-camera-frame.js @@ -20,7 +20,7 @@ import { RenderPassPrepass } from "./render-pass-prepass.js"; class RenderPassCameraFrame extends RenderPass { app; - prepPass; + prePass; scenePass; @@ -126,7 +126,7 @@ class RenderPassCameraFrame extends RenderPass { // create a render target to render the scene into const format = device.getRenderableHdrFormat() || PIXELFORMAT_RGBA8; const sceneTexture = new Texture(device, { - name: 'SceneTexture', + name: 'SceneColor', width: 4, height: 4, format: format, @@ -165,7 +165,7 @@ class RenderPassCameraFrame extends RenderPass { // ------ SCENE PREPASS ------ if (options.prepassEnabled) { - this.prepPass = new RenderPassPrepass(device, scene, renderer, cameraComponent, sceneDepth, sceneOptions); + this.prePass = new RenderPassPrepass(device, scene, renderer, cameraComponent, sceneDepth, sceneOptions); } // ------ SCENE RENDERING WITH OPTIONAL GRAB PASS ------ @@ -176,9 +176,10 @@ class RenderPassCameraFrame extends RenderPass { this.scenePass = new RenderPassForward(device, composition, scene, renderer); this.scenePass.init(rt, sceneOptions); - // if prepass is enabled, do not clear the depth buffer when rendering the scene + // if prepass is enabled, do not clear the depth buffer when rendering the scene, and preserve it if (options.prepassEnabled) { this.scenePass.noDepthClear = true; + this.scenePass.depthStencilOps.storeDepth = true; } // layers this pass renders depend on the grab pass being used @@ -213,8 +214,8 @@ class RenderPassCameraFrame extends RenderPass { let sceneTextureWithTaa = sceneTexture; if (options.taaEnabled) { - this.taaPass = new RenderPassTAA(device, sceneTexture); - sceneTextureWithTaa = this.taaPass.accumulationTexture; + this.taaPass = new RenderPassTAA(device, sceneTexture, cameraComponent); + sceneTextureWithTaa = this.taaPass.historyTexture; } // ------ BLOOM GENERATION ------ @@ -226,8 +227,8 @@ class RenderPassCameraFrame extends RenderPass { // create a compose pass, which combines the scene texture with the bloom texture this.composePass = new RenderPassCompose(app.graphicsDevice); - // this.composePass.sceneTexture = sceneTextureWithTaa; this.composePass.bloomTexture = this.bloomPass.bloomTexture; + this.composePass.taaEnabled = options.taaEnabled; // compose pass renders directly to target renderTarget this.composePass.init(targetRenderTarget); @@ -242,7 +243,7 @@ class RenderPassCameraFrame extends RenderPass { afterPass.addLayers(composition, cameraComponent, lastAddedIndex, clearRenderTarget); // use these prepared render passes in the order they should be executed - const allPasses = [this.prepPass, this.scenePass, colorGrabPass, scenePassTransparent, this.taaPass, this.bloomPass, this.composePass, afterPass]; + const allPasses = [this.prePass, this.scenePass, colorGrabPass, scenePassTransparent, this.taaPass, this.bloomPass, this.composePass, afterPass]; this.beforePasses = allPasses.filter(element => element !== undefined); } @@ -253,7 +254,7 @@ class RenderPassCameraFrame extends RenderPass { // scene texture is either output of taa pass or the scene render target const sceneTexture = this.taaPass?.update() ?? this._rt.colorBuffer; - // TAA accumulation buffer is double buffered, assign the current one to the follow up passes. + // TAA history buffer is double buffered, assign the current one to the follow up passes. this.composePass.sceneTexture = sceneTexture; if (this.bloomEnabled) { this.bloomPass.sourceTexture = sceneTexture; diff --git a/extras/render-passes/render-pass-compose.js b/extras/render-passes/render-pass-compose.js index cf8fc7ff7e6..269a300aa30 100644 --- a/extras/render-passes/render-pass-compose.js +++ b/extras/render-passes/render-pass-compose.js @@ -69,15 +69,25 @@ const fragmentShader = ` #endif void main() { - vec4 scene = texture2D(sceneTexture, uv0); + + vec2 uv = uv0; + + // TAA pass renders upside-down on WebGPU, flip it here + #ifdef TAA + #ifdef WEBGPU + uv.y = 1.0 - uv.y; + #endif + #endif + + vec4 scene = texture2D(sceneTexture, uv); vec3 result = scene.rgb; #ifdef FRINGING - result = fringing(uv0, result); + result = fringing(uv, result); #endif #ifdef BLOOM - vec3 bloom = texture2D(bloomTexture, uv0).rgb; + vec3 bloom = texture2D(bloomTexture, uv).rgb; result += bloom * bloomIntensity; #endif @@ -88,7 +98,7 @@ const fragmentShader = ` result = toneMap(result); #ifdef VIGNETTE - result *= vignette(uv0); + result *= vignette(uv); #endif result = gammaCorrectOutput(result); @@ -130,6 +140,8 @@ class RenderPassCompose extends RenderPassShaderQuad { fringingIntensity = 10; + _taaEnabled = false; + _key = ''; constructor(graphicsDevice) { @@ -154,6 +166,17 @@ class RenderPassCompose extends RenderPassShaderQuad { return this._bloomTexture; } + set taaEnabled(value) { + if (this._taaEnabled !== value) { + this._taaEnabled = value; + this._shaderDirty = true; + } + } + + get taaEnabled() { + return this._taaEnabled; + } + set gradingEnabled(value) { if (this._gradingEnabled !== value) { this._gradingEnabled = value; @@ -225,7 +248,8 @@ class RenderPassCompose extends RenderPassShaderQuad { `-${this.bloomTexture ? 'bloom' : 'nobloom'}` + `-${this.gradingEnabled ? 'grading' : 'nograding'}` + `-${this.vignetteEnabled ? 'vignette' : 'novignette'}` + - `-${this.fringingEnabled ? 'fringing' : 'nofringing'}`; + `-${this.fringingEnabled ? 'fringing' : 'nofringing'}` + + `-${this.taaEnabled ? 'taa' : 'notaa'}`; if (this._key !== key) { this._key = key; @@ -234,7 +258,8 @@ class RenderPassCompose extends RenderPassShaderQuad { (this.bloomTexture ? `#define BLOOM\n` : '') + (this.gradingEnabled ? `#define GRADING\n` : '') + (this.vignetteEnabled ? `#define VIGNETTE\n` : '') + - (this.fringingEnabled ? `#define FRINGING\n` : ''); + (this.fringingEnabled ? `#define FRINGING\n` : '') + + (this.taaEnabled ? `#define TAA\n` : ''); const fsChunks = shaderChunks.decodePS + diff --git a/extras/render-passes/render-pass-prepass.js b/extras/render-passes/render-pass-prepass.js index a22f68d07f1..7afa0e8b220 100644 --- a/extras/render-passes/render-pass-prepass.js +++ b/extras/render-passes/render-pass-prepass.js @@ -1,6 +1,11 @@ import { LAYERID_DEPTH, - SHADER_DEPTH, + SHADER_PREPASS_VELOCITY, + FILTER_NEAREST, + PIXELFORMAT_RGBA32F, + PIXELFORMAT_RGBA16F, + ADDRESS_CLAMP_TO_EDGE, + Texture, RenderPass, RenderTarget } from "playcanvas"; @@ -10,6 +15,9 @@ const tempMeshInstances = []; // uniform name of the depth texture const DEPTH_UNIFORM_NAME = 'uSceneDepthMap'; +// uniform name of the velocity texture +const VELOCITY_UNIFORM_NAME = 'uSceneVelocityMap'; + /** * A render pass which typically executes before the rendering of the main scene, and renders data * that is required for the main rendering pass (and also in following passes) into separate render @@ -21,6 +29,9 @@ class RenderPassPrepass extends RenderPass { /** @type {import("playcanvas").BindGroup[]} */ viewBindGroups = []; + /** @type {Texture} */ + velocityTexture; + constructor(device, scene, renderer, camera, depthBuffer, options) { super(device); this.scene = scene; @@ -32,7 +43,10 @@ class RenderPassPrepass extends RenderPass { destroy() { super.destroy(); - this.releaseRenderTarget(this.renderTarget); + this.renderTarget?.destroy(); + this.renderTarget = null; + this.velocityTexture?.destroy(); + this.velocityTexture = null; this.viewBindGroups.forEach((bg) => { bg.defaultUniformBuffer.destroy(); @@ -43,8 +57,25 @@ class RenderPassPrepass extends RenderPass { setupRenderTarget(depthBuffer, options) { + const { device } = this; + + // TODO: only two channel texture is needed here, but that is not supported by WebGL + const velocityFormat = device.getRenderableHdrFormat([PIXELFORMAT_RGBA32F, PIXELFORMAT_RGBA16F]); + this.velocityTexture = new Texture(device, { + name: 'VelocityTexture', + width: 4, + height: 4, + format: velocityFormat, + mipmaps: false, + minFilter: FILTER_NEAREST, + magFilter: FILTER_NEAREST, + addressU: ADDRESS_CLAMP_TO_EDGE, + addressV: ADDRESS_CLAMP_TO_EDGE + }); + const renderTarget = new RenderTarget({ name: 'PrepassRT', + // colorBuffer: this.velocityTexture, depthBuffer: depthBuffer }); @@ -52,11 +83,12 @@ class RenderPassPrepass extends RenderPass { this.depthStencilOps.storeDepth = true; } - before() { + after() { // Assign the depth to the uniform. Note that the depth buffer is still used as a render // target in the following scene passes, and cannot be used as a texture inside those passes. this.device.scope.resolve(DEPTH_UNIFORM_NAME).setValue(this.renderTarget.depthBuffer); + this.device.scope.resolve(VELOCITY_UNIFORM_NAME).setValue(this.velocityTexture); } execute() { @@ -91,7 +123,7 @@ class RenderPassPrepass extends RenderPass { } } - renderer.renderForwardLayer(camera, renderTarget, null, undefined, SHADER_DEPTH, this.viewBindGroups, { + renderer.renderForwardLayer(camera, renderTarget, null, undefined, SHADER_PREPASS_VELOCITY, this.viewBindGroups, { meshInstances: tempMeshInstances }); @@ -102,6 +134,9 @@ class RenderPassPrepass extends RenderPass { } frameUpdate() { + + super.frameUpdate(); + // depth clear value (1 or no clear) set up each frame const { camera } = this; this.setClearDepth(camera.clearDepthBuffer ? 1 : undefined); diff --git a/extras/render-passes/render-pass-taa.js b/extras/render-passes/render-pass-taa.js index 92cb52e17e1..9c25eba7977 100644 --- a/extras/render-passes/render-pass-taa.js +++ b/extras/render-passes/render-pass-taa.js @@ -1,50 +1,151 @@ import { FILTER_LINEAR, ADDRESS_CLAMP_TO_EDGE, + shaderChunks, RenderPassShaderQuad, Texture, RenderTarget } from "playcanvas"; +const fs = /* glsl */ ` + uniform highp sampler2D uSceneDepthMap; + uniform sampler2D sourceTexture; + uniform sampler2D historyTexture; + uniform mat4 matrix_viewProjectionPrevious; + uniform mat4 matrix_viewProjectionInverse; + uniform vec4 jitters; // xy: current frame, zw: previous frame + uniform vec2 textureSize; + + varying vec2 uv0; + + vec2 reproject(vec2 uv, float depth) { + + // fragment NDC + #ifndef WEBGPU + depth = depth * 2.0 - 1.0; + #endif + vec4 ndc = vec4(uv * 2.0 - 1.0, depth, 1.0); + + // remove jitter from the current frame + ndc.xy -= jitters.xy; + + // Transform NDC to world space of the current frame + vec4 worldPosition = matrix_viewProjectionInverse * ndc; + worldPosition /= worldPosition.w; + + // world position to screen space of the previous frame + vec4 screenPrevious = matrix_viewProjectionPrevious * worldPosition; + + return (screenPrevious.xy / screenPrevious.w) * 0.5 + 0.5; + } + + vec4 colorClamp(vec2 uv, vec4 historyColor) { + + // out of range numbers + vec3 minColor = vec3(9999.0); + vec3 maxColor = vec3(-9999.0); + + // sample a 3x3 neighborhood to create a box in color space + for(float x = -1.0; x <= 1.0; ++x) + { + for(float y = -1.0; y <= 1.0; ++y) + { + vec3 color = texture2D(sourceTexture, uv + vec2(x, y) / textureSize).rgb; + minColor = min(minColor, color); + maxColor = max(maxColor, color); + } + } + + // clamp the history color to min/max bounding box + vec3 clamped = clamp(historyColor.rgb, minColor, maxColor); + return vec4(clamped, historyColor.a); + } + + void main() + { + vec2 uv = uv0; + + #ifdef WEBGPU + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // This hack is needed on webgpu, which makes TAA work but the resulting image is upside-down. + // We could flip the image in the following pass, but ideally a better solution should be found. + uv.y = 1.0 - uv.y; + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #endif + + // current frame + vec4 srcColor = texture2D(sourceTexture, uv); + + // current depth + float depth = texture2DLodEXT(uSceneDepthMap, uv, 0.0).r; + + // previous frame + vec2 historyUv = reproject(uv0, depth); + + #ifdef QUALITY_HIGH + + // high quality history, sharper result + vec4 historyColor = SampleTextureCatmullRom(TEXTURE_PASS(historyTexture), historyUv, textureSize); + + #else + + // single sample history, more blurry result + vec4 historyColor = texture2D(historyTexture, historyUv); + + #endif + + // handle disocclusion by clamping the history color + vec4 historyColorClamped = colorClamp(uv, historyColor); + + // handle history buffer outside of the frame + float mixFactor = (historyUv.x < 0.0 || historyUv.x > 1.0 || historyUv.y < 0.0 || historyUv.y > 1.0) ? + 1.0 : 0.05; + + gl_FragColor = mix(historyColorClamped, srcColor, mixFactor); + } +`; + class RenderPassTAA extends RenderPassShaderQuad { /** - * The index of the accumulation texture to render to. + * The index of the history texture to render to. * * @type {number} */ - accumulationIndex = 0; + historyIndex = 0; - accumulationTexture = null; + /** + * @type {Texture} + */ + historyTexture = null; /** * @type {Texture[]} */ - accumulationTextures = []; + historyTextures = []; /** * @type {RenderTarget[]} */ - accumulationRenderTargets = []; + historyRenderTargets = []; - constructor(device, sourceTexture) { + constructor(device, sourceTexture, cameraComponent) { super(device); this.sourceTexture = sourceTexture; - - this.shader = this.createQuadShader('TaaResolveShader', ` - - uniform sampler2D sourceTexture; - uniform sampler2D accumulationTexture; - varying vec2 uv0; - - void main() - { - vec4 src = texture2D(sourceTexture, uv0); - vec4 acc = texture2D(accumulationTexture, uv0); - gl_FragColor = mix(acc, src, 0.05); - }` - ); - - this.sourceTextureId = device.scope.resolve('sourceTexture'); - this.accumulationTextureId = device.scope.resolve('accumulationTexture'); + this.cameraComponent = cameraComponent; + + const defines = ` + #define QUALITY_HIGH + `; + const fsChunks = shaderChunks.sampleCatmullRomPS; + this.shader = this.createQuadShader('TaaResolveShader', defines + fsChunks + fs); + + const { scope } = device; + this.sourceTextureId = scope.resolve('sourceTexture'); + this.textureSizeId = scope.resolve('textureSize'); + this.textureSize = new Float32Array(2); + this.historyTextureId = scope.resolve('historyTexture'); + this.viewProjPrevId = scope.resolve('matrix_viewProjectionPrevious'); + this.viewProjInvId = scope.resolve('matrix_viewProjectionInverse'); + this.jittersId = scope.resolve('jitters'); this.setup(); } @@ -59,10 +160,10 @@ class RenderPassTAA extends RenderPassShaderQuad { setup() { - // double buffered accumulation render target + // double buffered history render target for (let i = 0; i < 2; ++i) { - this.accumulationTextures[i] = new Texture(this.device, { - name: `TAA-Accumulation-${i}`, + this.historyTextures[i] = new Texture(this.device, { + name: `TAA-History-${i}`, width: 4, height: 4, format: this.sourceTexture.format, @@ -73,34 +174,41 @@ class RenderPassTAA extends RenderPassShaderQuad { addressV: ADDRESS_CLAMP_TO_EDGE }); - this.accumulationRenderTargets[i] = new RenderTarget({ - colorBuffer: this.accumulationTextures[i], + this.historyRenderTargets[i] = new RenderTarget({ + colorBuffer: this.historyTextures[i], depth: false }); } - this.accumulationTexture = this.accumulationTextures[0]; - this.init(this.accumulationRenderTargets[0], { + this.historyTexture = this.historyTextures[0]; + this.init(this.historyRenderTargets[0], { resizeSource: this.sourceTexture }); } - execute() { + before() { this.sourceTextureId.setValue(this.sourceTexture); - this.accumulationTextureId.setValue(this.accumulationTextures[1 - this.accumulationIndex]); + this.historyTextureId.setValue(this.historyTextures[1 - this.historyIndex]); + + this.textureSize[0] = this.sourceTexture.width; + this.textureSize[1] = this.sourceTexture.height; + this.textureSizeId.setValue(this.textureSize); - super.execute(); + const camera = this.cameraComponent.camera; + this.viewProjPrevId.setValue(camera._viewProjPrevious.data); + this.viewProjInvId.setValue(camera._viewProjInverse.data); + this.jittersId.setValue(camera._jitters); } // called when the parent render pass gets added to the frame graph update() { - // swap source and destination accumulation texture - this.accumulationIndex = 1 - this.accumulationIndex; - this.accumulationTexture = this.accumulationTextures[this.accumulationIndex]; - this.renderTarget = this.accumulationRenderTargets[this.accumulationIndex]; + // swap source and destination history texture + this.historyIndex = 1 - this.historyIndex; + this.historyTexture = this.historyTextures[this.historyIndex]; + this.renderTarget = this.historyRenderTargets[this.historyIndex]; - return this.accumulationTexture; + return this.historyTexture; } } diff --git a/src/framework/handlers/bundle.js b/src/framework/handlers/bundle.js index 8208f340ee0..a3c52d4284f 100644 --- a/src/framework/handlers/bundle.js +++ b/src/framework/handlers/bundle.js @@ -18,7 +18,23 @@ class BundleHandler extends ResourceHandler { super(app, 'bundle'); this._assets = app.assets; - this.maxRetries = 0; + } + + _fetchRetries(url, options, retries = 0) { + return new Promise((resolve, reject) => { + const tryFetch = () => { + fetch(url, options).then(resolve).catch((err) => { + retries++; + if (retries < this.maxRetries) { + Debug.error(`Bundle failed to load retrying (attempt ${retries}`); + tryFetch(); + } else { + reject(err); + } + }); + }; + tryFetch(); + }); } load(url, callback) { @@ -29,10 +45,10 @@ class BundleHandler extends ResourceHandler { }; } - fetch(url.load, { + this._fetchRetries(url.load, { mode: 'cors', credentials: 'include' - }).then((res) => { + }, this.maxRetries).then((res) => { const bundle = new Bundle(); callback(null, bundle); diff --git a/src/framework/script/script-type.js b/src/framework/script/script-type.js index 0dcceb8b87c..39bd0ea9df8 100644 --- a/src/framework/script/script-type.js +++ b/src/framework/script/script-type.js @@ -356,39 +356,37 @@ class ScriptType extends EventHandler { } /** - * @function - * @name ScriptType#[initialize] - * @description Called when script is about to run for the first time. + * Called when script is about to run for the first time. */ + initialize() {} /** - * @function - * @name ScriptType#[postInitialize] - * @description Called after all initialize methods are executed in the same tick or enabling chain of actions. + * Called after all initialize methods are executed in the same tick or enabling chain of actions. */ + postInitialize() {} /** - * @function - * @name ScriptType#[update] - * @description Called for enabled (running state) scripts on each tick. + * Called for enabled (running state) scripts on each tick. + * * @param {number} dt - The delta time in seconds since the last frame. */ + update(dt) {} /** - * @function - * @name ScriptType#[postUpdate] - * @description Called for enabled (running state) scripts on each tick, after update. + * Called for enabled (running state) scripts on each tick, after update. + * * @param {number} dt - The delta time in seconds since the last frame. */ + postUpdate(dt) {} /** - * @function - * @name ScriptType#[swap] - * @description Called when a ScriptType that already exists in the registry + * Called when a ScriptType that already exists in the registry * gets redefined. If the new ScriptType has a `swap` method in its prototype, * then it will be executed to perform hot-reload at runtime. + * * @param {ScriptType} old - Old instance of the scriptType to copy data to the new instance. */ + swap(old) {} } export { ScriptType }; diff --git a/src/scene/camera.js b/src/scene/camera.js index 7908fc5d999..3a8d1584fd1 100644 --- a/src/scene/camera.js +++ b/src/scene/camera.js @@ -92,6 +92,13 @@ class Camera { this._viewProjMat = new Mat4(); this._viewProjMatDirty = true; + // storage of actual matrices used by the shaders, needed by TAA + this._shaderMatricesVersion = 0; + this._viewProjInverse = new Mat4(); // inverse view projection matrix from the current frame + this._viewProjCurrent = null; // view projection matrix from the current frame + this._viewProjPrevious = new Mat4(); // view projection matrix from the previous frame + this._jitters = [0, 0, 0, 0]; // jitter values for TAA, 0-1 - current frame, 2-3 - previous frame + this.frustum = new Frustum(); // Set by XrManager @@ -116,6 +123,25 @@ class Camera { this.renderPasses.length = 0; } + /** + * Store camera matrices required by TAA. Only update them once per frame. + */ + _storeShaderMatrices(viewProjMat, jitterX, jitterY, renderVersion) { + if (this._shaderMatricesVersion !== renderVersion) { + this._shaderMatricesVersion = renderVersion; + + this._viewProjPrevious.copy(this._viewProjCurrent ?? viewProjMat); + this._viewProjCurrent ??= new Mat4(); + this._viewProjCurrent.copy(viewProjMat); + this._viewProjInverse.invert(viewProjMat); + + this._jitters[2] = this._jitters[0]; + this._jitters[3] = this._jitters[1]; + this._jitters[0] = jitterX; + this._jitters[1] = jitterY; + } + } + /** * True if the camera clears the full render target. (viewport / scissor are full size) */ diff --git a/src/scene/constants.js b/src/scene/constants.js index 6b60489e351..ae5e466b243 100644 --- a/src/scene/constants.js +++ b/src/scene/constants.js @@ -738,6 +738,8 @@ export const SHADER_PICK = 3; // shadow pass used by the shadow rendering code export const SHADER_SHADOW = 4; +export const SHADER_PREPASS_VELOCITY = 5; + /** * Shader that performs forward rendering. * diff --git a/src/scene/materials/standard-material.js b/src/scene/materials/standard-material.js index e456ac81f58..ad818b89b72 100644 --- a/src/scene/materials/standard-material.js +++ b/src/scene/materials/standard-material.js @@ -11,6 +11,7 @@ import { DITHER_NONE, FRESNEL_SCHLICK, SHADER_DEPTH, SHADER_PICK, + SHADER_PREPASS_VELOCITY, SPECOCC_AO, SPECULAR_BLINN, SPECULAR_PHONG } from '../constants.js'; @@ -880,9 +881,9 @@ class StandardMaterial extends Material { // update prefiltered lighting data this.updateEnvUniforms(device, scene); - // Minimal options for Depth and Shadow passes + // Minimal options for Depth, Shadow and Prepass passes const shaderPassInfo = ShaderPass.get(device).getByIndex(pass); - const minimalOptions = pass === SHADER_DEPTH || pass === SHADER_PICK || shaderPassInfo.isShadow; + const minimalOptions = pass === SHADER_DEPTH || pass === SHADER_PICK || pass === SHADER_PREPASS_VELOCITY || shaderPassInfo.isShadow; let options = minimalOptions ? standard.optionsContextMin : standard.optionsContext; if (minimalOptions) diff --git a/src/scene/renderer/renderer.js b/src/scene/renderer/renderer.js index a127dd0961b..1f56c0197d1 100644 --- a/src/scene/renderer/renderer.js +++ b/src/scene/renderer/renderer.js @@ -376,6 +376,8 @@ class Renderer { // camera jitter const { jitter } = camera; let noise = Vec4.ZERO; + let jitterX = 0; + let jitterY = 0; if (jitter > 0) { // render target size @@ -384,18 +386,18 @@ class Renderer { // offsets const offset = _haltonSequence[this.device.renderVersion % _haltonSequence.length]; - const offsetX = jitter * (offset.x * 2 - 1) / targetWidth; - const offsetY = jitter * (offset.y * 2 - 1) / targetHeight; + jitterX = jitter * (offset.x * 2 - 1) / targetWidth; + jitterY = jitter * (offset.y * 2 - 1) / targetHeight; // apply offset to projection matrix projMat = _tempProjMat4.copy(projMat); - projMat.data[8] = offsetX; - projMat.data[9] = offsetY; + projMat.data[8] = jitterX; + projMat.data[9] = jitterY; // apply offset to skybox projection matrix projMatSkybox = _tempProjMat5.copy(projMatSkybox); - projMatSkybox.data[8] = offsetX; - projMatSkybox.data[9] = offsetY; + projMatSkybox.data[8] = jitterX; + projMatSkybox.data[9] = jitterY; // blue noise vec4 - only set when jitter is enabled noise = this.blueNoise.vec4(_tempVec4); @@ -428,6 +430,9 @@ class Renderer { viewProjMat.mul2(projMat, viewMat); this.viewProjId.setValue(viewProjMat.data); + // store matrices needed by TAA + camera._storeShaderMatrices(viewProjMat, jitterX, jitterY, this.device.renderVersion); + this.flipYId.setValue(flipY ? -1 : 1); // View Position (world space) diff --git a/src/scene/shader-lib/chunks/chunks.js b/src/scene/shader-lib/chunks/chunks.js index 1473078d601..95f4929e92e 100644 --- a/src/scene/shader-lib/chunks/chunks.js +++ b/src/scene/shader-lib/chunks/chunks.js @@ -153,6 +153,7 @@ import reflectionSheenPS from './lit/frag/reflectionSheen.js'; import refractionCubePS from './lit/frag/refractionCube.js'; import refractionDynamicPS from './lit/frag/refractionDynamic.js'; import reprojectPS from './common/frag/reproject.js'; +import sampleCatmullRomPS from './common/frag/sampleCatmullRom.js'; import screenDepthPS from './common/frag/screenDepth.js'; import shadowCascadesPS from './lit/frag/shadowCascades.js'; import shadowEVSMPS from './lit/frag/shadowEVSM.js'; @@ -364,6 +365,7 @@ const shaderChunks = { refractionCubePS, refractionDynamicPS, reprojectPS, + sampleCatmullRomPS, screenDepthPS, shadowCascadesPS, shadowEVSMPS, diff --git a/src/scene/shader-lib/chunks/common/frag/sampleCatmullRom.js b/src/scene/shader-lib/chunks/common/frag/sampleCatmullRom.js new file mode 100644 index 00000000000..2f51c0b4652 --- /dev/null +++ b/src/scene/shader-lib/chunks/common/frag/sampleCatmullRom.js @@ -0,0 +1,50 @@ +// Shader function for sampling a 2D texture with Catmull-Rom filtering, using 9 texture samples instead of 16 +// based on https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1 + +export default /* glsl */` + +vec4 SampleTextureCatmullRom(TEXTURE_ACCEPT(tex), vec2 uv, vec2 texSize) { + // We're going to sample a a 4x4 grid of texels surrounding the target UV coordinate. We'll do this by rounding + // down the sample location to get the exact center of our "starting" texel. The starting texel will be at + // location [1, 1] in the grid, where [0, 0] is the top left corner. + vec2 samplePos = uv * texSize; + vec2 texPos1 = floor(samplePos - 0.5) + 0.5; + + // Compute the fractional offset from our starting texel to our original sample location, which we'll + // feed into the Catmull-Rom spline function to get our filter weights. + vec2 f = samplePos - texPos1; + + // Compute the Catmull-Rom weights using the fractional offset that we calculated earlier. + // These equations are pre-expanded based on our knowledge of where the texels will be located, + // which lets us avoid having to evaluate a piece-wise function. + vec2 w0 = f * (-0.5 + f * (1.0 - 0.5 * f)); + vec2 w1 = 1.0 + f * f * (-2.5 + 1.5 * f); + vec2 w2 = f * (0.5 + f * (2.0 - 1.5 * f)); + vec2 w3 = f * f * (-0.5 + 0.5 * f); + + // Work out weighting factors and sampling offsets that will let us use bilinear filtering to + // simultaneously evaluate the middle 2 samples from the 4x4 grid. + vec2 w12 = w1 + w2; + vec2 offset12 = w2 / (w1 + w2); + + // Compute the final UV coordinates we'll use for sampling the texture + vec2 texPos0 = (texPos1 - 1.0) / texSize; + vec2 texPos3 = (texPos1 + 2.0) / texSize; + vec2 texPos12 = (texPos1 + offset12) / texSize; + + vec4 result = vec4(0.0); + result += texture2DLodEXT(tex, vec2(texPos0.x, texPos0.y), 0.0) * w0.x * w0.y; + result += texture2DLodEXT(tex, vec2(texPos12.x, texPos0.y), 0.0) * w12.x * w0.y; + result += texture2DLodEXT(tex, vec2(texPos3.x, texPos0.y), 0.0) * w3.x * w0.y; + + result += texture2DLodEXT(tex, vec2(texPos0.x, texPos12.y), 0.0) * w0.x * w12.y; + result += texture2DLodEXT(tex, vec2(texPos12.x, texPos12.y), 0.0) * w12.x * w12.y; + result += texture2DLodEXT(tex, vec2(texPos3.x, texPos12.y), 0.0) * w3.x * w12.y; + + result += texture2DLodEXT(tex, vec2(texPos0.x, texPos3.y), 0.0) * w0.x * w3.y; + result += texture2DLodEXT(tex, vec2(texPos12.x, texPos3.y), 0.0) * w12.x * w3.y; + result += texture2DLodEXT(tex, vec2(texPos3.x, texPos3.y), 0.0) * w3.x * w3.y; + + return result; +} +`; diff --git a/src/scene/shader-lib/program-library.js b/src/scene/shader-lib/program-library.js index 383968ee577..cb1a69b2163 100644 --- a/src/scene/shader-lib/program-library.js +++ b/src/scene/shader-lib/program-library.js @@ -4,7 +4,7 @@ import { version, revision } from '../../core/core.js'; import { Shader } from '../../platform/graphics/shader.js'; -import { SHADER_FORWARD, SHADER_DEPTH, SHADER_PICK, SHADER_SHADOW } from '../constants.js'; +import { SHADER_FORWARD, SHADER_DEPTH, SHADER_PICK, SHADER_SHADOW, SHADER_PREPASS_VELOCITY } from '../constants.js'; import { ShaderPass } from '../shader-pass.js'; import { StandardMaterialOptions } from '../materials/standard-material-options.js'; @@ -269,7 +269,7 @@ class ProgramLibrary { _getDefaultStdMatOptions(pass) { const shaderPassInfo = ShaderPass.get(this._device).getByIndex(pass); - return (pass === SHADER_DEPTH || pass === SHADER_PICK || shaderPassInfo.isShadow) ? + return (pass === SHADER_DEPTH || pass === SHADER_PICK || pass === SHADER_PREPASS_VELOCITY || shaderPassInfo.isShadow) ? this._defaultStdMatOptionMin : this._defaultStdMatOption; } diff --git a/src/scene/shader-lib/programs/lit-shader.js b/src/scene/shader-lib/programs/lit-shader.js index 6f48717b31b..118d3e9802d 100644 --- a/src/scene/shader-lib/programs/lit-shader.js +++ b/src/scene/shader-lib/programs/lit-shader.js @@ -18,7 +18,7 @@ import { SHADOW_PCF1, SHADOW_PCF3, SHADOW_PCF5, SHADOW_VSM8, SHADOW_VSM16, SHADOW_VSM32, SHADOW_PCSS, SPECOCC_AO, SPECOCC_GLOSSDEPENDENT, SPECULAR_PHONG, - SPRITE_RENDERMODE_SLICED, SPRITE_RENDERMODE_TILED, shadowTypeToString + SPRITE_RENDERMODE_SLICED, SPRITE_RENDERMODE_TILED, shadowTypeToString, SHADER_PREPASS_VELOCITY } from '../../constants.js'; import { LightsBuffer } from '../../lighting/lights-buffer.js'; import { ShaderPass } from '../../shader-pass.js'; @@ -27,6 +27,7 @@ import { validateUserChunks } from '../chunks/chunk-validation.js'; import { ShaderUtils } from '../../../platform/graphics/shader-utils.js'; import { ChunkBuilder } from '../chunk-builder.js'; import { ShaderGenerator } from './shader-generator.js'; +import { Debug } from '../../../core/debug.js'; const builtinAttributes = { vertex_normal: SEMANTIC_NORMAL, @@ -224,6 +225,10 @@ class LitShader { codeBody += " vDepth = -(matrix_view * vec4(vPositionW,1.0)).z * camera_params.x;\n"; } + if (this.options.pass === SHADER_PREPASS_VELOCITY) { + Debug.error("SHADER_PREPASS_VELOCITY not implemented"); + } + if (this.options.useInstancing) { this.attributes.instance_line1 = SEMANTIC_ATTR12; this.attributes.instance_line2 = SEMANTIC_ATTR13; @@ -434,7 +439,6 @@ class LitShader { const chunks = this.chunks; let code = this._fsGetBeginCode(); - code += 'varying float vDepth;\n'; code += this.varyings; code += this.varyingDefines; @@ -449,6 +453,17 @@ class LitShader { return code; } + _fsGetPrePassVelocityCode() { + const code = ` + void main(void) + { + gl_FragColor = vec4(1, 0, 0, 1); + } + `; + + return code; + } + _fsGetShadowPassCode() { const device = this.device; const options = this.options; @@ -1578,6 +1593,8 @@ class LitShader { this.fshader = this._fsGetPickPassCode(); } else if (options.pass === SHADER_DEPTH) { this.fshader = this._fsGetDepthPassCode(); + } else if (options.pass === SHADER_PREPASS_VELOCITY) { + this.fshader = this._fsGetPrePassVelocityCode(); } else if (this.shadowPass) { this.fshader = this._fsGetShadowPassCode(); } else if (options.customFragmentShader) { diff --git a/src/scene/shader-pass.js b/src/scene/shader-pass.js index 86f9837eeda..8f2adab5e63 100644 --- a/src/scene/shader-pass.js +++ b/src/scene/shader-pass.js @@ -1,6 +1,6 @@ import { Debug } from '../core/debug.js'; import { - SHADER_FORWARD, SHADER_FORWARDHDR, SHADER_DEPTH, SHADER_PICK, SHADER_SHADOW + SHADER_FORWARD, SHADER_FORWARDHDR, SHADER_DEPTH, SHADER_PICK, SHADER_SHADOW, SHADER_PREPASS_VELOCITY } from './constants.js'; import { DeviceCache } from '../platform/graphics/device-cache.js'; @@ -107,6 +107,7 @@ class ShaderPass { add('depth', SHADER_DEPTH); add('pick', SHADER_PICK); add('shadow', SHADER_SHADOW); + add('prepass', SHADER_PREPASS_VELOCITY); } /** diff --git a/utils/types-fixup.mjs b/utils/types-fixup.mjs index 6cc5898703a..a8f23f62dfe 100644 --- a/utils/types-fixup.mjs +++ b/utils/types-fixup.mjs @@ -525,34 +525,3 @@ import { BoundingBox } from '../../core/shape/bounding-box.js'; import { Texture } from '../../platform/graphics/texture.js'; `; fs.writeFileSync(path, dts); - -path = './types/framework/script/script-type.d.ts'; -dts = fs.readFileSync(path, 'utf8'); -dts = dts.replace('get enabled(): boolean;', `get enabled(): boolean; - /** - * Called when script is about to run for the first time. - */ - initialize?(): void; - /** - * Called after all initialize methods are executed in the same tick or enabling chain of actions. - */ - postInitialize?(): void; - /** - * Called for enabled (running state) scripts on each tick. - * @param dt - The delta time in seconds since the last frame. - */ - update?(dt: number): void; - /** - * Called for enabled (running state) scripts on each tick, after update. - * @param dt - The delta time in seconds since the last frame. - */ - postUpdate?(dt: number): void; - /** - * Called when a ScriptType that already exists in the registry gets redefined. If the new - * ScriptType has a \`swap\` method in its prototype, then it will be executed to perform - * hot-reload at runtime. - * @param old - Old instance of the scriptType to copy data to the new instance. - */ - swap?(old: ScriptType): void; -`); -fs.writeFileSync(path, dts);