diff --git a/package-lock.json b/package-lock.json index 3be4330..e890e94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,22 @@ { "name": "immersive", - "version": "0.0.5", + "version": "0.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immersive", - "version": "0.0.5", + "version": "0.0.7", "dependencies": { - "@babylonjs/core": "^7.3.1", - "@babylonjs/gui": "^7.3.1", + "@babylonjs/core": "^7.6.0", + "@babylonjs/gui": "^7.6.0", "@babylonjs/havok": "1.3.4", - "@babylonjs/inspector": "^7.3.1", - "@babylonjs/loaders": "^7.3.1", - "@babylonjs/materials": "^7.3.1", - "@babylonjs/procedural-textures": "^7.3.1", - "@babylonjs/serializers": "^7.3.1", + "@babylonjs/inspector": "^7.6.0", + "@babylonjs/loaders": "^7.6.0", + "@babylonjs/materials": "^7.6.0", + "@babylonjs/procedural-textures": "^7.6.0", + "@babylonjs/serializers": "^7.6.0", + "@maptiler/client": "1.8.1", "@picovoice/cobra-web": "^2.0.3", "@picovoice/eagle-web": "^1.0.0", "@picovoice/web-voice-processor": "^4.0.9", @@ -55,14 +56,14 @@ } }, "node_modules/@babylonjs/core": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-7.5.0.tgz", - "integrity": "sha512-iP/z+j1eRrpv5g/JyqxMAuT7UVdfBWPHZ0Bl/89onq9hZmeyckHM+gXNpepo4sFrxoDefdAblh5eyVbN6iiXDg==" + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-7.6.0.tgz", + "integrity": "sha512-Pr5TlHQBdTYLkw+HmWZo1V6VYnP48wxEGBAscHcgZuuETo2ITCmsRLv0o+XY0QL1eAL8grQBM5eOkAInNt4SvA==" }, "node_modules/@babylonjs/gui": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-7.5.0.tgz", - "integrity": "sha512-kqS9HtveyG7W918u+h01JZBv9cDoPda3/o/vjbiZOUHem71Lj5MR7WeVr8bSRPbVRyj3mUdL2o1w2tYRPiDC/w==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-7.6.0.tgz", + "integrity": "sha512-5r954RVrnEvSCw/WKwl6CdKb5PIVEMyF6WKS+bDku3PrwaoTUqkd1jlhmyaH6k2bL/bcoaz25ts04TK6YrbmXA==", "peerDependencies": { "@babylonjs/core": "^7.0.0" } @@ -88,9 +89,9 @@ } }, "node_modules/@babylonjs/inspector": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-7.5.0.tgz", - "integrity": "sha512-B1ACpoljYX5Y3DHJZgCwfkhlS4M5xb9VJKtH3mN+VgXEt5AnOMT2F8pXJNz9ICQC7XhIxo34JL52OnE5iOhlvw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-7.6.0.tgz", + "integrity": "sha512-utytbidkVhYXNhVY5XOuGIHCPdmHrHw9q1rr9+P7nwUUwTtl3duAQiU8TE/w/6MUejg97kyN6lBGRqXAWpL5nA==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/free-regular-svg-icons": "^6.0.0", @@ -108,34 +109,34 @@ } }, "node_modules/@babylonjs/loaders": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-7.5.0.tgz", - "integrity": "sha512-rVkYkiT3XSGJ1s0rEuUCN3BXJGI4RexKXUrTp1cy0CVj3B2DvNt9pzET4038BmVp8N3EDPBd9+bPZenduicsPw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-7.6.0.tgz", + "integrity": "sha512-7tt/ImGExADJx5wGs/3vz8tijg1FHuVT2c/8dp0Q+qv3v47KJIgCOtggoXCVDTo+xYj2fQWtucmXwkNhIizg7w==", "peerDependencies": { "@babylonjs/core": "^7.0.0", "babylonjs-gltf2interface": "^7.0.0" } }, "node_modules/@babylonjs/materials": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-7.5.0.tgz", - "integrity": "sha512-IGYQt+0qaX4ZVTyMge4IjZnHcPpwf85/J+fg4QBUz0BoUgrFAvi0nXJl43X7FJaj0H2BJSaERBZm/kK2K9bKqA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-7.6.0.tgz", + "integrity": "sha512-o5PcKNf/NE+P81E1fQyuoNrCND6dV5RJZga03JzHIs48/+2veq11Dj4FGAOinmqQwe/0U2yrl3v4hsRVwt4YtQ==", "peerDependencies": { "@babylonjs/core": "^7.0.0" } }, "node_modules/@babylonjs/procedural-textures": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babylonjs/procedural-textures/-/procedural-textures-7.5.0.tgz", - "integrity": "sha512-xvJ/gQJBy1mgTJYc/YkzzdzJoK/L/wBi/rtW9o9ckLe+cjw5UQ0gIGEUIJlgIx2GX7hPVjKd73uN2l5LcWYVsA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babylonjs/procedural-textures/-/procedural-textures-7.6.0.tgz", + "integrity": "sha512-37Dheb8kGX0lWRevFw3mqMtuoJrJ+TT2bEQB/9BOmAKS2yEb2xlLlswkn7UaB4oHax8XzaY9N63zTSzzWj978Q==", "peerDependencies": { "@babylonjs/core": "^7.0.0" } }, "node_modules/@babylonjs/serializers": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-7.5.0.tgz", - "integrity": "sha512-DCQmT8RnBmF7jxIOhXjEmHL4dFmPrypBdMEoI9eVenQfLwBlRim4tbzHlHSwmk1DOy+ENxqODZzhzh4EmUaS4A==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-7.6.0.tgz", + "integrity": "sha512-U/umdH2dAcEuyb1OcaemV2LOgIohsoWAgUriub0SxA+DLlpiyEt6HUSvM+aX/90UyGNz5Dpb4VZXbqBIrUa0Hg==", "peerDependencies": { "@babylonjs/core": "^7.0.0", "babylonjs-gltf2interface": "^7.0.0" @@ -572,6 +573,14 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, + "node_modules/@maptiler/client": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@maptiler/client/-/client-1.8.1.tgz", + "integrity": "sha512-G1z2xCBwT5WU1CqeukY3R4g1saSjKzi6tmg24LJWZHP81RQ4quVvwdmsx829BxITlFbFhND+BSphFgrDGmwhcA==", + "dependencies": { + "quick-lru": "^7.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2579,6 +2588,17 @@ } ] }, + "node_modules/quick-lru": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-7.0.0.tgz", + "integrity": "sha512-MX8gB7cVYTrYcFfAnfLlhRd0+Toyl8yX8uBx1MrX7K0jegiz9TumwOK27ldXrgDlHRdVi+MqU9Ssw6dr4BNreg==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index 62d12e6..12fbb2a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "immersive", "private": true, - "version": "0.0.6", + "version": "0.0.7", "type": "module", "engines": { "node": ">=18.0.0" @@ -16,17 +16,18 @@ "havok": "cp ./node_modules/@babylonjs/havok/lib/esm/HavokPhysics.wasm ./node_modules/.vite/deps" }, "dependencies": { - "@babylonjs/core": "^7.3.1", - "@babylonjs/gui": "^7.3.1", + "@babylonjs/core": "^7.6.0", + "@babylonjs/gui": "^7.6.0", "@babylonjs/havok": "1.3.4", - "@babylonjs/inspector": "^7.3.1", - "@babylonjs/loaders": "^7.3.1", - "@babylonjs/materials": "^7.3.1", - "@babylonjs/procedural-textures": "^7.3.1", - "@babylonjs/serializers": "^7.3.1", + "@babylonjs/inspector": "^7.6.0", + "@babylonjs/loaders": "^7.6.0", + "@babylonjs/materials": "^7.6.0", + "@babylonjs/procedural-textures": "^7.6.0", + "@babylonjs/serializers": "^7.6.0", "@picovoice/cobra-web": "^2.0.3", "@picovoice/eagle-web": "^1.0.0", "@picovoice/web-voice-processor": "^4.0.9", + "@maptiler/client": "1.8.1", "@typed-mxgraph/typed-mxgraph": "^1.0.8", "@types/dom-to-image": "^2.6.7", "@types/file-saver": "^2.0.6", diff --git a/public/assets/icons/point.png b/public/assets/icons/point.png new file mode 100644 index 0000000..5c53ba2 Binary files /dev/null and b/public/assets/icons/point.png differ diff --git a/public/assets/icons/video.png b/public/assets/icons/video.png new file mode 100644 index 0000000..dc2829e Binary files /dev/null and b/public/assets/icons/video.png differ diff --git a/public/assets/icons/video.svg b/public/assets/icons/video.svg new file mode 100644 index 0000000..a5149ba --- /dev/null +++ b/public/assets/icons/video.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/public/assets/models/tinker.obj b/public/assets/models/tinker.obj new file mode 100644 index 0000000..650d6dc --- /dev/null +++ b/public/assets/models/tinker.obj @@ -0,0 +1,300 @@ +# Object Export From Tinkercad Server 2015 + +mtllib obj.mtl + +o obj_0 +v 4 3.5 4 +v 4 3.568 3.482 +v 4 3.768 3 +v 4 4.086 2.586 +v 4 4.5 2.268 +v 4 4.982 2.068 +v 4 5.5 2 +v 4 6.018 2.068 +v 4 6.5 2.268 +v 4 6.914 2.586 +v 4 7.232 3 +v 4 7.432 3.482 +v 4 7.5 4 +v 4 7.432 4.518 +v 4 7.232 5 +v 4 6.914 5.414 +v 4 6.5 5.732 +v 4 6.018 5.932 +v 4 5.5 6 +v 4 4.982 5.932 +v 4 4.5 5.732 +v 4 4.086 5.414 +v 4 3.768 5 +v 4 3.568 4.518 +v -8.308 4.5 7.952 +v 1 4.7272 4.2072 +v -8.587 4.5 7.81 +v 1 4.7 4 +v -8.81 4.5 7.587 +v 1 4.8072 4.4 +v -8.952 4.5 7.308 +v 1 4.9344 4.5656 +v -9 4.5 7.0043 +v 0.952 6.5 7.308 +v 1 5.1 4.6928 +v 1 6.5 7.0043 +v 0.81 6.5 7.587 +v 1 5.2928 4.7728 +v 0.587 6.5 7.81 +v 0.308 6.5 7.952 +v 1 5.5 4.8 +v 0.0033 6.5 8 +v 1 5.7072 4.7728 +v 1 5.9 4.6928 +v 1 6.0656 4.5656 +v 1 6.1928 4.4 +v 1 4.5 7.0043 +v 0.952 4.5 7.308 +v 0.81 4.5 7.587 +v 0.587 4.5 7.81 +v 0.308 4.5 7.952 +v 1 6.2728 4.2072 +v 0.0033 4.5 8 +v 1 6.3 4 +v -8.0043 4.5 0 +v -8.952 4.5 0.692 +v -9 4.5 0.9967 +v 1 6.2728 3.7928 +v -8.81 4.5 0.413 +v -8.587 4.5 0.19 +v 1 6.1928 3.6 +v -8.308 4.5 0.048 +v 1 6.0656 3.4344 +v 1 5.9 3.3072 +v -8.0043 6.5 8 +v -8.308 6.5 7.952 +v 1 5.7072 3.2272 +v -8.587 6.5 7.81 +v 1 5.5 3.2 +v -8.81 6.5 7.587 +v -8.952 6.5 7.308 +v 1 5.2928 3.2272 +v -9 6.5 7.0043 +v 1 5.1 3.3072 +v 1 4.9344 3.4344 +v 1 4.8072 3.6 +v 1 4.7272 3.7928 +v -9 6.5 0.9967 +v -8.0043 4.5 8 +v 0.81 6.5 0.413 +v 0.587 6.5 0.19 +v 0.952 6.5 0.692 +v 1 6.5 0.9967 +v 0.0033 6.5 0 +v 0.0033 4.5 0 +v 0.308 4.5 0.048 +v -8.0043 6.5 0 +v 0.587 4.5 0.19 +v 0.81 4.5 0.413 +v 0.952 4.5 0.692 +v 1 4.5 0.9967 +v -8.952 6.5 0.692 +v -8.81 6.5 0.413 +v -8.587 6.5 0.19 +v -8.308 6.5 0.048 +v 0.308 6.5 0.048 +# 96 vertices + +g group_0_8273816 + +usemtl color_8273816 +s 0 + +f 1 2 3 +f 1 3 4 +f 1 4 5 +f 1 5 6 +f 1 6 7 +f 1 7 8 +f 1 8 9 +f 1 9 10 +f 1 10 11 +f 1 11 12 +f 1 12 13 +f 1 13 14 +f 1 14 15 +f 1 15 16 +f 1 16 17 +f 1 17 18 +f 1 18 19 +f 1 19 20 +f 1 20 21 +f 1 21 22 +f 1 22 23 +f 1 23 24 +f 28 1 26 +f 24 26 1 +f 23 30 26 +f 23 26 24 +f 22 32 30 +f 22 30 23 +f 32 22 35 +f 21 35 22 +f 49 48 37 +f 35 21 38 +f 20 38 21 +f 19 41 38 +f 19 38 20 +f 41 19 43 +f 18 43 19 +f 34 48 36 +f 17 44 43 +f 17 43 18 +f 34 37 48 +f 16 45 44 +f 16 44 17 +f 49 37 39 +f 45 16 46 +f 15 46 16 +f 53 40 42 +f 36 48 47 +f 49 39 50 +f 51 50 40 +f 39 40 50 +f 46 15 52 +f 14 52 15 +f 40 53 51 +f 52 54 36 +f 83 36 54 +f 54 58 83 +f 58 61 83 +f 61 63 83 +f 93 60 59 +f 64 83 63 +f 69 72 91 +f 72 74 91 +f 73 33 71 +f 74 75 91 +f 75 76 91 +f 25 65 66 +f 66 68 27 +f 66 27 25 +f 29 27 70 +f 68 70 27 +f 91 76 77 +f 91 77 28 +f 70 71 31 +f 70 31 29 +f 71 33 31 +f 65 25 79 +f 57 33 78 +f 73 78 33 +f 42 65 53 +f 53 65 79 +f 81 88 86 +f 80 89 88 +f 80 88 81 +f 89 80 90 +f 82 90 80 +f 82 83 90 +f 91 90 83 +f 84 86 85 +f 55 87 85 +f 84 85 87 +f 95 87 55 +f 86 91 85 +f 88 91 86 +f 89 90 88 +f 91 88 90 +f 92 56 78 +f 93 56 92 +f 94 60 93 +f 94 95 60 +f 62 60 95 +f 56 55 57 +f 91 57 85 +f 62 95 55 +f 55 85 57 +f 59 55 56 +f 60 62 59 +f 55 59 62 +f 78 56 57 +f 93 59 56 +f 29 31 27 +f 33 27 31 +f 27 33 25 +f 13 54 52 +f 13 52 14 +f 54 13 58 +f 12 58 13 +f 49 50 57 +f 51 57 50 +f 53 57 51 +f 79 33 53 +f 61 58 11 +f 12 11 58 +f 63 61 10 +f 11 10 61 +f 10 9 64 +f 10 64 63 +f 49 57 48 +f 91 47 57 +f 25 33 79 +f 57 53 33 +f 47 48 57 +f 9 8 67 +f 9 67 64 +f 96 73 81 +f 84 73 96 +f 69 67 7 +f 8 7 67 +f 80 81 73 +f 7 6 72 +f 7 72 69 +f 74 72 5 +f 6 5 72 +f 93 92 94 +f 78 94 92 +f 95 78 87 +f 65 42 83 +f 75 74 4 +f 5 4 74 +f 66 83 68 +f 70 68 83 +f 4 3 76 +f 4 76 75 +f 39 37 40 +f 42 40 37 +f 42 37 34 +f 3 2 77 +f 3 77 76 +f 34 36 42 +f 28 77 1 +f 2 1 77 +f 36 83 42 +f 65 83 66 +f 71 70 83 +f 73 71 83 +f 82 80 73 +f 83 82 73 +f 87 78 84 +f 94 78 95 +f 73 84 78 +f 96 86 84 +f 81 86 96 +f 67 69 83 +f 64 67 83 +f 69 91 83 +f 91 28 47 +f 28 26 47 +f 26 30 47 +f 30 32 47 +f 38 41 47 +f 32 35 47 +f 35 38 47 +f 41 36 47 +f 41 43 36 +f 43 44 36 +f 44 45 36 +f 45 46 36 +f 46 52 36 +# 188 faces + + #end of obj_0 + diff --git a/public/assets/textures/rockford.jpeg b/public/assets/textures/rockford.jpeg new file mode 100644 index 0000000..26400a6 Binary files /dev/null and b/public/assets/textures/rockford.jpeg differ diff --git a/public/sw.js b/public/sw.js index bf02b55..ae9a474 100644 --- a/public/sw.js +++ b/public/sw.js @@ -2,6 +2,7 @@ importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.1.0/workbox const VERSION = '8'; const CACHE = "deepdiagram"; const IMAGEDELIVERY_CACHE = "deepdiagram-images"; +const MAPTILE_CACHE = 'maptiler'; // TODO: replace the following with the correct offline fallback page i.e.: const offlineFallbackPage = "offline.html"; const offlineFallbackPage = "/"; @@ -43,7 +44,27 @@ workbox.routing.registerRoute( cacheName: CACHE }) ); -console.warn('workbox'); + +workbox.routing.registerRoute( + new RegExp('.*api.maptiler.com/.*'), + new workbox.strategies.CacheFirst({ + plugins: [ + new workbox.expiration.ExpirationPlugin({ + maxEntries: 256, + maxAgeSeconds: 60 * 60 * 24 * 30, + purgeOnQuotaError: true, + matchOptions: { + ignoreVary: true + } + }), + new workbox.cacheableResponse.CacheableResponsePlugin({ + statuses: [0, 200] + }) + ], + cacheName: MAPTILE_CACHE + + }) +); workbox.routing.registerRoute( new RegExp('/assets/.*'), diff --git a/src/menus/cameraMenu.ts b/src/menus/cameraMenu.ts index 3356506..1b0f73c 100644 --- a/src/menus/cameraMenu.ts +++ b/src/menus/cameraMenu.ts @@ -1,45 +1,96 @@ -import {Color3, DynamicTexture, Mesh, MeshBuilder, Scene, StandardMaterial, Vector3} from "@babylonjs/core"; +import {Color3, DynamicTexture, MeshBuilder, Scene, StandardMaterial, Vector3} from "@babylonjs/core"; import log, {Logger} from "loglevel"; +import {MaptilerMap} from "../objects/maptilerMap"; export class CameraMenu { private readonly scene: Scene; - private xr; - private controllers; private readonly logger: Logger = log.getLogger('CameraMenu'); - constructor(scene, xr, controllers) { + constructor(scene) { this.scene = scene; - this.xr = xr; - this.controllers = controllers; - this.buildMenu(1, new Vector3(0, 1, 0)); + //this.buildMenu(3, new Vector3(-1, 2, 0)); //this.buildMenu(4, new Vector3(0,2,0)); //this.buildMenu(5, new Vector3(1,2,0)); - //this.buildMenu(6, new Vector3(1,1,0)); + //this.buildMenu(6, new Vector3(2,2,0)); + //this.buildIcon(); + //this.loadIcon(); + + this.buildMap(); } + private buildMap() { + const maptilerMap = new MaptilerMap('YnvhjBiU8oCWP0GXNdHL', this.scene, 'map-node', 3); + maptilerMap.node.position.y = 1; + maptilerMap.node.position.z = -4; + maptilerMap.node.rotation.y = Math.PI; + maptilerMap.node.rotation.x = Math.PI / 6; + maptilerMap.node.scaling = new Vector3(1, 1, 1); + //maptilerMap.setLocation('loves park, il' , 15); + maptilerMap.setLocation('rockford, il', 12).then(() => { + maptilerMap.plotPoint(42.33181896128866, -88.86844896012006); + }); + + } + + //https://maps.geoapify.com/v1/staticmap?style=osm-carto&scaleFactor=2&width=4096&height=4096¢er=lonlat:-89.0940,42.2711&zoom=12.4318&apiKey=d548c5ed24604be6a9dd0d989631f783 + private buildIcon() { + const icon = MeshBuilder.CreatePlane('camera-icon', {width: .1, height: .1}, this.scene); + icon.position = new Vector3(0, 3, 0); + icon.metadata = {grabbable: true}; + const material = new StandardMaterial('icon-material', this.scene); + + material.backFaceCulling = false; + const texture = new DynamicTexture('icon-texture', {width: 256, height: 256}, this.scene); + //const texture = new DynamicTexture('/assets/icons/video.png', this.scene); + + const image = new Image(); + //image.setAttribute('width', '256'); + //image.setAttribute('height', '256'); + //image.width=32; + //image.height=32; + image.src = '/assets/icons/video.png'; + image.onload = () => { + texture.getContext().drawImage(image, 0, 0); + texture.update(); + } + + material.emissiveColor = new Color3(.1, .1, .8); + material.opacityTexture = texture; + icon.material = material; + material.disableLighting = true; + + //material.diffuseTexture = texture; + //material.disableLighting; + //material.emissiveColor = new Color3(1, 1, 1); + //texture.uScale = 1; + //texture.vScale = 1; + + + } private buildMenu(camnum: number, position: Vector3) { - const camerasphere = MeshBuilder.CreateSphere("camerasphere", { - diameter: 10, - slice: .5, - sideOrientation: Mesh.DOUBLESIDE - }, this.scene); + const camerasphere = MeshBuilder.CreatePlane('camera-' + camnum, {width: 1, height: 1}, this.scene); camerasphere.position = position; const material = new StandardMaterial("cameramaterial", this.scene); - material.emissiveColor = new Color3(1, 1, 1); - + //material.emissiveColor = new Color3(1, 1, 1); + material.backFaceCulling = false; const texture = new DynamicTexture('texture', {width: 1600, height: 1600}, this.scene); - material.diffuseTexture = texture; + material.emissiveTexture = texture; + material.disableLighting = true; const img = new Image(); - img.src = 'https://cameras.immersiveidea.com/mjpg/video.mjpg?camera=' + camnum + '×tamp=1698497537140'; + + img.setAttribute('crossorigin', 'anonymous'); + img.src = 'https://cameras.immersiveidea.com/mjpg/video.mjpg?camera=' + camnum; + const ctx = texture.getContext(); img.onload = () => { - ctx.drawImage(img, 250, 0, 2112, 1940, 0, 0, 1600, 1600); + + ctx.drawImage(img, 0, 0); texture.update(); window.setInterval((texture, img, ctx) => { - ctx.drawImage(img, 250, 0, 2112, 1940, 0, 0, 1600, 1600); + ctx.drawImage(img, 0, 0); texture.update(); - }, 60, texture, img, ctx); + }, 1000, texture, img, ctx); } texture.onLoadObservable.add(() => { diff --git a/src/objects/cameraIcon.ts b/src/objects/cameraIcon.ts new file mode 100644 index 0000000..692b3e0 --- /dev/null +++ b/src/objects/cameraIcon.ts @@ -0,0 +1,66 @@ +import { + AbstractMesh, + ActionManager, + Color3, + ExecuteCodeAction, + InstancedMesh, + Mesh, + Scene, + SceneLoader, + StandardMaterial, + TransformNode, + Vector3 +} from "@babylonjs/core"; +import {DefaultScene} from "../defaultScene"; +import {CameraWindow} from "./cameraWindow"; + +export class CameraIcon { + private static _baseMesh: AbstractMesh; + private readonly _scene: Scene; + private _cam: CameraWindow; + + constructor(scene: Scene, mapNode: TransformNode, position: Vector3) { + this._scene = scene; + if (!CameraIcon._baseMesh) { + this.loadIcon(); + } + const newInstance = new InstancedMesh('cam', CameraIcon._baseMesh as Mesh); + newInstance.setParent(mapNode); + newInstance.position = position; + newInstance.actionManager = new ActionManager(this._scene); + newInstance.actionManager.registerAction(new ExecuteCodeAction(ActionManager.OnPickTrigger, () => { + if (this._cam) { + this._cam.dispose(); + this._cam = null; + } else { + this._cam = new CameraWindow(scene, null, 'https://cameras.immersiveidea.com/mjpg/video.mjpg?camera=3'); + this._cam.mesh.position.x = newInstance.absolutePosition.x; + this._cam.mesh.position.z = newInstance.absolutePosition.z; + this._cam.mesh.position.y = newInstance.absolutePosition.y + .5; + } + })); + + //newInstance.billboardMode = Mesh.BILLBOARDMODE_ALL; + } + + private loadIcon() { + SceneLoader.ImportMesh('', '/assets/models/', 'tinker.obj', this._scene, + (newMesh) => { + const myMesh = newMesh[0].getChildren()[0] as Mesh; + //myMesh.position = new Vector3(0, 1.5, 0); + myMesh.metadata = {grabbable: true}; + const SCALE = .006; + myMesh.scaling = new Vector3(SCALE, SCALE, SCALE); + myMesh.rotation.x = Math.PI / 2; + const material = new StandardMaterial('icon-material', DefaultScene.Scene); + material.emissiveColor = new Color3(.1, .1, .9); + material.disableLighting = true; + myMesh.material = material; + myMesh.name = 'cam'; + myMesh.setEnabled(false); + myMesh.setParent(null); + newMesh[0].dispose(); + CameraIcon._baseMesh = myMesh; + }) + } +} diff --git a/src/objects/cameraWindow.ts b/src/objects/cameraWindow.ts new file mode 100644 index 0000000..a87805e --- /dev/null +++ b/src/objects/cameraWindow.ts @@ -0,0 +1,59 @@ +import {AbstractMesh, DynamicTexture, MeshBuilder, Scene, StandardMaterial, TransformNode} from "@babylonjs/core"; + +export class CameraWindow { + private readonly _scene: Scene; + private readonly _url: string; + private readonly _parent: TransformNode; + private readonly _intervalId: number; + private _cameraMesh: AbstractMesh; + private _img: HTMLImageElement; + + constructor(scene: Scene, parent: TransformNode, url: string) { + this._scene = scene; + this._parent = parent; + this._url = url; + this.build(); + + } + + public get mesh() { + return this._cameraMesh; + } + + public dispose() { + window.clearInterval(this._intervalId); + this._cameraMesh.dispose(false, true); + this._img.remove(); + } + + private build() { + this._cameraMesh = MeshBuilder.CreatePlane('cam-' + this._url, {width: 1, height: 1}, this._scene); + this._cameraMesh.parent = this._parent; + //camerasphere.position = position; + const material = new StandardMaterial("cameramaterial", this._scene); + //material.emissiveColor = new Color3(1, 1, 1); + material.backFaceCulling = false; + const texture = new DynamicTexture('texture', {width: 1600, height: 1600}, this._scene); + material.emissiveTexture = texture; + material.disableLighting = true; + this._img = new Image(); + + this._img.setAttribute('crossorigin', 'anonymous'); + //img.src = 'https://cameras.immersiveidea.com/mjpg/video.mjpg?camera=' + camnum; + this._img.src = this._url; + const ctx = texture.getContext(); + this._img.onload = () => { + ctx.drawImage(this._img, 0, 0); + texture.update(); + window.setInterval((texture, img, ctx) => { + ctx.drawImage(img, 0, 0); + texture.update(); + }, 1000, texture, this._img, ctx); + } + + texture.onLoadObservable.add(() => { + + }); + this._cameraMesh.material = material; + } +} \ No newline at end of file diff --git a/src/objects/diagramObject.ts b/src/objects/diagramObject.ts index 2cc9c1f..b5a81f9 100644 --- a/src/objects/diagramObject.ts +++ b/src/objects/diagramObject.ts @@ -1,9 +1,55 @@ -import {Scene} from "@babylonjs/core"; +import {AbstractMesh, Scene} from "@babylonjs/core"; +import {DiagramEntity} from "../diagram/types/diagramEntity"; +import {buildMeshFromDiagramEntity} from "../diagram/functions/buildMeshFromDiagramEntity"; +import {toDiagramEntity} from "../diagram/functions/toDiagramEntity"; +type DiagramObjectOptionsType = { + diagramEntity?: DiagramEntity, + mesh?: AbstractMesh +} export class DiagramObject { - private scene: Scene; + private _scene: Scene; - constructor(scene: Scene) { - this.scene = scene; + constructor(scene: Scene, options?: DiagramObjectOptionsType) { + this._scene = scene; + if (options) { + if (options.diagramEntity) { + this.fromDiagramEntity(options.diagramEntity); + } + if (options.mesh) { + this._mesh = options.mesh; + this._diagramEntity = this.diagramEntity; + } + } + } + + private _mesh: AbstractMesh; + + public get mesh(): AbstractMesh { + return this._mesh; + } + + private _diagramEntity: DiagramEntity; + + public get diagramEntity(): DiagramEntity { + if (!this._diagramEntity) { + if (this._mesh) { + this._diagramEntity = toDiagramEntity(this._mesh); + } + } + return this._diagramEntity; + } + + public fromDiagramEntity(entity: DiagramEntity): DiagramObject { + this._diagramEntity = entity; + this._mesh = buildMeshFromDiagramEntity(this._diagramEntity, this._scene); + return this; + } + + public dispose() { + this._mesh.dispose(false, true); + this._mesh = null; + this._diagramEntity = null; + this._scene = null; } } \ No newline at end of file diff --git a/src/objects/maptilerMap.ts b/src/objects/maptilerMap.ts new file mode 100644 index 0000000..27f873a --- /dev/null +++ b/src/objects/maptilerMap.ts @@ -0,0 +1,256 @@ +import * as mapTilerClient from '@maptiler/client'; +import { + AbstractMesh, + MeshBuilder, + Observable, + Scene, + StandardMaterial, + Texture, + TransformNode, + Vector2, + Vector3 +} from "@babylonjs/core"; +import {CameraIcon} from "./cameraIcon"; + +export type MaptilerMapTile = { + lat: number, + lon: number, + zoom: number, + url: string, + x: number, + y: number, + bounds: Vector2[]; +} + + +export class MaptilerMap { + public readonly onReadyObservable = new Observable(); + private _lat: number; + private _lon: number; + private _min: Vector2; + private _max: Vector2; + private _zoom: number; + //private _bounds: Vector2[]; + private readonly _scene: Scene; + private _tileXYCount: number = 2; + private readonly _baseNode: TransformNode; + private readonly _key: string; + private _pendingPoints: Array = []; + private _points: Vector2[] = []; + + public constructor(key: string, scene: Scene, name: string = 'map-node', tileXYCount: number = 2) { + this._scene = scene; + this._key = key; + this._tileXYCount = tileXYCount; + this._baseNode = new TransformNode(name, this._scene); + this.onReadyObservable.addOnce(this.buildNodes.bind(this)); + this.onReadyObservable.addOnce(this.waitForMeshAdded.bind(this)); + } + + private _startFallback: number = 10; + + public set startFallback(value: number) { + this._startFallback = value; + } + + private _fallbackInterval: number = 10; + + public set fallbackInterval(value: number) { + this._fallbackInterval = value; + } + + public get node(): TransformNode { + return this._baseNode; + } + + public async setLocation(name: string, zoom: number = 18): Promise { + mapTilerClient.config.apiKey = this._key; + const result = await mapTilerClient.geocoding.forward(name) + if (result.features.length > 0) { + this.setInitialData(result.features[0].center[1], result.features[0].center[0], zoom); + const tileXY = await this.getTileXY(this._lat, this._lon); + const output = this.getTile(tileXY[0], tileXY[1], zoom); + this.onReadyObservable.notifyObservers({ + lat: this._lat, + lon: this._lon, + zoom: zoom, + x: tileXY[0], + y: tileXY[1], + url: output, + bounds: [] + }); + } else { + console.error(JSON.stringify(result)); + } + } + + public async setLocationByLatLon(lat: number, lon: number, zoom: number = 18): Promise { + this.setInitialData(lat, lon, zoom); + const tileXY = this.getTileXY(lat, lon); + const imageUrl = this.getTile(tileXY[0], tileXY[1], zoom); + this.onReadyObservable.notifyObservers({ + lat: this._lat, + lon: this._lon, + zoom: zoom, + x: tileXY[0], + y: tileXY[1], + url: imageUrl, + bounds: [] + }); + } + + public async plotPoint(lat: number, lon: number) { + const len = this._points.push(new Vector2(lat, lon)); + this._pendingPoints.push(len - 1); + } + + public getTile(x: number, y: number, z: number) { + return `https://api.maptiler.com/maps/streets-v2/256/${z}/${x}/${y}@2x.png?key=${this._key}`; + } + + private waitForMeshAdded() { + this._scene.onAfterRenderObservable.add(() => { + if (this._pendingPoints.length > 0) { + this._pendingPoints = this._pendingPoints.filter((item) => { + const point = this._points[item]; + const tileXY = this.getTileXY(point.x, point.y); + console.log(tileXY); + const mesh = this._scene.getMeshByName(`map-${tileXY[0]}-${tileXY[1]}-plane`); + const oldPoint = this._scene.getMeshByName(`map-${point.x}-${point.y}-point`); + if (!mesh) { + console.error(`map-${tileXY[0]}-${tileXY[1]}-plane not found`); + return true; + } else { + if (!oldPoint) { + const pixelx = lonOnTile(point.y, this._zoom) % 1; + const pixely = latOnTile(point.x, this._zoom) % 1; + console.log(`pixelx: ${pixelx}, pixely: ${pixely} found`); + try { + const newIcon = new CameraIcon(this._scene, this._baseNode, + new Vector3(mesh.position.x - .5 + pixelx, mesh.position.y + .5 - pixely, mesh.position.z - .05)); + return false; + } catch (err) { + return true; + } + } else { + return false; + //oldPoint.dispose(false, true); + } + } + }); + } + }, -1, false, this); + } + + private buildNodes(data: MaptilerMapTile) { + this.buildMapTile(0, 0, data.url, data.x, data.y).parent = this._baseNode; + let time = this._startFallback; + const tiles = this._tileXYCount; + if (this._tileXYCount < 1) { + return; + } + for (let x = -tiles; x < (tiles + 1); x++) { + for (let y = -tiles; y < (tiles + 1); y++) { + if (x !== 0 || y !== 0) { + const url = this.getTile(data.x + x, data.y + y, data.zoom); + window.setTimeout((that) => { + that.buildMapTile(x, -y, url, data.x + x, data.y + y).parent = this._baseNode; + }, time += this._fallbackInterval, this); + } + } + } + + } + + private getTileXY(lat: number, lon: number): number[] { + return [Math.floor(lonOnTile(lon, this._zoom)), Math.floor(latOnTile(lat, this._zoom))]; + } + + private setInitialData(lat: number, lon: number, zoom: number) { + + this._baseNode.getChildren().forEach((child) => { + child.dispose(false, true); + }); + this._lat = lat; + this._lon = lon; + this._zoom = zoom; + } + + private buildMapTile(x: number, y: number, url: string, xTile: number, yTile: number): AbstractMesh { + const map = MeshBuilder.CreatePlane(`map-${xTile}-${yTile}-plane`, {width: 1, height: 1}, this._scene); + const mapMaterial = new StandardMaterial(`map-${xTile}-${yTile}-material`, this._scene); + const mapTexture = new Texture(url, this._scene); + const lon = tile2long(xTile, this._zoom); + const lat = tile2lat(yTile, this._zoom); + if (!this._min || lat < this._min.x || lon < this._min.y) { + this._min = new Vector2(lat, lon); + console.log(`min: ${lat}, ${lon}`); + } + const maxLat = tile2lat(yTile + 1, this._zoom); + const maxLon = tile2long(xTile + 1, this._zoom); + if (!this._max || maxLat > this._max.y || maxLon > this._max.y) { + this._min = new Vector2(maxLat, maxLon); + console.log(`max: ${maxLat}, ${maxLon}`); + } + map.metadata = { + mapTile: {x: xTile, y: yTile}, bounds: + { + topleft: + {lat: lat, lon: lon}, + bottomright: + { + lat: tile2lat(yTile + 1, this._zoom), + lon: tile2long(xTile + 1, this._zoom) + } + } + }; + mapTexture.name = `map-${xTile}-${yTile}-texture`; + mapMaterial.emissiveTexture = mapTexture; + mapMaterial.disableLighting = true; + mapMaterial.backFaceCulling = false; + map.material = mapMaterial; + map.position.x = x; + map.position.y = y; + map.isPickable = true; + return map; + } +} + +function tile2long(x, z) { + return (x / Math.pow(2, z) * 360 - 180); +} + +function tile2lat(y, z) { + var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); + return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); +} + +const EARTH_CIR_METERS = 40075016.686; +const TILE_SIZE = 512; +const degreesPerMeter = 360 / EARTH_CIR_METERS; +const LIMIT_Y = toDegrees(Math.atan(Math.sinh(Math.PI))) // around 85.0511... + +function toRadians(degrees) { + return degrees * Math.PI / 180; +} + +function toDegrees(radians) { + return (radians / Math.PI) * 180 +} + + +function lonOnTile(lon, zoom) { + return ((lon + 180) / 360) * Math.pow(2, zoom) +} + +function latOnTile(lat, zoom) { + return ( + ((1 - + Math.log( + Math.tan((lat * Math.PI) / 180) + 1 / Math.cos((lat * Math.PI) / 180) + ) / + Math.PI) / + 2) * + Math.pow(2, zoom) + ) +} diff --git a/src/toolbox/functions/buildColor.ts b/src/toolbox/functions/buildColor.ts index 65db685..8c57c46 100644 --- a/src/toolbox/functions/buildColor.ts +++ b/src/toolbox/functions/buildColor.ts @@ -8,9 +8,10 @@ export function buildColor(color: Color3, scene: Scene, parent: TransformNode, i const height = .1; const material = new StandardMaterial("material-" + color.toHexString(), scene); material.diffuseColor = color; - material.ambientColor = color; - material.roughness = .1; - material.maxSimultaneousLights = 1; + + // material.ambientColor = color; + //material.roughness = .1; + //material.maxSimultaneousLights = 2; const colorBoxMesh = MeshBuilder.CreatePlane("toolbox-color-" + color.toHexString(), { width: width, diff --git a/src/util/customEnvironment.ts b/src/util/customEnvironment.ts index 2d5cb50..082d00b 100644 --- a/src/util/customEnvironment.ts +++ b/src/util/customEnvironment.ts @@ -1,13 +1,13 @@ import { Color3, GroundMesh, - HemisphericLight, Material, MeshBuilder, Observable, PBRMaterial, PhysicsAggregate, PhysicsShapeType, + PointLight, PointsCloudSystem, Scene, Sound, @@ -33,13 +33,14 @@ export class CustomEnvironment { if (loading) { loading.remove(); } - this.scene.ambientColor = new Color3(.2, .2, .2); - const light = new HemisphericLight("light1", new Vector3(1, 2, 1), this.scene); - light.groundColor = new Color3(.1, .1, .1) - light.diffuse = new Color3(1, 1, 1); - light.setDirectionToTarget(new Vector3(.4, .5, .5).normalize()); - light.intensity = .5; - + this.scene.ambientColor = new Color3(1, 1, 1); + //const light = new HemisphericLight("light1", new Vector3(1, 2, 1), this.scene); + //light.groundColor = new Color3(.1, .1, .1) + //light.diffuse = new Color3(1, 1, 1); + //light.setDirectionToTarget(new Vector3(.4, .5, .5).normalize()); + //light.intensity = .7; + const light = new PointLight("light1", new Vector3(0, 10, 10), this.scene); + const light2 = new PointLight("light1", new Vector3(0, 10, -10), this.scene); const physics = new CustomPhysics(this.scene, config); physics .initializeAsync() diff --git a/src/vrApp.ts b/src/vrApp.ts index 6b30bc5..20be695 100644 --- a/src/vrApp.ts +++ b/src/vrApp.ts @@ -60,6 +60,7 @@ export class VrApp { initEnvironment(diagramManager, spinner); const gamepadManager = new GamepadManager(scene); addSceneInspector(); + //const camMenu = new CameraMenu(scene); const el = document.querySelector('#download'); if (el) { el.addEventListener('click', () => {