block by nitaku b25e6f091e97667c6cae

3D building with zoom and pan (three.js)

Full Screen

A slightly convoluted example, showing how to enable d3.js’s zoom behavior on a 3D scene with ortographic camera in three.js (see the previous example). Since moving the camera seems difficult (and not supported by three.js’s built-in controls), the code acts on the camera extents (top, bottom, left and right) and updates its projection matrix explicitly.

A different method for specfifying camera orientation is also used, which needs the definition of the camera’s up vector.

index.js

// Generated by CoffeeScript 1.4.0
(function() {
  var DZOOM, OX, OY, aspect, camera, height, light, loader, render, renderer, scene, view, width, zoom;

  width = 960;

  height = 500;

  aspect = width / height;

  DZOOM = 10;

  scene = new THREE.Scene();

  camera = new THREE.OrthographicCamera(0, 2 * DZOOM * aspect, 0, -2 * DZOOM, -1000, 1000);

  renderer = new THREE.WebGLRenderer({
    alpha: true,
    antialias: true
  });

  renderer.setSize(width, height);

  document.body.appendChild(renderer.domElement);

  light = new THREE.DirectionalLight(0xffffff, 1);

  light.position.set(10, -20, 15);

  scene.add(light);

  OX = -35;

  OY = -5;

  camera.position.set(OX + 20, OY - 20, 20);

  camera.up = new THREE.Vector3(0, 0, 1);

  camera.lookAt(new THREE.Vector3(OX, OY, 0));

  render = function() {
    return renderer.render(scene, camera);
  };

  loader = new THREE.ColladaLoader();

  loader.load('CNR_master.dae', function(collada) {
    collada.scene.scale.set(0.005, 0.005, 0.005);
    scene.add(collada.scene);
    return render();
  });

  view = d3.select(renderer.domElement);

  zoom = d3.behavior.zoom().scaleExtent([0.2, 4]).on('zoom', function() {
    var x, y, z, _ref;
    z = zoom.scale();
    _ref = zoom.translate(), x = _ref[0], y = _ref[1];
    return window.requestAnimationFrame(function() {
      x = x - width / 2;
      y = y - height / 2;
      camera.left = -DZOOM / z * aspect - x / width * DZOOM / z * 2 * aspect;
      camera.right = DZOOM / z * aspect - x / width * DZOOM / z * 2 * aspect;
      camera.top = DZOOM / z + y / height * DZOOM / z * 2;
      camera.bottom = -DZOOM / z + y / height * DZOOM / z * 2;
      camera.updateProjectionMatrix();
      return render();
    });
  });

  view.call(zoom);

}).call(this);

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>3D Building with zoom and pan (three.js)</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
    <script src="//d3js.org/d3.v3.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r69/three.min.js"></script>
    <script src="https://cdn.rawgit.com/mrdoob/three.js/r69/examples/js/loaders/ColladaLoader.js"></script>
  </head>
  <body>
    <script src="index.js"></script>
  </body>
</html>

index.coffee

width = 960
height = 500
aspect = width/height
DZOOM = 10

scene = new THREE.Scene()
# this is configured to have the upper-left corner as origin
camera = new THREE.OrthographicCamera(0, 2*DZOOM*aspect, 0, -2*DZOOM, -1000, 1000)

renderer = new THREE.WebGLRenderer
  alpha: true
  antialias: true

renderer.setSize(width, height)
document.body.appendChild(renderer.domElement)

# create the light
light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(10, -20, 15)
scene.add(light)

# set the camera (upper-left corner!)
OX = -35
OY = -5
camera.position.set(OX+20, OY-20, 20)
camera.up = new THREE.Vector3(0,0,1)
camera.lookAt(new THREE.Vector3( OX, OY, 0 ))

render = () ->
  renderer.render(scene, camera)

# load DAE
loader = new THREE.ColladaLoader()
# loader.options.convertUpAxis = true
loader.load 'CNR_master.dae', (collada) ->  
  collada.scene.scale.set(0.005,0.005,0.005)
      
  scene.add collada.scene
  render()
  
# pan and zoom
view = d3.select(renderer.domElement)

# define a zoom behavior
zoom = d3.behavior.zoom()
  .scaleExtent([0.2,4])
  .on 'zoom', () ->
    z = zoom.scale()
    [x, y] = zoom.translate()
    window.requestAnimationFrame () ->
      x = x-width/2
      y = y-height/2
      camera.left = -DZOOM/z*aspect - x/width*DZOOM/z*2*aspect
      camera.right = DZOOM/z*aspect - x/width*DZOOM/z*2*aspect
      camera.top = DZOOM/z + y/height*DZOOM/z*2
      camera.bottom = -DZOOM/z + y/height*DZOOM/z*2
      camera.updateProjectionMatrix()
      render()

view.call(zoom)

index.css