Friday, August 31, 2012

Dirt Simple Text with Three.js

‹prev | My Chain | next›

Up today, I would like to display the text "Game Over" when the raft impacts an obstacle in my Three.js / Physijs game. I can already do it in the JavaScript console, but I need a way to convey to the user that she / he has lost.

I investigate TextGeomtry (example) some, but decide that feels like overkill for my needs. I just want to display regular text.

It turns out that simple HTML will suffice for this. That is how the Three.js stats does it. So I replace the "game over" console.log() with a call to a new gameOver() function:
function drawObstacles() {
  obstacle_markers.forEach(function(marker) {
    // build obstacle

    obstacle.addEventListener('collision', function(object) {
      if (object == raft) gameOver();
      else {
        console.log(object);
      }
    });
  });
}
In gameOver(), I create a new <div>, style it, and position it for maximum impact:
function gameOver() {
  console.log("Game Over!!!!");

  var text = document.createElement('div');
  text.style.position = 'absolute';
  text.innerHTML = '<h1>Game Over!</h1>';
  text.style.backgroundColor = 'white';
  text.style.borderRadius = "5px";
  text.style.padding = "0px 20px";
  text.style.left = "100px";
  text.style.top = "100px";
  document.body.appendChild(text);

  game_over = true;
}
For good measure, I set the global variable game_over to true. In my animate() function I then add a guard clause to stop the animation if the game is over:
function animate() {
  if (game_over) return;

  requestAnimationFrame(animate);
  applyRiverCurrent();
  scene.simulate(); // run physics
  render();
  drawObstacles();
}
With that, I have my desired game-over behavior:


That will do for a stopping point today. Now back to writing Gaming JavaScript. It's deadline day!


Day #495

Thursday, August 30, 2012

More Physijs Collisions

‹prev | My Chain | next›

I need to focus more on writing Gaming JavaScript for the next two nights. So the hope is that I can keep these posts relatively short. That's the hope anyway.

I finally came to an understanding with Physijs last night. I was able to use relative frames of reference to mark obstacles in my Three.js game. A second step then places the actual obstacles in the Physijs scene.

Tonight, I undertake a bit of code cleaning (I hope to include this game in the beta release tomorrow). While doing so for a bit, I notice that I am no longer seeing collisions. What could I possibly have done to cause this?

I am able to figure this out with a well-placed console.log() in my obstacle drawing function:
function drawObstacles() {
  markers.forEach(function(marker) {
    var position = marker.matrixWorld.multiplyVector3(new THREE.Vector3());

    var obstacle = new Physijs.BoxMesh(
      new THREE.CubeGeometry(
        40, 20, 40
      ),
      Physijs.createMaterial(
        new THREE.MeshNormalMaterial()
      )
    );
    obstacle.position.y = 21;
    obstacle.position.x = position.x;
    obstacle.position.z = position.z;
    scene.add(obstacle);

    obstacle.addEventListener('collision', function(object) {
      if (object == raft) console.log("Game Over!!!!")
      else {
        console.log(object);
      }
    });
  });
}
On the off chance that something else is happening, I have the else block to identify unexpected collisions with my obstacles. The happy path here should be that, when my raft collides with an obstacle, I ought to see the "game over" message.

Only I see no collision:


Well, I see lots of collisions, but not the "game over" kind. These are with plane objects that comprise the river. And they all happen within a second or so of loading the page.

What I suspect is happening is that the obstacle, whose height is 20, is falling from its height of 21 onto the water and colliding with the river. And once collided, Physijs is not registering subsequent collisions.

Based on a comment from Chandler Prall, I had expected no further collisions if the obstacle was colliding with the same object (or a different part of the same compound shape). But it seems that subsequent collision events are not fired if any object is mid-collision with any object.

Fortunately, this does not cause me much hardship. I can prevent the obstacle from colliding with the water object by making it static (zero mass):
function drawObstacles() {
  markers.forEach(function(marker) {
    // ...
    var obstacle = new Physijs.BoxMesh(
      new THREE.CubeGeometry(
        40, 20, 40
      ),
      Physijs.createMaterial(
        new THREE.MeshNormalMaterial()
      ),
      0
    );
    // ...

    obstacle.addEventListener('collision', function(object) {
      if (object == raft) console.log("Game Over!!!!")
      else {
        console.log(object);
      }
    });
  });
}
With that, I see no else collisions logged, but do see the desired "game over":


That will work fine for me in this case. Plane collisions are a bit weird anyway, so avoiding it entirely works. Waiiit....

That is what I changed that caused my problems in the first place. I had converted the obstacle markers from boxes to planes thinking that this would prevent the raft from bouncing over the markers accidentally. I also figured that I only needed to mark position and that a plane could do just as easily as a box could.

I think I'll ultimately stick with static obstacles. The less the physijs engine needs to do the better—20 new obstacles added to 20 river segments taxes the CPU momentarily at the game outset.

Day #494

Wednesday, August 29, 2012

Colliding with Relative Physijs Objects

‹prev | My Chain | next›

I ran into a bit of a problem last night with my "simple" Three.js / Physijs game. I have the raft and a twisting river. I even have current in the river pushing the raft. But a game without obstacles is a boring game indeed. And therein lies my problem.

In an attempt to keep things simple, I added obstacles directly to individual segments of the river. Even if the segments were far downstream and rotated, this kept the placement of obstacles a simple matter of placing them somewhere between postion.x = 0 and position.x = 1500 in the river segment's frame of reference. Of course that did not work.

After a brief discussion with Chandler Prall in the Three.js IRC channel, my problem is one of expectations. I had expected the obstacle to remain a separate entity. In fact, there is not really a way to group objects in Physijs—just to combine them into compound shapes. So the obstacle that I added to the various river segments we not obstacles—they were additional parts of the river segment.

Before attempting to solve this, I try to verify the theory. Based on observations last night, the theory does seem correct. The obstacles were more rigid than I expected—similar to the parent object. Still, if I understand correctly, I ought to see collision events from the river segment when my raft impacts the square obstacle. I add two collision event listeners, one to the obstacle and one to the river segment:
function addObstacle(water) {
  var width = 250
    , length = 1500
    , position = Math.random() * length;

  var obstacle = new Physijs.BoxMesh(
    new THREE.CubeGeometry(
      25, 25, 25
    ),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial()
    )
  );
  obstacle.position.y = 13;
  obstacle.position.x = position;

  obstacle.addEventListener("collision", function(object) {
    console.log("[obstacle] collision!");
  });

  water.addEventListener("collision", function(object) {
    console.log("[water] collision!");
  });

  water.add(obstacle);
}
After reloading, I do see the water collision—but only when the raft is first set on top of the river segment. When the raft finally impacts the "obstacle" part of the compound shape, I see no other events:


(the other debug output is stuff I am logging in Physijs and was too lazy to remove)

The event handler definitely works (as evidenced by the raft-on-water event). It simply does not recognize the compound shape collision as event-worthy.

So it seems that I still do not quite understand compound shapes in Physijs. Darn.

