Files
pronature-platform/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js
T
2025-11-12 07:27:42 +02:00

158 lines
6.2 KiB
JavaScript

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 }