Three.js – 3D Lineart

Just ported the previosly created Lineart into the Three.js version. It’s getting less time, but still the lack of API reference is a difficulty for me. Once it falls out of the tutorials, it requires diving into the deep Google searching, hmm.

It seems working, but not sure if it’s ok to have multiple geometries. Consolidating them provides better performance?

Screenshot
lineart_3d_paticle

Working Example
http://jsdo.it/parroty/6bZz

Source Code
It’s partial excerpt, which is related to Three.js. The full code can be referred in the above jsdo.it site.

var stats, camera, scene, renderer;
var lineart;
var geometries, materials;

var windowHalfX = window.innerWidth  / 2;
var windowHalfY = window.innerHeight / 2;

// parameters
var CAMERA_SPEED = 0.05;
var MOUSE_RANGE_MULTIPLY = 3;
var FRAME_RATE_WAIT = 1000 / 30;
var INITIAL_CAMERA_ZPOS = 1250;
var INITIAL_CAMERA_YPOS = 1250;
var BOX_SIZE = 1000;
var PARTICLE_SIZE = 10;
var PARTICLE_DURATION = 100;
var PARTICLES_COUNT = 10;
var PARTICLES_SPEED_MAX = 3;

var particles_meshes = [];
var particles_geos   = [];
var particles_life   = [];

var count = 0;
var mouseX = -300;
var mouseY = -300;

var canvas;
var context;
var texture;

init();
animate();

function init() {
  var container = document.createElement('div');
  document.body.appendChild(container);

  //camera and scene
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = INITIAL_CAMERA_ZPOS;
  camera.position.y = INITIAL_CAMERA_YPOS;
  scene = new THREE.Scene();

  //lineart object
  Lineart.initScreenSize(BOX_SIZE, BOX_SIZE, BOX_SIZE);
  lineart = new Lineart();

  //geometry for cube box
  cubeGeometry = new THREE.CubeGeometry(BOX_SIZE, BOX_SIZE, BOX_SIZE);
  material = new THREE.MeshBasicMaterial({
    color: 0xffffff, wireframe: true, transparent: true, opacity: 0.4, side: THREE.DoubleSide});
  mesh = new THREE.Mesh(cubeGeometry, material);
  scene.add(mesh);

  //geometry for lines
  geometries = [], materials = [];
  var polygons = lineart.polygons;
  for(var i = 0; i < polygons.length; i++) {
    var points = polygons[i].points;
    var pm = new THREE.LineBasicMaterial({color: 0x000000, opacity: 1.0, linewidth: 1});
    var pg = new THREE.Geometry();
    for(var j = 0; j < points.length + 1; j++) {
      pg.vertices.push(new THREE.Vector3(0, 0, 0));
    }
    scene.add(new THREE.Line(pg, pm));

    geometries.push(pg);
    materials.push(pm);
  }

  //init texture for particles
  canvas = document.createElement('canvas');
  canvas.width = 100;
  canvas.height = 100;

  context = canvas.getContext('2d');
  context.fillStyle = 'black';
  context.fillRect( 0, 0, 100, 100 );
  context.fillStyle = 'white';
  context.beginPath();
  context.arc(50, 50, 50, 0, 2 * Math.PI, false);
  context.stroke();
  context.closePath();
  context.fill();

  texture = new THREE.Texture(canvas);
  texture.needsUpdate = true;

  //renderer
  renderer = new THREE.WebGLRenderer({antialias: true});
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.appendChild(renderer.domElement);

  //stats
  stats = new Stats();
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.top = '0px';
  container.appendChild(stats.domElement);

  //events
  document.addEventListener('mousemove', onDocumentMouseMove, false);
  window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize() {
  windowHalfX = window.innerWidth  / 2;
  windowHalfY = window.innerHeight / 2;

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);
}

function onDocumentMouseMove(event) {
  mouseX = event.clientX - windowHalfX;
  mouseY = event.clientY - windowHalfY;
}

function generateParticles(x, y, z) {
  var geo = new THREE.Geometry();
  geo.direction = [];
  geo.speed = [];

  for(var i = 0; i < PARTICLES_COUNT; i++) {
    geo.vertices.push(new THREE.Vector3(x - 0.5 * BOX_SIZE, y - 0.5 * BOX_SIZE, z - 0.5 * BOX_SIZE));
    geo.direction.push(new THREE.Vector3(Math.random()*2 - 1, Math.random()*2 - 1, Math.random()*2 - 1));
    geo.speed.push(Math.random()*PARTICLES_SPEED_MAX);

    var color = new THREE.Color(0xFFFFFF);
    color.setRGB(Math.random(), Math.random(), Math.random());
    geo.colors.push(color);
  }

  var material = new THREE.ParticleBasicMaterial({
    map: texture,
    size: PARTICLE_SIZE,
    blending: THREE.AdditiveBlending,
    depthTest: false,
    vertexColors: true,
    transparent: true
  });

  var ps = new THREE.ParticleSystem(geo, material);
  ps.sortParticles = true;
  scene.add(ps);

  particles_meshes.push(ps);
  particles_geos.push(geo);
  particles_life.push(0);
}

function updateParticles() {
  for(var i = 0; i < particles_geos.length; i++) {
    var geo = particles_geos[i];

    for(var j = 0; j < PARTICLES_COUNT; j++) {
      var position  = geo.vertices[j];
      var direction = geo.direction[j];
      var speed     = geo.speed[j];

      position.x += direction.x * speed;
      position.y += direction.y * speed;
      position.z += direction.z * speed;
    }
  }
}

function animate() {
  setTimeout(function() {
    requestAnimationFrame(animate);
  }, FRAME_RATE_WAIT);
  render();
  stats.update();
}

function render() {
  camera.position.x += ( mouseX * MOUSE_RANGE_MULTIPLY - camera.position.x) * CAMERA_SPEED;
  camera.position.y += (-mouseY * MOUSE_RANGE_MULTIPLY - camera.position.y) * CAMERA_SPEED;
  camera.lookAt(scene.position);

  lineart.move();
  var polygons = lineart.polygons;
  for(var i = 0; i < polygons.length; i++) {
    var polygon = polygons[i];
    var color   = polygon.color.to_i();
    var points  = polygon.points;

    for(var j = 0; j < points.length + 1; j++) {
      var v = geometries[i].vertices[j];
      var p = points[j % points.length];

      v.x = p.x - 0.5 * BOX_SIZE;
      v.y = p.y - 0.5 * BOX_SIZE;
      v.z = p.z - 0.5 * BOX_SIZE;
    }
    geometries[i].verticesNeedUpdate = true;
    materials[i].color.setHex(color);
  }

  while(PointQueue.size() > 0) {
    var p = PointQueue.pop();
    generateParticles(p[0], p[1], p[2]);
  }

  updateParticles();
  renderer.render(scene, camera);

  var dieCount = 0
  for(var i = 0; i < particles_life.length; i++) {
    particles_life[i] += 1;
    if(particles_life[i] > PARTICLE_DURATION) {
      dieCount += 1;
    }
  }

  for(var i = 0; i < dieCount; i++) {
    var mesh = particles_meshes.shift();
    var geos = particles_geos.shift();
    var life = particles_life.shift();
    scene.remove(mesh);
  }
}

Posted on April 22, 2013, in JavaScript. Bookmark the permalink. Leave a comment.

Leave a comment