mazegame
This commit is contained in:
Generated
+61
@@ -43,6 +43,7 @@
|
|||||||
"eslint-plugin-vue": "^9.27.0",
|
"eslint-plugin-vue": "^9.27.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"sass": "1.77.6",
|
"sass": "1.77.6",
|
||||||
|
"troika-three-text": "^0.52.4",
|
||||||
"unplugin-auto-import": "^0.17.6",
|
"unplugin-auto-import": "^0.17.6",
|
||||||
"unplugin-fonts": "^1.1.1",
|
"unplugin-fonts": "^1.1.1",
|
||||||
"unplugin-vue-components": "^0.27.2",
|
"unplugin-vue-components": "^0.27.2",
|
||||||
@@ -2451,6 +2452,16 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/bidi-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
@@ -6461,6 +6472,16 @@
|
|||||||
"url": "https://github.com/sponsors/mysticatea"
|
"url": "https://github.com/sponsors/mysticatea"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/requireindex": {
|
"node_modules/requireindex": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
|
||||||
@@ -7306,6 +7327,39 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/troika-three-text": {
|
||||||
|
"version": "0.52.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz",
|
||||||
|
"integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bidi-js": "^1.0.2",
|
||||||
|
"troika-three-utils": "^0.52.4",
|
||||||
|
"troika-worker-utils": "^0.52.0",
|
||||||
|
"webgl-sdf-generator": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"three": ">=0.125.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/troika-three-utils": {
|
||||||
|
"version": "0.52.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz",
|
||||||
|
"integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"three": ">=0.125.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/troika-worker-utils": {
|
||||||
|
"version": "0.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz",
|
||||||
|
"integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tsconfig-paths": {
|
"node_modules/tsconfig-paths": {
|
||||||
"version": "3.15.0",
|
"version": "3.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
|
||||||
@@ -8173,6 +8227,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webgl-sdf-generator": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
"eslint-plugin-vue": "^9.27.0",
|
"eslint-plugin-vue": "^9.27.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"sass": "1.77.6",
|
"sass": "1.77.6",
|
||||||
|
"troika-three-text": "^0.52.4",
|
||||||
"unplugin-auto-import": "^0.17.6",
|
"unplugin-auto-import": "^0.17.6",
|
||||||
"unplugin-fonts": "^1.1.1",
|
"unplugin-fonts": "^1.1.1",
|
||||||
"unplugin-vue-components": "^0.27.2",
|
"unplugin-vue-components": "^0.27.2",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.6 MiB |
Binary file not shown.
@@ -0,0 +1,389 @@
|
|||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.4.55",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"floor",
|
||||||
|
"translation":[
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
-0.6499999761581421
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mesh":1,
|
||||||
|
"name":"tunnel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mesh":2,
|
||||||
|
"name":"wall",
|
||||||
|
"rotation":[
|
||||||
|
0,
|
||||||
|
0.7071068286895752,
|
||||||
|
0,
|
||||||
|
0.7071068286895752
|
||||||
|
],
|
||||||
|
"translation":[
|
||||||
|
0.6499999761581421,
|
||||||
|
0,
|
||||||
|
-1.100000023841858
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mesh":3,
|
||||||
|
"name":"door",
|
||||||
|
"translation":[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
-0.44999998807907104
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"materials":[
|
||||||
|
{
|
||||||
|
"alphaMode":"BLEND",
|
||||||
|
"name":"Material",
|
||||||
|
"pbrMetallicRoughness":{
|
||||||
|
"baseColorTexture":{
|
||||||
|
"index":0
|
||||||
|
},
|
||||||
|
"metallicFactor":0,
|
||||||
|
"roughnessFactor":0.8999999761581421
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Cube.001",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Curve.003",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":4,
|
||||||
|
"NORMAL":5,
|
||||||
|
"TEXCOORD_0":6
|
||||||
|
},
|
||||||
|
"indices":7,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Curve.005",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":8,
|
||||||
|
"NORMAL":9,
|
||||||
|
"TEXCOORD_0":10
|
||||||
|
},
|
||||||
|
"indices":11,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Curve.008",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":12,
|
||||||
|
"NORMAL":13,
|
||||||
|
"TEXCOORD_0":14
|
||||||
|
},
|
||||||
|
"indices":15,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"textures":[
|
||||||
|
{
|
||||||
|
"sampler":0,
|
||||||
|
"source":0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images":[
|
||||||
|
{
|
||||||
|
"mimeType":"image/png",
|
||||||
|
"name":"b1",
|
||||||
|
"uri":"b1.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"max":[
|
||||||
|
0.6000000238418579,
|
||||||
|
0.009999999776482582,
|
||||||
|
0.6000000238418579
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-0.6000000238418579,
|
||||||
|
-0.030000001192092896,
|
||||||
|
-0.6000000238418579
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":1,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":36,
|
||||||
|
"type":"SCALAR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":4,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":12,
|
||||||
|
"max":[
|
||||||
|
0.5994362831115723,
|
||||||
|
0.6900395154953003,
|
||||||
|
0.40000441670417786
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-0.6005637049674988,
|
||||||
|
0.010013699531555176,
|
||||||
|
-0.3999955654144287
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":5,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":12,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":6,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":12,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":7,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":18,
|
||||||
|
"type":"SCALAR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":8,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":4,
|
||||||
|
"max":[
|
||||||
|
0.600348174571991,
|
||||||
|
0.7055734992027283,
|
||||||
|
-0.04999995976686478
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-0.5996518731117249,
|
||||||
|
-0.03282167762517929,
|
||||||
|
-0.04999999329447746
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":9,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":4,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":10,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":4,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":11,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":6,
|
||||||
|
"type":"SCALAR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":12,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":40,
|
||||||
|
"max":[
|
||||||
|
0.6068381667137146,
|
||||||
|
0.7768232822418213,
|
||||||
|
0.05000004917383194
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-0.6075304746627808,
|
||||||
|
-0.03299999609589577,
|
||||||
|
-0.050000086426734924
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":13,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":40,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":14,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":40,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":15,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":60,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":288,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":192,
|
||||||
|
"byteOffset":576,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":72,
|
||||||
|
"byteOffset":768,
|
||||||
|
"target":34963
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":144,
|
||||||
|
"byteOffset":840,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":144,
|
||||||
|
"byteOffset":984,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":96,
|
||||||
|
"byteOffset":1128,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":36,
|
||||||
|
"byteOffset":1224,
|
||||||
|
"target":34963
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":48,
|
||||||
|
"byteOffset":1260,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":48,
|
||||||
|
"byteOffset":1308,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":32,
|
||||||
|
"byteOffset":1356,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":12,
|
||||||
|
"byteOffset":1388,
|
||||||
|
"target":34963
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":480,
|
||||||
|
"byteOffset":1400,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":480,
|
||||||
|
"byteOffset":1880,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":320,
|
||||||
|
"byteOffset":2360,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":120,
|
||||||
|
"byteOffset":2680,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"samplers":[
|
||||||
|
{
|
||||||
|
"magFilter":9729,
|
||||||
|
"minFilter":9987
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":2800,
|
||||||
|
"uri":"maze-reed.bin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -60,6 +60,7 @@ import { Game4 } from '@/components/InteractiveObjects/PuzzleGame4';
|
|||||||
import { Grass } from '@/components/InteractiveObjects/Grass';
|
import { Grass } from '@/components/InteractiveObjects/Grass';
|
||||||
import { VideoPlayer } from '@/components/InteractiveObjects/VideoPlayer';
|
import { VideoPlayer } from '@/components/InteractiveObjects/VideoPlayer';
|
||||||
import { useAppStore } from '@/stores/app';
|
import { useAppStore } from '@/stores/app';
|
||||||
|
import { MazeQuizGame } from '../InteractiveObjects/MazeQuizGame/MazeQuizGame';
|
||||||
|
|
||||||
const store = useAppStore();
|
const store = useAppStore();
|
||||||
|
|
||||||
@@ -205,22 +206,33 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let testGame1 = new Game1(gameEngine, '/static/textures/game1-test.jpg', 2, 3);
|
let testGame1 = new Game1(gameEngine, '/static/textures/game1-test.jpg', 2, 3);
|
||||||
gameEngine.activeObjects.add(testGame1.game);
|
gameEngine.activeObjects.add(testGame1.object);
|
||||||
testGame1.game.position.set(0, 1, -15);
|
testGame1.object.position.set(0, 1, -15);
|
||||||
|
|
||||||
let testGame2 = new Game2(gameEngine, '/static/textures/game2-test.jpg', 3, 3);
|
let testGame2 = new Game2(gameEngine, '/static/textures/game2-test.jpg', 3, 3);
|
||||||
gameEngine.activeObjects.add(testGame2.game);
|
gameEngine.activeObjects.add(testGame2.object);
|
||||||
testGame2.game.position.set(0, 1, 15);
|
testGame2.object.position.set(0, 1, 15);
|
||||||
testGame2.game.rotation.y += Math.PI;
|
testGame2.object.rotation.y += Math.PI;
|
||||||
|
|
||||||
let testGame4 = new Game4(gameEngine, '/static/feathers-game.glb', 3, 4);
|
let testGame4 = new Game4(gameEngine, '/static/feathers-game.glb', 3, 4);
|
||||||
gameEngine.activeObjects.add(testGame4.game);
|
gameEngine.activeObjects.add(testGame4.object);
|
||||||
testGame4.game.position.set(15, 1, 5);
|
testGame4.object.position.set(15, 1, 5);
|
||||||
|
|
||||||
let vp = new VideoPlayer(gameEngine, this.$refs.videoPlayer, 16, 9);
|
let vp = new VideoPlayer(gameEngine, this.$refs.videoPlayer, 16, 9);
|
||||||
gameEngine.activeObjects.add(vp.videoPlayer);
|
gameEngine.activeObjects.add(vp.object);
|
||||||
vp.videoPlayer.position.set(37, 5.5, 15);
|
vp.object.position.set(37, 5.5, 15);
|
||||||
vp.videoPlayer.rotation.y += -Math.PI/2;
|
vp.object.rotation.y += -Math.PI/2;
|
||||||
|
|
||||||
|
let maze = new MazeQuizGame(gameEngine, {}, [
|
||||||
|
{s: '1 + 1 = 2', h: 'Wrong answer', a: true},
|
||||||
|
{s: '1 + 1 = 2', h: 'Wrong answer', a: true},
|
||||||
|
{s: '1 + 1 = 2', h: 'Wrong answer', a: true},
|
||||||
|
{s: '1 + 1 = 2', h: 'Wrong answer', a: true},
|
||||||
|
])
|
||||||
|
maze.load().then(o=>{
|
||||||
|
gameEngine.activeObjects.add(o);
|
||||||
|
o.scale.set(5,5,5);
|
||||||
|
})
|
||||||
|
|
||||||
new Grass(Grass.positions(1000,50,50), '/static/textures/grass01.png', 1, .5).then(mesh=>{
|
new Grass(Grass.positions(1000,50,50), '/static/textures/grass01.png', 1, .5).then(mesh=>{
|
||||||
console.log('adding grass')
|
console.log('adding grass')
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
import { ImageObject } from "./ImageObject";
|
||||||
|
import { Hint } from "./Hint";
|
||||||
|
import { Group, AnimationMixer, LoopPingPong, Vector3 } from "three";
|
||||||
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||||
|
import { Utils } from "./Utils";
|
||||||
|
import { Game1 } from "./PuzzleGame1";
|
||||||
|
import { Game2 } from "./PuzzleGame2";
|
||||||
|
// import { Game3 } from "./games/Game3";
|
||||||
|
import { Game4 } from "./PuzzleGame4";
|
||||||
|
// import { Game5 } from "./games/Game5";
|
||||||
|
// import { Game6 } from "./games/Game6";
|
||||||
|
import { TextObject } from "./TextObject";
|
||||||
|
|
||||||
|
const games = {Game1, Game2, Game4};
|
||||||
|
|
||||||
|
class InteractiveObject {
|
||||||
|
constructor(obj, context) {
|
||||||
|
this.name = obj.name;
|
||||||
|
this.ready = new Promise((resolve, reject) => {
|
||||||
|
let mesh;
|
||||||
|
switch (obj.type) {
|
||||||
|
case 'group':
|
||||||
|
mesh = new Group();
|
||||||
|
obj.group.forEach(g => {
|
||||||
|
let go = new InteractiveObject(g, context);
|
||||||
|
go.ready.then((gameMesh) => {
|
||||||
|
mesh.add(gameMesh);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
resolve(mesh);
|
||||||
|
break;
|
||||||
|
case 'text':
|
||||||
|
let text = new TextObject(obj, context);
|
||||||
|
resolve(text.mesh);
|
||||||
|
break;
|
||||||
|
case 'mesh':
|
||||||
|
mesh = obj.value;
|
||||||
|
resolve(mesh);
|
||||||
|
break;
|
||||||
|
case 'image':
|
||||||
|
let imo = new ImageObject(obj, context);
|
||||||
|
mesh = imo.mesh;
|
||||||
|
resolve(mesh);
|
||||||
|
break;
|
||||||
|
case 'hint':
|
||||||
|
let hint = new Hint(obj, context);
|
||||||
|
mesh = hint.mesh;
|
||||||
|
resolve(mesh);
|
||||||
|
break;
|
||||||
|
case 'gltf':
|
||||||
|
new GLTFLoader().load(obj.value, (gltf) => {
|
||||||
|
let gltfObj = gltf.scene;
|
||||||
|
gltf.scene.traverse(function (object) {
|
||||||
|
object.frustumCulled = false;
|
||||||
|
if (obj.name && obj.name == object.name) {
|
||||||
|
gltfObj = object;
|
||||||
|
}
|
||||||
|
// object.castShadow = true;
|
||||||
|
// object.receiveShadow = true;
|
||||||
|
});
|
||||||
|
Utils.assignMaterial(gltfObj, obj, context);
|
||||||
|
if (gltf.animations && gltf.animations.length) {
|
||||||
|
let mixer = new AnimationMixer(gltfObj);
|
||||||
|
context.mixers.push(mixer);
|
||||||
|
let action = mixer.clipAction(gltf.animations[0]);
|
||||||
|
action.setLoop(LoopPingPong);
|
||||||
|
action.play();
|
||||||
|
}
|
||||||
|
resolve(gltfObj);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'asset':
|
||||||
|
mesh = context.assets[obj.value].clone();
|
||||||
|
Utils.assignMaterial(mesh, obj, context);
|
||||||
|
resolve(mesh);
|
||||||
|
break;
|
||||||
|
case 'Game1':
|
||||||
|
case 'Game2':
|
||||||
|
case 'Game3':
|
||||||
|
case 'Game4':
|
||||||
|
case 'Game5':
|
||||||
|
case 'Game6':
|
||||||
|
var game = new games[obj.type](context, obj.args[0], obj.args[1], obj.args[2]);
|
||||||
|
mesh = game.game;
|
||||||
|
mesh.game = game;
|
||||||
|
resolve(mesh);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.ready.then((mesh) => {
|
||||||
|
mesh.go = {};
|
||||||
|
let restriction;
|
||||||
|
if (!context.disableRestrictions && obj.restriction) {
|
||||||
|
restriction = {
|
||||||
|
type: 'deny',
|
||||||
|
a: [obj.room.localToWorld(new Vector3().fromArray(obj.restriction[0])), obj.room.localToWorld(new Vector3().fromArray(obj.restriction[1]))]
|
||||||
|
};
|
||||||
|
context.areas.push(restriction);
|
||||||
|
}
|
||||||
|
mesh.go.finish = () => {
|
||||||
|
if (obj.finish) {
|
||||||
|
var f;
|
||||||
|
if (obj.finish.nextAction) {
|
||||||
|
var next = obj.finish.nextAction;
|
||||||
|
delete obj.finish.nextAction;
|
||||||
|
f = () => {
|
||||||
|
if (next.activate) {
|
||||||
|
context.activate(next.activate);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var me = obj.finish._ || {};
|
||||||
|
delete obj.finish._;
|
||||||
|
context.motionEngine.add({ o: mesh, a: obj.finish, t: me.t || 1, f: me.f || f, d: me.d || 0 });
|
||||||
|
}
|
||||||
|
if (restriction) context.areas.splice(context.areas.indexOf(restriction), 1);
|
||||||
|
};
|
||||||
|
if (mesh.game) mesh.game.onfinish = mesh.go.finish;
|
||||||
|
Utils.assignParams(mesh, obj);
|
||||||
|
obj.animation && context.motionEngine.add({
|
||||||
|
o: mesh,
|
||||||
|
a: obj.animation.motion,
|
||||||
|
r: obj.animation.repeat,
|
||||||
|
t: obj.animation.duration || 1
|
||||||
|
});
|
||||||
|
this.mesh = mesh;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// function textObject(text, context){
|
||||||
|
// const geometry = new TextGeometry( text, {
|
||||||
|
// font: context.font,
|
||||||
|
// size: .05,
|
||||||
|
// height: .01,
|
||||||
|
// curveSegments: 1
|
||||||
|
// } );
|
||||||
|
// return new Mesh(geometry, context.fontMaterial);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
export {InteractiveObject}
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
import { Scene, Clock, PointLight, Group, TextGeometry, MeshStandardMaterial, MeshBasicMaterial, PlaneGeometry, Mesh, TextureLoader, sRGBEncoding,
|
||||||
|
AnimationMixer, LoopPingPong, Vector3, DirectionalLight, Matrix4, LoopRepeat, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three';
|
||||||
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
||||||
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||||
|
import {FontLoader} from 'three/examples/jsm/loaders/FontLoader';
|
||||||
|
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils';
|
||||||
|
import { Clickable } from '../lib/Clickable';
|
||||||
|
import { Draggable } from '../lib/Draggable';
|
||||||
|
import { GameObject } from '../lib/GameObject';
|
||||||
|
import { MotionEngine } from '../lib/MotionEngine';
|
||||||
|
|
||||||
|
class Maze {
|
||||||
|
constructor(context) {
|
||||||
|
const scene = new Scene();
|
||||||
|
const motionEngine = new MotionEngine();
|
||||||
|
this.context = context;
|
||||||
|
context.motionEngine = motionEngine;
|
||||||
|
context.scene = scene;
|
||||||
|
const clickable = new Clickable(2);
|
||||||
|
context.clickable = clickable;
|
||||||
|
context.draggable = new Draggable(2);
|
||||||
|
|
||||||
|
if (context.dashboard) context.dashboard.onpoints = context.onpoints;
|
||||||
|
|
||||||
|
context.wallSize = context.wallSize || .65;
|
||||||
|
context.tubeSize = context.tubeSize || .8;
|
||||||
|
context.wallDepth = context.wallDepth || .1;
|
||||||
|
context.fontPath = context.fontPath || './assets/fonts/ZapfChanceryC.otf';
|
||||||
|
scene.background = new TextureLoader().load('./assets/textures/room/a3-2.jpg');
|
||||||
|
scene.background.encoding = sRGBEncoding;
|
||||||
|
scene.background.mapping = EquirectangularRefractionMapping;
|
||||||
|
|
||||||
|
const _tf = {
|
||||||
|
rotation: {
|
||||||
|
r: 3 * Math.PI / 2, f: 0, l: Math.PI / 2, b: Math.PI
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
r: [-context.wallSize, context.wallSize],
|
||||||
|
f: [0, 2 * context.wallSize],
|
||||||
|
l: [context.wallSize, context.wallSize]
|
||||||
|
},
|
||||||
|
pNext: {
|
||||||
|
r: [-context.wallSize - (context.wallSize + context.wallDepth) / 2, context.wallSize],
|
||||||
|
f: [0, 2 * context.wallSize + context.wallSize / 2],
|
||||||
|
l: [context.wallSize + (context.wallSize + context.wallDepth) / 2, context.wallSize]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const _l = new PointLight(0xffffff, 1, 10, 0.5);
|
||||||
|
let pLoad = [];
|
||||||
|
const mixers = [];
|
||||||
|
context.mixers = mixers;
|
||||||
|
context.assets = {};
|
||||||
|
const clock = new Clock();
|
||||||
|
context.activate = function (what) {
|
||||||
|
scene.traverse(o => {
|
||||||
|
if (o.name == what) {
|
||||||
|
o.visible = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
context.gameObject = function (name) {
|
||||||
|
let result;
|
||||||
|
scene.traverse(o => {
|
||||||
|
if (o.name == name) {
|
||||||
|
result = o;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const areas = [];
|
||||||
|
context.areas = areas;
|
||||||
|
let mazeGeometries = [];
|
||||||
|
const cameraNear = .2;
|
||||||
|
let heroDistance = .2;
|
||||||
|
let o = {};
|
||||||
|
var loader = new GLTFLoader().setPath(context.path);
|
||||||
|
loader.setDRACOLoader(new DRACOLoader().setDecoderPath('./lib/draco/'));
|
||||||
|
const fontLoader = new FontLoader().setPath(context.path);
|
||||||
|
pLoad.push(new Promise((resolve, reject) => {
|
||||||
|
loader.load('maze2.gltf', function (gltf) {
|
||||||
|
console.log(gltf);
|
||||||
|
gltf.scene.traverse(function (object) {
|
||||||
|
if (object.isMesh || object.isObject3D) {
|
||||||
|
//object.castShadow = true;
|
||||||
|
//object.receiveShadow = true;
|
||||||
|
}
|
||||||
|
if (object.name) {
|
||||||
|
context.assets[object.name] = object;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
['tunnel', 'wall', 'door', 'floor'].forEach(e => {
|
||||||
|
o[e] = gltf.scene.getObjectByName(e);
|
||||||
|
});
|
||||||
|
resolve(gltf);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
pLoad.push(new Promise((resolve, reject) => {
|
||||||
|
loader.setPath(context.path + 'human2/');
|
||||||
|
loader.load('human.gltf', function (gltf) {
|
||||||
|
console.log(gltf);
|
||||||
|
gltf.scene.traverse(function (object) {
|
||||||
|
if (object.isMesh || object.isObject3D) {
|
||||||
|
//object.castShadow = true;
|
||||||
|
//object.receiveShadow = true;
|
||||||
|
object.frustumCulled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
o.hero = gltf.scene;
|
||||||
|
o.hero.scale.set(.033, .033, .033);
|
||||||
|
o.hero.position.y = 0;
|
||||||
|
let mixer = new AnimationMixer(gltf.scene);
|
||||||
|
mixers.push(mixer);
|
||||||
|
o.hero.actionWalk = mixer.clipAction(gltf.animations.find(a => a.name == 'walk'));
|
||||||
|
o.hero.actionIdle = mixer.clipAction(gltf.animations.find(a => a.name == 'idle'));
|
||||||
|
o.hero.actionIdle.play();
|
||||||
|
resolve(gltf);
|
||||||
|
});
|
||||||
|
loader.setPath(context.path);
|
||||||
|
}));
|
||||||
|
|
||||||
|
pLoad.push(new Promise((resolve, reject) => {
|
||||||
|
fontLoader.load('font.json', function (font) {
|
||||||
|
context.fontMaterial = new MeshBasicMaterial({
|
||||||
|
color: 0x885e2c,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.73
|
||||||
|
});
|
||||||
|
context.font = font;
|
||||||
|
resolve(font);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
const staticThings = new Group();
|
||||||
|
const staticPointer = new Mesh(
|
||||||
|
new PlaneGeometry(.010, .010),
|
||||||
|
new MeshStandardMaterial({
|
||||||
|
map: new TextureLoader().load('./assets/maze/x.png'),
|
||||||
|
alphaTest: .5,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
staticPointer.frustumCulled = false;
|
||||||
|
staticPointer.material.map.encoding = sRGBEncoding;
|
||||||
|
staticThings.add(staticPointer);
|
||||||
|
|
||||||
|
scene.add(staticThings);
|
||||||
|
|
||||||
|
Promise.all(pLoad).then(function () {
|
||||||
|
loadScene();
|
||||||
|
context.onload && context.onload();
|
||||||
|
});
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
function loadScene() {
|
||||||
|
mazeObject(context.maze, scene);
|
||||||
|
scene.add(o.hero);
|
||||||
|
scene.add(new Mesh(BufferGeometryUtils.mergeBufferGeometries(mazeGeometries, false), o.tunnel.material));
|
||||||
|
scene.add(_l);
|
||||||
|
that.ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function between(a, b, x) {
|
||||||
|
return (a < b && a < x && x < b) || (b < a && b < x && x < a);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkArea(x, z) {
|
||||||
|
let allowed = areas.filter(e => e.type != 'deny' && between(e.a[0].x, e.a[1].x, x) && between(e.a[0].z, e.a[1].z, z));
|
||||||
|
let denied = areas.filter(e => e.type == 'deny' && between(e.a[0].x, e.a[1].x, x) && between(e.a[0].z, e.a[1].z, z));
|
||||||
|
return { area: allowed, block: allowed.length == 0 || denied.length > 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scene = scene;
|
||||||
|
const _vector = new Vector3();
|
||||||
|
let heroState = 0;
|
||||||
|
let lastCamera = new Vector3(0, .5, 0.5);
|
||||||
|
this.update = function (camera) {
|
||||||
|
let currentArea = checkArea(camera.position.x, camera.position.z);
|
||||||
|
if (currentArea.block) {
|
||||||
|
if (!checkArea(camera.position.x, lastCamera.z).block) {
|
||||||
|
camera.position.z = lastCamera.z;
|
||||||
|
} else if (!checkArea(lastCamera.x, camera.position.z).block) {
|
||||||
|
camera.position.x = lastCamera.x;
|
||||||
|
} else {
|
||||||
|
camera.position.copy(lastCamera);
|
||||||
|
}
|
||||||
|
currentArea = checkArea(camera.position.x, camera.position.z);
|
||||||
|
}
|
||||||
|
_l.position.copy(camera.position);
|
||||||
|
context.onupdate && context.onupdate();
|
||||||
|
motionEngine.update();
|
||||||
|
let delta = clock.getDelta();
|
||||||
|
mixers.forEach(m => m.update(delta));
|
||||||
|
if (o.hero) {
|
||||||
|
let dst = Math.abs(camera.position.distanceTo(lastCamera));
|
||||||
|
if (dst <= 0.005 && heroState == 1) {
|
||||||
|
o.hero.actionWalk.crossFadeTo(o.hero.actionIdle.play(), .5);
|
||||||
|
heroState = 0;
|
||||||
|
}
|
||||||
|
else if (dst <= 0.0005 && heroState == 0 && o.hero.actionWalk.isRunning()) {
|
||||||
|
o.hero.actionWalk.stop();
|
||||||
|
} else if (dst > 0.005 && heroState == 0) {
|
||||||
|
o.hero.actionIdle.crossFadeTo(o.hero.actionWalk.reset().play(), .5);
|
||||||
|
heroState = 1;
|
||||||
|
} else if (dst > 0.007 && heroState == 1 && o.hero.actionIdle.isRunning()) {
|
||||||
|
o.hero.actionIdle.stop();
|
||||||
|
}
|
||||||
|
_vector.setFromMatrixColumn(camera.matrix, 0);
|
||||||
|
_vector.crossVectors(camera.up, _vector);
|
||||||
|
|
||||||
|
o.hero.position.copy(camera.position);
|
||||||
|
o.hero.position.addScaledVector(_vector, heroDistance);
|
||||||
|
o.hero.position.y = 0;
|
||||||
|
o.hero.rotation.y = camera.rotation.y - Math.PI;
|
||||||
|
|
||||||
|
if (checkArea(o.hero.position.x, o.hero.position.z).block) {
|
||||||
|
o.hero.visible = false;
|
||||||
|
} else {
|
||||||
|
o.hero.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
staticThings.position.copy(camera.position);
|
||||||
|
camera.getWorldDirection(_vector);
|
||||||
|
staticThings.position.addScaledVector(_vector, .6);
|
||||||
|
staticThings.rotation.copy(camera.rotation);
|
||||||
|
}
|
||||||
|
lastCamera.copy(camera.position);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onclick = function (camera, mouse, event) {
|
||||||
|
mouse = mouse || new Vector3(0, 0, 0);
|
||||||
|
clickable.update(mouse, camera, event);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onpointer = function (camera, mouse, action) {
|
||||||
|
mouse = mouse || new Vector3(0, 0, 0);
|
||||||
|
context.draggable.update(mouse, camera, action);
|
||||||
|
};
|
||||||
|
|
||||||
|
var mazeObject = function (def, room, step = 0) {
|
||||||
|
let offsetZ = 0, e;
|
||||||
|
def.len = def.len || 0;
|
||||||
|
if (step == 0) {
|
||||||
|
e = o.door.geometry.clone();
|
||||||
|
e.rotateY(_tf.rotation.f);
|
||||||
|
mazeGeometries.push(e);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < def.len; i++) {
|
||||||
|
let t = o.tunnel.geometry.clone();
|
||||||
|
t.translate(0, 0, i * context.tubeSize + context.wallDepth / 2);
|
||||||
|
def.matrix && t.applyMatrix4(def.matrix);
|
||||||
|
|
||||||
|
mazeGeometries.push(t);
|
||||||
|
}
|
||||||
|
offsetZ = context.wallDepth + def.len * context.tubeSize - context.tubeSize / 2;
|
||||||
|
if (!def.len) offsetZ = -.275;
|
||||||
|
areas.push({
|
||||||
|
a: [
|
||||||
|
room.localToWorld(new Vector3(-context.tubeSize / 2 + cameraNear, 0, -context.tubeSize / 2 - cameraNear)),
|
||||||
|
room.localToWorld(new Vector3(context.tubeSize / 2 - cameraNear, 0, offsetZ + cameraNear))
|
||||||
|
]
|
||||||
|
});
|
||||||
|
if (def.type == 'area') {
|
||||||
|
def.area.forEach(ar => {
|
||||||
|
areas.push({
|
||||||
|
a: [
|
||||||
|
room.localToWorld(new Vector3(ar[0] + cameraNear, 0, offsetZ + ar[1] - cameraNear)),
|
||||||
|
room.localToWorld(new Vector3(ar[2] - cameraNear, 0, offsetZ + ar[3] + cameraNear))
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (def.noRoom) {
|
||||||
|
// e = o.wall.geometry.clone();
|
||||||
|
// e.rotateY(_tf.rotation.f);
|
||||||
|
// e.translate(0,0,offsetZ + 0);
|
||||||
|
// def.matrix && e.applyMatrix4(def.matrix);
|
||||||
|
// mazeGeometries.push(e);
|
||||||
|
} else {
|
||||||
|
e = [o.floor.geometry.clone(), o.door.geometry.clone(), o[def.r ? 'door' : 'wall'].geometry.clone(),
|
||||||
|
o[def.f ? 'door' : 'wall'].geometry.clone(), o[def.l ? 'door' : 'wall'].geometry.clone()];
|
||||||
|
e[0].translate(0, 0, offsetZ + context.wallSize);
|
||||||
|
|
||||||
|
e[1].rotateY(_tf.rotation.b);
|
||||||
|
e[2].rotateY(_tf.rotation.r);
|
||||||
|
e[3].rotateY(_tf.rotation.f);
|
||||||
|
e[4].rotateY(_tf.rotation.l);
|
||||||
|
|
||||||
|
e[1].translate(0, 0, offsetZ + 0);
|
||||||
|
e[2].translate(-context.wallSize, 0, offsetZ + context.wallSize);
|
||||||
|
e[3].translate(0, 0, offsetZ + context.wallSize * 2);
|
||||||
|
e[4].translate(context.wallSize, 0, offsetZ + context.wallSize);
|
||||||
|
|
||||||
|
e.forEach(g => {
|
||||||
|
def.matrix && g.applyMatrix4(def.matrix);
|
||||||
|
mazeGeometries.push(g);
|
||||||
|
});
|
||||||
|
areas.push({
|
||||||
|
a: [
|
||||||
|
room.localToWorld(new Vector3(-context.wallSize + cameraNear, 0, offsetZ + cameraNear)),
|
||||||
|
room.localToWorld(new Vector3(context.wallSize - cameraNear, 0, offsetZ + context.wallSize * 2 - cameraNear))
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def.objects && def.objects.forEach(obj => {
|
||||||
|
obj.room = room;
|
||||||
|
let go = new GameObject(obj, context);
|
||||||
|
go.ready.then(mesh => {
|
||||||
|
room.add(mesh);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
def.room = room;
|
||||||
|
['r', 'f', 'l'].forEach((d, i) => {
|
||||||
|
if (!def[d]) return;
|
||||||
|
let mtx = new Matrix4();
|
||||||
|
mtx.makeRotationY(_tf.rotation[d]);
|
||||||
|
mtx.setPosition(_tf.pNext[d][0], 0, _tf.pNext[d][1] + offsetZ);
|
||||||
|
let rr = new Group();
|
||||||
|
scene.add(rr);
|
||||||
|
def[d].matrix = mtx.premultiply(def.matrix || new Matrix4());
|
||||||
|
rr.applyMatrix4(def[d].matrix);
|
||||||
|
rr.updateMatrixWorld();
|
||||||
|
mazeObject(def[d], rr, step + 1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Maze };
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import { Group, Vector3, Matrix4, Mesh, DoubleSide } from 'three';
|
||||||
|
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
|
||||||
|
import { TextObject } from '../TextObject';
|
||||||
|
|
||||||
|
class MazeObject {
|
||||||
|
constructor(engine, def, params = {}){
|
||||||
|
let room = new Group();
|
||||||
|
let scene = room;
|
||||||
|
this.object = room;
|
||||||
|
let context = {};
|
||||||
|
context.wallSize = params.wallSize || .65;
|
||||||
|
context.tubeSize = params.tubeSize || .8;
|
||||||
|
context.wallDepth = params.wallDepth || .1;
|
||||||
|
context.fontPath = params.fontPath || '/static/fonts/ZapfChanceryC.otf';
|
||||||
|
|
||||||
|
const cameraNear = .2;
|
||||||
|
|
||||||
|
this.context = context;
|
||||||
|
let _tf = {
|
||||||
|
rotation: {
|
||||||
|
r: 3 * Math.PI / 2, f: 0, l: Math.PI / 2, b: Math.PI
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
r: [-context.wallSize, context.wallSize],
|
||||||
|
f: [0, 2 * context.wallSize],
|
||||||
|
l: [context.wallSize, context.wallSize]
|
||||||
|
},
|
||||||
|
pNext: {
|
||||||
|
r: [-context.wallSize - (context.wallSize + context.wallDepth) / 2, context.wallSize],
|
||||||
|
f: [0, 2 * context.wallSize + context.wallSize / 2],
|
||||||
|
l: [context.wallSize + (context.wallSize + context.wallDepth) / 2, context.wallSize]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let o = {};
|
||||||
|
let mazeGeometries = [], areas = [];
|
||||||
|
|
||||||
|
this.mazeObject = function(def, room, step = 0) {
|
||||||
|
let offsetZ = 0, e;
|
||||||
|
def.len = def.len || 0;
|
||||||
|
if (step == 0) {
|
||||||
|
e = o.door.geometry.clone();
|
||||||
|
e.rotateY(_tf.rotation.f);
|
||||||
|
mazeGeometries.push(e);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < def.len; i++) {
|
||||||
|
let t = o.tunnel.geometry.clone();
|
||||||
|
t.translate(0, 0, i * context.tubeSize + context.wallDepth / 2);
|
||||||
|
def.matrix && t.applyMatrix4(def.matrix);
|
||||||
|
|
||||||
|
mazeGeometries.push(t);
|
||||||
|
}
|
||||||
|
offsetZ = context.wallDepth + def.len * context.tubeSize - context.tubeSize / 2;
|
||||||
|
if (!def.len) offsetZ = -.275;
|
||||||
|
areas.push({
|
||||||
|
a: [
|
||||||
|
room.localToWorld(new Vector3(-context.tubeSize / 2 + cameraNear, 0, -context.tubeSize / 2 - cameraNear)),
|
||||||
|
room.localToWorld(new Vector3(context.tubeSize / 2 - cameraNear, 0, offsetZ + cameraNear))
|
||||||
|
]
|
||||||
|
});
|
||||||
|
if (def.type == 'area') {
|
||||||
|
def.area.forEach(ar => {
|
||||||
|
areas.push({
|
||||||
|
a: [
|
||||||
|
room.localToWorld(new Vector3(ar[0] + cameraNear, 0, offsetZ + ar[1] - cameraNear)),
|
||||||
|
room.localToWorld(new Vector3(ar[2] - cameraNear, 0, offsetZ + ar[3] + cameraNear))
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (def.noRoom) {
|
||||||
|
// e = o.wall.geometry.clone();
|
||||||
|
// e.rotateY(_tf.rotation.f);
|
||||||
|
// e.translate(0,0,offsetZ + 0);
|
||||||
|
// def.matrix && e.applyMatrix4(def.matrix);
|
||||||
|
// mazeGeometries.push(e);
|
||||||
|
} else {
|
||||||
|
e = [o.floor.geometry.clone(), o.door.geometry.clone(), o[def.r ? 'door' : 'wall'].geometry.clone(),
|
||||||
|
o[def.f ? 'door' : 'wall'].geometry.clone(), o[def.l ? 'door' : 'wall'].geometry.clone()];
|
||||||
|
e[0].translate(0, 0, offsetZ + context.wallSize);
|
||||||
|
|
||||||
|
e[1].rotateY(_tf.rotation.b);
|
||||||
|
e[2].rotateY(_tf.rotation.r);
|
||||||
|
e[3].rotateY(_tf.rotation.f);
|
||||||
|
e[4].rotateY(_tf.rotation.l);
|
||||||
|
|
||||||
|
e[1].translate(0, 0, offsetZ + 0);
|
||||||
|
e[2].translate(-context.wallSize, 0, offsetZ + context.wallSize);
|
||||||
|
e[3].translate(0, 0, offsetZ + context.wallSize * 2);
|
||||||
|
e[4].translate(context.wallSize, 0, offsetZ + context.wallSize);
|
||||||
|
|
||||||
|
e.forEach(g => {
|
||||||
|
def.matrix && g.applyMatrix4(def.matrix);
|
||||||
|
mazeGeometries.push(g);
|
||||||
|
});
|
||||||
|
areas.push({
|
||||||
|
a: [
|
||||||
|
room.localToWorld(new Vector3(-context.wallSize + cameraNear, 0, offsetZ + cameraNear)),
|
||||||
|
room.localToWorld(new Vector3(context.wallSize - cameraNear, 0, offsetZ + context.wallSize * 2 - cameraNear))
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def.objects && def.objects.forEach(obj => {
|
||||||
|
obj.room = room;
|
||||||
|
// let go = new GameObject(obj, context);
|
||||||
|
let go = new TextObject(obj, context)
|
||||||
|
room.add(go.mesh);
|
||||||
|
// go.ready.then(mesh => {
|
||||||
|
// room.add(mesh);
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
def.room = room;
|
||||||
|
['r', 'f', 'l'].forEach((d, i) => {
|
||||||
|
if (!def[d]) return;
|
||||||
|
let mtx = new Matrix4();
|
||||||
|
mtx.makeRotationY(_tf.rotation[d]);
|
||||||
|
mtx.setPosition(_tf.pNext[d][0], 0, _tf.pNext[d][1] + offsetZ);
|
||||||
|
let rr = new Group();
|
||||||
|
scene.add(rr);
|
||||||
|
def[d].matrix = mtx.premultiply(def.matrix || new Matrix4());
|
||||||
|
rr.applyMatrix4(def[d].matrix);
|
||||||
|
rr.updateMatrixWorld();
|
||||||
|
this.mazeObject(def[d], rr, step + 1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.load = async function(){
|
||||||
|
let mazeAsset = await engine.load('/static/meshes/maze-reed.gltf');
|
||||||
|
['tunnel', 'wall', 'door', 'floor'].forEach(e => {
|
||||||
|
o[e] = mazeAsset.scene.getObjectByName(e);
|
||||||
|
o[e].frustumCulled = false;
|
||||||
|
});
|
||||||
|
o.tunnel.material.depthWrite = false
|
||||||
|
this.mazeObject(def, room);
|
||||||
|
mazeGeometries.forEach(mg=>{
|
||||||
|
scene.add(new Mesh(mg, o.tunnel.material));
|
||||||
|
})
|
||||||
|
//scene.add(new Mesh(BufferGeometryUtils.mergeGeometries(mazeGeometries, false), o.tunnel.material));
|
||||||
|
console.log(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { MazeObject }
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { MazeObject } from "./MazeObject";
|
||||||
|
|
||||||
|
class MazeQuizGame {
|
||||||
|
constructor(engine, context, questions) {
|
||||||
|
let def = this.generate(questions);
|
||||||
|
console.log(def)
|
||||||
|
this.mazeObject = new MazeObject(engine, def)
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(){
|
||||||
|
await this.mazeObject.load();
|
||||||
|
this.object = this.mazeObject.object;
|
||||||
|
return this.object;
|
||||||
|
}
|
||||||
|
|
||||||
|
generate(questions, idx = 0){
|
||||||
|
let cq = questions[idx]
|
||||||
|
if (!cq) return {};
|
||||||
|
let len = Math.round(Math.random()*4) + 2;
|
||||||
|
let lr = Math.round(Math.random()*4) + 2;
|
||||||
|
let lrv = Math.random() > 0.5;
|
||||||
|
return {
|
||||||
|
len,
|
||||||
|
objects:[
|
||||||
|
{
|
||||||
|
type: 'text', text: cq.s, position:[0,.4,len], rotation:[0,Math.PI, 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[lrv?'r':'l']:{
|
||||||
|
len: 10 - lr,
|
||||||
|
[lrv?'l':'r']: {
|
||||||
|
len: lr,
|
||||||
|
objects:[
|
||||||
|
{
|
||||||
|
type: 'text', text: cq.h, position:[0,.4,lr], rotation:[0,Math.PI, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[lrv?'l':'r']:{
|
||||||
|
len: lr,
|
||||||
|
[lrv?'r':'l']: this.generate(questions, idx + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export {MazeQuizGame}
|
||||||
@@ -4,7 +4,7 @@ import { MotionEngine } from '../../lib/MotionEngine';
|
|||||||
|
|
||||||
class Game1 {
|
class Game1 {
|
||||||
constructor(context, image, w, h) {
|
constructor(context, image, w, h) {
|
||||||
this.game = new Group();
|
this.object = new Group();
|
||||||
const aq = new MotionEngine();
|
const aq = new MotionEngine();
|
||||||
const pr = [[0, -1], [0, 1], [1, 0], [-1, 0], [0, 0], [0, 2]];
|
const pr = [[0, -1], [0, 1], [1, 0], [-1, 0], [0, 0], [0, 2]];
|
||||||
let d = 1.2;
|
let d = 1.2;
|
||||||
@@ -40,20 +40,20 @@ class Game1 {
|
|||||||
let ri;
|
let ri;
|
||||||
do {
|
do {
|
||||||
ri = Math.floor(Math.random() * 6);
|
ri = Math.floor(Math.random() * 6);
|
||||||
} while (ri == this.game.children.length);
|
} while (ri == this.object.children.length);
|
||||||
mesh.rotation.set(pr[ri][0] * Math.PI / 2, pr[ri][1] * Math.PI / 2, 0);
|
mesh.rotation.set(pr[ri][0] * Math.PI / 2, pr[ri][1] * Math.PI / 2, 0);
|
||||||
mesh._ri = ri;
|
mesh._ri = ri;
|
||||||
this.game.add(mesh);
|
this.object.add(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.game.children[0].onBeforeRender = () => {
|
this.object.children[0].onBeforeRender = () => {
|
||||||
this.update();
|
this.update();
|
||||||
};
|
};
|
||||||
|
|
||||||
var check = () => {
|
var check = () => {
|
||||||
if (!this.game.children.length) return false;
|
if (!this.object.children.length) return false;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (let c of this.game.children) {
|
for (let c of this.object.children) {
|
||||||
if (Math.abs(c.rotation.x - pr[i][0] * Math.PI / 2) > 0.0001 || Math.abs(c.rotation.y - pr[i][1] * Math.PI / 2) > 0.0001) return false;
|
if (Math.abs(c.rotation.x - pr[i][0] * Math.PI / 2) > 0.0001 || Math.abs(c.rotation.y - pr[i][1] * Math.PI / 2) > 0.0001) return false;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ class Game1 {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.game.children.forEach(c => {
|
this.object.children.forEach(c => {
|
||||||
context.clickable.add(c, clickFn);
|
context.clickable.add(c, clickFn);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ class Game1 {
|
|||||||
aq.update();
|
aq.update();
|
||||||
if (aq.isIdle() && !this.done && check()) {
|
if (aq.isIdle() && !this.done && check()) {
|
||||||
this.done = true;
|
this.done = true;
|
||||||
this.game.children.forEach((c, i) => {
|
this.object.children.forEach((c, i) => {
|
||||||
aq.add({
|
aq.add({
|
||||||
o: c,
|
o: c,
|
||||||
a: { position: { x: i % w, y: i % h } },
|
a: { position: { x: i % w, y: i % h } },
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Game2 {
|
|||||||
});
|
});
|
||||||
let last, lidx = w - 1;
|
let last, lidx = w - 1;
|
||||||
|
|
||||||
this.game = new Group();
|
this.object = new Group();
|
||||||
let d = 1.2, p = [];
|
let d = 1.2, p = [];
|
||||||
|
|
||||||
function check() {
|
function check() {
|
||||||
@@ -60,7 +60,7 @@ class Game2 {
|
|||||||
// }
|
// }
|
||||||
p.forEach((e, i) => {
|
p.forEach((e, i) => {
|
||||||
let x = e % w, y = ~~(e / h);
|
let x = e % w, y = ~~(e / h);
|
||||||
this.game.children[i].position.set(x * d, y * d, 0);
|
this.object.children[i].position.set(x * d, y * d, 0);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -85,11 +85,11 @@ class Game2 {
|
|||||||
}
|
}
|
||||||
let mesh = new Mesh(bg, i != lidx ? material : m2);
|
let mesh = new Mesh(bg, i != lidx ? material : m2);
|
||||||
mesh.position.set(x * d, y * d, 0);
|
mesh.position.set(x * d, y * d, 0);
|
||||||
this.game.add(mesh);
|
this.object.add(mesh);
|
||||||
}
|
}
|
||||||
last = this.game.children[lidx];
|
last = this.object.children[lidx];
|
||||||
|
|
||||||
this.game.children[0].onBeforeRender = () => {
|
this.object.children[0].onBeforeRender = () => {
|
||||||
this.update();
|
this.update();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class Game2 {
|
|||||||
|
|
||||||
let clickFn = (i) => {
|
let clickFn = (i) => {
|
||||||
if (!this.done && !aq.isActive(i.object)) {
|
if (!this.done && !aq.isActive(i.object)) {
|
||||||
let idx = this.game.children.indexOf(i.object);
|
let idx = this.object.children.indexOf(i.object);
|
||||||
if (idx == lidx) return; //we ignore the empty cell
|
if (idx == lidx) return; //we ignore the empty cell
|
||||||
let xc = p[idx] % w, yc = ~~(p[idx] / h);
|
let xc = p[idx] % w, yc = ~~(p[idx] / h);
|
||||||
let xl = p[lidx] % w, yl = ~~(p[lidx] / h);
|
let xl = p[lidx] % w, yl = ~~(p[lidx] / h);
|
||||||
@@ -119,7 +119,7 @@ class Game2 {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.game.children.forEach(c => {
|
this.object.children.forEach(c => {
|
||||||
context.clickable.add(c, clickFn);
|
context.clickable.add(c, clickFn);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ class Game2 {
|
|||||||
aq.update();
|
aq.update();
|
||||||
if (aq.isIdle() && !this.done && check()) {
|
if (aq.isIdle() && !this.done && check()) {
|
||||||
this.done = true;
|
this.done = true;
|
||||||
this.game.children.forEach((c, i) => {
|
this.object.children.forEach((c, i) => {
|
||||||
last.material = material;
|
last.material = material;
|
||||||
aq.add({
|
aq.add({
|
||||||
o: c,
|
o: c,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|||||||
import { MotionEngine } from '../../lib/MotionEngine';
|
import { MotionEngine } from '../../lib/MotionEngine';
|
||||||
|
|
||||||
var Game4 = function(context, gltf, w, h){
|
var Game4 = function(context, gltf, w, h){
|
||||||
this.game = new Group();
|
this.object = new Group();
|
||||||
const aq = new MotionEngine();
|
const aq = new MotionEngine();
|
||||||
const pr = [];
|
const pr = [];
|
||||||
let d = .51, c = w * h / 2, tc = w * h, m0=1, r0=.2;
|
let d = .51, c = w * h / 2, tc = w * h, m0=1, r0=.2;
|
||||||
@@ -41,17 +41,17 @@ var Game4 = function(context, gltf, w, h){
|
|||||||
pr.forEach((c, i)=>{
|
pr.forEach((c, i)=>{
|
||||||
c.position.set((i % w)*d , (~~(i / w))*d);
|
c.position.set((i % w)*d , (~~(i / w))*d);
|
||||||
c.rotation.set(0, Math.PI, 0);
|
c.rotation.set(0, Math.PI, 0);
|
||||||
this.game.add(c);
|
this.object.add(c);
|
||||||
context.clickable.add(c, clickFn);
|
context.clickable.add(c, clickFn);
|
||||||
})
|
})
|
||||||
|
|
||||||
this.game.children[0].onBeforeRender = ()=>{
|
this.object.children[0].onBeforeRender = ()=>{
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var check = ()=>{
|
var check = ()=>{
|
||||||
if (!this.game.children.length) return false;
|
if (!this.object.children.length) return false;
|
||||||
return pr.filter(c=>c.$active === false).length == tc;
|
return pr.filter(c=>c.$active === false).length == tc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ var Game4 = function(context, gltf, w, h){
|
|||||||
aq.update();
|
aq.update();
|
||||||
if (!this.done && check()){
|
if (!this.done && check()){
|
||||||
this.done = true;
|
this.done = true;
|
||||||
this.game.children.forEach((c, i)=>{
|
this.object.children.forEach((c, i)=>{
|
||||||
aq.add({
|
aq.add({
|
||||||
o: c,
|
o: c,
|
||||||
a: {material:{opacity:1}},
|
a: {material:{opacity:1}},
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { MeshStandardMaterial, Color, Vector3 } from "three";
|
||||||
|
import { Text } from "troika-three-text";
|
||||||
|
import Utils from "@/lib/utils";
|
||||||
|
|
||||||
|
class TextObject {
|
||||||
|
constructor(obj, params) {
|
||||||
|
const txt = new Text();
|
||||||
|
// Set properties to configure:
|
||||||
|
txt.text = obj.text;
|
||||||
|
txt.fontSize = 0.022;
|
||||||
|
txt.lineHeight = 1.1;
|
||||||
|
txt.maxWidth = obj.width || params.wallSize * .73;
|
||||||
|
txt.textAlign = 'center';
|
||||||
|
txt.font = params.fontPath;
|
||||||
|
txt.anchorX = 'center';
|
||||||
|
txt.anchorY = 'bottom';
|
||||||
|
txt.curveRadius = 0;
|
||||||
|
txt.outlineColor = 0xffffff;
|
||||||
|
txt.outlineWidth = '15%';
|
||||||
|
txt.outlineBlur = '50%';
|
||||||
|
Utils.assignMeshParams(txt, obj)
|
||||||
|
let m = new MeshStandardMaterial({
|
||||||
|
roughness: .73,
|
||||||
|
metalness: .37,
|
||||||
|
});
|
||||||
|
txt.material = m;
|
||||||
|
txt.color = new Color(0x0);
|
||||||
|
txt.sync();
|
||||||
|
this.txt = txt;
|
||||||
|
this.mesh = txt;
|
||||||
|
if (obj.effect == 'distance') {
|
||||||
|
let dstm = .8;
|
||||||
|
var oldBR = txt.onBeforeRender;
|
||||||
|
txt.material[1].opacity = 0.01;
|
||||||
|
txt.onBeforeRender = function (renderer, scene, camera) {
|
||||||
|
oldBR && oldBR.apply(this, arguments);
|
||||||
|
var v = new Vector3();
|
||||||
|
txt.getWorldPosition(v);
|
||||||
|
var dst = camera.position.distanceTo(v);
|
||||||
|
if (dst < dstm * 2 && dst > dstm * 1) {
|
||||||
|
txt.material[1].opacity = dstm * 1 - (dst - dstm * 1);
|
||||||
|
}
|
||||||
|
if (dst < .5 * dstm) {
|
||||||
|
txt.material[1].opacity = dstm * dst * 2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {TextObject}
|
||||||
@@ -13,7 +13,7 @@ class VideoPlayer {
|
|||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
} );
|
} );
|
||||||
let plane = new THREE.Mesh( geometry, material );
|
let plane = new THREE.Mesh( geometry, material );
|
||||||
this.videoPlayer = plane;
|
this.object = plane;
|
||||||
|
|
||||||
context.clickable.add(plane, ()=>{
|
context.clickable.add(plane, ()=>{
|
||||||
material.opacity = 0.9
|
material.opacity = 0.9
|
||||||
|
|||||||
+8
-1
@@ -53,5 +53,12 @@ export default {
|
|||||||
|
|
||||||
rad2deg(rad){
|
rad2deg(rad){
|
||||||
return rad * 180 / Math.PI;
|
return rad * 180 / Math.PI;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
assignMeshParams(mesh, params){
|
||||||
|
['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p]));
|
||||||
|
['visible', 'name'].forEach(p=>{
|
||||||
|
if (params[p]!==undefined) mesh[p] = params[p];
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user