diff --git a/eslint.config.mjs b/eslint.config.mjs index 42b1f2f2e92..b68415bc8f8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,6 +2,12 @@ import playcanvasConfig from '@playcanvas/eslint-config'; import babelParser from '@babel/eslint-parser'; import globals from 'globals'; +// Extract or preserve existing JSDoc tags +const jsdocRule = playcanvasConfig.find( + config => config.rules && config.rules['jsdoc/check-tag-names'] +); +const existingTags = jsdocRule?.rules['jsdoc/check-tag-names'][1]?.definedTags || []; + export default [ ...playcanvasConfig, { @@ -27,7 +33,14 @@ export default [ } }, rules: { - 'import/order': 'off' + 'import/order': 'off', + 'jsdoc/check-tag-names': [ + 'error', + { + // custom mjs script tags to not error on, add them to those from parent config + definedTags: [...new Set([...existingTags, 'range', 'step', 'precision'])] + } + ] } }, { diff --git a/examples/src/examples/animation/events.example.mjs b/examples/src/examples/animation/events.example.mjs index 74f14bcb017..8d3c856d2ed 100644 --- a/examples/src/examples/animation/events.example.mjs +++ b/examples/src/examples/animation/events.example.mjs @@ -1,6 +1,5 @@ -import { deviceType, rootPath, fileImport } from 'examples/utils'; +import { deviceType, rootPath } from 'examples/utils'; import * as pc from 'playcanvas'; -const { CameraFrame } = await fileImport(`${rootPath}/static/assets/scripts/misc/camera-frame.mjs`); const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); window.focus(); @@ -34,13 +33,11 @@ createOptions.componentSystems = [ pc.RenderComponentSystem, pc.CameraComponentSystem, pc.LightComponentSystem, - pc.ScriptComponentSystem, pc.AnimComponentSystem ]; createOptions.resourceHandlers = [ pc.TextureHandler, pc.ContainerHandler, - pc.ScriptHandler, pc.AnimClipHandler, pc.AnimStateGraphHandler ]; @@ -76,12 +73,12 @@ assetListLoader.load(() => { // ------ Custom render passes set up ------ - cameraEntity.addComponent('script'); - const cameraFrame = cameraEntity.script.create(CameraFrame); + const cameraFrame = new pc.CameraFrame(app, cameraEntity.camera); cameraFrame.rendering.toneMapping = pc.TONEMAP_NEUTRAL; cameraFrame.rendering.samples = 4; cameraFrame.bloom.enabled = true; cameraFrame.bloom.intensity = 0.01; + cameraFrame.update(); // ------------------------------------------ diff --git a/examples/src/examples/camera/first-person.example.mjs b/examples/src/examples/camera/first-person.example.mjs index 8dd6c65fde2..e8e7eee742f 100644 --- a/examples/src/examples/camera/first-person.example.mjs +++ b/examples/src/examples/camera/first-person.example.mjs @@ -1,9 +1,7 @@ // @config DESCRIPTION
(WASD) Move
(Space) Jump
(Mouse) Look
-import { deviceType, rootPath, fileImport } from 'examples/utils'; +import { deviceType, rootPath } from 'examples/utils'; import * as pc from 'playcanvas'; -const { CameraFrame } = await fileImport(`${rootPath}/static/assets/scripts/misc/camera-frame.mjs`); - const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); window.focus(); @@ -148,15 +146,12 @@ cameraEntity.setLocalPosition(0, 0.5, 0); // ------ Custom render passes set up ------ -cameraEntity.addComponent('script'); -/** @type { CameraFrame } */ -const cameraFrame = cameraEntity.script.create(CameraFrame); - +const cameraFrame = new pc.CameraFrame(app, cameraEntity.camera); cameraFrame.rendering.samples = 4; cameraFrame.rendering.toneMapping = pc.TONEMAP_ACES2; - cameraFrame.bloom.enabled = true; cameraFrame.bloom.intensity = 0.01; +cameraFrame.update(); // ------------------------------------------ diff --git a/examples/src/examples/graphics/ambient-occlusion.example.mjs b/examples/src/examples/graphics/ambient-occlusion.example.mjs index b01146cb31e..a3e59ca99ef 100644 --- a/examples/src/examples/graphics/ambient-occlusion.example.mjs +++ b/examples/src/examples/graphics/ambient-occlusion.example.mjs @@ -1,7 +1,6 @@ import { data } from 'examples/observer'; -import { deviceType, rootPath, fileImport } from 'examples/utils'; +import { deviceType, rootPath } from 'examples/utils'; import * as pc from 'playcanvas'; -const { CameraFrame } = await fileImport(`${rootPath}/static/assets/scripts/misc/camera-frame.mjs`); const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); window.focus(); @@ -168,8 +167,7 @@ assetListLoader.load(() => { // ------ Custom render passes set up ------ - /** @type { CameraFrame } */ - const cameraFrame = cameraEntity.script.create(CameraFrame); + const cameraFrame = new pc.CameraFrame(app, cameraEntity.camera); cameraFrame.rendering.samples = 4; cameraFrame.rendering.toneMapping = pc.TONEMAP_NEUTRAL; @@ -183,6 +181,8 @@ assetListLoader.load(() => { cameraFrame.ssao.samples = data.get('data.ssao.samples'); cameraFrame.ssao.minAngle = data.get('data.ssao.minAngle'); cameraFrame.ssao.scale = data.get('data.ssao.scale'); + + cameraFrame.update(); }; // apply UI changes diff --git a/examples/src/examples/graphics/clustered-area-lights.example.mjs b/examples/src/examples/graphics/clustered-area-lights.example.mjs index 5c1ab2dd92d..42af210d94a 100644 --- a/examples/src/examples/graphics/clustered-area-lights.example.mjs +++ b/examples/src/examples/graphics/clustered-area-lights.example.mjs @@ -1,7 +1,6 @@ import { data } from 'examples/observer'; -import { deviceType, rootPath, fileImport } from 'examples/utils'; +import { deviceType, rootPath } from 'examples/utils'; import * as pc from 'playcanvas'; -const { CameraFrame } = await fileImport(`${rootPath}/static/assets/scripts/misc/camera-frame.mjs`); const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); window.focus(); @@ -228,11 +227,11 @@ assetListLoader.load(() => { app.root.addChild(camera); // custom render passes - const cameraFrame = camera.script.create(CameraFrame); + const cameraFrame = new pc.CameraFrame(app, camera.camera); cameraFrame.rendering.samples = 4; - cameraFrame.bloom.enabled = true; cameraFrame.bloom.intensity = 0.01; - cameraFrame.bloom.lastMipLevel = 4; + cameraFrame.bloom.blurLevel = 4; + cameraFrame.update(); // if the device renders in HDR mode, disable tone mapping to output HDR values without any processing cameraFrame.rendering.toneMapping = device.isHdr ? pc.TONEMAP_NONE : pc.TONEMAP_NEUTRAL; diff --git a/examples/src/examples/graphics/dithered-transparency.example.mjs b/examples/src/examples/graphics/dithered-transparency.example.mjs index ae3077b0d6a..3997f706c1c 100644 --- a/examples/src/examples/graphics/dithered-transparency.example.mjs +++ b/examples/src/examples/graphics/dithered-transparency.example.mjs @@ -1,7 +1,6 @@ import { data } from 'examples/observer'; -import { deviceType, rootPath, fileImport } from 'examples/utils'; +import { deviceType, rootPath } from 'examples/utils'; import * as pc from 'playcanvas'; -const { CameraFrame } = await fileImport(`${rootPath}/static/assets/scripts/misc/camera-frame.mjs`); const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); window.focus(); @@ -159,15 +158,16 @@ assetListLoader.load(() => { // ------ Custom render passes set up ------ - /** @type { CameraFrame } */ - const cameraFrame = cameraEntity.script.create(CameraFrame); + const cameraFrame = new pc.CameraFrame(app, cameraEntity.camera); cameraFrame.rendering.toneMapping = pc.TONEMAP_ACES; cameraFrame.rendering.sceneColorMap = true; cameraFrame.taa.jitter = 1; + cameraFrame.update(); const applySettings = () => { cameraFrame.taa.enabled = data.get('data.taa'); cameraFrame.rendering.sharpness = cameraFrame.taa.enabled ? 1 : 0; + cameraFrame.update(); }; // ------ diff --git a/examples/src/examples/graphics/portal.example.mjs b/examples/src/examples/graphics/portal.example.mjs index 3b4256164c3..95802f84850 100644 --- a/examples/src/examples/graphics/portal.example.mjs +++ b/examples/src/examples/graphics/portal.example.mjs @@ -1,6 +1,5 @@ -import { deviceType, rootPath, fileImport } from 'examples/utils'; +import { deviceType, rootPath } from 'examples/utils'; import * as pc from 'playcanvas'; -const { CameraFrame } = await fileImport(`${rootPath}/static/assets/scripts/misc/camera-frame.mjs`); const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); window.focus(); @@ -159,13 +158,11 @@ assetListLoader.load(() => { // ------ Custom render passes set up ------ - camera.addComponent('script'); - /** @type { CameraFrame } */ - const cameraFrame = camera.script.create(CameraFrame); - + const cameraFrame = new pc.CameraFrame(app, camera.camera); cameraFrame.rendering.stencil = true; cameraFrame.rendering.samples = 4; cameraFrame.rendering.toneMapping = pc.TONEMAP_ACES2; + cameraFrame.update(); // ------------------------------------------ diff --git a/examples/src/examples/graphics/post-processing.controls.mjs b/examples/src/examples/graphics/post-processing.controls.mjs index 4bac61b5ae5..da87e2cc68e 100644 --- a/examples/src/examples/graphics/post-processing.controls.mjs +++ b/examples/src/examples/graphics/post-processing.controls.mjs @@ -86,12 +86,12 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => { ), jsx( LabelGroup, - { text: 'last mip level' }, + { text: 'blur level' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'data.bloom.lastMipLevel' }, + link: { observer, path: 'data.bloom.blurLevel' }, min: 1, - max: 10, + max: 16, precision: 0 }) ) diff --git a/examples/src/examples/graphics/post-processing.example.mjs b/examples/src/examples/graphics/post-processing.example.mjs index 9b73ca371c4..fe0872eeaf6 100644 --- a/examples/src/examples/graphics/post-processing.example.mjs +++ b/examples/src/examples/graphics/post-processing.example.mjs @@ -1,7 +1,6 @@ import { data } from 'examples/observer'; -import { deviceType, rootPath, fileImport } from 'examples/utils'; +import { deviceType, rootPath } from 'examples/utils'; import * as pc from 'playcanvas'; -const { CameraFrame } = await fileImport(`${rootPath}/static/assets/scripts/misc/camera-frame.mjs`); const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); window.focus(); @@ -216,9 +215,9 @@ assetListLoader.load(() => { // ------ Custom render passes set up ------ - /** @type { CameraFrame } */ - const cameraFrame = cameraEntity.script.create(CameraFrame); + const cameraFrame = new pc.CameraFrame(app, cameraEntity.camera); cameraFrame.rendering.sceneColorMap = true; + cameraFrame.update(); const applySettings = () => { @@ -247,9 +246,8 @@ assetListLoader.load(() => { cameraFrame.taa.jitter = data.get('data.taa.jitter'); // Bloom - cameraFrame.bloom.enabled = data.get('data.bloom.enabled'); - cameraFrame.bloom.intensity = pc.math.lerp(0, 0.1, data.get('data.bloom.intensity') / 100); - cameraFrame.bloom.lastMipLevel = data.get('data.bloom.lastMipLevel'); + cameraFrame.bloom.intensity = data.get('data.bloom.enabled') ? pc.math.lerp(0, 0.1, data.get('data.bloom.intensity') / 100) : 0; + cameraFrame.bloom.blurLevel = data.get('data.bloom.blurLevel'); // grading cameraFrame.grading.enabled = data.get('data.grading.enabled'); @@ -258,15 +256,16 @@ assetListLoader.load(() => { cameraFrame.grading.contrast = data.get('data.grading.contrast'); // vignette - cameraFrame.vignette.enabled = data.get('data.vignette.enabled'); cameraFrame.vignette.inner = data.get('data.vignette.inner'); cameraFrame.vignette.outer = data.get('data.vignette.outer'); cameraFrame.vignette.curvature = data.get('data.vignette.curvature'); - cameraFrame.vignette.intensity = data.get('data.vignette.intensity'); + cameraFrame.vignette.intensity = data.get('data.vignette.enabled') ? data.get('data.vignette.intensity') : 0; // fringing - cameraFrame.fringing.enabled = data.get('data.fringing.enabled'); - cameraFrame.fringing.intensity = data.get('data.fringing.intensity'); + cameraFrame.fringing.intensity = data.get('data.fringing.enabled') ? data.get('data.fringing.intensity') : 0; + + // apply all settings + cameraFrame.update(); }; // apply UI changes @@ -285,7 +284,7 @@ assetListLoader.load(() => { bloom: { enabled: true, intensity: 5, - lastMipLevel: 1 + blurLevel: 16 }, grading: { enabled: false, diff --git a/examples/src/examples/graphics/taa.example.mjs b/examples/src/examples/graphics/taa.example.mjs index df8c9e35c11..a794b78f6b0 100644 --- a/examples/src/examples/graphics/taa.example.mjs +++ b/examples/src/examples/graphics/taa.example.mjs @@ -1,7 +1,6 @@ import { data } from 'examples/observer'; -import { deviceType, rootPath, fileImport } from 'examples/utils'; +import { deviceType, rootPath } from 'examples/utils'; import * as pc from 'playcanvas'; -const { CameraFrame } = await fileImport(`${rootPath}/static/assets/scripts/misc/camera-frame.mjs`); const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas')); window.focus(); @@ -118,20 +117,21 @@ assetListLoader.load(() => { // ------ Custom render passes set up ------ - /** @type { CameraFrame } */ - const cameraFrame = cameraEntity.script.create(CameraFrame); + const cameraFrame = new pc.CameraFrame(app, cameraEntity.camera); cameraFrame.rendering.toneMapping = pc.TONEMAP_ACES; cameraFrame.bloom.intensity = 0.02; + cameraFrame.update(); // ------ const applySettings = () => { - cameraFrame.bloom.enabled = data.get('data.scene.bloom'); + cameraFrame.bloom.intensity = data.get('data.scene.bloom') ? 0.02 : 0; cameraFrame.taa.enabled = data.get('data.taa.enabled'); cameraFrame.taa.jitter = data.get('data.taa.jitter'); cameraFrame.rendering.renderTargetScale = data.get('data.scene.scale'); cameraFrame.rendering.sharpness = data.get('data.scene.sharpness'); + cameraFrame.update(); }; // apply UI changes diff --git a/package-lock.json b/package-lock.json index 55ceb5c697b..ccd7a7bb641 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,9 @@ }, "devDependencies": { "@babel/core": "^7.24.4", - "@babel/eslint-parser": "^7.25.1", + "@babel/eslint-parser": "^7.25.9", "@babel/preset-env": "^7.24.4", - "@playcanvas/eslint-config": "^2.0.2", + "@playcanvas/eslint-config": "^2.0.8", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-strip": "^3.0.4", @@ -24,7 +24,7 @@ "@rollup/pluginutils": "^5.1.0", "c8": "^10.1.2", "chai": "^5.1.0", - "eslint": "^9.10.0", + "eslint": "^9.15.0", "fflate": "^0.8.2", "global-jsdom": "^24.0.0", "globals": "^15.9.0", @@ -118,9 +118,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.1.tgz", - "integrity": "sha512-Y956ghgTT4j7rKesabkh5WeqgSFZVFwaPR0IWFm7KFHFmmJ4afbG49SmfW4S+GyRPx0Dy5jxEWA5t0rpxfElWg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz", + "integrity": "sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==", "dev": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", @@ -1774,9 +1774,9 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.48.0.tgz", - "integrity": "sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz", + "integrity": "sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==", "dev": true, "dependencies": { "comment-parser": "1.4.1", @@ -1815,18 +1815,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", + "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", "dev": true, "dependencies": { "@eslint/object-schema": "^2.1.4", @@ -1837,10 +1837,19 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/core": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", + "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -1873,9 +1882,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.10.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", - "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", + "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1891,9 +1900,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", - "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, "dependencies": { "levn": "^0.4.1" @@ -1902,6 +1911,41 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1916,9 +1960,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, "engines": { "node": ">=18.18" @@ -2147,41 +2191,6 @@ "eslint-scope": "5.1.1" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2205,60 +2214,19 @@ } }, "node_modules/@playcanvas/eslint-config": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@playcanvas/eslint-config/-/eslint-config-2.0.2.tgz", - "integrity": "sha512-APNbTTT/XZVamyfopn3dHnrLZnXXSIls+RMSqTTWNtvzG+wXooAiQjjKuiRlWHDOWtwqZwYVf/E2dJtTQH/9Wg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@playcanvas/eslint-config/-/eslint-config-2.0.8.tgz", + "integrity": "sha512-/bRWBdAScQfLUEkxYo22MhsfSOfNRW9zA0mjqdfFnpOsAmt1M6Ejlsp3I+bAeIMOj8Om20g4rBjlQtWxzow1Ag==", "dev": true, "dependencies": { - "eslint-plugin-import": "^2.30.0", - "eslint-plugin-jsdoc": "^50.2.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.3.1", "eslint-plugin-regexp": "^2.6.0" }, "peerDependencies": { "eslint": ">= 8" } }, - "node_modules/@playcanvas/eslint-config/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/@playcanvas/eslint-config/node_modules/eslint-plugin-import": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", - "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", - "dev": true, - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.9.0", - "hasown": "^2.0.2", - "is-core-module": "^2.15.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.0", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, "node_modules/@rollup/plugin-babel": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz", @@ -2692,6 +2660,12 @@ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -2755,9 +2729,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3929,9 +3903,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -4349,9 +4323,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", + "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -4369,7 +4343,7 @@ "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.4", "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", + "globalthis": "^1.0.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", @@ -4385,10 +4359,10 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", + "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", @@ -4512,28 +4486,31 @@ } }, "node_modules/eslint": { - "version": "9.10.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", - "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", + "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.10.0", - "@eslint/plugin-kit": "^0.1.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.15.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.5", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.2", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -4543,14 +4520,11 @@ "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -4591,9 +4565,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz", - "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -4616,13 +4590,55 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/eslint-plugin-jsdoc": { - "version": "50.2.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.2.2.tgz", - "integrity": "sha512-i0ZMWA199DG7sjxlzXn5AeYZxpRfMJjDPUl7lL9eJJX8TPRoIaxJU4ys/joP5faM5AXE1eqW/dslCj3uj4Nqpg==", + "version": "50.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.5.0.tgz", + "integrity": "sha512-xTkshfZrUbiSHXBwZ/9d5ulZ2OcHXxSvm/NPo494H/hadLRJwOq5PMV0EUpMqsb9V+kQo+9BAgi6Z7aJtdBp2A==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.48.0", + "@es-joy/jsdoccomment": "~0.49.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.1", "debug": "^4.3.6", @@ -4708,6 +4724,12 @@ "node": ">=10" } }, + "node_modules/eslint/node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -4770,9 +4792,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", - "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -4786,9 +4808,9 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4828,14 +4850,14 @@ } }, "node_modules/espree": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4845,9 +4867,9 @@ } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4990,15 +5012,6 @@ "punycode": "^1.3.2" } }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -5810,6 +5823,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -5946,6 +5974,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz", + "integrity": "sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -5955,6 +5998,21 @@ "node": ">=8" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -5967,6 +6025,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -6009,15 +6079,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -6061,6 +6122,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-shared-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", @@ -6145,6 +6218,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -6157,6 +6242,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -7484,9 +7585,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, "engines": { "node": ">= 0.4" @@ -7705,9 +7806,9 @@ "dev": true }, "node_modules/parse-imports": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.1.1.tgz", - "integrity": "sha512-TDT4HqzUiTMO1wJRwg/t/hYk8Wdp3iF/ToMIlAoVQfL1Xs/sTxq1dKWSMjMbQmIarfWKymOyly40+zmPHXMqCA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", + "integrity": "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==", "dev": true, "dependencies": { "es-module-lexer": "^1.5.3", @@ -7936,26 +8037,6 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8051,6 +8132,27 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz", + "integrity": "sha512-bMvFGIUKlc/eSfXNX+aZ+EL95/EgZzuwA0OBPTbZZDEJw/0AkentjMuM1oiRfwHrshqk4RzdgiTg5CcDalXN5g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "which-builtin-type": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -8098,15 +8200,15 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -8225,16 +8327,6 @@ "node": ">=4" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -8387,29 +8479,6 @@ "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", "dev": true }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -9299,9 +9368,9 @@ "dev": true }, "node_modules/synckit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", - "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, "dependencies": { "@pkgr/core": "^0.1.0", @@ -9434,12 +9503,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -9558,9 +9621,9 @@ } }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true }, "node_modules/type-check": { @@ -9643,9 +9706,9 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz", + "integrity": "sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", @@ -9653,7 +9716,8 @@ "for-each": "^0.3.3", "gopd": "^1.0.1", "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.13", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -9663,17 +9727,17 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -10071,6 +10135,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-builtin-type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.0.tgz", + "integrity": "sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", diff --git a/package.json b/package.json index c1f54fd11e8..0f8b0225135 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,9 @@ "./build/*": "./build/*", "./scripts/*": "./scripts/*" }, - "sideEffects": ["./build/playcanvas/src/deprecated/deprecated.js"], + "sideEffects": [ + "./build/playcanvas/src/deprecated/deprecated.js" + ], "type": "module", "bugs": { "url": "https://github.com/playcanvas/engine/issues" @@ -67,9 +69,9 @@ }, "devDependencies": { "@babel/core": "^7.24.4", - "@babel/eslint-parser": "^7.25.1", + "@babel/eslint-parser": "^7.25.9", "@babel/preset-env": "^7.24.4", - "@playcanvas/eslint-config": "^2.0.2", + "@playcanvas/eslint-config": "^2.0.8", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-strip": "^3.0.4", @@ -77,7 +79,7 @@ "@rollup/pluginutils": "^5.1.0", "c8": "^10.1.2", "chai": "^5.1.0", - "eslint": "^9.10.0", + "eslint": "^9.15.0", "fflate": "^0.8.2", "global-jsdom": "^24.0.0", "globals": "^15.9.0", diff --git a/scripts/esm/camera-controls.mjs b/scripts/esm/camera-controls.mjs index 9c3e6af8416..a97a5650109 100644 --- a/scripts/esm/camera-controls.mjs +++ b/scripts/esm/camera-controls.mjs @@ -1,4 +1,5 @@ -import { Vec2, Vec3, Mat4, Quat, Ray, Plane, Script, math } from 'playcanvas'; +/* eslint-disable-next-line import/no-unresolved */ +import { Vec2, Vec3, Ray, Plane, Mat4, Quat, Script, math } from 'playcanvas'; /** @import { CameraComponent } from 'playcanvas' */ diff --git a/examples/assets/scripts/misc/camera-frame.mjs b/scripts/utils/camera-frame.mjs similarity index 56% rename from examples/assets/scripts/misc/camera-frame.mjs rename to scripts/utils/camera-frame.mjs index 622a1828e69..cfd9a7c6989 100644 --- a/examples/assets/scripts/misc/camera-frame.mjs +++ b/scripts/utils/camera-frame.mjs @@ -1,11 +1,5 @@ -import { - Script, - Color, - math, - CameraFrameOptions, - RenderPassCameraFrame, - SSAOTYPE_NONE -} from 'playcanvas'; +/* eslint-disable-next-line import/no-unresolved */ +import { CameraFrame as EngineCameraFrame, Script, Color } from 'playcanvas'; /** @enum {number} */ const ToneMapping = { @@ -154,11 +148,11 @@ class Bloom { /** * @attribute - * @range [0, 12] + * @range [0, 16] * @precision 0 * @step 0 */ - lastMipLevel = 1; + blurLevel = 1; } /** @interface */ @@ -292,110 +286,91 @@ class CameraFrame extends Script { */ fringing = new Fringing(); - options = new CameraFrameOptions(); + engineCameraFrame = new EngineCameraFrame(this.app, this.entity.camera); initialize() { - this.updateOptions(); - this.createRenderPass(); - this.on('enable', () => { - this.createRenderPass(); + this.engineCameraFrame.enabled = true; }); this.on('disable', () => { - this.destroyRenderPass(); + this.engineCameraFrame.enabled = false; }); this.on('destroy', () => { - this.destroyRenderPass(); + this.engineCameraFrame.destroy(); }); } - createRenderPass() { - const cameraComponent = this.entity.camera; - this.renderPassCamera = new RenderPassCameraFrame(this.app, cameraComponent, this.options); - cameraComponent.renderPasses = [this.renderPassCamera]; - } - - destroyRenderPass() { - const cameraComponent = this.entity.camera; - cameraComponent.renderPasses?.forEach((renderPass) => { - renderPass.destroy(); - }); - cameraComponent.renderPasses = []; - cameraComponent.rendering = null; - - cameraComponent.jitter = 0; - } - - updateOptions() { - - const { options, rendering, bloom, taa, ssao } = this; - options.stencil = rendering.stencil; - options.samples = rendering.samples; - options.sceneColorMap = rendering.sceneColorMap; - options.prepassEnabled = rendering.sceneDepthMap; - options.bloomEnabled = bloom.enabled; - options.taaEnabled = taa.enabled; - options.ssaoType = ssao.type; - options.ssaoBlurEnabled = ssao.blurEnabled; - options.formats = [rendering.renderFormat, rendering.renderFormatFallback0, rendering.renderFormatFallback1]; - } - postUpdate(dt) { - const cameraComponent = this.entity.camera; - const { options, renderPassCamera, rendering, bloom, grading, vignette, fringing, taa, ssao } = this; - - // options that can cause the passes to be re-created - this.updateOptions(); - renderPassCamera.update(options); - - // update parameters of individual render passes - const { composePass, bloomPass, ssaoPass } = renderPassCamera; - - renderPassCamera.renderTargetScale = math.clamp(rendering.renderTargetScale, 0.1, 1); - composePass.toneMapping = rendering.toneMapping; - composePass.sharpness = rendering.sharpness; - - if (options.bloomEnabled && bloomPass) { - composePass.bloomIntensity = bloom.intensity; - bloomPass.lastMipLevel = bloom.lastMipLevel; + const cf = this.engineCameraFrame; + const { rendering, bloom, grading, vignette, fringing, taa, ssao } = this; + + const dstRendering = cf.rendering; + dstRendering.renderFormats.length = 0; + dstRendering.renderFormats.push(rendering.renderFormat); + dstRendering.renderFormats.push(rendering.renderFormatFallback0); + dstRendering.renderFormats.push(rendering.renderFormatFallback1); + dstRendering.stencil = rendering.stencil; + dstRendering.renderTargetScale = rendering.renderTargetScale; + dstRendering.samples = rendering.samples; + dstRendering.sceneColorMap = rendering.sceneColorMap; + dstRendering.sceneDepthMap = rendering.sceneDepthMap; + dstRendering.toneMapping = rendering.toneMapping; + dstRendering.sharpness = rendering.sharpness; + + // ssao + const dstSsao = cf.ssao; + dstSsao.type = ssao.type; + if (ssao.type !== SsaoType.NONE) { + dstSsao.intensity = ssao.intensity; + dstSsao.radius = ssao.radius; + dstSsao.samples = ssao.samples; + dstSsao.power = ssao.power; + dstSsao.minAngle = ssao.minAngle; + dstSsao.scale = ssao.scale; } - if (options.ssaoType !== SSAOTYPE_NONE) { - ssaoPass.intensity = ssao.intensity; - ssaoPass.power = ssao.power; - ssaoPass.radius = ssao.radius; - ssaoPass.sampleCount = ssao.samples; - ssaoPass.minAngle = ssao.minAngle; - ssaoPass.scale = ssao.scale; + // bloom + const dstBloom = cf.bloom; + dstBloom.intensity = bloom.enabled ? bloom.intensity : 0; + if (bloom.enabled) { + dstBloom.blurLevel = bloom.blurLevel; } - composePass.gradingEnabled = grading.enabled; + // grading + const dstGrading = cf.grading; + dstGrading.enabled = grading.enabled; if (grading.enabled) { - composePass.gradingSaturation = grading.saturation; - composePass.gradingBrightness = grading.brightness; - composePass.gradingContrast = grading.contrast; - composePass.gradingTint = grading.tint; + dstGrading.brightness = grading.brightness; + dstGrading.contrast = grading.contrast; + dstGrading.saturation = grading.saturation; + dstGrading.tint.copy(grading.tint); } - composePass.vignetteEnabled = vignette.enabled; + // vignette + const dstVignette = cf.vignette; + dstVignette.intensity = vignette.enabled ? vignette.intensity : 0; if (vignette.enabled) { - composePass.vignetteInner = vignette.inner; - composePass.vignetteOuter = vignette.outer; - composePass.vignetteCurvature = vignette.curvature; - composePass.vignetteIntensity = vignette.intensity; + dstVignette.inner = vignette.inner; + dstVignette.outer = vignette.outer; + dstVignette.curvature = vignette.curvature; } - composePass.fringingEnabled = fringing.enabled; - if (fringing.enabled) { - composePass.fringingIntensity = fringing.intensity; + // taa + const dstTaa = cf.taa; + dstTaa.enabled = taa.enabled; + if (taa.enabled) { + dstTaa.jitter = taa.jitter; } - // enable camera jitter if taa is enabled - cameraComponent.jitter = taa.enabled ? taa.jitter : 0; + // fringing + const dstFringing = cf.fringing; + dstFringing.intensity = fringing.enabled ? fringing.intensity : 0; + + cf.update(); } } diff --git a/src/core/preprocessor.js b/src/core/preprocessor.js index 3f5f271e64e..bd8871a36fc 100644 --- a/src/core/preprocessor.js +++ b/src/core/preprocessor.js @@ -30,8 +30,11 @@ const IDENTIFIER = /([\w-]+)/; // [!]defined(EXPRESSION) const DEFINED = /(!|\s)?defined\(([\w-]+)\)/; +// Matches comparison operators like ==, !=, <, <=, >, >= +const COMPARISON = /([a-z_]\w*)\s*(==|!=|<|<=|>|>=)\s*([\w"']+)/i; + // currently unsupported characters in the expression: | & < > = + - -const INVALID = /[><=|&+-]/g; +const INVALID = /[|&+-]/g; // #include "identifier" const INCLUDE = /include[ \t]+"([\w-]+)"\r?(?:\n|$)/g; @@ -374,6 +377,7 @@ class Preprocessor { * - expression * - defined(expression) * - !defined(expression) + * - simple comparisons like "XX == 3" or "XX != test" * * But does not handle more complex cases, which would require more complex system: * @@ -388,7 +392,7 @@ class Preprocessor { const correct = INVALID.exec(expression) === null; Debug.assert(correct, `Resolving expression like this is not supported: ${expression}`); - // if the format is defined(expression), extract expression + // if the format is 'defined(expression)', extract expression let invert = false; const defined = DEFINED.exec(expression); if (defined) { @@ -396,6 +400,29 @@ class Preprocessor { expression = defined[2]; } + // if the expression is a comparison, evaluate it + const comparison = COMPARISON.exec(expression); + if (comparison) { + const left = defines.get(comparison[1]) ?? comparison[1]; + const right = defines.get(comparison[3]) ?? comparison[3]; + const operator = comparison[2]; + + let result = false; + switch (operator) { + case '==': result = left === right; break; + case '!=': result = left !== right; break; + case '<': result = left < right; break; + case '<=': result = left <= right; break; + case '>': result = left > right; break; + case '>=': result = left >= right; break; + } + + return { + result, + error: !correct + }; + } + // test if expression define exists expression = expression.trim(); let exists = defines.has(expression); diff --git a/src/extras/index.js b/src/extras/index.js index c6df3f3165f..09b9c17594d 100644 --- a/src/extras/index.js +++ b/src/extras/index.js @@ -14,7 +14,7 @@ export { UsdzExporter } from './exporters/usdz-exporter.js'; export { GltfExporter } from './exporters/gltf-exporter.js'; // RENDER PASSES -export { SSAOTYPE_NONE, SSAOTYPE_LIGHTING, SSAOTYPE_COMBINE } from './render-passes/render-pass-camera-frame.js'; +export { SSAOTYPE_NONE, SSAOTYPE_LIGHTING, SSAOTYPE_COMBINE } from './render-passes/constants.js'; export { RenderPassCameraFrame, CameraFrameOptions } from './render-passes/render-pass-camera-frame.js'; export { RenderPassCompose } from './render-passes/render-pass-compose.js'; export { RenderPassDepthAwareBlur } from './render-passes/render-pass-depth-aware-blur.js'; @@ -23,6 +23,7 @@ export { RenderPassUpsample } from './render-passes/render-pass-upsample.js'; export { RenderPassBloom } from './render-passes/render-pass-bloom.js'; export { RenderPassSsao } from './render-passes/render-pass-ssao.js'; export { RenderPassTAA } from './render-passes/render-pass-taa.js'; +export { CameraFrame } from './render-passes/camera-frame.js'; // GIZMOS export { diff --git a/src/extras/render-passes/camera-frame.js b/src/extras/render-passes/camera-frame.js new file mode 100644 index 00000000000..eca69368f9f --- /dev/null +++ b/src/extras/render-passes/camera-frame.js @@ -0,0 +1,388 @@ +import { Debug } from '../../core/debug.js'; +import { Color } from '../../core/math/color.js'; +import { math } from '../../core/math/math.js'; +import { PIXELFORMAT_111110F, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F } from '../../platform/graphics/constants.js'; +import { SSAOTYPE_NONE } from './constants.js'; +import { CameraFrameOptions, RenderPassCameraFrame } from './render-pass-camera-frame.js'; + +/** + * @import { AppBase } from '../../framework/app-base.js' + * @import { CameraComponent } from '../../framework/components/camera/component.js' + */ + +/** + * Properties related to scene rendering, encompassing settings that control the rendering resolution, + * pixel format, multi-sampling for anti-aliasing, tone-mapping and similar. + * + * @typedef {Object} Rendering + * @property {number[]} renderFormats - The preferred render formats of the frame buffer, in order of + * preference. First format from this list that is supported by the hardware is used. When none of + * the formats are supported, {@link PIXELFORMAT_RGBA8} is used, but this automatically disables + * bloom effect, which requires HDR format. The list can contain the following formats: + * {@link PIXELFORMAT_111110F}, {@link PIXELFORMAT_RGBA16F}, {@link PIXELFORMAT_RGBA32F} and {@link + * PIXELFORMAT_RGBA8}. Typically the default option should be used, which prefers the faster formats, + * but if higher dynamic range is needed, the list can be adjusted to prefer higher precision formats. + * Defaults to [{@link PIXELFORMAT_111110F}, {@link PIXELFORMAT_RGBA16F}, {@link PIXELFORMAT_RGBA32F}]. + * @property {boolean} stencil - Whether the render buffer has a stencil buffer. Defaults to false. + * @property {number} renderTargetScale - The scale of the render target, 0.1-1 range. This allows the + * scene to be rendered to a lower resolution render target as an optimization. The post-processing + * is also applied at this lower resolution. The image is then up-scaled to the full resolution and + * any UI rendering that follows is applied at the full resolution. Defaults to 1 which represents + * full resolution rendering. + * @property {number} samples - The number of samples of the {@link RenderTarget} used for the scene + * rendering, in 1-4 range. Value of 1 disables multisample anti-aliasing, other values enable + * anti-aliasing, Typically set to 1 when TAA is used, even though both anti-aliasing options can be + * used together at a higher cost. Defaults to 1. + * @property {boolean} sceneColorMap - Whether rendering generates a scene color map. Defaults to false. + * @property {boolean} sceneDepthMap - Whether rendering generates a scene depth map. Defaults to false. + * @property {number} toneMapping - The tone mapping. Defaults to {@link ToneMapping.LINEAR}. Can be: + * + * - {@link TONEMAP_LINEAR} + * - {@link TONEMAP_FILMIC} + * - {@link TONEMAP_HEJL} + * - {@link TONEMAP_ACES} + * - {@link TONEMAP_ACES2} + * - {@link TONEMAP_NEUTRAL} + * + * @property {number} sharpness - The sharpening intensity, 0-1 range. This can be used to increase + * the sharpness of the rendered image. Often used to counteract the blurriness of the TAA effect, + * but also blurriness caused by rendering to a lower resolution render target by using + * rendering.renderTargetScale property. Defaults to 0. + */ + +/** + * Properties related to the Screen Space Ambient Occlusion (SSAO) effect, a postprocessing technique + * that approximates ambient occlusion by calculating how exposed each point in the screen space is + * to ambient light, enhancing depth perception and adding subtle shadowing in crevices and between + * objects. + * + * @typedef {Object} Ssao + * @property {string} type - The type of the SSAO determines how it is applied in the rendering + * process. Defaults to {@link SSAOTYPE_NONE}. Can be: + * + * - {@link SSAOTYPE_NONE} + * - {@link SSAOTYPE_LIGHTING} + * - {@link SSAOTYPE_COMBINE} + * + * @property {boolean} blurEnabled - Whether the SSAO effect is blurred. Defaults to true. + * @property {number} intensity - The intensity of the SSAO effect, 0-1 range. Defaults to 0.5. + * @property {number} radius - The radius of the SSAO effect, 0-100 range. Defaults to 30. + * @property {number} samples - The number of samples of the SSAO effect, 1-64 range. Defaults to 12. + * @property {number} power - The power of the SSAO effect, 0.1-10 range. Defaults to 6. + * @property {number} minAngle - The minimum angle of the SSAO effect, 1-90 range. Defaults to 10. + * @property {number} scale - The scale of the SSAO effect, 0.5-1 range. Defaults to 1. + */ + +/** + * Properties related to the HDR bloom effect, a postprocessing technique that simulates the natural + * glow of bright light sources by spreading their intensity beyond their boundaries, creating a soft + * and realistic blooming effect. + * + * @typedef {Object} Bloom + * @property {number} intensity - The intensity of the bloom effect, 0-0.1 range. Defaults to 0, + * making it disabled. + * @property {number} blurLevel - The number of iterations for blurring the bloom effect, with each + * level doubling the blur size. Once the blur size matches the dimensions of the render target, + * further blur passes are skipped. The default value is 16. + */ + +/** + * Properties related to the color grading effect, a postprocessing technique used to adjust and the + * visual tone of an image. This effect modifies brightness, contrast, saturation, and overall color + * balance to achieve a specific aesthetic or mood. + * + * @typedef {Object} Grading + * @property {boolean} enabled - Whether grading is enabled. Defaults to false. + * @property {number} brightness - The brightness of the grading effect, 0-3 range. Defaults to 1. + * @property {number} contrast - The contrast of the grading effect, 0.5-1.5 range. Defaults to 1. + * @property {number} saturation - The saturation of the grading effect, 0-2 range. Defaults to 1. + * @property {Color} tint - The tint color of the grading effect. Defaults to white. + */ + +/** + * Properties related to the vignette effect, a postprocessing technique that darkens the image + * edges, creating a gradual falloff in brightness from the center outward. The effect can be also + * reversed, making the center of the image darker than the edges, by specifying the outer distance + * smaller than the inner distance. + * + * @typedef {Object} Vignette + * @property {number} intensity - The intensity of the vignette effect, 0-1 range. Defaults to 0, + * making it disabled. + * @property {number} inner - The inner distance of the vignette effect measured from the center of + * the screen, 0-3 range. This is where the vignette effect starts. Value larger than 1 represents + * the value off screen, which allows more control. Defaults to 0.5, representing half the distance + * from center. + * @property {number} outer - The outer distance of the vignette effect measured from the center of + * the screen, 0-3 range. This is where the vignette reaches full intensity. Value larger than 1 + * represents the value off screen, which allows more control. Defaults to 1, representing the full + * screen. + * @property {number} curvature - The curvature of the vignette effect, 0.01-10 range. The vignette + * is rendered using a rectangle with rounded corners, and this parameter controls the curvature of + * the corners. Value of 1 represents a circle. Smaller values make the corners more square, while + * larger values make them more rounded. Defaults to 0.5. + */ + +/** + * Properties related to the fringing effect, a chromatic aberration phenomenon where the red, green, + * and blue color channels diverge increasingly with greater distance from the center of the screen. + * + * @typedef {Object} Fringing + * @property {number} intensity - The intensity of the fringing effect, 0-100 range. Defaults to 0, + * making it disabled. + */ + +/** + * Properties related to temporal anti-aliasing (TAA), which is a technique used to reduce aliasing + * in the rendered image by blending multiple frames together over time. + * + * @typedef {Object} Taa + * @property {boolean} enabled - Whether Taa is enabled. Defaults to false. + * @property {number} jitter - The intensity of the camera jitter, 0-1 range. The larger the value, + * the more jitter is applied to the camera, making the anti-aliasing effect more pronounced. This + * also makes the image more blurry, and rendering.sharpness parameter can be used to counteract. + * Defaults to 1. + */ + +/** + * Implementation of a simple to use camera rendering pass, which supports SSAO, Bloom and + * other rendering effects. + * + * @category Render Pass + */ +class CameraFrame { + /** + * Rendering settings. + * + * @type {Rendering} + */ + rendering = { + renderFormats: [PIXELFORMAT_111110F, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F], + stencil: false, + renderTargetScale: 1.0, + samples: 1, + sceneColorMap: false, + sceneDepthMap: false, + toneMapping: 0, + sharpness: 0.0 + }; + + /** + * SSAO settings. + * + * @type {Ssao} + */ + ssao = { + type: SSAOTYPE_NONE, + blurEnabled: true, + intensity: 0.5, + radius: 30, + samples: 12, + power: 6, + minAngle: 10, + scale: 1 + }; + + /** + * Bloom settings. + * + * @type {Bloom} + */ + bloom = { + intensity: 0, + blurLevel: 16 + }; + + /** + * Grading settings. + * + * @type {Grading} + */ + grading = { + enabled: false, + brightness: 1, + contrast: 1, + saturation: 1, + tint: new Color(1, 1, 1, 1) + }; + + /** + * Vignette settings. + * + * @type {Vignette} + */ + vignette = { + intensity: 0, + inner: 0.5, + outer: 1, + curvature: 0.5 + }; + + /** + * Taa settings. + * + * @type {Taa} + */ + taa = { + enabled: false, + jitter: 1 + }; + + /** + * Fringing settings. + * + * @type {Fringing} + */ + fringing = { + intensity: 0 + }; + + options = new CameraFrameOptions(); + + /** + * @type {RenderPassCameraFrame|null} + * @private + */ + renderPassCamera = null; + + /** + * Creates a new CameraFrame instance. + * + * @param {AppBase} app - The application. + * @param {CameraComponent} cameraComponent - The camera component. + */ + constructor(app, cameraComponent) { + this.app = app; + this.cameraComponent = cameraComponent; + Debug.assert(cameraComponent, 'CameraFrame: cameraComponent must be defined'); + + this.updateOptions(); + this.enabled = true; + } + + /** + * Destroys the camera frame, removing all render passes. + */ + destroy() { + this.disable(); + } + + enable() { + if (!this.renderPassCamera) { + const cameraComponent = this.cameraComponent; + this.renderPassCamera = new RenderPassCameraFrame(this.app, cameraComponent, this.options); + cameraComponent.renderPasses = [this.renderPassCamera]; + } + } + + disable() { + if (this.renderPassCamera) { + const cameraComponent = this.cameraComponent; + cameraComponent.renderPasses?.forEach((renderPass) => { + renderPass.destroy(); + }); + cameraComponent.renderPasses = []; + cameraComponent.rendering = null; + + cameraComponent.jitter = 0; + } + } + + /** + * Sets the enabled state of the camera frame. This disabled the render passes, and releases + * any resources. + * + * @type {boolean} + */ + set enabled(value) { + if (value) { + this.enable(); + } else { + this.disable(); + } + } + + /** + * Gets the enabled state of the camera frame. + * + * @type {boolean} + */ + get enabled() { + return this.renderPassCamera !== null; + } + + updateOptions() { + + const { options, rendering, bloom, taa, ssao } = this; + options.stencil = rendering.stencil; + options.samples = rendering.samples; + options.sceneColorMap = rendering.sceneColorMap; + options.prepassEnabled = rendering.sceneDepthMap; + options.bloomEnabled = bloom.intensity > 0; + options.taaEnabled = taa.enabled; + options.ssaoType = ssao.type; + options.ssaoBlurEnabled = ssao.blurEnabled; + options.formats = rendering.renderFormats.slice(); + } + + /** + * Applies any changes made to the properties of this instance. + */ + update() { + + if (!this.enabled) return; + + const cameraComponent = this.cameraComponent; + const { options, renderPassCamera, rendering, bloom, grading, vignette, fringing, taa, ssao } = this; + + // options that can cause the passes to be re-created + this.updateOptions(); + renderPassCamera.update(options); + + // update parameters of individual render passes + const { composePass, bloomPass, ssaoPass } = renderPassCamera; + + renderPassCamera.renderTargetScale = math.clamp(rendering.renderTargetScale, 0.1, 1); + composePass.toneMapping = rendering.toneMapping; + composePass.sharpness = rendering.sharpness; + + if (options.bloomEnabled && bloomPass) { + composePass.bloomIntensity = bloom.intensity; + bloomPass.blurLevel = bloom.blurLevel; + } + + if (options.ssaoType !== SSAOTYPE_NONE) { + ssaoPass.intensity = ssao.intensity; + ssaoPass.power = ssao.power; + ssaoPass.radius = ssao.radius; + ssaoPass.sampleCount = ssao.samples; + ssaoPass.minAngle = ssao.minAngle; + ssaoPass.scale = ssao.scale; + } + + composePass.gradingEnabled = grading.enabled; + if (grading.enabled) { + composePass.gradingSaturation = grading.saturation; + composePass.gradingBrightness = grading.brightness; + composePass.gradingContrast = grading.contrast; + composePass.gradingTint = grading.tint; + } + + composePass.vignetteEnabled = vignette.intensity > 0; + if (composePass.vignetteEnabled) { + composePass.vignetteInner = vignette.inner; + composePass.vignetteOuter = vignette.outer; + composePass.vignetteCurvature = vignette.curvature; + composePass.vignetteIntensity = vignette.intensity; + } + + composePass.fringingEnabled = fringing.intensity > 0; + if (composePass.fringingEnabled) { + composePass.fringingIntensity = fringing.intensity; + } + + // enable camera jitter if taa is enabled + cameraComponent.jitter = taa.enabled ? taa.jitter : 0; + } +} + +export { CameraFrame }; diff --git a/src/extras/render-passes/constants.js b/src/extras/render-passes/constants.js new file mode 100644 index 00000000000..8626ddbe007 --- /dev/null +++ b/src/extras/render-passes/constants.js @@ -0,0 +1,24 @@ +/** + * SSAO is disabled. + * + * @type {string} + */ +export const SSAOTYPE_NONE = 'none'; + +/** + * SSAO is applied during the lighting calculation stage, allowing it to blend seamlessly with scene + * lighting. This results in ambient occlusion being more pronounced in areas where direct light is + * obstructed, enhancing realism. + * + * @type {string} + */ +export const SSAOTYPE_LIGHTING = 'lighting'; + +/** + * SSAO is applied as a standalone effect after the scene is rendered. This method uniformly + * overlays ambient occlusion across the image, disregarding direct lighting interactions. While + * this may sacrifice some realism, it can be advantageous for achieving specific artistic styles. + * + * @type {string} + */ +export const SSAOTYPE_COMBINE = 'combine'; diff --git a/src/extras/render-passes/render-pass-bloom.js b/src/extras/render-passes/render-pass-bloom.js index 6f85693b7b3..ae91d86682b 100644 --- a/src/extras/render-passes/render-pass-bloom.js +++ b/src/extras/render-passes/render-pass-bloom.js @@ -7,6 +7,7 @@ import { FILTER_LINEAR, ADDRESS_CLAMP_TO_EDGE } from '../../platform/graphics/co import { RenderPassDownsample } from './render-pass-downsample.js'; import { RenderPassUpsample } from './render-pass-upsample.js'; +import { math } from '../../core/math/math.js'; // based on https://learnopengl.com/Guest-Articles/2022/Phys.-Based-Bloom /** @@ -18,7 +19,7 @@ import { RenderPassUpsample } from './render-pass-upsample.js'; class RenderPassBloom extends RenderPass { bloomTexture; - lastMipLevel = 1; + blurLevel = 16; bloomRenderTarget; @@ -151,8 +152,8 @@ class RenderPassBloom extends RenderPass { super.frameUpdate(); // create an appropriate amount of render passes - let numPasses = this.calcMipLevels(this._sourceTexture.width, this._sourceTexture.height, 2 ** this.lastMipLevel); - numPasses = Math.max(1, numPasses); + const maxNumPasses = this.calcMipLevels(this._sourceTexture.width, this._sourceTexture.height, 1); + const numPasses = math.clamp(maxNumPasses, 1, this.blurLevel); if (this.renderTargets.length !== numPasses) { diff --git a/src/extras/render-passes/render-pass-camera-frame.js b/src/extras/render-passes/render-pass-camera-frame.js index 34ea9a27eac..7e539c4c860 100644 --- a/src/extras/render-passes/render-pass-camera-frame.js +++ b/src/extras/render-passes/render-pass-camera-frame.js @@ -11,10 +11,8 @@ import { RenderPassCompose } from './render-pass-compose.js'; import { RenderPassTAA } from './render-pass-taa.js'; import { RenderPassPrepass } from './render-pass-prepass.js'; import { RenderPassSsao } from './render-pass-ssao.js'; - -export const SSAOTYPE_NONE = 'none'; -export const SSAOTYPE_LIGHTING = 'lighting'; -export const SSAOTYPE_COMBINE = 'combine'; +import { SSAOTYPE_COMBINE, SSAOTYPE_LIGHTING, SSAOTYPE_NONE } from './constants.js'; +import { Debug } from '../../core/debug.js'; class CameraFrameOptions { formats; @@ -82,6 +80,7 @@ class RenderPassCameraFrame extends RenderPass { rt = null; constructor(app, cameraComponent, options = {}) { + Debug.assert(app); super(app.graphicsDevice); this.app = app; this.cameraComponent = cameraComponent; @@ -211,8 +210,8 @@ class RenderPassCameraFrame extends RenderPass { this.rt = new RenderTarget({ colorBuffer: this.sceneTexture, - // depthBuffer: this.sceneDepth, depth: true, + stencil: options.stencil, samples: options.samples, flipY: !!targetRenderTarget?.flipY // flipY is inherited from the target renderTarget }); diff --git a/src/framework/xr/xr-joint.js b/src/framework/xr/xr-joint.js index 35d980042b9..4d98aa89b6d 100644 --- a/src/framework/xr/xr-joint.js +++ b/src/framework/xr/xr-joint.js @@ -35,7 +35,7 @@ class XrJoint { _index; /** - * @type {string} + * @type {XRHandJoint} * @private */ _id; @@ -47,7 +47,7 @@ class XrJoint { _hand; /** - * @type {XrFinger} + * @type {XrFinger|null} * @private */ _finger; @@ -65,7 +65,7 @@ class XrJoint { _tip; /** - * @type {number} + * @type {number|null} * @private */ _radius = null; @@ -116,7 +116,7 @@ class XrJoint { * Create an XrJoint instance. * * @param {number} index - Index of a joint within a finger. - * @param {string} id - Id of a joint based on WebXR Hand Input Specs. + * @param {XRHandJoint} id - Id of a joint based on WebXR Hand Input Specs. * @param {XrHand} hand - Hand that joint relates to. * @param {XrFinger|null} finger - Finger that joint is related to. Can be null in the case of * the wrist joint. @@ -181,6 +181,15 @@ class XrJoint { return this._rotation; } + /** + * Id of a joint based on WebXR Hand Input Specs. + * + * @type {XRHandJoint} + */ + get id() { + return this._id; + } + /** * Index of a joint within a finger, starting from 0 (root of a finger) all the way to tip of * the finger. diff --git a/src/platform/graphics/graphics-device.js b/src/platform/graphics/graphics-device.js index 11abc43884e..6e9bc869b56 100644 --- a/src/platform/graphics/graphics-device.js +++ b/src/platform/graphics/graphics-device.js @@ -275,6 +275,14 @@ class GraphicsDevice extends EventHandler { */ supportsUniformBuffers = false; + /** + * True if the device supports clip distances (WebGPU only). Clip distances allow you to restrict + * primitives' clip volume with user-defined half-spaces in the output of vertex stage. + * + * @type {boolean} + */ + supportsClipDistances = false; + /** * True if 32-bit floating-point textures can be used as a frame buffer. * diff --git a/src/platform/graphics/webgpu/webgpu-graphics-device.js b/src/platform/graphics/webgpu/webgpu-graphics-device.js index 1841c9c8f47..449b0b09037 100644 --- a/src/platform/graphics/webgpu/webgpu-graphics-device.js +++ b/src/platform/graphics/webgpu/webgpu-graphics-device.js @@ -210,7 +210,6 @@ class WebgpuGraphicsDevice extends GraphicsDevice { */ this.gpuAdapter = await window.navigator.gpu.requestAdapter(adapterOptions); - // request optional features const requiredFeatures = []; const requireFeature = (feature) => { @@ -231,6 +230,7 @@ class WebgpuGraphicsDevice extends GraphicsDevice { this.supportsShaderF16 = requireFeature('shader-f16'); this.supportsStorageRGBA8 = requireFeature('bgra8unorm-storage'); this.textureRG11B10Renderable = requireFeature('rg11b10ufloat-renderable'); + this.supportsClipDistances = requireFeature('clip-distances'); Debug.log(`WEBGPU features: ${requiredFeatures.join(', ')}`); // copy all adapter limits to the requiredLimits object - to created a device with the best feature sets available diff --git a/src/scene/batching/batch-manager.js b/src/scene/batching/batch-manager.js index 1aad48cf211..d0570bc53aa 100644 --- a/src/scene/batching/batch-manager.js +++ b/src/scene/batching/batch-manager.js @@ -1,7 +1,6 @@ import { Debug } from '../../core/debug.js'; import { now } from '../../core/time.js'; import { Mat3 } from '../../core/math/mat3.js'; -import { Vec3 } from '../../core/math/vec3.js'; import { BoundingBox } from '../../core/shape/bounding-box.js'; import { PRIMITIVE_TRIANGLES, PRIMITIVE_TRIFAN, PRIMITIVE_TRISTRIP, @@ -663,7 +662,6 @@ class BatchManager { let verticesOffset = 0; let indexOffset = 0; let transform; - const vec = new Vec3(); // allocate indices const indexArrayType = batchNumVerts <= 0xffff ? Uint16Array : Uint32Array; @@ -703,24 +701,48 @@ class BatchManager { // transform position, normal and tangent to world space if (!dynamic && stream.numComponents >= 3) { if (semantic === SEMANTIC_POSITION) { + const m = transform.data; + const m0 = m[0]; + const m1 = m[1]; + const m2 = m[2]; + const m4 = m[4]; + const m5 = m[5]; + const m6 = m[6]; + const m8 = m[8]; + const m9 = m[9]; + const m10 = m[10]; + const m12 = m[12]; + const m13 = m[13]; + const m14 = m[14]; + + let x, y, z; + for (let j = 0; j < totalComponents; j += stream.numComponents) { - vec.set(subarray[j], subarray[j + 1], subarray[j + 2]); - transform.transformPoint(vec, vec); - subarray[j] = vec.x; - subarray[j + 1] = vec.y; - subarray[j + 2] = vec.z; + x = subarray[j]; + y = subarray[j + 1]; + z = subarray[j + 2]; + + // mat4.transformVector + subarray[j] = x * m0 + y * m4 + z * m8 + m12; + subarray[j + 1] = x * m1 + y * m5 + z * m9 + m13; + subarray[j + 2] = x * m2 + y * m6 + z * m10 + m14; } } else if (semantic === SEMANTIC_NORMAL || semantic === SEMANTIC_TANGENT) { - // handle non-uniform scale by using transposed inverse matrix to transform vectors mat3.invertMat4(transform).transpose(); + const [m0, m1, m2, m3, m4, m5, m6, m7, m8] = mat3.data; + let x, y, z; + for (let j = 0; j < totalComponents; j += stream.numComponents) { - vec.set(subarray[j], subarray[j + 1], subarray[j + 2]); - mat3.transformVector(vec, vec); - subarray[j] = vec.x; - subarray[j + 1] = vec.y; - subarray[j + 2] = vec.z; + x = subarray[j]; + y = subarray[j + 1]; + z = subarray[j + 2]; + + // mat3.transformVector + subarray[j] = x * m0 + y * m3 + z * m6; + subarray[j + 1] = x * m1 + y * m4 + z * m7; + subarray[j + 2] = x * m2 + y * m5 + z * m8; } } } diff --git a/test/core/preprocessor.test.mjs b/test/core/preprocessor.test.mjs index 79503ca4b23..d722980cc1c 100644 --- a/test/core/preprocessor.test.mjs +++ b/test/core/preprocessor.test.mjs @@ -73,6 +73,28 @@ describe('Preprocessor', function () { #ifdef (UNKNOWN) #define TEST14 // this should not be defined #endif + + #define INDEX 3 + #if INDEX == 3 + CMP1 + #endif + + #if INDEX != 3 + CMP2 + #endif + + #if INDEX > 2 + CMP3 + #endif + + #define NAME hello + #if NAME == hello + CMP4 + #endif + + #if NAME != hello + CMP5 + #endif `; it('returns false for MORPH_A', function () { @@ -154,4 +176,25 @@ describe('Preprocessor', function () { it('returns true for nested', function () { expect(Preprocessor.run(srcData, includes).includes('nested')).to.equal(true); }); + + it('returns true for CMP1', function () { + expect(Preprocessor.run(srcData, includes).includes('CMP1')).to.equal(true); + }); + + it('returns false for CMP2', function () { + expect(Preprocessor.run(srcData, includes).includes('CMP2')).to.equal(false); + }); + + it('returns true for CMP3', function () { + expect(Preprocessor.run(srcData, includes).includes('CMP3')).to.equal(true); + }); + + it('returns true for CMP4', function () { + expect(Preprocessor.run(srcData, includes).includes('CMP4')).to.equal(true); + }); + + it('returns false for CMP5', function () { + expect(Preprocessor.run(srcData, includes).includes('CMP5')).to.equal(false); + }); + }); diff --git a/utils/typedoc-plugin.mjs b/utils/typedoc-plugin.mjs index 17dc26c4522..847c0cad146 100644 --- a/utils/typedoc-plugin.mjs +++ b/utils/typedoc-plugin.mjs @@ -1,6 +1,7 @@ import { readFileSync } from 'fs'; import { resolve } from 'path'; +/* eslint-disable-next-line import/no-unresolved */ import { ArrayType, Converter, DeclarationReflection, IntrinsicType, ReflectionFlag, ReflectionKind, ReferenceType, UnionType } from 'typedoc'; /**