I set the event handler issue aside for now. I still need some way to position the obstacles relative to river segments and actually respond to collision events. Last night I was able to do both—just not at the same time.

I am already placing the obstacle in the desired frame of reference—that of the river segment. And last night, I found that, if I manually added the obstacle directly to the Physijs scene, then collision events were generated and received as desired.

So the challenge then becomes to mark the relative position of the obstacles, grab the "world" coordinates of those obstacle markers, and use the global position to place scene-level obstacles.

So the addObstacle() method now does no more than marking positions, storing the markers in a global:
function addObstacle(water) {
  var width = 250
    , length = 1500
    , x = Math.random() * length;

  var marker = new Physijs.BoxMesh(
    new THREE.CubeGeometry(2, 2, 2)
  );
  marker.position.y = 1;
  marker.position.x = x;
  water.add(marker);

  obstacles.push(marker);
}
After the scene is updated (so that the markers are laid out), I draw the obstacles:
function animate() {
  requestAnimationFrame(animate);
  applyRiverCurrent();
  scene.simulate(); // run physics
  render();
}

function render() {
  camera.position.x = raft.position.x + 0.4 * window.innerHeight ;
  camera.position.z = raft.position.z;

  drawObstacles();

  renderer.render(scene, camera);
}
If the scene level objects have not been rendered, then I do so:
var _scene_obstacles = [];
function drawObstacles() {
  if (_scene_obstacles.length > 0) return;

  obstacles.forEach(function(marker) {
    var position = marker.matrixWorld.multiplyVector3(new THREE.Vector3());
    console.log(position);
    var obstacle = new Physijs.BoxMesh(
      new THREE.CubeGeometry(
        25, 25, 25
      ),
      Physijs.createMaterial(
        new THREE.MeshNormalMaterial()
      )
    );
    obstacle.position.y = 13;
    obstacle.position.x = position.x;
    obstacle.position.z = position.z;
    scene.add(obstacle);

    obstacle.addEventListener('collision', function(object) {
      if (object == raft) console.log("Game Over!!!!");
    });

    _scene_obstacles.push(obstacle);
  });
}
Most of this comes directly from the previous obstacle building code. The main difference is the use of the matrixWorld matrix to calculated the world coordinates of the markers so that the obstacles can be placed in the proper location.

And it works!

When my raft impacts the globally places obstacle, I see expected "Game Over" event:


This is pretty messy, but it's a start.

Day #493

Tuesday, August 28, 2012

Game Obstacles Can't be Gouped in Physijs

‹prev | My Chain | next›

My Three.js / Physijs rafting game benefited from a step back yesterday. The code and the concepts are substantially simpler at this point. I am unsure if this is simple enough yet for anything less than an advanced chapter in Gaming JavaScript, but at least it is worth including at this point.

I will worry about further simplification another day. Tonight, I need to finish the game. This means that I need to add some obstacles that prevent the raft from reaching the end of its river.

After last night, I can group my river segment objects (water, river banks, joints between segments) in an invisible Physijs object—I use a Plane Mesh to ensure that it does not interact with my raft or any other game elements. To this river segment, I add a single (for now) obstacle:
function riverSegment(rotatiaon, offset) {
  // ...
  var length = 1500
    , half = length / 2;

  var segment = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(10, 10),
    new THREE.Material()
  );
  segment.rotation.y = rotation;
  // ...

  var water = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(length, 500),
    new THREE.MeshBasicMaterial({color: 0x483D8B})
  );
  water.position.x = half;
  segment.add(water);

  addObstacle(water);

  scene.add(segment);
  // ...
}
The addObstacle() function randomly places a box along the river segment:
function addObstacle(water) {
  var width = 250
    , length = 1500
    , position = Math.random() * length;

  var obstacle = new Physijs.BoxMesh(
    new THREE.CubeGeometry(
      25, 25, 25
    ),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial()
    )
  );
  obstacle.position.y = 13;
  obstacle.position.x = position;

  water.add(obstacle);
}
This seems to work well enough as the raft is able to collide with the obstacles and can even come to rest on them:


Upon further reflection, that does not seem quite right. I gave no mass to the obstacles so the raft, continuously pushed by the river current, should be able to push the obstacles. I will worry about that another day.

For now, I would simply like to end the game once the raft collides with an obstacle. I start by adding a tracer bullet where the gameOver() function call will eventually go:
function addObstacle(water) {
  // ...
  obstacle.addEventListener("collision", function(object) {
    console.log("Game over dude!")
  });

  water.add(obstacle);
}
Only this does not work. I was sure that I had the callback syntax right. What could I be missing?

Eventually, I try adding the obstacle directly to the scene instead of to the river segment group:
function addObstacle(water) {
  // ...
  obstacle.addEventListener("collision", function(object) {
    console.log("Game over dude!")
  });

  scene.add(obstacle);
}
This works… unfortunately. In fact, not only do I see the collision event, but I also notice that the raft is now able to push the obstacles along, sometimes into each other:


Although it works to add the obstacle to the scene, this is not a workable solution for my game. I need to add the obstacles to individual segments of the river—not attempt to calculate the world location of river segments so that I can try to place obstacles in the scene.

I try digging through the Physijs source some, but this seems to be related to ammo.js interactions. Such investigation is going to require more than a night's worth of work. Bummer.


Day #492

Monday, August 27, 2012

Frame of Reference with Physijs Objects

‹prev | My Chain | next›

After cleaning up my Three.js / Physijs rafting game last night, I ended up with more questions than answers. Although I was able to fill in some of the gaps between river segments with last night's work, some gaps were not as filled as others:


I think that part of my problem at least is that I cannot take advantage of my old friend fame-of-reference. Instead of creating a simple frame of reference for a river segment's frame of reference, rotating it, then adding Physijs objects to that frame of reference, I am forced to perform some pretty hairy calculations for each river segment:
function riverSegment(scene, rotation, offset) {
  if (!offset) offset = {x: 0, z: 0};

  var length = 1500
    , sin = Math.sin(rotation)
    , cos = Math.cos(rotation)
    , z_frame = 0.5 * length * sin
    , z_offset = z_frame + offset.z
    , x_frame = 0.5 * length * cos
    , x_offset = x_frame + offset.x
    , new_x = 2 * x_frame + offset.x
    , new_z = 2 * z_frame + offset.z;

  // ...
}
But am I forced to do that?

Now that I think about it, as long as I have Physijs objects everywhere and as long as I add all of them to the the parent object before adding them to the scene, I ought to be able to get myself a frame of reference:
function riverSegment(rotation, offset) {
  if (!offset) offset = {x: 0, z: 0};

  var length = 1500
    , half = length / 2;

  var segment = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(10, 10),
    new THREE.Material()
  );
  segment.rotation.y = rotation;
  segment.position.x = offset.x;
  segment.position.z = offset.z;
  // ...
}
That segment is effectively a frame of reference. I shift it to the end of the previous segement's X-Z position. Then I rotate it however the river is supposed to be rotated:
  offset = riverSegment(Math.PI/8);
  offset = riverSegment(0, offset);
  offset = riverSegment(0,          offset);
  offset = riverSegment(-Math.PI/8, offset);
  // ...
