import { InteractiveObject } from '@/components/InteractiveObjects/InteractiveObject'; import { VideoPlayer } from '@/components/InteractiveObjects/VideoPlayer'; import { GameEngine } from '@/lib/GameEngine'; import { Hero } from '@/lib/Hero'; import { autoScale, getBoundingBox, getBoundingBoxSize } from '@/lib/MeshUtils'; let engine = null; export default { async mounted(){ this.engine = engine = new GameEngine(); await engine.init(this.$refs.target, { xr: true, gizmo: this.env == 'GameDesigner', stats: this.env != 'GamePlay', designMode: this.env == 'GameDesigner', depthSense: this.env == 'GameDesigner' ? false : this.store.prefs.xr.depthSense, telemetry: this.$api.user.tm, mode: this.env }); //engine.scene.add(new engine.$.GridHelper(100,100)); if (this.env == 'GameDesigner'){ engine.scene.add(engine.transformControls.getHelper()); }else{ engine.dashboard.enable(); } this.resize(); //engine.setCamera(engine.orthographicCamera) //engine.setCameraOrthographic(); if (!this.scenario) { await this.loadScenario(); } window.addEventListener('resize', this.resize); }, unmounted(){ window.removeEventListener('resize', this.resize); engine.clearScene(); engine.stop(); engine.tm?.setGame(null); }, computed:{ scenes(){ return this.scenario?.scenes || []; }, scene(){ return this.scenesList[0]; }, currentObject(){ return this.objectsList[0]; }, sceneObjects(){ return this.object.scenes?.[this.scene?.data?.id]?.objects; }, object(){ return this.modelValue; }, objectAnimations(){ return this.currentObject?.__g?.animations?.map(a => ({ name: a.name, id: a.uuid, a })); } }, watch:{ async scene(n){ this.object.scenes = this.object.scenes || {} this.object.scenes[n.data.id] = this.object.scenes[n.data.id] || {} await this.loadEnvironment(n, this.object.scenes[n.data.id]); }, mode(n){ engine.transformControls.setMode(n) }, async 'object.scenario'(n){ await this.loadScenario() }, currentObject(n){ if (this.env == 'GameDesigner'){ engine.transformControls.attach(n.__o); engine.gizmo.target = n.__o.position; engine.camera.updateProjectionMatrix() } }, renderType(v){ engine.renderType = v; }, cameraType(v){ if (v == 'perspective'){ engine.setCameraPerspective(); }else{ engine.setCameraOrthographic(); } } }, methods:{ async loadScenario(){ if (this.object.scenario){ this.scenario = (await this.$api.scenario.load(this.object.scenario)).data; }else{ this.scenario = null; } }, async expandScenarioData(scene){ const promises = []; ['environment', 'scene', 'intro', 'audio'].filter(e=>scene.data[e]).forEach(e=>{ promises.push(this.$api.gameObject.load(scene.data[e]).then(r=>scene.data['$'+e] = r.data)) }) for (let i of scene.data.items || []) { Object.keys(i.data).filter(k=>k == 'go' || k.startsWith('go_')).forEach(k=>{ promises.push(this.$api.gameObject.load(i.data[k]).then(r=>i.data['$'+k] = r.data)); }) } await Promise.all(promises); }, /** * loads all environment objects * @param scene Scene object from the Scenario Module * @param target Target scene definition from Game Module */ async loadEnvironment(scene, target){ engine.tm?.setGame(this.object?.id); //await engine.loadPanorama(`/asset/default/43.webp`); let intro; engine.clearScene(); engine.activeObjects.visible = false; await engine.dashboard.ready; engine.dashboard?.initScene(scene, async ()=>{ if (this.scene.data.$audio){ await engine.playAmbientSound(this.scene.data.$audio.asset.name); engine.ambientSound.setVolume( 0.5 ); } engine.tm?.setScene(scene.data.id); if (intro){ intro.play(); }else{ engine.activeObjects.visible = true; } }); await this.expandScenarioData(scene); engine.dashboard?.loading(0.05); engine.orbitControls.enableRotate = this.env == 'GameDesigner' //this is needed cause when mounted canvas has different size this.resize(); target.objects = target.objects || {}; let l = target.objects; if (this.scene.data.$environment){ await engine.loadPanorama(this.scene.data.$environment.asset.name); } if (this.scene.data.$scene){ let env = await engine.load(this.scene.data.$scene.asset.name); this.setObjectAttributes(l, this.scene.data, env.scene, env, null); if (this.env != 'GameDesigner'){ env.scene.traverse(o=>{ if (o.name.startsWith('land') || o.name == 'Sphere'){ console.log('Fixing ground. TODO!!!') engine.physics.add(o, 'fixed', true, undefined, 'mesh', { root: env.scene}) } }) } engine.activeObjects.add(env.scene); } let expectToFinish = 0, finished = 0;; if (this.scene.data.items){ let loaded = 0; for (let i of this.scene.data.items) { if (this.env != 'GameDesigner'){ if (i.data.activationTriggers?.length || i.data.activationScore){ i.data.shouldBeLocked = true; } } let io = await new InteractiveObject(engine, i.data) i.__io = io; if (io.emits?.includes('finish')){ expectToFinish++; } this.setObjectAttributes(l, i.data, io.object, io.source, 1); engine.activeObjects.add(io.object); if (this.env != 'GameDesigner'){ if (i.data.$go?.type == 'player3d'){ let hero = new Hero(engine, io); }else{ if (io.source?.animations?.length){ engine.playAnimation(engine.scene, io.source.animations[0]); } if (!i.data.noPhysics){ let bb = getBoundingBox(io.object); let bbs = getBoundingBoxSize(bb); engine.physics.add(io.object, 'fixed', false, undefined, 'capsule', { radius: Math.max(bbs.x, bbs.z)/2, halfHeight: bbs.y/2 }) } } io.addEventListener('finish', ()=>{ finished ++; if (!i.data.pointsGiven){ engine.dashboard?.addPoints(i.data.points) } i.data.pointsGiven = true; this.scene.data.items.forEach(di=>{ if (di.data.activationTriggers?.includes(i.data.id)){ let idx = di.data.activationTriggers.indexOf(i.data.id) di.data.activationTriggers.splice(idx, 1); } if (!di.data.activationTriggers?.length && di.__io.object.__active === false && engine.dashboard.points > di.data.activationScore){ di.__io.activator.activate(); } }); if (finished == expectToFinish){ //GO TO NEXT LEVEL console.log('LEVEL FINISHED') } engine.tm?.setGameObject(null); }); io.addEventListener('sceneSwitch', (e)=>{ this.scenesList = [this.scenes.find(s=>s.data.id == e.scene)] }) } loaded += 1/this.scene.data.items.length engine.dashboard?.loading(0.1 + 0.89*loaded); } } if (this.env == 'GameDesigner'){ engine.activeObjects.visible = true; }else if (this.scene.data.$intro){ intro = await new VideoPlayer(engine, { $go: this.scene.data.$intro, skipTransition: true, playInHud: true, hideBackground: true }); engine.activeObjects.add(intro.object); intro.video.addEventListener('pause',()=>{ intro.object.removeFromParent(); engine.clickable.remove(intro.object); //TODO!!!! engine.activeObjects.visible = true; }); } engine.dashboard?.loading(1) engine.physics.start(); }, targetPointerDown(){ this.pointerDownTime = performance.now(); }, targetClick(e){ if (this.env == 'GameDesigner'){ if (performance.now() - this.pointerDownTime < 200){ let intersects = engine.intersect(e, this.$refs.target, engine.activeObjects.children, true); //console.log(intersects) if (intersects.length){ //console.log('attaching controls to', intersects[0].object) //engine.transformControls.attach(intersects[0].object); //console.log(this.sceneObjects[intersects[0].object.__pn_id]) this.objectsList[0] = this.sceneObjects[intersects[0].object.__pn_id] }else{ engine.transformControls.detach(); } } }else{ engine.onClick(e, this.$refs.target); } }, targetPointer(e, t){ engine.onPointer(e, this.$refs.target, t); }, setObjectAttributes(l, data, object, source, autoScaleFactor = 1){ if (l[data.id]){ ['position', 'scale', 'rotation'].forEach(p=>{ object[p].copy(l[data.id][p]) }) }else if (!data.type || data.type == 'GenericObject'){ autoScale(object, autoScaleFactor); } l[data.id] = l[data.id] || {}; ['position', 'scale', 'rotation', 'visible'].forEach(p=>{ l[data.id][p] = object[p]; }) l[data.id].__o = object; l[data.id].__g = source; l[data.id].__title = data.title; object.__pn_id = data.id; }, async toggleAnimation(animation){ animation.playing = !animation.playing; engine.playAnimation(engine.scene, animation.a, animation.playing); }, resize(){ let r = this.$refs.target; console.log('resizing!!', r.clientWidth, r.clientHeight, r) engine.resize(r.clientWidth, r.clientHeight); //this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h); }, control(){ engine.hero.lockControls(); }, async fullScreen(){ await engine.renderer.domElement.requestFullscreen() } } }