import { Group, Vector3, Matrix4, Mesh, Quaternion, PlaneGeometry, MeshStandardMaterial, DoubleSide} from 'three'; import { InteractiveObject } from '../InteractiveObject'; class MazeObject { constructor(engine, def, params){ let room = new Group(); let root = room; this.object = room; const bbox = {l:0, r:0, f:0} const { wallSize, tubeSize, scale } = params let _tf = { rotation: { r: 3 * Math.PI / 2, f: 0, l: Math.PI / 2, b: 0 }, pNext: { r: [-wallSize/2, wallSize/2], f: [0, wallSize], l: [wallSize/2, wallSize/2] } }; let o = {}; function addPhysics(matrix, position, size, placement = 'side', isSensor = false, userData){ let quat = new Quaternion().setFromRotationMatrix(matrix); let v = new Vector3(...position).applyMatrix4(matrix); if (typeof size == 'number'){ size = { width: 0.1, height:1, depth:size/2 } } let po = engine.physics.add( {position: v}, 'fixed', false, undefined, 'cuboid', { ...size, isSensor, userData } ) if (placement == 'front') { quat.multiply(new Quaternion(0, 0.7071068, 0, 0.7071068)) //rotate by 90deg } po.rigidBody.setRotation(quat, true) return po; } function addRoom(elements, def, offsetZ){ let e = elements.map(e=>o[e].clone()) e[0].position.set(0, 0, offsetZ + wallSize/2); 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].position.set(0, 0, offsetZ + 0); e[2].position.set(-wallSize/2, 0, offsetZ + wallSize/2); e[3].position.set(0, 0, offsetZ + wallSize); e[4].position.set(wallSize/2, 0, offsetZ + wallSize/2); if (elements[1] == 'wall'){ addPhysics(def.matrix, [0, 0, offsetZ], wallSize, 'front') } if (elements[2] == 'wall'){ addPhysics(def.matrix, [-wallSize/2, 0, offsetZ + wallSize/2], wallSize) } if (elements[3] == 'wall'){ addPhysics(def.matrix, [0, 0, offsetZ + wallSize], wallSize, 'front') } if (elements[4] == 'wall'){ addPhysics(def.matrix, [wallSize/2, 0, offsetZ + wallSize/2], wallSize) } e.forEach(g => { g.applyMatrix4(def.matrix); root.add(g); bbox.l = Math.min(g.position.x, bbox.l) bbox.r = Math.max(g.position.x, bbox.r) bbox.f = Math.max(g.position.z, bbox.f) }); } this.mazeObject = function(def, room, step = 0) { if (!def.matrix){ def.matrix = new Matrix4(); } let offsetZ = 0, e; def.len = def.len || 0; if (step == 0) { addRoom(['floor', 'wall', 'wall', 'door', 'wall'], def, -wallSize) } if (def.userData?.answer !== undefined){ addPhysics(def.matrix, [0,0,0], wallSize, 'front', true, def.userData) } for (let i = 0; i < def.len; i++) { let t = o.tunnel.clone(); t.position.set(0, 0, i * tubeSize); def.matrix && t.applyMatrix4(def.matrix); root.add(t); } offsetZ = def.len * tubeSize; addPhysics(def.matrix, [tubeSize / 2, 0.6, offsetZ/2], offsetZ) addPhysics(def.matrix, [-tubeSize / 2, 0.6, offsetZ/2], offsetZ) addRoom(['floor', 'door', def.r ? 'door' : 'wall', def.f ? 'door' : 'wall', def.l ? 'door' : 'wall'], def, offsetZ) if (def.userData?.qid !== undefined || def.userData?.finish){ addPhysics(def.matrix, [0,0,offsetZ + wallSize/2], { width: wallSize/2, height: wallSize/2, depth: wallSize/2}, 'side', true, def.userData) } if (def.userData?.corner){ addPhysics(def.matrix, [0,0,-wallSize/2], { width: wallSize/2, height: wallSize/2, depth: wallSize/2}, 'side', true, {corner: def.userData.corner}) } //console.log('loadingggg', def.objects) def.objects?.forEach(async obj => { obj.room = room; let go = await new InteractiveObject(engine, obj) go.object.scale.multiplyScalar(wallSize) go.object.position.multiplyScalar(wallSize) go.object.applyMatrix4(def.matrix); root.add(go.object); }); 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(); root.add(rr); def[d].matrix = mtx.premultiply(def.matrix); 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/quiz.gltf', ''); ['tunnel', 'wall', 'door', 'floor'].forEach(e => { o[e] = mazeAsset.scene.getObjectByName(e); //o[e].frustumCulled = false; o[e].scale.set(scale, scale, scale) }); this.mazeObject(def, room); const floorGeometry = new PlaneGeometry(bbox.r - bbox.l + 10*scale, bbox.f + 10*scale); const floor = new Mesh(floorGeometry,new MeshStandardMaterial({ roughness: 0, metalness:1, color: 0x00ffff, side: DoubleSide })) floor.rotation.set(Math.PI/2, 0, 0) floor.position.set((bbox.l + bbox.r)/2, 0, bbox.f/2); root.add(floor); } } } export { MazeObject }