UNPKG

janusweb

Version:
399 lines (384 loc) 13.2 kB
/* * for room-specific teleport sound use: * * <room teleport_sound_id="mysound" ...> * */ janus.registerElement('locomotion_teleporter', { active: false, longpresstime: 500, deadzone: 5, xrplayer: null, // *TODO* use janus.engine.client.xrplayer instead (and allow desktop too) linesegments: 100, create() { this.marker = this.createObject('Object', { }); this.ring = this.marker.createObject('object', { id: 'pipe', col: V(0,0,155), emissive: V(0,0,155), scale: V(.5,.01,.5), collidable: false, pickable: false, shadow: false, shadow_cast: false, shadow_receive: false, renderorder: 501, depth_test: false, depth_write: false, }); this.cylinder = this.marker.createObject('object', { id: 'cylinder', col: V(0,0,155,.4), scale: V(.5,1.5,.5), collidable: false, pickable: false, shadow: false, opacity: .1, shadow_cast: false, shadow_receive: false, renderorder: 500, }); this.pointer = this.marker.createObject('Object', { id: 'pyramid', col: 'red', rotation: V(90,0,0), scale: V(.1,.5,.0125), pos: V(0,.01,0), collidable: false, pickable: false, shadow: false, shadow_cast: false, shadow_receive: false, renderorder: 502, }); /* this.light = this.createObject('Light', { col: '#009', pos: V(0,1,0), light_range: 20, light_intensity: 8 }); */ this.particles = room.createObject('Particle', { col: V(0,.2,1,.4), pos: V(-.25,.1,-.25), scale: V(.05), //vel: V(-.5,0,-.5), particle_vel: V(-.4,2,-.4), rand_vel: V(.8,2,.8), rand_col: V(.5,.5,1), rand_pos: V(.5,0,.5), accel: V(0,-1,0), rand_accel: V(0,2,0), rate: 10, count: 60, duration: 3, collision_id: '', collidable: false, pickable: false, loop: true }); this.shroud = this.createObject('object', { id: 'sphere', scale: V(2), lighting: false, col: 'black', cull_face: 'none', depth_test: false, depth_write: false, shadow_cast: false, shadow_receive: false, renderorder: 1000, visible: false, }); let linepositions = []; for (let i = 0; i < this.linesegments; i++) { linepositions.push(V(0)); linepositions.push(V(0)); } this.laser = room.createObject('linesegments', { positions: linepositions, opacity: .8, col: 'blue', visible: false, }); player.head.add(this.shroud._target); this.particles.particle_vel = V(-.4, 0, -.4); // FIXME - particle velocity isn't being set on spawn let locomotion = janus.ui.apps.default.apps.locomotion; let asseturl = locomotion.resolveFullURL('./locomotion-assets.json'); fetch(asseturl).then(res => res.json()).then(assetlist => { this.assetpack = elation.engine.assets.loadJSON(assetlist, locomotion.resolveFullURL('./')); this.sound = room.createObject('Sound', { id: room.teleport_sound_id || 'button-teleport' }, this); }) this.disableCursor(); window.addEventListener('mousemove', this.handleMouseMove); window.addEventListener('touchmove', this.handleTouchMove, true); this.activecontrols = this.addControlContext('teleporter', { 'teleport_x': { defaultbindings: 'gamepad_any_axis_0,gamepad_any_axis_2', onchange: ev => this.handleTeleportChange(ev), onactivate: ev => this.handleTeleportTurn(ev), }, 'teleport_y_right': { defaultbindings: 'gamepad_any_axis_1,gamepad_any_axis_3', onchange: ev => this.handleTeleportChange(ev, 'right'), onactivate: ev => this.handleTeleportStart(ev, 'right'), }, /* 'teleport_y_left': { defaultbindings: 'gamepad_6_axis_1,gamepad_6_axis_3', onchange: ev => this.handleTeleportChange(ev, 'left'), onactivate: ev => this.handleTeleportStart(ev, 'left'), }, */ 'teleport_trigger': { defaultbindings: 'gamepad_any_button_0', onactivate: ev => this.handleTeleportTrigger(ev), }, }); this.activateControlContext('teleporter'); // emulated mouse-events (via webxr handtracking e.g.) room.addEventListener('mousemove', (ev) => this.handleMouseMove(ev)); room.addEventListener('mousedown', (ev) => this.handleMouseDown(ev)); room.addEventListener('mouseup', (ev) => this.handleMouseUp(ev)); elation.events.add(janus._target, 'room_change', (ev) => this.handleRoomChange(ev)); }, update() { if (this.active) { let hand = this.xrplayer ? this.xrplayer.trackedobjects['hand_' + this.teleporthand] : null let startpoint, segments = this.linesegments, duration = .25, speed = 4, g = V(0, -1, 0), p1 = V(), v1 = V(), v0; if (hand) { startpoint = hand.pointer.localToWorld(V(0, 0, 0)); v0 = hand.pointer.localToWorld(V(0, 0, -speed)).sub(startpoint); hand.teleportPos = hand.teleportPos || v0 } else { startpoint = player.head.localToWorld(V(0,0,0)); v0 = player.head.localToWorld(V(0,0,-speed)).sub(startpoint); return; // Head-locked teleporter is awkward, do we really still support this? } let i = 0, p0 = startpoint.clone(), dir = v0.clone().normalize(); let laserpoints = this.laser.positions; // Trace our arc in steps, performing a raytrace at each step for (i = 0; i < segments; i++) { let t = (i + 1) * duration / segments; dir.copy(v0); speed = dir.length(); dir.divideScalar(speed); let hits = room.raycast(dir, p0, null, speed); let hit = false; for (let j = 0; j < hits.length; j++) { if (hits[j].object !== player) { hit = hits[j]; break; } } if (hit) { p1.copy(hit.point); if (hit.distance < 200) { this.pos = player.worldToLocal(hit.point); this.localToWorld(this.particles.emitter_pos.set(0,0,0)); this.updateTeleportAngle(); // track movement to cancel longpresstimer if( hand ) hand.teleportPos = hand.teleportPos || this.pos.clone() } laserpoints[i * 2].copy(p0); laserpoints[i * 2 + 1].copy(p1); // Zero out any further line segments for (let k = i+1; k < segments; k++) { laserpoints[k * 2].set(0,0,0); laserpoints[k * 2 + 1].set(0,0,0); } break; } else { p1.copy(g).multiplyScalar(.5 * t * t).add(v0.clone().multiplyScalar(t)).add(p0);; v1.copy(g).multiplyScalar(t).add(v0); laserpoints[i * 2].copy(p0); laserpoints[i * 2 + 1].copy(p1); p0.copy(p1); v0.copy(v1); } } this.laser.updateLine(); if (this.laser.room !== room._target) room.appendChild(this.laser); this.pointer.orientation._target.setFromEuler(new THREE.Euler(Math.PI/2, this.teleportangle, 0, "YXZ")); } else { if (this.shroud.visible) { ////this.worldToLocal(player.head.localToWorld(this.shroud.pos.set(0,0,0))); if (this.shroud.opacity > .001) { this.shroud.opacity *= .9; if (this.shroud.opacity <= .001) { this.shroud.visible = false; this.shroud.opacity = 0; } } } } }, handleRoomChange(ev) { this.setRoom(room); room.addEventListener('mousedown', (ev) => this.handleMouseDown(ev)); room.addEventListener('mouseup', (ev) => this.handleMouseUp(ev)); }, handleTeleportChange(ev) { //if (!room.teleport) return; if (this.teleportactive) { let controls = this.activecontrols; let xy = new THREE.Vector2(controls.teleport_x, controls['teleport_y_' + this.teleporthand]), len = xy.length(); if (len > .8) { this.updateTeleportAngle(); } else if (len < .01) { this.teleport(); this.teleportactive = false; this.teleporthand = false; this.disableCursor(); } } }, handleTeleportStart(ev, hand) { if (!room.teleport) return; if (!this.teleportactive && Math.abs(ev.value) > .8) { this.teleportactive = true; this.teleporthand = hand || 'right'; this.enableCursor(); this.updateTeleportAngle(); } }, handleTeleportTurn(ev) { if (!this.teleportactive) { this.showShroud(); let turn = new THREE.Quaternion(); turn.setFromEuler(new THREE.Euler(0, Math.PI / 4 * (ev.value > 0 ? -1 : 1), 0)); player.orientation.multiply(turn); } }, handleTeleportTrigger(ev) { if (!room.teleport) return; if (this.active) { } }, teleport(mouse) { //if (!room.teleport) return; let pos = player.localToWorld(this.pos.clone()); pos.y += player.fatness; // FIXME - This accounts for wonky player sphere collider, when we switch to a capsule we won't need it player.pos = pos; player.vel = V(0,0,.01); // "wake up" physics engine console.log('Teleport player', pos); this.showShroud(); if( this.sound ){ this.sound.pos = pos; this.sound.play(); } if( this.xrplayer && this.teleportangle && !mouse){ player.angular.set(0,0,0); let playerangle = Math.atan2(this.pos.x, this.pos.z); this.xrplayer.orientation._target.setFromEuler(new THREE.Euler(0, this.teleportangle - playerangle, 0)); } }, updateTeleportAngle() { let controls = this.activecontrols; let controllerangle = Math.atan2(controls.teleport_x, controls['teleport_y_' + this.teleporthand]); let playerangle = Math.atan2(this.pos.x, this.pos.z); this.teleportangle = (controllerangle + playerangle) + Math.PI; }, handleMouseDown(ev) { if (!room.teleport) return; if (ev.button == 0 && (player.enabled || janus.hmd)) { if( ev.inputSourceObject && this.xrplayer ){ // translate webxr emulated mousedown event this.teleporthand = ev.inputSourceObject.device.handedness let hand = this.xrplayer.trackedobjects['hand_' + this.teleporthand]; if( hand ) hand.teleportPos = false } if (!this.longpresstimer) { this.longpresstimer = setTimeout(() => { console.log('timer fired'); this.enableCursor(); }, this.longpresstime); this.mousediff = [0,0]; }else this.cancelLongPress() } }, handleMouseMove(ev) { if (!room.teleport) return; //if (this.longpresstimer) { // this.mousediff[0] += ev.movementX; // this.mousediff[1] += ev.movementY // var distance = Math.sqrt(this.mousediff[0] * this.mousediff[0] + this.mousediff[1] * this.mousediff[1]); // if (distance > this.deadzone) this.cancelLongPress() //} }, handleTouchMove(ev) { if (!room.teleport || !this.active) return; if (this.longpresstimer) { var touch = ev.changedTouches[0]; if (this.lasttouch) { this.mousediff[0] += touch.clientX - this.lasttouch[0]; this.mousediff[1] += touch.clientY - this.lasttouch[1]; } else { this.mousediff[0] = 0; this.mousediff[1] = 0; } this.lasttouch = [touch.clientX, touch.clientY]; var distance = Math.sqrt(this.mousediff[0] * this.mousediff[0] + this.mousediff[1] * this.mousediff[1]); if (distance > this.deadzone) this.cancelLongPress() if (this.active) { ev.stopPropagation(); ev.preventDefault(); } } }, handleMouseUp(ev) { if (!room.teleport) return; if (this.teleportactive) { this.teleport(true); this.active = false this.teleportactive = false; this.teleporthand = false; this.disableCursor(); }else if( this.longpresstimer ) this.disableCursor(); }, enableCursor() { if (!room.teleport) return; if( this.xrplayer ){ let hand = this.xrplayer.trackedobjects['hand_' + this.teleporthand]; if( hand ) hand.pointer.laser.visible = false // prevent (portal)clicks } this.active = true; this.teleportactive = true; this.visible = true; this.particles.resetParticles(); this.particles.visible = true; if (this.laser.room !== room._target) room.appendChild(this.laser); if (this.particles.room !== room._target) room.appendChild(this.particles); this.laser.visible = true; this.particles.start(); }, disableCursor() { if( this.xrplayer ){ let hand = this.xrplayer.trackedobjects['hand_' + this.teleporthand]; if( hand ) hand.pointer.laser.visible = true // restore original laser } this.teleportactive = false; this.visible = false; this.particles.visible = false; this.laser.visible = false; this.active = false; this.particles.stop(); this.cancelLongPress() }, showShroud() { this.shroud.visible = true; this.shroud.opacity = 1; }, cancelLongPress(){ clearTimeout(this.longpresstimer); this.longpresstimer = false } });