diff --git a/package-lock.json b/package-lock.json index 983847e..c894ef9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supersplat", - "version": "0.20.0", + "version": "0.20.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "supersplat", - "version": "0.20.0", + "version": "0.20.1", "license": "MIT", "devDependencies": { "@playcanvas/eslint-config": "^1.7.1", @@ -19,16 +19,16 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", "@types/wicg-file-system-access": "^2023.10.5", - "@typescript-eslint/eslint-plugin": "^7.14.1", - "@typescript-eslint/parser": "^7.14.1", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", "concurrently": "^8.2.2", "cors": "^2.8.5", "cross-env": "^7.0.3", "eslint": "^8.56.0", "jest": "^29.7.0", - "playcanvas": "^1.72.0", + "playcanvas": "^1.72.1", "rollup": "^4.18.0", - "rollup-plugin-sass": "^1.13.0", + "rollup-plugin-sass": "^1.13.1", "rollup-plugin-visualizer": "^5.12.0", "serve": "^14.2.3", "tslib": "^2.6.3" @@ -1882,16 +1882,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.14.1.tgz", - "integrity": "sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz", + "integrity": "sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.14.1", - "@typescript-eslint/type-utils": "7.14.1", - "@typescript-eslint/utils": "7.14.1", - "@typescript-eslint/visitor-keys": "7.14.1", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/type-utils": "7.15.0", + "@typescript-eslint/utils": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1915,15 +1915,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.14.1.tgz", - "integrity": "sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.15.0.tgz", + "integrity": "sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.14.1", - "@typescript-eslint/types": "7.14.1", - "@typescript-eslint/typescript-estree": "7.14.1", - "@typescript-eslint/visitor-keys": "7.14.1", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "debug": "^4.3.4" }, "engines": { @@ -1943,13 +1943,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.14.1.tgz", - "integrity": "sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", + "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.14.1", - "@typescript-eslint/visitor-keys": "7.14.1" + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1960,13 +1960,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.14.1.tgz", - "integrity": "sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz", + "integrity": "sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.14.1", - "@typescript-eslint/utils": "7.14.1", + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/utils": "7.15.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1987,9 +1987,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.14.1.tgz", - "integrity": "sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", + "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2000,13 +2000,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.14.1.tgz", - "integrity": "sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", + "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.14.1", - "@typescript-eslint/visitor-keys": "7.14.1", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2028,15 +2028,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.14.1.tgz", - "integrity": "sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", + "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.14.1", - "@typescript-eslint/types": "7.14.1", - "@typescript-eslint/typescript-estree": "7.14.1" + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2050,12 +2050,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.14.1.tgz", - "integrity": "sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", + "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/types": "7.15.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -6299,9 +6299,9 @@ } }, "node_modules/playcanvas": { - "version": "1.72.0", - "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-1.72.0.tgz", - "integrity": "sha512-emtQBXvBrr5gl/9bNT+FK+fl7Zc1Z2/9faxDuo/t/J1Vrz2XFimhjQeJt6QB2F2G2typwylXm6HvRxjVF5TX8w==", + "version": "1.72.1", + "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-1.72.1.tgz", + "integrity": "sha512-I6mx9wzi5yTCU+YLPI0S876R0Kf9K/t554/u7r2syRd4+Or/QjBqu6YJYtTMcSFtp2s0gtVjwVZKHcsg//NtvQ==", "dev": true, "dependencies": { "@types/webxr": "^0.5.16", @@ -6651,9 +6651,9 @@ } }, "node_modules/rollup-plugin-sass": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-sass/-/rollup-plugin-sass-1.13.0.tgz", - "integrity": "sha512-TL/pBbuqN3Qftiub1rLWiPnUGyL5PC7/+4x1ZgFJWzu1Y8n2vwYILt1kPR83AUzPOwqgFfD+B/LqgV6ee0+CYQ==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-sass/-/rollup-plugin-sass-1.13.1.tgz", + "integrity": "sha512-ZppWT9mHha0KT2mYOCqRujFP7BMavOsKcWX1X7fz3foAFpjcEA50704oJWf/BI2zx3wTt8gC0+DqlPvV2a1maA==", "dev": true, "dependencies": { "@rollup/pluginutils": "^3 || ^4 || ^5", diff --git a/package.json b/package.json index be2a351..8112a72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supersplat", - "version": "0.20.0", + "version": "0.21.0", "author": "PlayCanvas", "homepage": "https://playcanvas.com/supersplat/editor", "description": "3D Gaussian Splat Editor", @@ -61,16 +61,16 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", "@types/wicg-file-system-access": "^2023.10.5", - "@typescript-eslint/eslint-plugin": "^7.14.1", - "@typescript-eslint/parser": "^7.14.1", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", "concurrently": "^8.2.2", "cors": "^2.8.5", "cross-env": "^7.0.3", "eslint": "^8.56.0", "jest": "^29.7.0", - "playcanvas": "^1.72.0", + "playcanvas": "^1.72.1", "rollup": "^4.18.0", - "rollup-plugin-sass": "^1.13.0", + "rollup-plugin-sass": "^1.13.1", "rollup-plugin-visualizer": "^5.12.0", "serve": "^14.2.3", "tslib": "^2.6.3" diff --git a/src/asset-loader.ts b/src/asset-loader.ts index 82fbfaa..42f39be 100644 --- a/src/asset-loader.ts +++ b/src/asset-loader.ts @@ -53,6 +53,14 @@ class AssetLoader { ); asset.on('load', () => { stopSpinner(); + + // support loading 2d splats by adding scale_2 property with almost 0 scale + const splatData = asset.resource.splatData; + if (splatData.getProp('scale_0') && splatData.getProp('scale_1') && !splatData.getProp('scale_2')) { + const scale2 = new Float32Array(splatData.numSplats).fill(Math.log(1e-6)); + splatData.addProp('scale_2', scale2); + } + resolve(new Splat(asset)); }); asset.on('error', (err: string) => { diff --git a/src/camera.ts b/src/camera.ts index 63653a5..ebb8418 100644 --- a/src/camera.ts +++ b/src/camera.ts @@ -317,6 +317,9 @@ class Camera extends Element { } } + // controller update + this.controller.update(deltaTime); + // update underlying values this.focalPointTween.update(deltaTime); this.azimElevTween.update(deltaTime); @@ -445,6 +448,7 @@ class Camera extends Element { } if (closestSplat) { + this.setDistance(cameraPos.sub(closestP).length() / this.focusDistance); this.setFocalPoint(closestP); scene.events.fire('selection', closestSplat); } diff --git a/src/controllers.ts b/src/controllers.ts index 5b52ccd..9915901 100644 --- a/src/controllers.ts +++ b/src/controllers.ts @@ -10,6 +10,7 @@ const dist = (x0: number, y0: number, x1: number, y1: number) => Math.sqrt((x1 - class PointerController { destroy: () => void; + update: (deltaTime: number) => void; constructor(camera: Camera, target: HTMLElement) { @@ -30,7 +31,7 @@ class PointerController { worldDiff.sub2(toWorldPoint, fromWorldPoint); worldDiff.add(camera.focalPoint); - + camera.setFocalPoint(worldDiff); }; @@ -142,12 +143,52 @@ class PointerController { event.preventDefault(); }; + // key state + const keys: any = { + ArrowUp: 0, + ArrowDown: 0, + ArrowLeft: 0, + ArrowRight: 0 + }; + + const keydown = (event: KeyboardEvent) => { + if (keys.hasOwnProperty(event.key)) { + keys[event.key] = event.shiftKey ? 10 : (event.ctrlKey || event.metaKey || event.altKey ? 0.1 : 1); + event.preventDefault(); + event.stopPropagation(); + } + }; + + const keyup = (event: KeyboardEvent) => { + if (keys.hasOwnProperty(event.key)) { + keys[event.key] = 0; + event.preventDefault(); + event.stopPropagation(); + } + }; + + this.update = (deltaTime: number) => { + const x = keys.ArrowRight - keys.ArrowLeft; + const z = keys.ArrowDown - keys.ArrowUp; + + if (x || z) { + const factor = deltaTime * camera.distance * 20; + const worldTransform = camera.entity.getWorldTransform(); + const xAxis = worldTransform.getX().mulScalar(x * factor); + const zAxis = worldTransform.getZ().mulScalar(z * factor); + const p = camera.focalPoint.add(xAxis).add(zAxis); + camera.setFocalPoint(p); + } + }; + target.addEventListener('pointerdown', pointerdown); target.addEventListener('pointerup', pointerup); target.addEventListener('pointermove', pointermove); target.addEventListener('wheel', wheel); target.addEventListener('dblclick', dblclick); target.addEventListener('contextmenu', contextmenu); + document.addEventListener('keydown', keydown); + document.addEventListener('keyup', keyup); this.destroy = () => { target.removeEventListener('pointerdown', pointerdown); @@ -156,6 +197,8 @@ class PointerController { target.removeEventListener('wheel', wheel); target.removeEventListener('dblclick', dblclick); target.removeEventListener('contextmenu', contextmenu); + document.removeEventListener('keydown', keydown); + document.removeEventListener('keyup', keyup); }; } }