Tuesday, December 13, 2011

Multi-Processing


One of the main points of this multi-processing class was to become acclimated to the use of web workers to improve the execution speed of particular actions.One example we used was program that used two workers to add a group of numbers. In principle, the workers were to take turns accessing the numbers and keeping separate totals to be added together in the end.
From these results, though, we encountered a problem where one worker would do all of the work without the other worker ever having a chance involved. This problem occurs due to the setup, in which the workers communicate through the main to request access to the data and while one has access the other worker is forced to wait, which could then lead to the same worker gaining repeated access to the data. To avoid a deadlock, where both attempt to gain access at the same time causing the program to freeze or crash, a provision is added for one worker to concede to the other if they both are making a request at the same time. And this is the reason for one worker being allowed to do all of the work.
Another example used was a Pi calculator using multiple web workers. The purpose here was to observe how quickly multiple web workers could perform the calculation.
The results show that while workers perform the calculations relatively quickly, the addition of more workers increases the time it takes for all workers to complete their tasks.



Thursday, October 6, 2011

porting to the newest version of three.js release r45

Three.js recently updated to release r45 which includes major changes in the way cameras are set up. Basically the camera and the "controller" which moves around the users viewpoint have been separated. This post is to walk through the steps of changing from the old style of camera setup to the new.

Old setup

Initialization
function init() {
 container = document.createElement("div");
 document.body.appendChild(container);

 scene = new THREE.Scene();

 addCamera();
 addLights();
 addGrid();
 for (var i = 0; i <= NUM; i++)  //add cubes and projectile to the scene
  createCube(i);

 renderer = new THREE.WebGLRenderer();  //set up for rendering
 renderer.setSize(window.innerWidth, window.innerHeight);
 renderer.setClearColor(new THREE.Color(0x99CCFF), 1);

 container.appendChild(renderer.domElement);

 setEventListeners();  //double click to fire cannon
}
Camera Setup
function addCamera() {
 camera = new THREE.TrackballCamera({
  fov: 60,
  aspect: window.innerWidth / window.innerHeight,
  near: 1,
  far: 1e3,
  rotateSpeed: 1.0,
  zoomSpeed: 1.2,
  panSpeed: 0.8,
  noZoom: false,
  noPan: false,
  staticMoving: true,
  dynamicDampingFactor: 0.3,
  keys: [65, 83, 68]
 });
 camera.position.x = -15;
 camera.position.y = 6;
 camera.position.z = 15;
 camera.target.position.y = 6.0;
}
Render Loop
function render() {
 // Resize client if necessary
 if (w !== window.innerWidth || h !== window.innerHeight) {
  renderer.setSize(window.innerWidth, window.innerHeight);
  // set old sizes for comparison again
  w = window.innerWidth, h = window.innerHeight;
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
 }

 renderer.render(scene, camera);
}

Step 1 - The Init() needs to have an "addControls" function after the renderer is setup and an "onWindowResize" handler is a much better practice than having an "if resized" in the middle of the render loop
function init() {
 container = document.createElement("div");
 document.body.appendChild(container);

 scene = new THREE.Scene();

 addCamera();
 addLights();
 addGrid();
 for (var i = 0; i <= NUM; i++)  //add cubes and projectile to the scene
  createCube(i);

 renderer = new THREE.WebGLRenderer();  //set up for rendering
 renderer.setSize(window.innerWidth, window.innerHeight);
 renderer.setClearColor(new THREE.Color(0x99CCFF), 1);

 container.appendChild(renderer.domElement);

 addControls();  //NEW with R45

 setEventListeners();  //double click to fire cannon
 window.addEventListener( 'resize', onWindowResize, false );  //NEW with R45
 
} 
Step 2 Many of the camera parameters have migrated to the controller so our addCamera() function is smaller - NOTE the removal of all "camera.target" code which will have to migrate to the controller
function addCamera() {
 
 camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,1,1e3); 
 camera.position.x = -15;
 camera.position.y = 6;
 camera.position.z = 15;
 //camera.target.position.y = 6.0;  //camera.target doesn't exist in r45 
}
Step 3 Many of the old camera parameters and a few snazzy new ones are used to make a controller for the camera.
function addControls(){
 controls = new THREE.TrackballControls( camera, renderer.domElement );
 controls.rotateSpeed = 1.0;
 controls.zoomSpeed =1.2;
 controls.panSpeed = 0.8;
 controls.noZoom = false;
 controls.noPan = false;
 controls.staticMoving = true;
 controls.dynamicDampingFactor =  0.3;
 controls.keys = [65, 83, 68];
 controls.minDistance = 1.1;  //new with r45
 controls.maxDistance = 100;  //new with r45
}
Step 4 The new window resize handler changes the all relevant properties including the controller which would otherwise act strangely by miscalculating various mouse clicks and drags in a resized window
function onWindowResize( event ) {

    width = window.innerWidth;
    height = window.innerHeight;

    renderer.setSize( width, height );

    camera.aspect = width / height;
    camera.updateProjectionMatrix();

    controls.screen.width = width;  //new in r45
    controls.screen.height = height; //new in r45

    camera.radius = ( width + height ) / 4;

 };
Step 5 Render is cleaner without the old resize branch and a new function call is made each loop to the controller with "controls.update()"
function render() {
 controls.update();  //new in r45

 renderer.clear();
 renderer.render(scene, camera);
}

There are a few more changes in r45 - notably scene.add is now overloaded so various lights and objects can be added with scene.add(something) but you code should now run and in the future it will be easier to make custom controllers which are separate from the camera

strelz

Saturday, September 24, 2011

Live demo

Rendering with three.js/webgl
ammo.js in one worker
communication with pmrpc.js
 live demo

Friday, September 23, 2011

PMRPC.js 3D

PMRPC.js for Physics Web Workers

This stream of ideas began with a simple prototype which moved ammo.js physics into a worker. Rendering with three.js/webgl library was smoother with ammo.js running in the worker but the code was a mess. see live demo of original non-pmrpc.js version here
pmrpc .js was used to clean up the code by wrapping up communication between the main (rendering thread) and the worker (physics thread). This post is to explain the current state of code as an example of use of pmrps.js for communication with workers and as a useful test-bench for further experimentation with physics in workers.

The example has three files:
  • index.html: loads libraries used by the main thread
  • client.js: the main thread which renders the scene with three.js
  • worker.js: a single worker which runs ammo.js
Index.html is very simple so we're gong to begin by explaining how client.js works:

client.js (1) sets globals, (2) initializes the rendering of the scene, (3) sets up the mouse listeners, (4) sends an initialization message to the worker to start up physics simulation with ammo.js, (5) receives and processes updates from physics and (6) passes user commands to the worker.

(1) setting globals: these are mostly properties needed either by the rendering engine (such as window size) or by the physics engine (such as number of blocks to simulate).  The array "boxes" will be used for the keeping track and moving around all the dynamic objects in the scene.

var shots = 5;
var NUM = 30;  //number of blocks to simulate
var container, stats;
var camera, scene, renderer, objects;
var pointlight;
var dt;
var myprojectiletype = "sphere";  //"box" //"cone"
var myprojectilespeed = 150;
var w = window.innerWidth;
var h = window.innerHeight;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var workerCount = 1;
var delta = 40;  //physics time step in ms
var boxes = [];

(2) initialize the rendering of the scene: camera, lights, floor, objects and the webgl renderer are all created.

