Imported "ThreeJS + WebVR" Boilerplate
This commit is contained in:
BIN
static/img/cat.jpg
Normal file
BIN
static/img/cat.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
139
static/js/Raycaster.js
Normal file
139
static/js/Raycaster.js
Normal file
@@ -0,0 +1,139 @@
|
||||
const Ray = THREE.Ray
|
||||
/**
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author bhouston / http://clara.io/
|
||||
* @author stephomi / http://stephaneginier.com/
|
||||
*/
|
||||
|
||||
function Raycaster( origin, direction, near, far ) {
|
||||
|
||||
this.ray = new Ray( origin, direction );
|
||||
// direction is assumed to be normalized (for accurate distance calculations)
|
||||
|
||||
this.near = near || 0;
|
||||
this.far = far || Infinity;
|
||||
|
||||
this.params = {
|
||||
Mesh: {},
|
||||
Line: {},
|
||||
LOD: {},
|
||||
Points: { threshold: 1 },
|
||||
Sprite: {}
|
||||
};
|
||||
|
||||
Object.defineProperties( this.params, {
|
||||
PointCloud: {
|
||||
get: function () {
|
||||
|
||||
console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' );
|
||||
return this.Points;
|
||||
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
function ascSort( a, b ) {
|
||||
|
||||
return a.distance - b.distance;
|
||||
|
||||
}
|
||||
|
||||
let count = 0
|
||||
|
||||
function intersectObject( object, raycaster, intersects, recursive ) {
|
||||
|
||||
if ( object.visible === false ) return;
|
||||
if(raycaster.recurseFilter && !raycaster.recurseFilter(object)) return;
|
||||
|
||||
count++
|
||||
object.raycast( raycaster, intersects );
|
||||
|
||||
if ( recursive === true ) {
|
||||
|
||||
var children = object.children;
|
||||
|
||||
for ( var i = 0, l = children.length; i < l; i ++ ) {
|
||||
|
||||
intersectObject( children[ i ], raycaster, intersects, true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Object.assign( Raycaster.prototype, {
|
||||
|
||||
linePrecision: 1,
|
||||
|
||||
set: function ( origin, direction ) {
|
||||
|
||||
// direction is assumed to be normalized (for accurate distance calculations)
|
||||
|
||||
this.ray.set( origin, direction );
|
||||
|
||||
},
|
||||
|
||||
setFromCamera: function ( coords, camera ) {
|
||||
|
||||
if ( ( camera && camera.isPerspectiveCamera ) ) {
|
||||
|
||||
this.ray.origin.setFromMatrixPosition( camera.matrixWorld );
|
||||
this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();
|
||||
|
||||
} else if ( ( camera && camera.isOrthographicCamera ) ) {
|
||||
|
||||
this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera
|
||||
this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
|
||||
|
||||
} else {
|
||||
|
||||
console.error( 'THREE.Raycaster: Unsupported camera type.' );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
intersectObject: function ( object, recursive, optionalTarget ) {
|
||||
|
||||
var intersects = optionalTarget || [];
|
||||
|
||||
intersectObject( object, this, intersects, recursive);
|
||||
|
||||
intersects.sort( ascSort );
|
||||
|
||||
return intersects;
|
||||
|
||||
},
|
||||
|
||||
intersectObjects: function ( objects, recursive, optionalTarget ) {
|
||||
count = 0
|
||||
var intersects = optionalTarget || [];
|
||||
|
||||
if ( Array.isArray( objects ) === false ) {
|
||||
|
||||
console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );
|
||||
return intersects;
|
||||
|
||||
}
|
||||
|
||||
for ( var i = 0, l = objects.length; i < l; i ++ ) {
|
||||
|
||||
intersectObject( objects[ i ], this, intersects, recursive);
|
||||
|
||||
}
|
||||
|
||||
// console.log("intersected objects",count)
|
||||
|
||||
intersects.sort( ascSort );
|
||||
|
||||
return intersects;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
|
||||
export { Raycaster };
|
||||
305
static/js/pointer.js
Normal file
305
static/js/pointer.js
Normal file
@@ -0,0 +1,305 @@
|
||||
import {Raycaster} from "./Raycaster.js"
|
||||
export const POINTER_ENTER = "enter"
|
||||
export const POINTER_EXIT = "exit"
|
||||
export const POINTER_CLICK = "click"
|
||||
export const POINTER_MOVE = "move"
|
||||
export const POINTER_PRESS = "press"
|
||||
export const POINTER_RELEASE = "release"
|
||||
|
||||
// import * as THREE from "./node_modules/three/build/three.module.js"
|
||||
|
||||
const toRad = (degrees) => degrees*Math.PI/180
|
||||
|
||||
export class Pointer {
|
||||
constructor(scene, renderer, camera, opts) {
|
||||
this.listeners = {}
|
||||
this.opts = opts || {}
|
||||
this.opts.enableLaser = (opts.enableLaser !== undefined) ? opts.enableLaser : true
|
||||
this.opts.laserLength = (opts.laserLength !== undefined) ? opts.laserLength : 3
|
||||
this.opts.enableMoveEvents = (opts.enableMoveEvents !== undefined) ? opts.enableMoveEvents : true
|
||||
this.scene = scene
|
||||
this.renderer = renderer
|
||||
this.canvas = renderer.domElement
|
||||
this.camera = camera
|
||||
|
||||
this.raycaster = new Raycaster()
|
||||
this.waitcb = null
|
||||
this.hoverTarget = null
|
||||
|
||||
this.intersectionFilter = this.opts.intersectionFilter || (() => true)
|
||||
this.raycaster.recurseFilter = this.opts.recurseFilter || (()=> true)
|
||||
|
||||
|
||||
// setup the mouse
|
||||
this.canvas.addEventListener('mousemove', this.mouseMove.bind(this))
|
||||
this.canvas.addEventListener('click', this.mouseClick.bind(this))
|
||||
this.canvas.addEventListener('mousedown',this.mouseDown.bind(this))
|
||||
this.canvas.addEventListener('mouseup',this.mouseUp.bind(this))
|
||||
|
||||
//touch events
|
||||
this.canvas.addEventListener('touchstart',this.touchStart.bind(this))
|
||||
this.canvas.addEventListener('touchmove',this.touchMove.bind(this))
|
||||
this.canvas.addEventListener('touchend',this.touchEnd.bind(this))
|
||||
|
||||
// setup the VR controllers
|
||||
this.controller1 = this.renderer.vr.getController(0);
|
||||
this.controller1.addEventListener('selectstart', this.controllerSelectStart.bind(this));
|
||||
this.controller1.addEventListener('selectend', this.controllerSelectEnd.bind(this));
|
||||
|
||||
|
||||
this.controller2 = this.renderer.vr.getController(1);
|
||||
this.controller2.addEventListener('selectstart', this.controllerSelectStart.bind(this));
|
||||
this.controller2.addEventListener('selectend', this.controllerSelectEnd.bind(this));
|
||||
|
||||
this.setMouseSimulatesController(opts.mouseSimulatesController)
|
||||
|
||||
this.scene.add(this.controller1);
|
||||
this.scene.add(this.controller2);
|
||||
|
||||
|
||||
|
||||
if(this.opts.enableLaser) {
|
||||
//create visible lines for the two controllers
|
||||
const geometry = new THREE.BufferGeometry()
|
||||
geometry.addAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, -this.opts.laserLength], 3));
|
||||
geometry.addAttribute('color', new THREE.Float32BufferAttribute([1.0, 0.5, 0.5, 0, 0, 0], 3));
|
||||
|
||||
const material = new THREE.LineBasicMaterial({
|
||||
vertexColors: false,
|
||||
color: 0x880000,
|
||||
linewidth: 5,
|
||||
blending: THREE.NormalBlending
|
||||
})
|
||||
|
||||
this.controller1.add(new THREE.Line(geometry, material));
|
||||
this.controller2.add(new THREE.Line(geometry, material));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//override this to do something w/ the controllers on every tick
|
||||
tick(time) {
|
||||
this.controllerMove(this.controller1)
|
||||
this.controllerMove(this.controller2)
|
||||
}
|
||||
|
||||
|
||||
fire(obj, type, payload) {
|
||||
obj.dispatchEvent(payload)
|
||||
}
|
||||
fireSelf(type,payload) {
|
||||
if(!this.listeners[type]) return
|
||||
this.listeners[type].forEach(cb => cb(payload))
|
||||
}
|
||||
|
||||
//make the camera follow the mouse in desktop mode. Helps w/ debugging.
|
||||
cameraFollowMouse(e) {
|
||||
const bounds = this.canvas.getBoundingClientRect()
|
||||
const ry = ((e.clientX - bounds.left) / bounds.width) * 2 - 1
|
||||
const rx = 1 - ((e.clientY - bounds.top) / bounds.height) * 2
|
||||
this.camera.rotation.y = -ry*2
|
||||
this.camera.rotation.x = +rx
|
||||
}
|
||||
|
||||
mouseMove(e) {
|
||||
const mouse = new THREE.Vector2()
|
||||
const bounds = this.canvas.getBoundingClientRect()
|
||||
mouse.x = ((e.clientX - bounds.left) / bounds.width) * 2 - 1
|
||||
mouse.y = -((e.clientY - bounds.top) / bounds.height) * 2 + 1
|
||||
this.raycaster.setFromCamera(mouse, this.camera)
|
||||
if(this.opts.mouseSimulatesController) {
|
||||
//create target from the mouse controls
|
||||
const target = new THREE.Vector3()
|
||||
target.x = mouse.x
|
||||
target.y = mouse.y
|
||||
target.z = -3
|
||||
//convert to camera space
|
||||
target.add(this.camera.position)
|
||||
this.spot.position.copy(target)
|
||||
this.controller1.lookAt(target)
|
||||
//have to flip over because the UP is down on controllers
|
||||
const flip = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0),toRad(180))
|
||||
this.controller1.quaternion.multiply(flip)
|
||||
}
|
||||
this._processMove()
|
||||
|
||||
if(this.opts.cameraFollowMouse) this.cameraFollowMouse(e)
|
||||
}
|
||||
|
||||
touchStart(e) {
|
||||
e.preventDefault()
|
||||
if(e.changedTouches.length <= 0) return
|
||||
const tch = e.changedTouches[0]
|
||||
const mouse = new THREE.Vector2()
|
||||
const bounds = this.canvas.getBoundingClientRect()
|
||||
mouse.x = ((tch.clientX - bounds.left) / bounds.width) * 2 - 1
|
||||
mouse.y = -((tch.clientY - bounds.top) / bounds.height) * 2 + 1
|
||||
this.raycaster.setFromCamera(mouse, this.camera)
|
||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true)
|
||||
.filter(it => this.intersectionFilter(it.object))
|
||||
intersects.forEach((it) => {
|
||||
this.fire(it.object, POINTER_PRESS, {type: POINTER_PRESS})
|
||||
})
|
||||
}
|
||||
touchMove(e) {
|
||||
e.preventDefault()
|
||||
if(e.changedTouches.length <= 0) return
|
||||
const tch = e.changedTouches[0]
|
||||
const mouse = new THREE.Vector2()
|
||||
const bounds = this.canvas.getBoundingClientRect()
|
||||
mouse.x = ((tch.clientX - bounds.left) / bounds.width) * 2 - 1
|
||||
mouse.y = -((tch.clientY - bounds.top) / bounds.height) * 2 + 1
|
||||
this.raycaster.setFromCamera(mouse, this.camera)
|
||||
this._processMove()
|
||||
}
|
||||
touchEnd(e) {
|
||||
e.preventDefault()
|
||||
if(e.changedTouches.length <= 0) return
|
||||
const tch = e.changedTouches[0]
|
||||
const mouse = new THREE.Vector2()
|
||||
const bounds = this.canvas.getBoundingClientRect()
|
||||
mouse.x = ((tch.clientX - bounds.left) / bounds.width) * 2 - 1
|
||||
mouse.y = -((tch.clientY - bounds.top) / bounds.height) * 2 + 1
|
||||
this.raycaster.setFromCamera(mouse, this.camera)
|
||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true)
|
||||
.filter(it => this.intersectionFilter(it.object))
|
||||
intersects.forEach((it) => {
|
||||
this.fire(it.object, POINTER_RELEASE, {type: POINTER_RELEASE, point: it.point})
|
||||
})
|
||||
this._processClick()
|
||||
}
|
||||
|
||||
controllerMove(controller) {
|
||||
if(!controller.visible) return
|
||||
const c = controller
|
||||
const dir = new THREE.Vector3(0, 0, -1)
|
||||
dir.applyQuaternion(c.quaternion)
|
||||
this.raycaster.set(c.position, dir)
|
||||
this._processMove()
|
||||
}
|
||||
|
||||
_processMove() {
|
||||
if(!this.opts.enableMoveEvents)return
|
||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true)
|
||||
.filter(it => this.intersectionFilter(it.object))
|
||||
|
||||
if(intersects.length === 0 && this.hoverTarget) {
|
||||
this.fire(this.hoverTarget, POINTER_EXIT, {type: POINTER_EXIT})
|
||||
this.hoverTarget = null
|
||||
}
|
||||
if(intersects.length >= 1) {
|
||||
const it = intersects[0]
|
||||
const obj = it.object
|
||||
if (!obj) return
|
||||
this.fire(obj, POINTER_MOVE, {type: POINTER_MOVE, point: it.point, intersection:it})
|
||||
if (obj === this.hoverTarget) {
|
||||
//still inside
|
||||
} else {
|
||||
if (this.hoverTarget)
|
||||
this.fire(this.hoverTarget, POINTER_EXIT, {type: POINTER_EXIT})
|
||||
this.hoverTarget = obj
|
||||
this.fire(this.hoverTarget, POINTER_ENTER, {type: POINTER_ENTER})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_processClick() {
|
||||
if (this.waitcb) {
|
||||
this.waitcb()
|
||||
this.waitcb = null
|
||||
return
|
||||
}
|
||||
|
||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true)
|
||||
.filter(it => this.intersectionFilter(it.object))
|
||||
if(intersects.length > 0) {
|
||||
const it = intersects[0]
|
||||
this.fire(it.object, POINTER_CLICK, {type: POINTER_CLICK, point: it.point, intersection:it})
|
||||
}
|
||||
|
||||
this.fireSelf(POINTER_CLICK, {})
|
||||
}
|
||||
mouseClick(e) {
|
||||
const mouse = new THREE.Vector2()
|
||||
const bounds = this.canvas.getBoundingClientRect()
|
||||
mouse.x = ((e.clientX - bounds.left) / bounds.width) * 2 - 1
|
||||
mouse.y = -((e.clientY - bounds.top) / bounds.height) * 2 + 1
|
||||
this.raycaster.setFromCamera(mouse, this.camera)
|
||||
this._processClick()
|
||||
}
|
||||
mouseDown(e) {
|
||||
const mouse = new THREE.Vector2()
|
||||
const bounds = this.canvas.getBoundingClientRect()
|
||||
mouse.x = ((e.clientX - bounds.left) / bounds.width) * 2 - 1
|
||||
mouse.y = -((e.clientY - bounds.top) / bounds.height) * 2 + 1
|
||||
this.raycaster.setFromCamera(mouse, this.camera)
|
||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true)
|
||||
.filter(it => this.intersectionFilter(it.object))
|
||||
intersects.forEach((it) => {
|
||||
this.fire(it.object, POINTER_PRESS, {type: POINTER_PRESS, point: it.point, intersection:it})
|
||||
})
|
||||
}
|
||||
mouseUp(e) {
|
||||
const mouse = new THREE.Vector2()
|
||||
const bounds = this.canvas.getBoundingClientRect()
|
||||
mouse.x = ((e.clientX - bounds.left) / bounds.width) * 2 - 1
|
||||
mouse.y = -((e.clientY - bounds.top) / bounds.height) * 2 + 1
|
||||
this.raycaster.setFromCamera(mouse, this.camera)
|
||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true)
|
||||
.filter(it => this.intersectionFilter(it.object))
|
||||
intersects.forEach((it) => {
|
||||
this.fire(it.object, POINTER_RELEASE, {type: POINTER_RELEASE, point: it.point, intersection:it})
|
||||
})
|
||||
}
|
||||
|
||||
controllerSelectStart(e) {
|
||||
e.target.userData.isSelecting = true;
|
||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true)
|
||||
.filter(it => this.intersectionFilter(it.object))
|
||||
intersects.forEach((it) => {
|
||||
this.fire(it.object, POINTER_PRESS, {type: POINTER_PRESS, point: it.point, intersection:it})
|
||||
})
|
||||
}
|
||||
|
||||
controllerSelectEnd(e) {
|
||||
e.target.userData.isSelecting = false;
|
||||
const c = e.target
|
||||
const dir = new THREE.Vector3(0, 0, -1)
|
||||
dir.applyQuaternion(c.quaternion)
|
||||
this.raycaster.set(c.position, dir)
|
||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true)
|
||||
.filter(it => this.intersectionFilter(it.object))
|
||||
intersects.forEach((it) => {
|
||||
this.fire(it.object, POINTER_RELEASE, {type: POINTER_RELEASE, point: it.point})
|
||||
})
|
||||
this._processClick()
|
||||
}
|
||||
|
||||
waitSceneClick(cb) {
|
||||
this.waitcb = cb
|
||||
}
|
||||
|
||||
|
||||
on(type,cb) {
|
||||
if(!this.listeners[type]) this.listeners[type] = []
|
||||
this.listeners[type].push(cb)
|
||||
}
|
||||
off(type,cb) {
|
||||
this.listeners[type] = this.listeners[type].filter(c => c !== cb)
|
||||
}
|
||||
setMouseSimulatesController(val) {
|
||||
this.opts.mouseSimulatesController = val
|
||||
if(this.opts.mouseSimulatesController) {
|
||||
this.controller1 = new THREE.Group()
|
||||
this.controller1.position.set(0,1,-2)
|
||||
this.controller1.quaternion.setFromUnitVectors(THREE.Object3D.DefaultUp, new THREE.Vector3(0,0,1))
|
||||
this.spot = new THREE.Mesh(
|
||||
new THREE.SphereBufferGeometry(0.1),
|
||||
new THREE.MeshLambertMaterial({color: 'red'})
|
||||
)
|
||||
this.scene.add(this.spot)
|
||||
} else {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
103
static/js/vrmanager.js
Normal file
103
static/js/vrmanager.js
Normal file
@@ -0,0 +1,103 @@
|
||||
function printError(err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
export const VR_DETECTED = "detected"
|
||||
export const VR_CONNECTED = "connected"
|
||||
export const VR_DISCONNECTED = "disconnected"
|
||||
export const VR_PRESENTCHANGE = "presentchange"
|
||||
export const VR_ACTIVATED = "activated"
|
||||
|
||||
|
||||
|
||||
export default class VRManager {
|
||||
constructor(renderer) {
|
||||
this.device = null
|
||||
this.renderer = renderer
|
||||
if(!this.renderer) throw new Error("VR Manager requires a valid ThreeJS renderer instance")
|
||||
this.listeners = {}
|
||||
|
||||
if ('xr' in navigator) {
|
||||
console.log("has webxr")
|
||||
navigator.xr.requestDevice().then((device) => {
|
||||
device.supportsSession({immersive: true, exclusive: true /* DEPRECATED */})
|
||||
.then(() => {
|
||||
this.device = device
|
||||
this.fire(VR_DETECTED,{})
|
||||
})
|
||||
.catch(printError);
|
||||
|
||||
}).catch(printError);
|
||||
} else if ('getVRDisplays' in navigator) {
|
||||
console.log("has webvr")
|
||||
|
||||
window.addEventListener( 'vrdisplayconnect', ( event ) => {
|
||||
this.device = event.display
|
||||
this.fire(VR_CONNECTED)
|
||||
}, false );
|
||||
|
||||
window.addEventListener( 'vrdisplaydisconnect', ( event ) => {
|
||||
this.fire(VR_DISCONNECTED)
|
||||
}, false );
|
||||
|
||||
window.addEventListener( 'vrdisplaypresentchange', ( event ) => {
|
||||
this.fire(VR_PRESENTCHANGE)
|
||||
}, false );
|
||||
|
||||
window.addEventListener( 'vrdisplayactivate', ( event ) => {
|
||||
this.device = event.display
|
||||
this.device.requestPresent([{source:this.renderer.domElement}])
|
||||
this.fire(VR_ACTIVATED)
|
||||
}, false );
|
||||
|
||||
navigator.getVRDisplays()
|
||||
.then( ( displays ) => {
|
||||
console.log("vr scanned")
|
||||
if ( displays.length > 0 ) {
|
||||
|
||||
// showEnterVR( displays[ 0 ] );
|
||||
console.log("found vr",displays[0])
|
||||
this.device = displays[0]
|
||||
this.fire(VR_DETECTED,{})
|
||||
|
||||
} else {
|
||||
console.log("no vr at all")
|
||||
// showVRNotFound();
|
||||
}
|
||||
|
||||
} ).catch(printError);
|
||||
|
||||
} else {
|
||||
// no vr
|
||||
console.log("no vr at all")
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener(type, cb) {
|
||||
if(!this.listeners[type]) this.listeners[type] = []
|
||||
this.listeners[type].push(cb)
|
||||
}
|
||||
fire(type,evt) {
|
||||
if(!evt) evt = {}
|
||||
evt.type = type
|
||||
if(!this.listeners[type]) this.listeners[type] = []
|
||||
this.listeners[type].forEach(cb => cb(evt))
|
||||
}
|
||||
|
||||
enterVR() {
|
||||
if(!this.device) {
|
||||
console.warn("tried to connect VR on an invalid device")
|
||||
return
|
||||
}
|
||||
console.log("entering VR")
|
||||
const prom = this.renderer.vr.setDevice( this.device );
|
||||
console.log('promise is',prom)
|
||||
|
||||
if(this.device.isPresenting) {
|
||||
this.device.exitPresent()
|
||||
} else {
|
||||
this.device.requestPresent([{source: this.renderer.domElement}]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
61
static/js/vrstats.js
Normal file
61
static/js/vrstats.js
Normal file
@@ -0,0 +1,61 @@
|
||||
export default class VRStats extends THREE.Group {
|
||||
constructor(renderer) {
|
||||
super();
|
||||
this.renderer = renderer
|
||||
const can = document.createElement('canvas')
|
||||
can.width = 256
|
||||
can.height = 128
|
||||
this.canvas = can
|
||||
const c = can.getContext('2d')
|
||||
c.fillStyle = '#00ffff'
|
||||
c.fillRect(0,0,can.width,can.height)
|
||||
const ctex = new THREE.CanvasTexture(can)
|
||||
const mesh = new THREE.Mesh(
|
||||
new THREE.PlaneGeometry(1,0.5),
|
||||
// new THREE.BoxGeometry(1,0.5,0.1),
|
||||
new THREE.MeshBasicMaterial({map:ctex})
|
||||
)
|
||||
mesh.position.z = -3
|
||||
mesh.position.y = 1.5
|
||||
mesh.material.depthTest = false
|
||||
mesh.material.depthWrite = false
|
||||
mesh.renderOrder = 1000
|
||||
this.add(mesh)
|
||||
this.cmesh = mesh
|
||||
|
||||
this.last = 0
|
||||
this.lastFrame = 0
|
||||
this.customProps = {}
|
||||
}
|
||||
|
||||
update(time) {
|
||||
if(time - this.last > 300) {
|
||||
// console.log("updating",this.rendereer.info)
|
||||
// console.log(`stats calls:`,this.renderer.info)
|
||||
|
||||
const fps = ((this.renderer.info.render.frame - this.lastFrame)*1000)/(time-this.last)
|
||||
// console.log(fps)
|
||||
|
||||
const c = this.canvas.getContext('2d')
|
||||
c.fillStyle = 'white'
|
||||
c.fillRect(0, 0, this.canvas.width, this.canvas.height)
|
||||
c.fillStyle = 'black'
|
||||
c.font = '16pt sans-serif'
|
||||
c.fillText(`calls: ${this.renderer.info.render.calls}`, 3, 20)
|
||||
c.fillText(`tris : ${this.renderer.info.render.triangles}`, 3, 40)
|
||||
c.fillText(`fps : ${fps.toFixed(2)}`,3,60)
|
||||
Object.keys(this.customProps).forEach((key,i) => {
|
||||
const val = this.customProps[key]
|
||||
c.fillText(`${key} : ${val}`,3,80+i*20)
|
||||
})
|
||||
this.cmesh.material.map.needsUpdate = true
|
||||
this.last = time
|
||||
this.lastFrame = this.renderer.info.render.frame
|
||||
}
|
||||
}
|
||||
|
||||
setProperty(name, value) {
|
||||
this.customProps[name] = value
|
||||
}
|
||||
|
||||
}
|
||||
242
static/js/webvr.js
Normal file
242
static/js/webvr.js
Normal file
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* @author mrdoob / http://mrdoob.com
|
||||
* @author Mugen87 / https://github.com/Mugen87
|
||||
*
|
||||
* Based on @tojiro's vr-samples-utils.js
|
||||
*/
|
||||
|
||||
var WEBVR = {
|
||||
|
||||
createButton: function ( renderer, options ) {
|
||||
|
||||
// if ( options && options.frameOfReferenceType ) {
|
||||
//
|
||||
// renderer.vr.setFrameOfReferenceType( options.frameOfReferenceType );
|
||||
//
|
||||
// }
|
||||
|
||||
function showEnterVR( device ) {
|
||||
|
||||
button.style.display = '';
|
||||
|
||||
button.style.cursor = 'pointer';
|
||||
// button.style.left = 'calc(50% - 50px)';
|
||||
// button.style.width = '100px';
|
||||
|
||||
button.textContent = 'ENTER VR';
|
||||
|
||||
button.onmouseenter = function () { button.style.opacity = '1.0'; };
|
||||
button.onmouseleave = function () { button.style.opacity = '0.5'; };
|
||||
|
||||
button.onclick = function () {
|
||||
|
||||
device.isPresenting ? device.exitPresent() : device.requestPresent( [ { source: renderer.domElement } ] );
|
||||
|
||||
};
|
||||
|
||||
renderer.vr.setDevice( device );
|
||||
|
||||
}
|
||||
|
||||
function showEnterXR( device ) {
|
||||
|
||||
var currentSession = null;
|
||||
|
||||
function onSessionStarted( session ) {
|
||||
|
||||
session.addEventListener( 'end', onSessionEnded );
|
||||
|
||||
renderer.vr.setSession( session );
|
||||
button.textContent = 'EXIT VR';
|
||||
|
||||
currentSession = session;
|
||||
|
||||
}
|
||||
|
||||
function onSessionEnded( event ) {
|
||||
|
||||
currentSession.removeEventListener( 'end', onSessionEnded );
|
||||
|
||||
renderer.vr.setSession( null );
|
||||
button.textContent = 'ENTER VR';
|
||||
|
||||
currentSession = null;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
button.style.display = '';
|
||||
|
||||
button.style.cursor = 'pointer';
|
||||
button.style.left = 'calc(50% - 50px)';
|
||||
button.style.width = '100px';
|
||||
|
||||
button.textContent = 'ENTER VR';
|
||||
|
||||
button.onmouseenter = function () { button.style.opacity = '1.0'; };
|
||||
button.onmouseleave = function () { button.style.opacity = '0.5'; };
|
||||
|
||||
button.onclick = function () {
|
||||
|
||||
if ( currentSession === null ) {
|
||||
|
||||
device.requestSession( { immersive: true, exclusive: true /* DEPRECATED */ } ).then( onSessionStarted );
|
||||
|
||||
} else {
|
||||
|
||||
currentSession.end();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
renderer.vr.setDevice( device );
|
||||
|
||||
}
|
||||
|
||||
function showVRNotFound() {
|
||||
|
||||
button.style.display = '';
|
||||
|
||||
button.style.cursor = 'auto';
|
||||
// button.style.left = 'calc(50% - 75px)';
|
||||
// button.style.width = '150px';
|
||||
|
||||
button.textContent = 'VR NOT FOUND';
|
||||
|
||||
button.onmouseenter = null;
|
||||
button.onmouseleave = null;
|
||||
|
||||
button.onclick = null;
|
||||
|
||||
renderer.vr.setDevice( null );
|
||||
|
||||
}
|
||||
|
||||
function stylizeElement( element ) {
|
||||
// element.style.position = 'absolute';
|
||||
// element.style.bottom = '20px';
|
||||
/*
|
||||
|
||||
element.style.padding = '12px 6px';
|
||||
element.style.border = '1px solid #fff';
|
||||
element.style.borderRadius = '4px';
|
||||
element.style.background = 'rgba(0,0,0,0.1)';
|
||||
element.style.color = '#fff';
|
||||
element.style.font = 'normal 13px sans-serif';
|
||||
element.style.textAlign = 'center';
|
||||
element.style.opacity = '0.5';
|
||||
element.style.outline = 'none';
|
||||
*/
|
||||
// element.style.zIndex = '999';
|
||||
|
||||
}
|
||||
|
||||
if ( 'xr' in navigator ) {
|
||||
|
||||
var button = document.createElement( 'button' );
|
||||
button.style.display = 'none';
|
||||
|
||||
stylizeElement( button );
|
||||
|
||||
navigator.xr.requestDevice().then( function ( device ) {
|
||||
|
||||
device.supportsSession( { immersive: true, exclusive: true /* DEPRECATED */ } )
|
||||
.then( function () { showEnterXR( device ); } )
|
||||
.catch( showVRNotFound );
|
||||
|
||||
} ).catch( showVRNotFound );
|
||||
|
||||
return button;
|
||||
|
||||
} else if ( 'getVRDisplays' in navigator ) {
|
||||
|
||||
var button = document.createElement( 'button' );
|
||||
button.setAttribute("id","enter-vr")
|
||||
button.setAttribute("disabled",true)
|
||||
button.style.display = 'none';
|
||||
|
||||
stylizeElement( button );
|
||||
|
||||
window.addEventListener( 'vrdisplayconnect', function ( event ) {
|
||||
|
||||
showEnterVR( event.display );
|
||||
|
||||
}, false );
|
||||
|
||||
window.addEventListener( 'vrdisplaydisconnect', function ( event ) {
|
||||
|
||||
showVRNotFound();
|
||||
|
||||
}, false );
|
||||
|
||||
window.addEventListener( 'vrdisplaypresentchange', function ( event ) {
|
||||
|
||||
button.textContent = event.display.isPresenting ? 'EXIT VR' : 'ENTER VR';
|
||||
|
||||
}, false );
|
||||
|
||||
window.addEventListener( 'vrdisplayactivate', function ( event ) {
|
||||
|
||||
event.display.requestPresent( [ { source: renderer.domElement } ] );
|
||||
|
||||
}, false );
|
||||
|
||||
navigator.getVRDisplays()
|
||||
.then( function ( displays ) {
|
||||
|
||||
if ( displays.length > 0 ) {
|
||||
|
||||
showEnterVR( displays[ 0 ] );
|
||||
|
||||
} else {
|
||||
|
||||
showVRNotFound();
|
||||
|
||||
}
|
||||
|
||||
} ).catch( showVRNotFound );
|
||||
|
||||
return button;
|
||||
|
||||
} else {
|
||||
|
||||
var message = document.createElement( 'a' );
|
||||
message.href = 'https://webvr.info';
|
||||
message.innerHTML = 'WEBVR NOT SUPPORTED';
|
||||
|
||||
message.style.left = 'calc(50% - 90px)';
|
||||
message.style.width = '180px';
|
||||
message.style.textDecoration = 'none';
|
||||
|
||||
stylizeElement( message );
|
||||
|
||||
return message;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// DEPRECATED
|
||||
|
||||
checkAvailability: function () {
|
||||
console.warn( 'WEBVR.checkAvailability has been deprecated.' );
|
||||
return new Promise( function () {} );
|
||||
},
|
||||
|
||||
getMessageContainer: function () {
|
||||
console.warn( 'WEBVR.getMessageContainer has been deprecated.' );
|
||||
return document.createElement( 'div' );
|
||||
},
|
||||
|
||||
getButton: function () {
|
||||
console.warn( 'WEBVR.getButton has been deprecated.' );
|
||||
return document.createElement( 'div' );
|
||||
},
|
||||
|
||||
getVRDisplay: function () {
|
||||
console.warn( 'WEBVR.getVRDisplay has been deprecated.' );
|
||||
}
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user