Since the segment is a PlaneMesh, it will not physically interact with my raft. Since the Material is a top-level Three.Material, it has no visible substance (a subclass is required for it to be visible).

The frame of reference is already rotated, so I can add the water to it and shift it by half its length:
  // ...
  var water = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(length, 500),
    new THREE.MeshBasicMaterial({color: 0x483D8B})
  );
  water.position.x = half;
  segment.add(water);
  //
Then I add the riverbanks to the water as I have been doing:
 //...
  water.add(bank(-250));
  water.add(bank(250));
  // ...
I can add the joint / river end as I have been doing and add the segment to the scene:
  // ...
  scene.add(segment);
  river_segments.push(water);
  // ...
Again, since everything from the segment frame of reference down to the riverbanks is a Physijs object and since the parent frame of reference is not added to the scene until all objects are added to the frame of reference, Physijs is able to handle the correct physics for everything.

Last up, I still need a little but of trigonometry, but it is much, much simpler than what I had been doing:
  // ...
  return {
    x: Math.cos(rotation) * 1500 + offset.x,
    z: -Math.sin(rotation) * 1500 + offset.z
  };
I still feel as though I need some clean-up. Especially if this is going to be a game that kids will have to write / type in. Still, this is a big improvement over last night.



Day #491

Sunday, August 26, 2012

Cleaning Up Three.js Code

‹prev | My Chain | next›

Up tonight, I am going to take care of a few loose ends in my raft Three.js / Physijs game.

Mostly, this involves cleaning things up like the invidual river banks. I create a helper function for this:
function bank(offset) {
  var width = 100
    , half = width / 2;

  var bank = new Physijs.BoxMesh(
    new THREE.CubeGeometry(1400, 100, 100),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 0.9
    ),
    0
  );
  bank.position.x = 100;
  if (offset < 0) {
    bank.position.z = offset - half;
  }
  else {
    bank.position.z = offset + half;
  }

  return bank;
}
One particular problem that I notice is that, as my river works itself back and forth across the land, there are gaps in the banks:


I solve this my adding "joints" to the turns in the river segments:
var joint = new THREE.Mesh(
    new THREE.CylinderGeometry(300, 300, 1),
    new THREE.MeshBasicMaterial({color: 0x483D8B})
  );
  joint.position.z = offset.z;
  joint.position.x = offset.x;
  scene.add(joint);
Not the cleanest solution, but it works:


I do the same for the end of the game (adding a little pond). Hopefully all of this leaves me with decent enough code for kids to type in. I will pick back up tomorrow with adding a few obstacles in the river. That ought to be enough for this game.

Day #490

Saturday, August 25, 2012

Physijs River Current

‹prev | My Chain | next›

Up today, would like to get the river in my Three.js / Physijs rafting simulation to push the raft. Last night, I tried to accomplish this with Physijs events, but was thwarted in my efforts. Having considered it some, I believe that an events-based approach would not be an appropriate solution, even had I been successful.

So tonight, I give another approach a shot: Three.js rays. The idea here is that on every animation() step, I ask if a ray from the raft pointing straight down impacts a river segment. It should always do so (because the river banks should bound the raft) and I should then be able to query the segment for its rotation so that I can apply a force in the appropriate direction.

I collect the active river segments in a river_segments array. Then, in the animate() function, I call to a function that will use those segments to see where the raft is:
function animate() {
  requestAnimationFrame(animate);
  applyRiverCurrent();
  scene.simulate(); // run physics
  render();
}
In applyRiverCurrent(), I create a ray from the raft down. Then I check to see which active river segment that ray passes through:
function applyRiverCurrent() {
  var ray = new THREE.Ray(
    raft.position,
    new THREE.Vector3(0, -1, 0)
  );
  var intersects = ray.intersectObjects(river_segments);

  console.log(intersects);

  // raft.applyCentralForce(new THREE.Vector3(1e8 * c, 0, 1e8 * s));
}
Once I verify that I am, in fact, seeing the correct river segments in the console.log() output, I apply a force in the appropriate direction:
function applyRiverCurrent() {
  var ray = new THREE.Ray(
    raft.position,
    new THREE.Vector3(0, -1, 0)
  );

  var intersects = ray.intersectObjects(river_segments);
  if (!intersects[0]) return;

  var current_segment = intersects[0].object;
  if (!current_segment) return;

  var angle = -current_segment.rotation.y
    , cos = Math.cos(angle)
    , sine = Math.sin(angle);

  raft.applyCentralForce(
    new THREE.Vector3(1e6 * cos, 0, 1e6 * sine)
  );
}
And oooh! I think that does the trick. When I first start off on my journey, the river current pushes me straight down the river, even though the first segment is rotated:


Momentum carries me into the bank of the second river segment, but it is a Physijs bank, so I bounce off. At this point, the downward current also kicks in, carrying me further downstream.

There is still room for clean-up here. Applying current seems a bit CPU intensive, so I might modulo the application of the river current to some number of clock cycles. Still, it works and it feels like a pretty decent approach, which makes this a good stopping point for today.


Day #489

Friday, August 24, 2012

Collision Events in Physijs

‹prev | My Chain | next›

Up tonight, I would like to apply a constant force on the raft in my simple Three.js / Physijs game. My first thought is to use Physijs events to detect when the raft hits individual segments of the river. I can then push the raft in the same direction that the river segment is rotated.

So I start with a heavily logged addCurrent() function:
function addCurrent(river_segment, c, s) {
  river_segment.addEventListener('collision', function(object) {
    if (object.id != raft.id) return;
    console.log(this.id);
    console.log(object.id);
    console.log(c + " ::: " + s);
    raft.applyCentralForce(new THREE.Vector3(1e8 * c, 0, 1e8 * s));
  });
}
The c and s variables are the sine and cosine of the river's current angle. Unfortunately, when the animation starts, I find that I am colliding immediately with all three plane segments that I have added:
39
42
0.9238795325112867 ::: -0.3826834323650898 
36 
42 
1 ::: 0 
33 
42 
0.9238795325112867 ::: 0.3826834323650898
The same raft object (as evidenced byt the ID 42) is colliding with each plane simultaneously. As the raft moves down the river from one segment to another, no subsequent collision events are registered.

If I invert the collision detect to find what the raft is colliding with:
  raft = buildRaft();
  scene.add(raft);
  raft.setDamping(0.0, 1.0);
  raft.addEventListener('collision', function(object) {
    console.log('raft');
    console.log(object);
  });
Then I see similar results. The raft is colliding with multiple PlaneMesh objects even though it seems to only overlap one:


Bah! I do not really need to use collisions to implement this. Three.js rays might be a more appropriate solution. Still, I can't let this pass without investigation.

So I create a new, simpler scene with no gravity:
var renderer, scene, camera, cube1, cube2;

Physijs.scripts.worker = 'scripts/physijs_worker.js';
Physijs.scripts.ammo = 'ammo.js';

