Last active
November 22, 2016 07:49
-
-
Save nickjanssen/21b46c8c7002eb2d60caadc0f785536d to your computer and use it in GitHub Desktop.
Example code showing how to create a Google Cardboard web demo. Link: http://cardboard.nickjanssen.com/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react' | |
const sceneConfig = { | |
url: 'Classroom/scene.json', | |
clearColor: 0xdbfff8, | |
startPosition: new THREE.Vector3(3, 2, 3), | |
lookAt: new THREE.Vector3(0, 2, 0) | |
} | |
const isMobile = typeof window.orientation !== 'undefined' | |
export default class App extends Component { | |
constructor(props) { | |
super(props) | |
this.state = { | |
loaded: false, | |
started: false, | |
enabledVR: isMobile | |
} | |
} | |
componentDidMount() { | |
const clock = new THREE.Clock() | |
const renderer = new THREE.WebGLRenderer() | |
renderer.setPixelRatio( window.devicePixelRatio ) | |
renderer.setSize( window.innerWidth, window.innerHeight ) | |
this._container.appendChild(renderer.domElement) | |
const effect = new THREE.StereoEffect( renderer ) | |
effect.setSize( window.innerWidth, window.innerHeight ) | |
const loader = new THREE.ObjectLoader() | |
const scene = new THREE.Scene() | |
const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 1000 ) | |
const deviceOrientationControls = new THREE.DeviceOrientationControls( camera ) | |
const mouseControls = new THREE.MouseControls( camera ) | |
let touchMoveForward = false | |
window.position = camera.position | |
window.addEventListener('resize', () => { | |
camera.aspect = window.innerWidth / window.innerHeight | |
camera.updateProjectionMatrix() | |
renderer.setSize( window.innerWidth, window.innerHeight ) | |
}, false) | |
const init = () => { | |
loader.load(`scenes/${sceneConfig.url}?v1_1`, (mesh) => { | |
renderer.setClearColor(sceneConfig.clearColor) | |
scene.add(mesh) | |
this.setState({ | |
loaded: true | |
}) | |
camera.position.copy(sceneConfig.startPosition) | |
camera.lookAt(sceneConfig.lookAt) | |
}) | |
// Add a cubemap/skybox | |
const imagePrefix = 'images/skybox/saitama_park_2048x1024_' | |
const directions = ['left', 'right', 'up', 'down', 'front', 'back'] | |
const imageSuffix = '.png' | |
const skyGeometry = new THREE.CubeGeometry( 50, 50, 50 ) | |
const materialArray = [] | |
const textureLoader = new THREE.TextureLoader() | |
for (let i = 0; i < 6; i++) { | |
materialArray.push( new THREE.MeshBasicMaterial({ | |
map: textureLoader.load( imagePrefix + directions[i] + imageSuffix ), | |
side: THREE.BackSide | |
})) | |
} | |
const skyMaterial = new THREE.MeshFaceMaterial( materialArray ) | |
const skyBox = new THREE.Mesh( skyGeometry, skyMaterial ) | |
scene.add( skyBox ) | |
// end cubemap/skybox code | |
renderer.domElement.addEventListener( 'touchstart', () => { | |
touchMoveForward = true | |
}) | |
document.addEventListener( 'touchmove', (e) => { | |
e.preventDefault() | |
}) | |
renderer.domElement.addEventListener( 'touchend', () => { | |
touchMoveForward = false | |
}) | |
} | |
const tick = (dt) => { | |
if (isMobile) { | |
deviceOrientationControls.update(dt) | |
} | |
if (!isMobile) { | |
mouseControls.update(dt) | |
} | |
if (touchMoveForward) { | |
camera.translateZ(-dt * 2) | |
} | |
camera.position.clamp(new THREE.Vector3(-4.6, 1.4, -3.2), new THREE.Vector3(4.6, 1.4, 3.2)) | |
} | |
const animate = () => { | |
const delta = clock.getDelta() | |
requestAnimationFrame(animate) | |
tick(delta) | |
if (this.state.enabledVR) { | |
effect.render(scene, camera) | |
} | |
else { | |
renderer.render(scene, camera) | |
} | |
} | |
this.renderer = renderer | |
init() | |
animate() | |
} | |
render() { | |
return ( | |
<div ref={(c) => this._container = c} className="render-view"> | |
<div className={`blocker ${this.state.started && 'loaded'}`}> | |
<div className="instructions"> | |
<div className="section"> | |
<h1>Google Cardboard Web Demo</h1> | |
</div> | |
<div className="section"> | |
{this.state.loaded ? | |
<button onClick={() => { | |
this.setState({ | |
started: true | |
}) | |
}} className={'button'}>Start</button> | |
: | |
<div className="spinner"> | |
<div className="double-bounce1"></div> | |
<div className="double-bounce2"></div> | |
</div> | |
} | |
</div> | |
<div className="section"> | |
{isMobile ? | |
<img className="cb-icon" src="images/rotate-instructions.svg" /> | |
: | |
<div> | |
<p className="warning">Mobile device not detected.</p> | |
<p>Arrow keys to move.<br />Mouse to look around.</p> | |
</div> | |
} | |
</div> | |
<div className="section"> | |
{isMobile ? | |
<div> | |
<p>Rotate device to look around.<br />Touch screen to move forward.</p> | |
</div> | |
: | |
<img src="images/drag.png" /> | |
} | |
</div> | |
</div> | |
</div> | |
{this.state.started && | |
<div className={`vr-enable-button ${this.state.enabledVR ? 'on' : ''}`}> | |
<img | |
src={'images/google-cardboard.svg'} | |
/> | |
<label className="switch"> | |
<input type="checkbox" checked={this.state.enabledVR} | |
onClick={() => { | |
this.setState({ | |
enabledVR: !this.state.enabledVR | |
}) | |
this.renderer.setSize( window.innerWidth, window.innerHeight ) | |
}} /> | |
<div className="slider round"></div> | |
</label> | |
</div>} | |
</div> | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment