text: var renderer, camera;
  var scene, element;
  var ambient, point;
  var aspectRatio, windowHalf;
  var mouse, time;
  
  var controls;
  var clock;
  
  var useRift = true;
  
  var riftCam;
  
  var boxes = [];
  var core = [];
  var dataPackets = [];
  
  var ground, groundGeometry, groundMaterial;
  
  var bodyAngle;
  var bodyAxis;
  var bodyPosition;
  var viewAngle;
  
  var velocity;
  //var oculusBridge;
  
  
  
  var hmdConfig;
  
  
  
  // Map for key states
  var keys = [];
  for(var i = 0; i < 130; i++){
    keys.push(false);
  }
  
  
  function initScene() {
    clock = new THREE.Clock();
    mouse = new THREE.Vector2(0, 0);
  
    windowHalf = new THREE.Vector2(window.innerWidth / 2, window.innerHeight / 2);
    aspectRatio = window.innerWidth / window.innerHeight;
    
    scene = new THREE.Scene();  
  
    camera = new THREE.PerspectiveCamera(45, aspectRatio, 1, 10000);
    camera.useQuaternion = true;
  
    camera.position.set(100, 150, 100);
    camera.lookAt(scene.position);
  
    // Initialize the renderer
    renderer = new THREE.WebGLRenderer({antialias:true});
    renderer.setClearColor(0xdbf7ff);
    renderer.setSize(window.innerWidth, window.innerHeight);
  
     scene.fog = new THREE.Fog(0xdbf7ff, 300, 700);
  
    element = document.getElementById('viewport');
    element.appendChild(renderer.domElement);
  
    controls = new THREE.OrbitControls(camera);
  }
  
  
  function initLights(){
  
    ambient = new THREE.AmbientLight(0x222222);
    scene.add(ambient);
  
    point = new THREE.DirectionalLight( 0xffffff, 1, 0, Math.PI, 1 );
    point.position.set( -250, 250, 150 );
    
    scene.add(point);
  }
  
  var floorTexture;
  function initGeometry(){
  
    floorTexture = new THREE.ImageUtils.loadTexture( "textures/tile.jpg" );
    floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; 
    floorTexture.repeat.set( 50, 50 );
    floorTexture.anisotropy = 32;
  
    var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, transparent:true, opacity:0.80 } );
    var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
    var floor = new THREE.Mesh(floorGeometry, floorMaterial);
    floor.rotation.x = -Math.PI / 2;
  
    scene.add(floor);
  
    // add some boxes.
    var boxTexture = new THREE.ImageUtils.loadTexture( "textures/blue_blue.jpg" );
    for(var i = 0; i < 200; i++){
      var material = new THREE.MeshLambertMaterial({ emissive:0x505050, map: boxTexture, color: 0xffffff});
      
      var height = Math.random() * 150+10;
      var width = Math.random() * 20 + 2;
      
      var box = new THREE.Mesh( new THREE.CubeGeometry(width, height, width), material);
  
      box.position.set(Math.random() * 1000 - 500, height/2 ,Math.random() * 1000 - 500);
      box.rotation.set(0, Math.random() * Math.PI * 2, 0);
      
      boxes.push(box);
      scene.add(box);
    }
  
    var coreTexture = new THREE.ImageUtils.loadTexture( "textures/purple_blue.jpg" );
    for(var i = 0; i < 50; i++){
      var material = new THREE.MeshLambertMaterial({ emissive:0x505050, map: coreTexture, color: 0xffffff});
      
      var height = Math.random() * 100+30;
      
      var box = new THREE.Mesh( new THREE.CubeGeometry(height, height, height), material);
  
      box.position.set(Math.random() * 1000 - 500, Math.random() * 150 - 300 ,Math.random() * 1000 - 500);
      box.rotation.set(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
      
      core.push(box);
      scene.add(box);
    }
  
    for(var i = 0; i < 100; i++){
      var material = new THREE.MeshLambertMaterial({ emissive:0x008000, color: 0x00FF00});
      
      var size = Math.random() * 15+3;
      
      var box = new THREE.Mesh( new THREE.CubeGeometry(size, size*0.1, size*0.1), material);
  
      box.position.set(Math.random() * 1000 - 500, Math.random() * 100 + 100 ,Math.random() * 1000 - 500);
      //box.rotation.set(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
      
      var speedVector;
      if(Math.random() > 0.5){
        speedVector = new THREE.Vector3(0, 0, Math.random() * 1.5 + 0.5);
        box.rotation.y = Math.PI / 2;
      } else {
        speedVector = new THREE.Vector3(Math.random() * 1.5 + 0.5, 0, 0);
      }
  
      dataPackets.push({
        obj: box,
        speed: speedVector
      });
      scene.add(box);
    }
  }
  
  
  function init(){
  
    document.addEventListener('keydown', onKeyDown, false);
    document.addEventListener('keyup', onKeyUp, false);
    document.addEventListener('mousedown', onMouseDown, false);
    document.addEventListener('mousemove', onMouseMove, false);
  
    document.getElementById("toggle-render").addEventListener("click", function(){
      useRift = !useRift;
      onResize();
    });
  
    document.getElementById("help").addEventListener("click", function(){
      var el = document.getElementById("help-text");
      el.style.display = (el.style.display == "none") ? "" : "none";
    });
  
    window.addEventListener('resize', onResize, false);
  
    time          = Date.now();
    bodyAngle     = 0;
    bodyAxis      = new THREE.Vector3(0, 1, 0);
    bodyPosition  = new THREE.Vector3(0, 15, 0);
    velocity      = new THREE.Vector3();
  
    initScene();
    initGeometry();
    initLights();
    
    /*
    oculusBridge = new OculusBridge({
      "debug" : true,
      "onOrientationUpdate" : bridgeOrientationUpdated,
      "onConfigUpdate"      : bridgeConfigUpdated,
      "onConnect"           : bridgeConnected,
      "onDisconnect"        : bridgeDisconnected
    });
    oculusBridge.connect();
    */
  
    riftCam = new THREE.OculusRiftEffect(renderer);
  
    // make sure it loads up to fill the window right
    onResize();
  }
  
  
  function onResize() {
    if(!useRift){
      windowHalf = new THREE.Vector2(window.innerWidth / 2, window.innerHeight / 2);
      aspectRatio = window.innerWidth / window.innerHeight;
     
      camera.aspect = aspectRatio;
      camera.updateProjectionMatrix();
     
      renderer.setSize(window.innerWidth, window.innerHeight);
    } else {
      riftCam.setSize(window.innerWidth, window.innerHeight);
      updateHMDResolution(window.innerWidth, window.innerHeight);
    }
  }
  
  
  
  function updateHMDResolution(width, height) {
  
    if (!hmdConfig) {
      hmdConfig = riftCam.getHMD();
    }
  
    hmdConfig.hResolution = width;
    hmdConfig.vResolution = height;
  
    riftCam.setHMD(hmdConfig);
  }
  
  function bridgeConnected(){
    document.getElementById("logo").className = "";
  }
  
  function bridgeDisconnected(){
    document.getElementById("logo").className = "offline";
  }
  
  function bridgeConfigUpdated(config){
    console.log("Oculus config updated.");
    riftCam.setHMD(config);      
    onResize();
  }
  
  var quat = new THREE.Quaternion();
  var quatCam = new THREE.Quaternion();
  var xzVector = new THREE.Vector3(0, 0, 1);
  
  function bridgeOrientationUpdated(quatValues) {
    // Do first-person style controls (like the Tuscany demo) using the rift and keyboard.
  
    // Don't instantiate new objects in here, these should be re-used to avoid garbage collection.
  
    // make a quaternion for the the body angle rotated about the Y axis.
    //var quat = new THREE.Quaternion();
    quat.setFromAxisAngle(bodyAxis, bodyAngle);
  
    // make a quaternion for the current orientation of the Rift
    //var quatCam = new THREE.Quaternion(quatValues.x, quatValues.y, quatValues.z, quatValues.w);
    quatCam.set(quatValues.x, quatValues.y, quatValues.z, quatValues.w);
  
    // multiply the body rotation by the Rift rotation.
    quat.multiply(quatCam);
  
  
    // Make a vector pointing along the Z axis and rotate it accoring to the combined look/body angle.
    //var xzVector = new THREE.Vector3(0, 0, 1);
    xzVector.set(0,0,1);
    xzVector.applyQuaternion(quat);
  
    // Compute the X/Z angle based on the combined look/body angle.  This will be used for FPS style movement controls
    // so you can steer with a combination of the keyboard and by moving your head.
    viewAngle = Math.atan2(xzVector.z, xzVector.x) + Math.PI;
  
    // Apply the combined look/body angle to the camera.
    camera.quaternion.copy(quat);
  }
  
  
  function onMouseMove(event) {
    mouse.set( (event.clientX / window.innerWidth - 0.5) * 2, (event.clientY / window.innerHeight - 0.5) * 2);
  }
  
  
  function onMouseDown(event) {
    // Stub
    floorTexture.needsUpdate = true;
    console.log("update.");
  }
  
  
  function onKeyDown(event) {
  
    if(event.keyCode == 48){ // zero key.
      useRift = !useRift;
      onResize();
    }
  
    // prevent repeat keystrokes.
    if(!keys[32] && (event.keyCode == 32)){ // Spacebar to jump
      velocity.y += 1.9;
    }
  
    keys[event.keyCode] = true;
  }
  
  
  function onKeyUp(event) {
    keys[event.keyCode] = false;
  }
  
  
  function updateInput(delta) {
    
    var step        = 25 * delta;
    var turn_speed  = (55 * delta) * Math.PI / 180;
  
  
    // Forward/backward
  
    if(keys[87] || keys[38]){ // W or UP
        bodyPosition.x += Math.cos(viewAngle) * step;
        bodyPosition.z += Math.sin(viewAngle) * step;
    }
  
    if(keys[83] || keys[40]){ // S or DOWN
        bodyPosition.x -= Math.cos(viewAngle) * step;
        bodyPosition.z -= Math.sin(viewAngle) * step;
    }
  
    // Turn
  
    if(keys[81]){ // E
        bodyAngle += turn_speed;
    }   
    
    if(keys[69]){ // Q
         bodyAngle -= turn_speed;
    }
  
    // Straif
  
    if(keys[65] || keys[37]){ // A or LEFT
        bodyPosition.x -= Math.cos(viewAngle + Math.PI/2) * step;
        bodyPosition.z -= Math.sin(viewAngle + Math.PI/2) * step;
    }   
    
    if(keys[68] || keys[39]){ // D or RIGHT
        bodyPosition.x += Math.cos(viewAngle+Math.PI/2) * step;
        bodyPosition.z += Math.sin(viewAngle+Math.PI/2) * step;
    }
    
  
    // VERY simple gravity/ground plane physics for jumping.
    
    velocity.y -= 0.15;
    
    bodyPosition.y += velocity.y;
    
    if(bodyPosition.y < 15){
      velocity.y *= -0.12;
      bodyPosition.y = 15;
    }
  
    // update the camera position when rendering to the oculus rift.
    if(useRift) {
      camera.position.set(bodyPosition.x, bodyPosition.y, bodyPosition.z);
    }
  }
  
  
  function animate() {
    var delta = clock.getDelta();
    time += delta;
    
    updateInput(delta);
    for(var i = 0; i < core.length; i++){
      core[i].rotation.x += delta * 0.25;
      core[i].rotation.y -= delta * 0.33;
      core[i].rotation.z += delta * 0.1278;
    }
  
    var bounds = 600;
    for(var i = 0; i < dataPackets.length; i++){
      dataPackets[i].obj.position.add( dataPackets[i].speed);
      if(dataPackets[i].obj.position.x < -bounds) {
        dataPackets[i].obj.position.x = bounds;
      } else if(dataPackets[i].obj.position.x > bounds){
        dataPackets[i].obj.position.x = -bounds;
      }
      if(dataPackets[i].obj.position.z < -bounds) {
        dataPackets[i].obj.position.z = bounds;
      } else if(dataPackets[i].obj.position.z > bounds){
        dataPackets[i].obj.position.z = -bounds;
      }
    }
  
    requestAnimationFrame(animate);
    render();
  }
  
  
  function render() { 
    if(useRift){
      riftCam.render(scene, camera);
    }else{
      controls.update();
      renderer.render(scene, camera);
    }
  }
  
  
  window.onload = function() {
    init();
    animate();
  }
  
  
  
  
  
  
  //==================
  // Cupola client
  
  var cupola = new Cupola({
    onConnect : bridgeConnected,
    onDisconnect : bridgeDisconnected,
    onConfigUpdate : bridgeConfigUpdated,
    onOrientationUpdate : bridgeOrientationUpdated,
    debug: false
  });
  cupola.connect();
  