document.addEventListener( "DOMContentLoaded", function() {
  init();
  animate();
});

function init() {
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColorHex(0x87CEEB);
  document.body.appendChild(renderer.domElement);

  scene = new Physijs.Scene;
  scene.setGravity(
    new THREE.Vector3(0,0,0)
  );

  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = 100;
  scene.add(camera);
}

function animate() {
  requestAnimationFrame(animate);
  scene.simulate(); // run physics
  render();
}

function render() {
  renderer.render(scene, camera);
}
In the init() function, I add two cubes, one of which gets an event listener:
  cube1 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(10, 10, 10),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 0.9
    )
  );
  cube1.position.x = -50;
  scene.add(cube1);
  console.log("cube 1: " + cube1.id);

  cube2 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(10, 10, 10),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 0.9
    )
  );
  cube2.position.x = 50;
  scene.add(cube2);
  console.log("cube 2: " + cube2.id);

  cube2.addEventListener('collision', function(object) {
    console.log("Object " + this.id + " collided with " + object.id);
  });
In the JavaScript console, I give one cube a shove, wait, and then see the expected collision:


Brilliant. Collision events work in Physijs. But what am I doing wrong in my raft game? Perhaps planes are the culprit. To test that theory, I add a plane:
var plane = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(15, 1000),
    new THREE.MeshBasicMaterial({color: 0x7CFC00})
  );
  plane.position.y = -5;
  scene.add(plane);
This goes right down the middle of the two cubes so it should collide with nothing. However, after reloading, I find:


Ah ha! Cube #2 is registers a collision with the plane without even touching it. That certainly explains the behavior that I see in my raft game.

I am unsure if this is a bug. On the one hand the two objects do not touch so how can they collide? On the other hand, the face of a cube touching a plane does not feel like a "collision".

As I said, I think that I can likely get river segments to push the raft through judicious use of Three.js rays. I will give that a try tomorrow.


Day #488

Thursday, August 23, 2012

Rotation Motion Controls in Three.js

‹prev | My Chain | next›

I enjoyed a huge breakthrough last night. It's one of those breakthroughs that makes a developer's pain and struggles all seem worthwhile. Where you finally have a brilliant stroke of genius to solve a problem in a new or unexpected way. Or, you actually read the documentation. Anyhow...

With my Three.js Physijs rafting simulation in better shape, I am ready to add motion controls to the raft.

First I add a "rudder" to the raft to indicate the direction in which the raft is pointing. I do not have to mess with Physijs for this, since it is just an indicator. I do have to mess around with manual veritices in Three.js in order to create a line:
  var geometry = new THREE.Geometry();
  geometry.vertices.push(new THREE.Vector3(0, 0, 0));
  geometry.vertices.push(new THREE.Vector3(10, 0, 0));
  var rudder = new THREE.Line(
    geometry,
    new THREE.LineBasicMaterial({color: 0xff0000})
  );
  rudder.position.z = 0;
  rudder.position.x = 35;
  raft.add(rudder);

  raft.position.y = 20;
  raft.rotation.x = Math.PI/2;
I suspect that it might be easier to create a cylinder, but I am glad for a way to create lines. With that, I have my direction indicated:


To add motion controls, first I use the space bar to add a force in the current direction. A simple document listener ought to suffice:
document.addEventListener("keydown", function(event) {
  var code = event.which || event.keyCode;
  if (code == 0x20) { // space
    pushRaft();
  }
  else {
    console.log(code);
  }
});

function pushRaft() {
  var angle = raft.rotation.z
    , cos = Math.cos(angle)
    , sin = Math.sin(angle);

  raft.applyCentralForce(new THREE.Vector3(1e8 * cos, 0, 1e8 * sin));
}
And indeed that does work. I am able to move forward in whatever direction I happen to be pointing.

I need a little more than that in my game, however. I need to be able to steer clear of trouble. For that, I add two more keys to listen for: J/K. When one of those is pressed, I rotate the raft accordingly:
document.addEventListener("keydown", function(event) {
  var code = event.which || event.keyCode;
  if (code == 0x20) { // space
    pushRaft();
  }
  else if (code == 0x4a) { // J
    rotateRaft(-1);
  }
  else if (code == 0x4b) { // K
    rotateRaft(1);
  }
  else {
    console.log(code);
  }
});

function rotateRaft(direction) {
  raft.__dirtyRotation = true;
  raft.rotation.z = raft.rotation.z + direction * Math.PI / 100;
}
And that actually works I am now able to rotate and move my raft. Two problems still remain.

First, when I collide with a river bank, the raft sometimes gets some angular velocity. I can likely solve that with some Physijs damping.

Second, when I apply enough force, I can move back upriver. The force that I applied last night from the river needs to be constant and greater than the force that my raft can exert. That seems a trickier proposition. Hopefully I will come up with something tomorrow.



Day #487

Wednesday, August 22, 2012

Grouping Physijs Objects!

‹prev | My Chain | next›

Sometimes it really helps to read the documentation. Or remember it.

For the past several days, I have struggled with Physijs' seeming lack of ability to group objects. Well, it seems that Physijs not only has this ability, but has a wiki page describing how to do it.

I could have sworn that I tried that solution. That is, I thought I had added the river banks to the river when both were Physijs Mesh objects. Maybe not.

So I add the water:
  var water = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(1500, 500),
    new THREE.MeshBasicMaterial({color: 0x483D8B})
  );
  water.position.x = x_offset;
  water.position.z = -z_offset;
  water.rotation.y = rotation;
  // scene.add(water);
Then I add the river banks to the water (not the scene):
  var bank1 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(1500, 100, 100),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 0.9
    ),
    0
  );
  bank1.position.z = -250;
  water.add(bank1);
I do the same for the other river bank, then add the parent object—the water—to the scene:
  scene.add(water);
And... it works.

My river banks now bounce my raft as desired:


Holy cow. I have wasted three days of this chain because I couldn't read the documentation. The most important part of the wiki entry (at least the one that I failed to consider) was:
The only thing you need to remember when working with compound objects is all children must be added to the parent before the parent is added to the scene. When a parent is put into the scene its shape is finalized and cannot be added to.
I do not have a git commit to verify it, but I am quite certain that, when I tried this on my own, I added the water to the scene and then added the associated river banks. And indeed, if I remove the post-banks scene.add(water) and instead do it right after creating the water:
var water = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(1500, 500),
    new THREE.MeshBasicMaterial({color: 0x483D8B})
  );
  water.position.x = x_offset;
  water.position.z = -z_offset;
  water.rotation.y = rotation;
  scene.add(water);
Then the raft passes right through the river banks. I am such an idiot.

The real reason that I was looking into the documentation was refreshing my memory on Physijs events. Why I remembered that the wiki contained events, but failed to even look at the documentation during three days of struggle is beyond me. Anyhow...

I am interested in applying a pushing force on the raft from the water. My first pass is going to be applying a force in the direction in which the river is pointing as soon as the raft hits the river. Tracer bulletting the solution, I start with:
  water.addEventListener('collision', function() {
    console.log("water collision!");
  });
