diff --git a/public/static/feathers-game.glb b/public/static/feathers-game.glb new file mode 100644 index 0000000..37d88ee Binary files /dev/null and b/public/static/feathers-game.glb differ diff --git a/public/static/textures/game1-test.jpg b/public/static/textures/game1-test.jpg new file mode 100644 index 0000000..79f9268 Binary files /dev/null and b/public/static/textures/game1-test.jpg differ diff --git a/public/static/textures/game2-test.jpg b/public/static/textures/game2-test.jpg new file mode 100644 index 0000000..e8dbed6 Binary files /dev/null and b/public/static/textures/game2-test.jpg differ diff --git a/src/components/GamePlaying/GamePlaying.vue b/src/components/GamePlaying/GamePlaying.vue index 71f3583..3fd033c 100644 --- a/src/components/GamePlaying/GamePlaying.vue +++ b/src/components/GamePlaying/GamePlaying.vue @@ -53,6 +53,9 @@ import { GameEngine } from '@/lib/GameEngine'; import { Hero } from '@/lib/Hero'; +import { Game1 } from '@/lib/mini-games/Game1'; +import { Game2 } from '@/lib/mini-games/Game2'; +import { Game4 } from '@/lib/mini-games/Game4'; import { useAppStore } from '@/stores/app'; const store = useAppStore(); @@ -181,6 +184,18 @@ export default { // gameEngine.camera.rotation.copy(l.camera.position) // gameEngine.camera.scale.copy(l.camera.position) } + + // let testGame1 = new Game1(gameEngine, '/static/textures/game1-test.jpg', 2, 3); + // gameEngine.activeObjects.add(testGame1.game); + // testGame1.game.position.set(0, 1, -5); + + // let testGame2 = new Game2(gameEngine, '/static/textures/game2-test.jpg', 3, 3); + // gameEngine.activeObjects.add(testGame2.game); + // testGame2.game.position.set(0, 1, 5); + + // let testGame4 = new Game4(gameEngine, '/static/feathers-game.glb', 3, 4); + // gameEngine.activeObjects.add(testGame4.game); + // testGame4.game.position.set(0, 1, 5); }, async expandScenarioData(scene){ scene.data.$environment = (await this.$api.gameObject.load(scene.data.environment)).data @@ -192,15 +207,16 @@ export default { this.pointerDownTime = performance.now(); }, targetClick(e){ - if (performance.now() - this.pointerDownTime < 200){ - let intersects = gameEngine.intersect(e, this.$refs.target, gameEngine.activeObjects.children, true); - //console.log(intersects) - if (intersects.length){ + // if (performance.now() - this.pointerDownTime < 200){ + // let intersects = gameEngine.intersect(e, this.$refs.target, gameEngine.activeObjects.children, true); + // //console.log(intersects) + // if (intersects.length){ - }else{ + // }else{ - } - } + // } + // } + gameEngine.onClick(e, this.$refs.target); }, setObjectAttributes(l, data, o, autoScaleFactor = 1){ if (l[data.id]){ diff --git a/src/lib/Clickable.js b/src/lib/Clickable.js new file mode 100644 index 0000000..61db150 --- /dev/null +++ b/src/lib/Clickable.js @@ -0,0 +1,45 @@ +import { Raycaster, Vector3 } from "three" + +class Clickable { + constructor(defaultDistance) { + const objects = []; + const raycaster = new Raycaster(); + let v = new Vector3; + + this.add = function (object, fn, distance) { + objects.push(object); + object._clickable = { fn, distance: distance || defaultDistance }; + }; + + this.remove = function (object) { + objects.splice(objects.indexOf(object), 1); + delete object._clickable; + }; + + this.update = function (mouse, camera, event) { + raycaster.setFromCamera(mouse, camera); + let forExecute = []; + objects.forEach(o => { + console.log(1) + o.getWorldPosition(v); + if (camera.position.distanceTo(v) <= o._clickable.distance && o.visible) { + console.log(2) + const intersects = raycaster.intersectObject(o); + // intersects.forEach(i=>{ + // forExecute.push({o, i}) + // }) + if (intersects[0]) forExecute.push({ o, i: intersects[0] }); + } + }); + if (forExecute[0]) { + let sorted = forExecute.sort((a, b) => a.i.distance - b.i.distance); + sorted[0].o._clickable.fn(sorted[0].i); + if (this.onclick) { + this.onclick(sorted[0].o, sorted[0].i, event); + } + } + }; + } +} + +export {Clickable} \ No newline at end of file diff --git a/src/lib/Hero.js b/src/lib/Hero.js index 14e5e86..d15c013 100644 --- a/src/lib/Hero.js +++ b/src/lib/Hero.js @@ -96,7 +96,8 @@ class Hero{ } update(){ - return; + return + if (this.gameEngine.renderer.xr.isPresenting) return; let { inputVelocity, velocity, euler, quat, v, targetQuaternion, clock } = this; let pc = this.pointerControls; pc.update(); diff --git a/src/lib/gameEngine.js b/src/lib/gameEngine.js index e06270e..957cb3c 100644 --- a/src/lib/gameEngine.js +++ b/src/lib/gameEngine.js @@ -14,6 +14,7 @@ import { ARButton } from 'three/addons/webxr/ARButton.js'; import { XRButton } from 'three/addons/webxr/XRButton.js'; import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; import * as CANNON from 'cannon-es'; +import { Clickable } from './Clickable.js'; class GameEngine { async init(domNode, opts = {}) { @@ -68,8 +69,8 @@ class GameEngine { const renderer = new THREE.WebGLRenderer({ antialias: true, // alpha: true, - preserveDrawingBuffer: true //this is important for screenshots capturing - //powerPreference: "high-performance", + preserveDrawingBuffer: true, //this is important for screenshots capturing + powerPreference: "high-performance", }); renderer.setPixelRatio(window.devicePixelRatio); // renderer.toneMapping = THREE.CineonToneMapping; @@ -173,6 +174,8 @@ class GameEngine { } : {})); this.initXrControllers(); } + + this.clickable = new Clickable(20); } initXrControllers() { @@ -466,6 +469,12 @@ class GameEngine { return intersects; } + onClick(mouseEvent, domElement){ + let mouse = this.getMouseVector(mouseEvent, domElement); + this.raycaster.setFromCamera(mouse, this.camera); + this.clickable.update(mouse, this.camera, mouseEvent); + } + autoScale(object, mk = 1) { let bb = new THREE.Box3().setFromObject(object); let k = Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z); diff --git a/src/lib/mini-games/Game1.js b/src/lib/mini-games/Game1.js index bf13551..fa69f04 100644 --- a/src/lib/mini-games/Game1.js +++ b/src/lib/mini-games/Game1.js @@ -1,4 +1,4 @@ -import { BoxGeometry, Mesh, MeshBasicMaterial, Group, sRGBEncoding } from 'three'; +import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three'; import { TextureLoader } from 'three/src/loaders/TextureLoader'; import { MotionEngine } from '../MotionEngine'; @@ -31,7 +31,7 @@ class Game1 { let material = new MeshBasicMaterial({ map: new TextureLoader().load(image) }); - material.map.encoding = sRGBEncoding; + //material.map.encoding = sRGBEncoding; for (let i = 0; i < 6; i++) { let b = bm.clone(); @@ -87,7 +87,7 @@ class Game1 { f: i == 0 && this.onfinish }); }); - context.dashboard.addPoints(10); + //context.dashboard.addPoints(10); } }; } diff --git a/src/lib/mini-games/Game2.js b/src/lib/mini-games/Game2.js index 47deb0d..3a1df60 100644 --- a/src/lib/mini-games/Game2.js +++ b/src/lib/mini-games/Game2.js @@ -1,11 +1,11 @@ -import { BoxGeometry, Mesh, MeshBasicMaterial, Group, sRGBEncoding } from 'three'; +import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three'; import { TextureLoader } from 'three/src/loaders/TextureLoader'; import { MotionEngine } from '../MotionEngine'; class Game2 { constructor(context, image, w, h) { const texture = new TextureLoader().load(image); - texture.encoding = sRGBEncoding; + //texture.encoding = sRGBEncoding; const material = new MeshBasicMaterial({ map: texture }); @@ -136,7 +136,7 @@ class Game2 { f: i == 0 && this.onfinish }); }); - context.dashboard.addPoints(10); + //context.dashboard.addPoints(10); } }; diff --git a/src/lib/mini-games/Game4.js b/src/lib/mini-games/Game4.js new file mode 100644 index 0000000..957821a --- /dev/null +++ b/src/lib/mini-games/Game4.js @@ -0,0 +1,124 @@ +import { Group, RGBAFormat } from 'three'; +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { MotionEngine } from '../MotionEngine'; + +var Game4 = function(context, gltf, w, h){ + this.game = new Group(); + const aq = new MotionEngine(); + const pr = []; + let d = .51, c = w * h / 2, tc = w * h, m0=1, r0=.2; + let lastClicked; + + function setMaterial(m, v){ + if (v){ + m.metalness = .5; + m.roughness = .383; + }else{ + m.metalness = m0; + m.roughness = r0; + m.transparent = true; + m.format = RGBAFormat; + } + } + + new GLTFLoader().load(gltf, gltf=>{ + for (let i = 0; i{ + c.position.set((i % w)*d , (~~(i / w))*d); + c.rotation.set(0, Math.PI, 0); + this.game.add(c); + context.clickable.add(c, clickFn); + }) + + this.game.children[0].onBeforeRender = ()=>{ + this.update(); + } + }); + + var check = ()=>{ + if (!this.game.children.length) return false; + return pr.filter(c=>c.$active === false).length == tc; + } + + let clickFn = (i)=>{ + let clicked = i.object; + if (this.done && clicked){ + aq.add({ + o: clicked, + a: {rotation:{y: Math.PI}}, + t: 1, + m: 'offset' + }); + } + if (!clicked || clicked.$active === false || aq.isActive(clicked)) return; + setMaterial(clicked.material, 1); + let f; + if (lastClicked && lastClicked.$ref == clicked){ + clicked.$active = lastClicked.$active = false; + [clicked, lastClicked].forEach(c => { + aq.add({ + o: c, + a: {material:{opacity:0}}, + t: 1, + d:1 + }); + }); + lastClicked = null; + }else if(!lastClicked){ + lastClicked = clicked; + }else{ + f = ()=>{ + setTimeout(()=>{ + [clicked, lastClicked].filter(c=>c).forEach(c=>{ + aq.add({ + o: c, + a: {rotation:{y: Math.PI}, material:{metalness:m0, roughness:r0}}, + t: 1 + }); + }); + lastClicked = null; + }, 500); + } + } + aq.add({ + o: clicked, + a: {rotation:{y: 0}}, + t: .5, + f + }); + } + + this.update = ()=>{ + aq.update(); + if (!this.done && check()){ + this.done = true; + this.game.children.forEach((c, i)=>{ + aq.add({ + o: c, + a: {material:{opacity:1}}, + t: 1, + d:1+0.1*i + }) + }) + this.onfinish && this.onfinish(); + //context.dashboard.addPoints(10); + } + } +} + +export { Game4 } \ No newline at end of file