function init() { 
container = document.createElement("div"); 
document.body.appendChild(container); 
scene = new THREE.Scene(); 
addCamera(); 
addLights(); 
addGrid(); 
for (var i = 0; i <= NUM; i++)  //add cubes and projectile to the scene  
  createCube(i); 
renderer = new THREE.WebGLRenderer();  //set up for rendering 
renderer.setSize(window.innerWidth, window.innerHeight); 
renderer.setClearColor(new THREE.Color(0x99CCFF), 1); 
container.appendChild(renderer.domElement);
creating the camera: this is a standard three.js "trackball camera" which allows the user to use the three mouse buttons to rotate, zoom and pan through the scene.
function addCamera() { 
camera = new THREE.TrackballCamera({  
   fov: 60,  
   aspect: window.innerWidth / window.innerHeight,  
   near: 1,  far: 1e3,  
   rotateSpeed: 1.0,  zoomSpeed: 1.2,  panSpeed: 0.8,  
   noZoom: false,  noPan: false,  staticMoving: true,  
   dynamicDampingFactor: 0.3,  
   keys: [65, 83, 68] }); 
   camera.position.x = -15; 
   camera.position.y = 6; 
   camera.position.z = 15; 
   camera.target.position.y = 6.0;}

adding lights: we add a point light which will provide nice shading for our Lambert textures and ambient light so the back sides of the cubes don't look too dark.

function addLights() { 
   var pointLight = new THREE.PointLight(0xffffff); 
   pointLight.position.set(20, 30, 20); 
   scene.addLight(pointLight); 
   var ambientLight = new THREE.AmbientLight(0x909090); 
   scene.addLight(ambientLight);}

adding the floor: we add a simple plane and add a texture with grass to provide a ground plane for our scene. The physical ground plane upon which objects bounce will be added separately in the worker later. Here is an example jsfiddle with a tilted physical ground plane here 
function addGrid() { 
   var geometry = new THREE.PlaneGeometry(100, 100); 
   var xm = []; 
   xm.push(new THREE.MeshBasicMaterial({  map: THREE.ImageUtils.loadTexture('textures/bigGrass.jpg') })); 
   /*  xm.push(new THREE.MeshBasicMaterial({  color: 0x000000,  wireframe: true  }));*/  
   geometry = new THREE.PlaneGeometry(100, 100, 40, 40);  
   var ground = new THREE.Mesh(geometry, xm);  
   ground.position.set(0, 0, 0);  
   ground.rotation.x = -1.57;  
   scene.addObject(ground);}

adding the dynamic objects: boxes[0] is the projectile so it gets different treatment. In general a material is created (baseball for the projectile, bricks for the wall) and then a geometry is created (sphere for the ball, cubes for the wall). Material and Geometry are combined to make a three.js mesh which is then added to the scene. Before physics takes effect you will see a stack of objects at the origin - this is the default position of the objects before physics takes over.

function createCube(i) { 
   var material = []; 
   material.push(new THREE.MeshLambertMaterial({  map: THREE.ImageUtils.loadTexture('textures/redbrick.jpg') })); 
   if((myprojectiletype == "sphere") && (i == 0)){  
      var geometry = new THREE.SphereGeometry(1.4, 16, 8);  
      material.push(new THREE.MeshLambertMaterial({   map:       THREE.ImageUtils.loadTexture('textures/baseball.jpg')  })); 
   } 
   else if ((myprojectiletype == "cone") && (i == 0)){  
      var geometry = new THREE.CylinderGeometry(16, 0.1, 1.0, 1.4);  
      material.push(new THREE.MeshBasicMaterial({"color":0x000000,"wireframe":true})); 
   } 
   else{  
      var geometry = new THREE.CubeGeometry(2, 2, 2); 
   } 
   boxes[i] = new THREE.Mesh(geometry, material); 
   boxes[i].position.x = 0;
   boxes[i].position.y = (i * 10) + 5;
   boxes[i].position.z = 0; 
   if ((myprojectiletype == "cone") && (i == 0)){  
       boxes[i].rotation.x = Math.Pi/2; 
   } 
   scene.addObject(boxes[i]);
}
creating the renderer: boilerplate code for adding the webgl div to the body of the html page and attaching the three.js renderer to the div. 0x99CCFF is for the baby blue sky background.

container = document.createElement("div"); 
   document.body.appendChild(container); 
   renderer = new THREE.WebGLRenderer();  //set up for rendering        
   renderer.setSize(window.innerWidth, window.innerHeight);
   renderer.setClearColor(new THREE.Color(0x99CCFF), 1);     
   container.appendChild(renderer.domElement);

(3) create mouse listeners: the camera has built-in listeners for the mouse so we just need to add a "double-click" listener which we will use to trigger calls the physics worker .
function setEventListeners() { 
   document.addEventListener('dblclick', onDocumentMouseDown, false);
}
function onDocumentMouseDown(event) { 
   if(shots == 0){  
       wall();  //5 shots completed - tell worker to rebuild the wall  
       shots = 5; 
    } else{    
       fire(event.clientX,event.clientY);  //tell the worker to fire the ball 
       shots--; 
}}

(4) initialize the worker: this code creates a worker with the code worker.js and uses a pmrpc call to start up the ammo.js physics engine on the worker.
function startworkers(){ 
    workers = new Array(workerCount);  //in this example 1 worker 
    for (var i = 0; i < workerCount; i++) {  
       workers[i] = new Worker("js/worker.js");  //create the worker 
    } 
    for (var i = 0; i < workerCount; i++) {  //call "initWorker" on the worker  
       pmrpc.call( {   destination : workers[i],   
                       publicProcedureName : "initWorker",  
                       params : [delta,NUM] } ); //send the worker time steps to take and # blocks } 
(5) Listen for updates from the physics worker: this code registers the "update" function which the worker will call to set the positions of the ball and blocks: NOTE- update changes the position of one item for each call - it would seem logical that updating all positions in one call would have less overhead but it proved to be slower (?).
pmrpc.register( {  
      publicProcedureName : "update",  
      procedure : function(i,position,quaternion) 
     {   
          boxes[i].position.x = position[0];  
          boxes[i].position.y = position[1];   
          boxes[i].position.z = position[2];   
          boxes[i].quaternion.x = quaternion[0];  
          boxes[i].quaternion.y = quaternion[1];   
          boxes[i].quaternion.z = quaternion[2];   
          boxes[i].quaternion.w = quaternion[3];   
          boxes[i].useQuaternion = true;  }});
     }
(6) pass user commands to the physics worker: the main thread has two functions, wall and fire which are calls to the worker to make changes in the physical scene.
//pmrpc calls to various physics functions
function wall(){
 for (var i = 0; i < workerCount; i++) {
  //workers[i]. postMessage('{"type":"wall"}');
  pmrpc.call( {
   destination : workers[i],
   publicProcedureName : "wall",
  params : [] } );
 }
}

function fire(x,y) {
 var vector = new THREE.Vector3((x / window.innerWidth) * 2 - 1,
 -(y / window.innerHeight) * 2 + 1,
 1);
 var projector = new THREE.Projector();
 projector.unprojectVector(vector, camera);
 vector.normalize();
 vector.multiplyScalar(myprojectilespeed);
 for (var i = 0; i < workerCount; i++) {
  pmrpc.call( {
   destination : workers[i],
   publicProcedureName : "fire",
  params : [camera.position,vector] } );
 }
}


In this view, web worker is used in a 3D graphic engine. The following code shows us how to declare and make a window for a stack of boxes
on a plane.
Using Web Worker with pmrpc
init(); startworkers(); animate();  function init() { container = document.createElement("div"); document.body.appendChild(container); scene = new THREE.Scene();  addCamera(); addLights(); addGrid(); for (var i = 0; i <= NUM; i++)  //add cubes and projectile to the scene createCube(i); renderer = new THREE.WebGLRenderer(); //set up for rendering renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(new THREE.Color(0xffff99), 1); container.appendChild(renderer.domElement); setEventListeners(); //double click to fire cannon }


The init function renders the scene, camera, lights, and the container, which has a div element. We are using a library from a software called THREE.js and WebGL. Notice the instantiation for the scene and renderer when we initialize our scene and renderer variable. In the For loop, Strelz's team are creating the number of boxes that was declared in the previous section.

function addCamera() { camera = new THREE.TrackballCamera({ fov: 60, aspect: window.innerWidth / window.innerHeight, near: 1, far: 1e3, rotateSpeed: 1.0, zoomSpeed: 1.2, panSpeed: 0.8, noZoom: false, noPan: false, staticMoving: true, dynamicDampingFactor: 0.3, keys: [65, 83, 68] }); camera.position.x = -15; camera.position.y = 6; camera.position.z = 15; camera.target.position.y = 6.0; } 


Capturing the graphics at a different angle shows some of the attributes that are embedded in the THREE.js library. We instantiate an object called TrackballCamera which provides several attributes. For example, we can modify the window, rotate speed, zoom speed, camera position, etc.

//worker setup - starts worker(s) and registers pmrpc function for physics updates function startworkers()  { workers = new Array(workerCount); for (var i = 0; i < workerCount; i++) { workers[i] = new Worker("worker.js"); }  for (var i = 0; i < workerCount; i++) { pmrpc.call( {  destination : workers[i], publicProcedureName : "initWorker", params : [delta,NUM] } ); }  pmrpc.register( {  publicProcedureName : "update",  procedure : function(i,position,quaternion) {  boxes[i].position.x = position[0]; boxes[i].position.y = position[1]; boxes[i].position.z = position[2]; boxes[i].quaternion.x = quaternion[0]; boxes[i].quaternion.y = quaternion[1]; boxes[i].quaternion.z = quaternion[2]; boxes[i].quaternion.w = quaternion[3]; boxes[i].useQuaternion = true; }}); }   //pmrpc calls to various physics functions function wall(){  for (var i = 0; i < workerCount; i++)  {      //workers [i]. postMessage('{"type":"wall"}'); pmrpc.call( {  destination : workers[i], publicProcedureName : "wall", params : [] } ); } } 


The function startworkers starts the worker(s) and registers the pmrpc function for physics updates. The For loop loops the number of workers that is being used. The pmrpc "call" object has a destination function, which calls the web worker. The publicProcedureName's function calls the initWorker function, which acts as the main for the web worker to communicate. The params function takes in the delta variable to starts the physics clock in milli-seconds, and NUM is the number of boxes you want to create. The pmrpc register object has a publicProceduralName variable defined as "update" that waits for an update of positions from the web worker, which is in the "worker.js" file.

function fire(x,y) { var vector = new THREE.Vector3((x / window.innerWidth) * 2 - 1, -(y / window.innerHeight) * 2 + 1, 1); var projector = new THREE.Projector(); projector.unprojectVector(vector, camera); vector.normalize(); vector.multiplyScalar(myprojectilespeed);   for (var i = 0; i < workerCount; i++) { pmrpc.call( { destination : workers[i], publicProcedureName : "fire", params : [camera.position,vector] } ); } } 


In this section, we are looking at a sphere instantiated by an object called Vector3. The variable "vector" is combine with the attribute "projector" to simulate a ball traveling at a certain speed toward the background.

//creates and renders the animation
function animate() {
requestAnimationFrame(animate);
render();
}