I am uncertain why, but I see more of these events logged than expected. When the page loads, there are three events immediately logged. I also see these events when the raft hits the river banks. I cannot quite rationalize why that might be. I log the colliding object:
water.addEventListener('collision', function(object) {
    console.log("water collision!");
    console.log(object);
  });
But it is always the raft as evidenced by the torus geometry. I had thought perhaps the river bank was somehow being forced down into the water. I table this for now in favor of adding a force to the raft, which I accomplish with:
  water.addEventListener('collision', function(object) {
    raft.applyCentralForce(new THREE.Vector3(1e9 * cos, 0, 1e9 * sin));
  });
And that seems to work. At least well enough for a first pass. I call it a night here, having finally made significant progress. Up tomorrow: I am ready to add controls to my raft.


Day #486

Tuesday, August 21, 2012

Poor Man's Grouping of Physijs Objects

‹prev | My Chain | next›

I remain thwarted in my efforts to group related Physijs objects inside a Three.js game, so tonight, I do it the hard way. Instead of positioning and rotating a grouped segment of objects, I will ask a generator function to create the object and add them directly to the Physijs scene.

So I switch from a more ideal generator that just generates a river segment:
river_segment = riverSegment();
  river_segment.position.x = 100;
  scene.add(river_segment);
Instead, I tell that same riverSegment() function to draw the river and its two riverbanks in addition to creating them. This is a relatively quick fix. Instead of adding my Physijs objects to a grouping THREE.Object3D object, I add them directly to the scene:
function riverSegment(scene) {
  var water = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(1500, 500),
    new THREE.MeshBasicMaterial({color: 0x483D8B})
  );
  water.position.x = 750;
  scene.add(water);

  var bank1 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(1500, 100, 100),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 0.9
    ),
    0
  );
  bank1.position.x = 750;
  bank1.position.z = -250;
  bank1.__dirtyPosition = true;
  scene.add(bank1);
  // ...

  return water;
}
The problem that I had hoped to solve by grouping things involved twists and turns in the river. Rotating a single thing (the group of river segment plus banks) is much easier than rotating a bunch of things separately.

For example, I might want the river to start off rotated one direction, then to straighten out, then to turn back the other direction:
  offset = riverSegment(scene, Math.PI/8);
  offset = riverSegment(scene, 0, offset);
  offset = riverSegment(scene, -Math.PI/8, offset);
I can get this to work, but even with simple rotation, things get complicated rather quickly:
function riverSegment(scene, rotation, offset) {
  if (!offset) offset = {x: 0, z: 0};

  var length = 1500
    , z_frame = 0.5 * length * Math.sin(rotation)
    , z_offset = z_frame + offset.z
    , x_frame = 0.5 * length * Math.cos(rotation)
    , x_offset = x_frame + offset.x;

  var water = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(1500, 500),
    new THREE.MeshBasicMaterial({color: 0x483D8B})
  );
  water.position.x = x_offset;
  water.position.z = -z_offset;
  water.rotation.y = rotation;
  scene.add(water);

  var bank1 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(1500, 100, 100),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 0.9
    ),
    0
  );
  bank1.position.x = x_offset;
  bank1.position.z = -z_offset -250;
  bank1.rotation.y = rotation;
  scene.add(bank1);
  // ....
  return {x: 2 * x_frame + offset.x - 50, z: 2 * z_frame + offset.z};
}

I think I may give Box2D a try with this. This solution is getting way too complicated for a simple game for kids to write.


Day #485

Monday, August 20, 2012

Still Can't Group Physijs Objects

‹prev | My Chain | next›

I ran into a bit of a problem in my Three.js / Physijs game yesterday. I had hoped to use a generator to create segments of a river. Normally in Three.js, I would group all of the components (water, left and right river banks) inside a THREE.Object3d() object to make for easier positioning and rotation. But I found yesterday that Physijs ignores my Object3d river segment and everything grouped inside.

The obvious solution is to manually position each of the individual parts. And I almost get started down that path. But then I wonder...

Is there a way to register the component parts manually?

I continue to add the result of the riverSegment() generator function to the Physijs scene:
function init() {
  // ...
  scene = new Physijs.Scene;
  // ...
  // River
  scene.add(riverSegment());
  // ...
}
In the add() method on the Physijs.Scene prototype, it seems as though I might be able to get away with marking a grouping object with _physijs to make this work:
 Physijs.Scene.prototype.add = function( object ) {
  THREE.Mesh.prototype.add.call( this, object );
  
  if ( object._physijs ) {
      // ...
   if ( object.children.length ) {
    object._physijs.children = [];
    addObjectChildren( object, object );
   }
      // ...
    }
  }
So I mark my Object3D group as a Physijs container:
function riverSegment() {
  var segment = new THREE.Object3D();
  segment._physijs = true

  // Add water and two banks to segment

  return segment;
}
Not surprisingly, that does not work right away. I get the following error from line 336 of physi.js:
Uncaught TypeError: Cannot call method 'push' of undefined
After working through that and another couple of messages, I find that I at least need to decorate my group with the following Physijs properties:
  segment._physijs = {
    children: [],
    touches: 0
  };
  segment.material = {};
Unfortunately, I am still left with an error on line 1801 of ammo.js:
Uncaught TypeError: Cannot read property 'a' of undefined 
Bah! Ammo.js is uglified code, so I am going to be hard pressed to figure out what that problem is. And it is a problem because my raft can shoot right though the river banks:


The last thing that I try along these lines is to add the pieces children of my Object3D individually:
  river_segment = riverSegment();
  river_segment.position.z = 100;
  river_segment.position.x = 100;
  river_segment.rotation.y = Math.PI/8;
  while (river_segment.children.length > 0) {
    var child = river_segment.children[0];
    child.__dirtyPosition = true;
    child.__dirtyRotation = true;
    scene.add(child);
  }
That works in that the banks will again bounce the raft back to the middle of the river. What does not work is the positioning of the entire river segment. Above, I tried to re-position and rotate the segment, but it is positioned vertically in the center of the screen:


Ah well, it was worth a shot. Tomorrow, unless I get a brainwave (or external help), I will repurpose the segment function to generate a segment and place the individual pieces on the scene. That is not at all ideal, but it (hopefully) will not cause too many problems.


Day #484

Sunday, August 19, 2012

Can't Group Physijs Objects in Three.js

‹prev | My Chain | next›

Up tonight, I plan to add some physics to my Three.js rafting game. This means adding the Physijs physics engine to my HTML:
<!DOCTYPE html>
<html>
  <head>
    <script src="scripts/Three.js"></script>
    <script src="scripts/physi.js"></script>
    <script src="scripts/raft.js"></script>
    <title>Raft</title>
  </head>
  <body>
    <p>j / k to rotate</p>
  </body>
</html>
Then, in raft.js, I configure Physijs:
var raft, camera, scene, renderer;

Physijs.scripts.worker = 'scripts/physijs_worker.js';
Physijs.scripts.ammo = 'ammo.js';

