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';
/**