document.addEventListener( "DOMContentLoaded", function() {
  init();
  animate();
});
//...
Then I convert the objects in the game into Physijs objects:
function init() {
  // ...
  scene = new Physijs.Scene;

  // Land
  var land = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(1e6, 1e6),
    new THREE.MeshBasicMaterial({color: 0x7CFC00})
  );
  scene.add(land);

  raft = new Physijs.ConvexMesh(
    new THREE.TorusGeometry(25, 10, 16, 16),
    new THREE.MeshNormalMaterial()
  );
  raft.position.y = 10;
  raft.rotation.x = Math.PI/2;
  scene.add(raft);
  // ...
}
And lastly, I add physics updates to the animate() function:
function animate() {
  requestAnimationFrame(animate);
  scene.simulate(); // run physics
  render();
}
With my simple setup physijs-ified, I am ready to start adding things to bounce against and push my raft. That is, I create a river with banks:
function riverSegment() {
  var segment = new THREE.Object3D();

  var water = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(1500, 500),
    new THREE.MeshBasicMaterial({color: 0x483D8B})
  );
  water.position.x = 750;
  segment.add(water);

  var bank1 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(1500, 100, 100),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 1.0
    ),
    0
  );
  bank1.position.x = 750;
  bank1.position.z = -250;
  segment.add(bank1);

  return segment;
}
Only when I push my raft with a simple force:
raft.applyCentralForce(new THREE.Vector3(1e9, 0, -1e9));
It goes right through the wall.

It takes me a very long time to track the problem down. It seems that Physijs objects added to an Object3D are not seen by scene when updated for physics in the animate() block.

I am able to bounce my raft off of the bank if I add it directly to the scene:
function init() {
  // ..
  scene = new Physijs.Scene;

  // River
  var river1 = riverSegment();
  river1.position.x = 50;
  scene.add(river1);

  var bank1 = new Physijs.BoxMesh(
    new THREE.CubeGeometry(1500, 100, 100),
    Physijs.createMaterial(
      new THREE.MeshNormalMaterial(), 0.2, 1.0
    ),
    0
  );
  bank1.position.x = 750;
  bank1.position.z = -250;
  scene.add(bank1);

  raft = new Physijs.ConvexMesh(/* ... */);
  // ...
}
Although that does give me a river bank, I am at a loss for how to easily add it to the scene for the raft to float down. I suppose my riverSegment() function will need to create segments directly in the scene. That is not exactly ideal as I will have to position and rotate river and bank segments individually.

Hopefully I can come up with something better before tomorrow.

Day #483

Saturday, August 18, 2012

Getting Started on a New Three.js Game

‹prev | My Chain | next›

Up tonight, I hope to get started on writing some simple Three.js / Physijs games for Gaming JavaScript. First up is going to be a simple raft game that bounces down a river.

I am going to need to start up new games often in the next few months. Tonight I talk through the setup. Hopefully that will not be necessary in the future.

I start by declaring my important games object and determine which renderer to use:
var raft, camera, scene, renderer;

var canvas = !! window.CanvasRenderingContext2D;
var webgl = ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )();

document.addEventListener( "DOMContentLoaded", function() {
  init();
  animate();
});
The initialization in the init() function sets up the appropriate renderer:
function init() {
  if (webgl) renderer = new THREE.WebGLRenderer();
  else if (canvas) renderer = new THREE.CanvasRenderer();
  if (renderer) {
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setClearColorHex(0x87CEEB);
    document.body.appendChild(renderer.domElement);
  }
  else {
    alert("Can't make renderer. What kind of browser are you using?!");
  }
}
Also in the init() function, I define the scene, the camera and the raft:
  scene = new THREE.Scene();

  raft = new THREE.Mesh(
    new THREE.TorusGeometry(25, 10, 4, 4),
    new THREE.MeshNormalMaterial()
  );
  scene.add(raft);

  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.y = 100;
  camera.lookAt(raft);

  scene.add(camera);
New to me is the torus object, which is just a tube thing. It ought to work just fine for a raft. I place the camera 100 pixels above the scene and look down at the raft.

That is all that I need for the init() function tonight. I will worry about physics tomorrow. Moving on to the animate() function, getting started is similarly simple:
function animate() {
  // requestAnimationFrame(animate);
  render();
}

function render() {
  renderer.render(scene, camera);
}
I have not quite gotten the hang of what goes in the animate() function and what goes in the render() function. For the most part, I have been putting everything in render(), but I have not really given it much thought. Where do control updates go, for instance? A question for another day.

Anyhow, at this early juncture there is very little in either. In fact, since nothing needs to be animated at all, I do not even need an animation frame (though I will definitely uncomment that tomorrow).

With that, I take a look at my scene and see... nothing. It turns out that my camera is not, in fact, looking at the raft. It seems that the lookAt() method needs a position, not an object. Fortunately, the fix is easy enough:
  camera.lookAt(raft.position);
With that, my raft now comes into view:


That is not quite right. But I only need to rotate the object 90°:

raft = new THREE.Mesh(
    new THREE.TorusGeometry(25, 10, 4, 4),
    new THREE.MeshNormalMaterial()
  );
  raft.rotation.x = Math.PI/2;
  scene.add(raft);
Now I get to see my raft:


Hrm... that is a bit boxy. I will file that away for future use, but tonight I want more of an inner tube. That is easy enough—I just need more that 4 segments to comprise my mesh. Sixteen ought to do:

  raft = new THREE.Mesh(
    new THREE.TorusGeometry(25, 10, 16, 16),
    new THREE.MeshNormalMaterial()
  );
  raft.rotation.x = Math.PI/2;
  scene.add(raft);


That is a good stopping point for tonight. Up tomorrow: physics and maybe a river.

Day #483

Friday, August 17, 2012

Finishing Up with Physijs Avatar

‹prev | My Chain | next›

Yesterday, I discovered the benefits of __dirtyRotation in Physijs, this physics engine for Three.js. I used it yesterday to prevent my player from spinning out of control after contact with other objects. Tonight, I plan to use it to restore the spin-to-walk ability.

I am not going to rewrite the Three.js first person controls, but I need some of that ability. I start with global variables that hold state (moving and state):
var moving = false;
var controlState = {};
Next, I need a keydown handler to set the state:
document.addEventListener("keydown", function(event) {
  // Last wins (for now) because we set the velocity, not apply force
  function setState(state) {
    moving = true;
    controlState = {};
    controlState[state] = true;
  }
  
  var speed = 500;
  var code = event.which || event.keyCode;
  if (code == 0x57) { // w
    setState("moveForward");
    a_frame.setLinearVelocity({z: -speed, y: 0, x: 0 });
  }
  // ...
});
I feel a little bad about overwriting controlState and the velocity like that, but it works for now—movement is only in one direction.

I also use a corresponding keyup handler to reset controlState and mark the avatar as no longer moving:
document.addEventListener("keyup", function(event) {
  function stopState(state) {
    if (controlState[state]) {
      moving = false;
      controlState = {};
    }
  }

  var code = event.which || event.keyCode;
  if (code == 0x57) { // w
    stopState("moveForward");
    // a_frame.setLinearVelocity({z: 0, y: 0, x: 0 });
  }
  // ...
});
This allows me to restore the motion of the legs when moving is true. I can also spin the avatar in the proper direction depending on the controls' state:
var w = 500;
function render() {
  var t_float = clock.getElapsedTime()
    , t = t_float * 1000
    , amplitude = (w/2 - Math.abs((t % (2*w)) - w))/w;

  if (moving) {
    avatar_left_leg.rotation.x = amplitude*(Math.PI/6);
    // ...
    if (controlState.moveLeft) spinAvatar(-Math.PI/2);
    // ...
  }
  else {
    spinAvatar(0);
  }

  scene.simulate(); // run physics

  renderer.render(scene, camera);
}
Even the spinAvatar() function still works. It works because it spins the avatar inside the Physijs frame so no changes are required:
function spinAvatar(angle) {
  new TWEEN.Tween( { y: avatar.rotation.y } )
      .to( { y: angle }, 100 )
      .onUpdate( function () {
         avatar.rotation.y = this.y;
      })
      .start();
}
So it turns out that I did not need any additional use of __dirtyRotation. As long as it keeps the frame of reference steady, everything just works. Nice.

This is all going so smoothly. Something is going to go horribly wrong soon. I just know it.

But failure does not arrive when I set up invisible fences around the island:
  scene = new Physijs.Scene;

  var fence = new Physijs.BoxMesh(
    new THREE.CubeGeometry(ISLAND_WIDTH, 1000, 10),
    new THREE.Material(),
    0
  );
  fence.position.z = -ISLAND_HALF;
  fence.position.y = 500;
  scene.add(fence);
The last argument of zero to the BoxMesh constructor specifies a mass of zero, which is interpreted as a static object in Physijs. In other words, my fence will not yield, keeping the avatar on the island:


I finally do run into a small problem with the avatar's interaction with the ball that I have left on the island for the avatar's amusement. My avatar can no longer budge it:


What fun is that?

Fortunately, it does not take too long to identify the problem. I specified a mass of 1000 for my avatar's frame of reference / Physijs box:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250),
    new THREE.Material(),
    1000
  );
It seems that, in Physijs, 1000 is very light. I suppose it is measured in grams.

Anyhow, with a slightly heavier mass:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250),
    new THREE.Material(),
    1000*1000*1000
  );
I can now send my ball rolling and bouncing around:


It is literally minutes of fun!

Actually, what this really means is that I seem to have gotten the hang of Physijs.


Day #481

Thursday, August 16, 2012

Better Player Movement in Physijs/Three.js

‹prev | My Chain | next›


I had a minor breakthrough last night in my efforts to understand Physijs, the physics engine for Three.js. I was finally able to get damping (slowing) to work. That, combined with simple controls allow my to move my player and interact with things fairly nicely.

Despite setting the angular damping to the highest possible setting (1.0), my avatar still has a tendency to spin:


Even though the Physijs project is fairly new, it has the beginning of a decent wiki. The page on materials promises some friction and bounciness settings. However, these settings have little effect in my setup:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250),
    Physijs.createMaterial(
      new THREE.Material(),
      1.0,
      0.0
    ),
    1000
  );
I have set the friction to its maximum (1.0), and the bounciness to its minimum (0.0). I also specified a mass of 1000 in case that had any effect on the friction calculation. Even with minimal bouncing and maximum friction, my avatar has a tendency to fly off into space:


I am a little unclear why this would happen—gravity ought to bring me right back down to the ground. This may be something worth experimenting with another day, although with a simpler setup. For now, I move back to my damping setup, which seemed to behave much closer to what I want.

In the end, I am able to eliminate all spinning with the __dirtyRotation flag that Physijs exposes to manually set the rotation (there is a similar setting for position):
function render() {
  a_frame.rotation.set(0,0,0);
  a_frame.__dirtyRotation = true;

  scene.simulate(); // run physics
  renderer.render(scene, camera);
}
With that, even if my avatar's enclosing cube strikes the very edge of the rock/static sphere, my player remains facing forward:


Satisfied, I call it a night here. Tomorrow, I plan to restore the remaining movement of the avatar. Assuming that goes well (never a safe bet), I hope to get started on some actual games the following day.


Day #480

Wednesday, August 15, 2012

I Can Damp Physijs

‹prev | My Chain | next›

I am struggling with damping motion with the Physijs physics engine for Three.js. I have tried a variety of arguments to the setDamping() method, so far to no avail. Depending on how I hit obstacles, my avatar can go into a very long spin:



Mikael Eliasson was kind enough to point me to the GitHub issue that introduced the damping arguments. So it seems that I need to supply two float values to the setDamping() method—one for the linear damping and the other for the rotational damping (hopefully this means that my avatar will stop spinning uncontrollably).

I specify a linear damping of 0.8, which should stop positional motion relatively quickly. I also specify a rotational damping of 1.0, which should stop spins almost immediately. The code:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250),
    new THREE.Material()
  );
  a_frame.position.y = 125;
  a_frame.setDamping(0.8, 1.0);
Unfortunately, this also proves to have no effect. If I set the velocity in the X-direction:
a_frame.setLinearVelocity({x: 500, y: 0, z: 0});
Then not only does the motion continue for a long time, it never stops.

But...

I eventually realize that, if I manually specify the damping in the JavaScript console and then set the velocity:
a_frame.setDamping(0.8, 1.0);
a_frame.setLinearVelocity({x: 500, y: 0, z: 0});
Then I get my desired behavior. My avatar spurts to the right, but only very briefly before the 0.8 linear damping slows my avatar to a halt.

It only takes a little bit of experimentation to find that setDamping() only has an effect if invoked after the object in question has been added to the scene:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250),
    new THREE.Material()
  );
  a_frame.position.y = 125;
  a_frame.add(avatar);

  scene.add(a_frame);
  a_frame.setDamping(0.8, 1.0);
In retrospect, I probably should have suspected something like this from my investigation of the Physijs source code. I had thought the method definition more or less a dead-end:
 // Physijs.Mesh.setDamping
 Physijs.Mesh.prototype.setDamping = function ( linear, angular ) {
  if ( this.world ) {
   this.world.execute( 'setDamping', { id: this._physijs.id, linear: linear, angular: angular } );
  }
 };
On the face of it, this seemed to me little more than handing off to the equivalent ammo.js call. Since ammo.js is generated code, I was unable to make heads or tails of it.

The key to the Physijs method, however, is not the subsequent handling of the damping. Rather, it is the conditional that only applies damping if a world (scene) has been defined.

I suppose that, to a certain extent, this makes sense. After all, it would be the world that might exert some friction on an object to stop its motion. But, of course, this was not obvious (at least to me). At any rate, there are a number of Physijs methods that need a world/scene:
  • applyImpulse()
  • applyCentralForce()
  • applyForce()
  • setAngularVelocity()
  • setLinearVelocity()
  • setAngularFactor()
  • setLinearFactor()
  • setDamping()
  • setCcdMotionThreshold()
  • setCcdSweptSphereRadius()
In the end, even a damping of 1.0 is not sufficient to prevent rotation-after-collision entirely. It is probably good enough, though I may explore it a bit more tomorrow. For now, I am thrilled to finally have Physijs damping working.


Day #479

Tuesday, August 14, 2012

Rocks in Physijs / Three.js

‹prev | My Chain | next›

Last night I managed to come with a passable solution for moving (and stopping) a player in a Three.js game governed by the Physijs physics engine. I may revisit that solution as it did not feel as solid as the one I came up with for Gladius and Box2D.js. But first, I would like to understand how to make immovable objects in Physijs.

Ultimately, I plan to make an invisible fence to keep my player on its tropical island:


But first, I need to figure out how to make static objects. Happily, there is a wiki page for that. Following along, I set the mass of the ball in my scene to zero, which should make it static:
  // A ball
  var ball = new Physijs.SphereMesh(
    new THREE.SphereGeometry(100),
    new THREE.MeshNormalMaterial(),
    0
  );
  ball.position.z = -500;
  ball.position.y = 150;
  scene.add(ball);
And it works. To an extent.

When I run into the ball, the ball remains static. But, unlike in Gladius and Box2D, my Physijs avatar bounces off of the ball and goes into a tailspin:


And keeps spinning for quite a while:


I cannot give the avatar zero weight, because then it will not move at all. So, instead, I make it really, really heavy:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250),
    new THREE.Material(),
    1000* 1000
  );
But that has almost no effect. I think the avatar may hit the ground a little (thought it shouldn't), but that aside, the tailspin behavior continues.

It helps a little to convert the ball's Physijs mesh from a sphere to a box:
  // A ball
  var ball = new Physijs.BoxMesh(
    new THREE.SphereGeometry(100),
    new THREE.MeshNormalMaterial(),
    0
  );
Now, if I hit the box/ball head on, or exactly from the side, my avatar stops cold:


But, if I hit the corner (my avatar's corner on the "ball" corner), I am again in a tailspin.

I really think that I want linear damping here, but I still cannot figure it out. I am not even sure what the arguments are. From the code, it takes two arguments (linear and angular):
 // Physijs.Mesh.setDamping
 Physijs.Mesh.prototype.setDamping = function ( linear, angular ) {
But I am not clear what kind of objects linear and angular are. I try arrays, objects and Vector3s:
  // a_frame.setDamping({x: 100, y: 100, z: 0});
  // a_frame.setDamping([100, 100, 0], [100, 100, 100]);
  a_frame.setDamping(
    new THREE.Vector3( 100, 1000, 0 ),
    new THREE.Vector3( 1000, 1000, 1000 )
  );
But none see to have any effect. I can still get in a tailspin.

I have to call it a night here. I think I will give movement one more try tomorrow.


Day #478

Monday, August 13, 2012

Physijs Controls

‹prev | My Chain | next›

Up today, I hope to build on last night's progress on adding Physijs to my Three.js avatar island. Last night, I was able to add a Physijs box around my avatar:


It interacts with other Physijs objects (like the ball) as desired, but it breaks the first person controls that I had been using. So tonight I am going to add my own controls tonight.

The easiest way that I can accomplish this is to add a document event listener for keydowns:
document.addEventListener("keydown", function(event) {
  var code = event.which || event.keyCode;
  console.log(code);
  if (code == 0x57) { // w
    console.log("moveForward");
    a_frame.setLinearVelocity({z: -100, y: 0, x: 0 });
  }
  else if (code == 0x41) { // a
    console.log("moveleft");
    a_frame.setLinearVelocity({z: 0, y: 0, x: -100 });
  }
  else if (code == 0x53) { // s
    console.log("moveBackward");
    a_frame.setLinearVelocity({z: 100, y: 0, x: 0 });
  }
  else if (code == 0x44) { // d
    console.log("moveRight");
    a_frame.setLinearVelocity({z: 0, y: 0, x: 100 });
  }
});
And that works pretty much as desired. Except that I run into the same problem that I saw with Box2D.js and Gladius–the avatar keeps moving even after I let go of the movement keys.

So I try the same solution that I used over there. I set the linear damping to zero:
a_frame.setDamping(10);
Only that doesn't work at all. My avatar is ejected out into space.

So I am stuck adding a corresponding keydown event listener:
document.addEventListener("keyup", function(event) {
  var code = event.which || event.keyCode;
  console.log(code);
  if (code == 0x57) { // w
    console.log("stopForward");
    a_frame.setLinearVelocity({z: 0, y: 0, x: 0 });
  }
  else if (code == 0x41) { // a
    console.log("stopleft");
    a_frame.setLinearVelocity({z: 0, y: 0, x: 0 });
  }
  else if (code == 0x53) { // s
    console.log("stopBackward");
    a_frame.setLinearVelocity({z: 0, y: 0, x: 0 });
  }
  else if (code == 0x44) { // d
    console.log("stopRight");
    a_frame.setLinearVelocity({z: 0, y: 0, x: 0 });
  }
});
That works, but it is not entirely satisfactory. If I am hold W and A at the same time, then letting go of either will stop the avatar. I can live with that, but I would certainly prefer something a little more robust. Shame the damping seems broken.

I'll sleep on that and hopefully come up with something better tomorrow.


Day #477

Sunday, August 12, 2012

No Physijs Controls

‹prev | My Chain | next›

I got started yesterday with Physijs, a physics engine for Three.js, but was unable to get it all quite working.

The first of my two problems was that the ball that placed on avatar island is melting into the island:


I suspect that this is because the island is not a Physijs object. So I test that theory by making it one:
  // Island
  var island = new Physijs.PlaneMesh(
    new THREE.PlaneGeometry(ISLAND_WIDTH, ISLAND_WIDTH),
    new THREE.MeshBasicMaterial({color: 0x7CFC00})
  );
  scene.add(island);
And indeed, the ball no longer melts:


It does, however, still retain its annoying Kitty Pride-like for my avatar:


I had been making the body for the avatar a Physijs object:
  var body = new Physijs.CylinderMesh(
    new THREE.CylinderGeometry(1, 100, 100),
    material
  );
  avatar.add(body);
Perhaps that is the wrong approach. Instead, convert the avatar's frame of reference into a Physijs box:
  a_frame = new Physijs.BoxMesh(
    new THREE.CubeGeometry(250, 250, 250)
  );
  a_frame.position.y = 125;
  a_frame.add(avatar);
I find that I have to set the Y (up/down) position. Otherwise the cube starts embedded in the island and is forcibly ejected into the air. It is an interesting effect, but not at all desirable in this case.

At any rate, I find that I can now move my avatar's Physijs frame of reference with setLinearVelocity():
a_frame.setLinearVelocity({z: 0, y: 0, x: 100 })
But only if I remove the Three.js first person controls.

The combination of no Three.js controls and setLinearVelocity() allows me to play ball:


Though every now and then I fall down (bringing the camera with me):


It seems as though, if I want input controls for my avatar and physics, that I will need to write my own controls. I think I will get started on that tomorrow.


Day #476