Thursday, January 31, 2013

On Demand Physics with Physijs

‹prev | My Chain | next›

I have noticed that running the Physijs physics engine in my Three.js games is a fine way to test the fan on my MacBook Air (running linux). Now that I am quite sure that my fan is in good working order, I would prefer it if my fan were not running all of the time.

So I start by removing the call to scene.simulate from the usual Three.js animate() function:
  var pause = false;
  function animate() {
    requestAnimationFrame(animate);
    if (pause) return;
    scene.simulate(); // run physics

    renderer.render(scene, camera);
  }
  animate();
For smooth physics, the physics engine only needs to run at 60 frames per second, not whatever rate the animation is currently running at. So I start a separate function for this game logic:
  function gameStep() {
    scene.simulate(); // run physics

    // when moving, process the game logic at a target 60 FPS
    setTimeout(gameStep, 1000/60);
  }
  gameStep();
Unfortunately, this has little to no effect. The animation must be running at about that rate since my fan is still being exercised. It seems that an alternate strategy will be required.

Instead, I try to stop the physics engine whenever my player is no longer in motion. Sure the fan might be taxed when I am actively moving my player, but it should quickly go back to normal when I am resting. In the move() function that is invoked in response to keyboard activity, I add a call to a new startPhysics() function:
  function move(x) {
    // ...
    startPhysics();
    player.setLinearVelocity(
      new THREE.Vector3(v_x + x, v_y, 0)
    );
  }
The order of this winds up being important. Specifically, bad things happen when I setLinearVelocity() before the physics engine is running. That might seem intuitive, but I had expected whatever linear velocity was applied to be applied on the next call to scene.simulate()—regardless of whether or not the physics engine was already active.

Anyhow, the startPhysics() function is responsible for two things: starting the engine (naturally) and starting a timer that watches for then end of physics movement:
  function startPhysics() {
    if (game_step_timer) return;
    gameStep();
    waitForStop(true);
  }
The guard clause in this function is checking to see if the gameStep() function is already simulating physics. Since I need that timer variable in this function, it now needs to be defined and set in the gameStep() function:
  var game_step_timer;
  function gameStep() {
    scene.simulate(); // run physics

    // when moving, process the game logic at a target 60 FPS
    game_step_timer = setTimeout(gameStep, 1000/60);
  }
That starts the physics engine and, thanks to a setTimeout() call, it continues to processes all of the necessary physics in the scene 60 times per second.

To stop the physics engine, I need only clear that timeout. This is what the waitForStop() function does:
  function waitForStop(just_started) {
    if (just_started) return setTimeout(waitForStop, 10*1000);
    if (player.getLinearVelocity().length() > 0) return setTimeout(waitForStop, 2*1000);

    clearTimeout(game_step_timer);
    game_step_timer = null;
  }
Since it is unlikely that physics will cease to be a factor immediately after the first call, the first guard clause will wait for 10 seconds until checking to see if physics can stop. Not only is it nice to have this 10 second grace period, it turns out to be required. Event though waitForStop() is invoked after the physics engine is running, oftentimes I get a velocity of zero. This grace period also ensures that the physics engine has ample time to get things moving.

After the starting grace period, I check to see if physics is no longer needed every two seconds. Finally, once nothing is moving under the influence of physics, I clear the game_step_timer that would otherwise be updating physics 60 times a second.

And that seems to do the trick. The game is still running, but I no longer hear the fan as I am writing in a different desktop. If I switch back to the game, the player is immediately responsive to keyboard input and moves fluidly.

All of this seems too much to introduce to kids in 3D Game Programming for Kids. So, unless I can think of an easier way to do this, I will mentally file this under "good to know" and move back to useful stuff for kids tomorrow.


Day #647

Wednesday, January 30, 2013

No Joy with a Three.js DOM Event Library

‹prev | My Chain | next›

I was able to get a fairly decent click-and-drag implementation for Three.js in place yesterday. Most of the complication came from translating page coordinates into Three.js coordinates. Which is to say that it was not that complicated. Still, it might be a bit much for kids—both typing and one too many concepts. So tonight I hope to find something a little more the speed for 3D Game Programming for Kids.

Jerome Etienne has a repository with interesting and useful Three.js extensions. Among the extensions is a DOM event library that might be just what I need.

I start by adding the DOM event library to my game with a <script> tag:
<script src="https://raw.github.com/jeromeetienne/threex/master/threex.domevent.js"></script>

In the game, I would like for the player to click and drag ramps around the screen to make reaching goals possible:


So I add an event listener for one of the ramps:
  ramp1.addEventListener('mousedown', function(object3d){ 
    console.log("mousedown ramp1");
  });
But, when I try to click on the ramp, I get nothing—no console output at all.

Instead, I try the alternative syntax in which events are added to a DOM event object:
  var domEvent = new THREEx.DomEvent(camera);
  domEvent.bind(ramp1, 'click', function() {console.log("click ramp1")});
Unfortunately, I still see no output when I click on the ramp or anywhere else in the game.

It is possible that this library is too old for the version of Three.js that I am using (r52). It is also possible that this library does not work with Physijs (objects susceptible to physics simulation) meshes. To test the latter theory, I add a plain old Three.js mesh to the scene, along with an event handler:
  var ball = new THREE.Mesh(
    new THREE.SphereGeometry(100),
    new THREE.MeshNormalMaterial()
  );
  scene.add(ball);
  ball.addEventListener('click', function() {console.log("clicked the three.js ball");});
Unfortunately, this still does not work. Not only do I not see the desired click message logged, but I am seeing errors:
Uncaught TypeError: Cannot read property 'projectionMatrixInverse' of null Three.js:3699
Uncaught TypeError: Cannot read property 'projectionMatrixInverse' of null Three.js:3699
Uncaught TypeError: Cannot read property 'projectionMatrixInverse' of null Three.js:3699
...
Bummer.

So it seems that this library may be a bit too old. I think this is probably a good place to call it a night. I will sleep on the decision to attempt a fix for this (or possibly sticking with last night's solution).


Day #646

Tuesday, January 29, 2013

Dragging Three.js Objects

‹prev | My Chain | next›

Last night I got started on a motion puzzle game for 3D Game Programming for Kids. As with all games in the book, it is built with Three.js. It also uses a little bit of Physijs for gravity and collisions.

The idea behind the game it to position and orient platforms so that the player can reach a goal:


To position the platforms, I thought to introduce something new in the book: mouse interaction. My understanding is that this is a difficult thing in Three.js, though some people have made some helper libraries. Tonight I try it without those helper libraries.

Since Three.js lacks first class support for mouse events, I (or a library) needs to translate DOM mouse events into Three.js action. In this game, the gaming area comprises the entire viewport of the browser, so only a little bit of translation is required:
  document.addEventListener("click", function(event) {
    var position = new THREE.Vector3(
      event.clientX - width/2,
      height/2 - event.clientY,
      500
    );
    var vector = new THREE.Vector3(0, 0, -1);
    var ray = new THREE.Ray(position, vector);
    var intersects = ray.intersectObject(ramp1);
    if (intersects.length > 0) console.log("Clicked ramp1");
    intersects = ray.intersectObject(ramp2);
    if (intersects.length > 0) console.log("Clicked ramp2");    
  });
Once I have the position of the click event translated into Three.js coordinates, I use it to create a ray pointing from that point into the screen (Z=-1). I then use the intersectObject() method of Ray to decide if the ray crosses through either of the ramps, logging to the console if it does.

This works as expected:


So now I am ready to add the ability to drag the selected item. Unfortunately, there does not seem to be proper drag events for Three.js / canvas / WebGL, so I have to settle for marking a ramp active in the mousedown event and storing the current X-Y position of the mouse:
  var active_ramp, mouse_x, mouse_y;
  document.addEventListener("mousedown", function(event) {
    //console.log(event.clientX);
    //console.log(event.clientY);

    mouse_x = event.clientX;
    mouse_y = event.clientY;
    var position = new THREE.Vector3(
      event.clientX - width/2,
      height/2 - event.clientY,
      500
    );
    var vector = new THREE.Vector3(0, 0, -1);
    var ray = new THREE.Ray(position, vector);
    var intersects = ray.intersectObject(ramp1);
    if (intersects.length > 0) active_ramp = ramp1;
    intersects = ray.intersectObject(ramp2);
    if (intersects.length > 0) active_ramp = ramp2;    
  });
I can then use this information in the mousemove handler to determine how much the mouse has moved:
  document.addEventListener("mousemove", function(event) {
    if (!active_ramp) return;
    var x_diff = event.clientX - mouse_x,
        y_diff = event.clientY - mouse_y;
        
    active_ramp.__dirtyPosition = true;
    active_ramp.position.x = active_ramp.position.x + x_diff;
    active_ramp.position.y = active_ramp.position.y - y_diff;
    
    mouse_x = event.clientX;
    mouse_y = event.clientY;
  });
The mousemove event happens constantly, so the guard clause at the top ensures that this particular handler only applies when a ramp has been clicked.

Lastly, I need a mouseup handler to deactivate the current ramp when the player lets go of it:
  document.addEventListener("mouseup", function(event) {
    active_ramp = null;
  });
And that actually works fairly well. I can now move both ramps, making this game much easier to win:



That is actually a rather nice effect and not too much work. I would have preferred proper drag events, but this seems almost simple enough for the book.

(the code so far)


Day #645

Monday, January 28, 2013

Constrained Motion in Physijs

‹prev | My Chain | next›

Up tonight, I switch to a new Three.js / Physijs potential game for 3D Game Programming for Kids. This will be another 2D game, so I copy bits and pieces of last night's platform game.

In this game, the player will click, drag and rotate platforms to help the player reach a goal, normally near the top of the screen. The player itself can only move forward or backward, so ramps will be needed to move up.

The player is a simple Physijs object, shaped like a disc:
  var player = new Physijs.ConvexMesh(
    new THREE.CylinderGeometry(30, 30, 5, 16),
      Physijs.createMaterial(
        new THREE.MeshBasicMaterial({color:0xbb0000}), 0.2, 0.5
      )
  );

  player.setAngularFactor(new THREE.Vector3( 0, 0, 0 )); // don't rotate
  player.setLinearFactor(new THREE.Vector3( 1, 1, 0 )); // only move on X and Y axis
Since this is a 2D, not three dimensional game, I constrain movement to the X-Y plane. As with the platformer game, I use simple keyboard events to move the player:
  document.addEventListener("keydown", function(event) {
    var code = event.keyCode;
    if (code == 37) left();  // left arrow
    if (code == 39) right(); // right arrow
  });
The left and right functions set a maximum velocity in the appropriate direction.

All of that is more or less a copy from the previous game. What is new is that I need four borders that constrain the player to the current screen. Since the player is restricted to the X-Y plane, all four borders are added with Z=0. What changes is the X-Y position of each and the size:
  function makeBorder(x, y, w, h)  {
    var border = new Physijs.BoxMesh(
      new THREE.CubeGeometry(w, h, 100),
      Physijs.createMaterial(
        new THREE.MeshBasicMaterial({color: 0x000000}), 0.2, 1.0    
      ),
      0 
    );
    border.position.set(x, y, 0);
    return border;
  }
I think I might do this chapter after the introduction to JavaScript objects, in which case I might fiddle with default options. For now I require all four arguments.

With that function, and the browser width and height already calculated, I have my borders with:
  scene.add(makeBorder(width/-2, 0,         50,    height));
  scene.add(makeBorder(width/2,  0,         50,    height));
  scene.add(makeBorder(0,        height/-2, width, 50));
  scene.add(makeBorder(0,        height/2,  width, 50));
As for the ramps, I define a similar generator function. In this case, the shape will be fixed, but the rotation can change:
  function makeRamp(x, y, rotation) {
    var ramp = new Physijs.ConvexMesh(
      new THREE.CubeGeometry(50, 500, 10),
        Physijs.createMaterial(
          new THREE.MeshBasicMaterial({color:0x0000cc}), 0.2, 1.0
        ),
        0
    );
    ramp.rotation.set(0, 0, rotation);
    ramp.position.set(x, y, 0);
    return ramp;
  }
  scene.add(makeRamp(0.4*width/2, -height/2 + 25, -Math.PI/4));
  scene.add(makeRamp(-0.3*width/2, 0, Math.PI/3));
The last thing that I do tonight is to create a goal for the game. I add a hoop to the top-left corner of the window:
  var goal = new Physijs.ConvexMesh(
    new THREE.TorusGeometry(90, 5, 5, 16),
    Physijs.createMaterial(
      new THREE.MeshBasicMaterial({color:0x00bb00}), 0.2, 0.5
    ),
    0
  );
  goal.position.set(width/-2, height/2, 0);
  goal.isGoal = true;
  scene.add(goal);
The isGoal property is an easy way for the collision detection to determine how (or if) to handle the event:
  player.addEventListener('collision', function(object) {
    console.log('Collision!');
    if (object.isGoal) gameOver();
  });
With that, I have a fairly functional game:


The code so far is available if anyone cares to try their luck. Up tomorrow, I will get started trying to figure out how best to expose the ability to move those ramps around the game area.


Day #644

Sunday, January 27, 2013

Three.js Shifting Frame of Reference

‹prev | My Chain | next›

The last outstanding question in my Three.js Physijs is how to move the camera along with the player as both move up:


I had thought I might be able to keep both in the same frame of reference, but, upon further reflection, I don't think that will work. The player really belongs in the platform frame of reference so that physics can work on it. The camera, on the other hand, defines a different frame of reference—one that includes the game-over line at the bottom.

Since they need to be in different frames of reference, I need to figure out how to move the camera along with the player as it moves up. This turns out to be fairly easy:
  function moveHigher() {
    if (player.position.y < camera.position.y + 100) return;
    camera.position.y = camera.position.y + 100;     
  }
If the player is below the camera + 100 mark, then this function does nothing. If the player has reached that level, then I bump the camera up 100. If I end up including this in 3D Game Programming for Kids, then I will likely have them tween the camera up rather than jerking it up like this.

This feels like another function that does not need to be directly in the animate() function, but I stick it there anyway for now:
  function animate() {
    requestAnimationFrame(animate);
    if (pause) return;
    scene.simulate(); // run physics

    resetPlayerFallingZ();
    moveHigher();
    gameOver();
    renderer.render(scene, camera);
  }
  animate();
While I am at it, I also add a gameOver() function to ensure that the player has not hit the bottom of the screen:
  function gameOver() {
    if (player.position.y > camera.position.y - height/2) return;
    console.log("GAME OVER!!!");
    pause = true;
  }
That seems a reasonable stopping point for tonight. I have enough of this game to be able to include it in the book if I so choose. It offers some level of familiarity for kids so it has that going for it. I am not sold on it offering much new for them. Still, it is good to have this in reserve in case I need to rework some of the other existing chapters or in case some of the planned chapters do not pan out.

(the code)

Day #643

Saturday, January 26, 2013

Wrapping Three.js Ghosts for Physijs Players

‹prev | My Chain | next›

I continue to explore a platform-based game for possible inclusion in 3D Game Programming for Kids.


This Three.js + Physijs game seems like it might appeal to kids. I still need to ensure that the game is a fit for the book. It needs to somewhat short—too much typing is never a good idea. It needs to introduce new concepts or aspects of programming and / or gaming. Most importantly, it needs to be playable.

Currently, it is still missing a few things to make it playable. Chief among them is boundary conditions. In mobile games like Doodle Jump, the sides of the screen wrap. This seems like it may be prohibitively difficult to accomplish in a kids' book. Still, I would like to see if I can make it work. But first, a fallback.

I make fixed walls on the sides of the screen to hold the player in the game area:
  function makeWall(x)  {
    var wall = new Physijs.BoxMesh(
      new THREE.CubeGeometry(350, 5000, 100),
      Physijs.createMaterial(
        new THREE.MeshBasicMaterial({color: 0x000000}), 0.2, 1.0    
      ),
      0 
    );
    wall.position.x = x;
    return wall;
  }
  scene.add(makeWall(width/-2));
  scene.add(makeWall(width/2));
This seems like the solution that I would use in the book. It re-uses existing concepts and works:


I don't think it is possible for a thing to be in two places at once in Three.js and it almost certainly violates the laws of physics in Physijs. So if I want to try a wraparound solution, I think I need to "fake" it. That is, until a player is either fully on one side or the other, I need a ghost to appear on the opposite side.

For my ghost, I create a non-physical (i.e. Three.js) object:
  var ghost = new THREE.Mesh(
    new THREE.CubeGeometry(50, 75, 5),
    new THREE.MeshNormalMaterial()      
  );
  ghost.visible = false;
  scene.add(ghost);
I start it invisible because it should only show when the player nears the boundary. For the boundary check, I add a call to animateGhost() in the main animate() function:
  function animate() {
    requestAnimationFrame(animate);
    scene.simulate(); // run physics

    animateGhost();
    renderer.render(scene, camera);
  }
  animate();
To get started with animating the ghost, I start with just a single side boundary, the left / negative X. I also start the animation short of the actual side—two thirds of the way there. This should make it easier to see it in action:
  function animateGhost() {
    if (player.position.x > width/-3) {
      ghost.visible = false; 
      return;
    }
    ghost.visible = true;
    ghost.position.y = player.position.y;
    ghost.position.x = width/3 - (width/-3 - player.position.x);
  }
If the player is more than a third away from the left wall, then the ghost is invisible and animateGhost() does nothing else. Otherwise the ghost is shown and it mirror the player's position on the other side of the screen.

And it actually works:


I can see where this is going. At some point, I would need to swap the ghost and the actual player (using the dirty position trick from last night for the Physijs player). I would also need to be able to perform the inverse. I am reasonably confident that I could make that work, but this does seem too complicated for a kid's game.

For now, I think it best to stick with the black borders. Tomorrow I will move forward with the remainder of the game by adding the ability for the camera to move up along with the player.

(the code so far)

Day #642

Friday, January 25, 2013

One Way Platforms in Physijs

‹prev | My Chain | next›

I have a slight problem with my Three.js / Physijs platform game:


Currently the player (the circle) bounces when it hits the platforms, which is what I want. But I want the player to bounce only when it hits the top of a platform—that is, when the player is falling down and hits the top of a platform it should bounce. When the player then bounces up into the bottom of another platform, I want the player to pass right through and continue moving up. The real world does not work that way and neither does Physijs. Instead the player bounces off the bottom of the platform and quickly shoots down.

I cannot think of an obvious way that Physijs might facilitate that—making objects ghost-like sometimes and completely solid at other times. I do know that Physijs supports collision events that can fire before the physics engine starts to affect things, but I cannot see how that helps in this case.

Instead, what I'd like to try is to move the player "out" of the screen when it is moving up, and place it back at z=0 when it is going down. This should give it a miss when moving up, but allow collisions to kick in when going down.

My first pass will assume that all collisions are down collisions. That is, when I see a collision, I move the player out of the screen 25:
  player.addEventListener('collision', function(object) {
    console.log('Collision!');
    player.__dirtyPosition = true;
    player.position.z = 25;
  }); 
Physijs does not normally allow instantaneous transport—that kind of defeats the laws of physics that it is working so hard to reproduce—hence the __dirtyPosition. That flag is how developers tell Physijs that yes, I know that I am asking something horrible of you, but I want to do it any way. It lasts only until the next physics update, which is typically done along with animation.

And this works, I am now allowed one collision and then my player is stuck at z=25:


To reset the player's Z position, I add a check to the animation() function:
  function animate() {
    requestAnimationFrame(animate);
    scene.simulate(); // run physics

    resetPlayerFallingZ();
    renderer.render(scene, camera);
  }
  animate();  
I define that function to reset the player's z back to zero, but only if it makes it through the guard clauses:
  function resetPlayerFallingZ() {
    if (player.position.z === 0) return;
    if (player.getLinearVelocity().y > 0) return;
    
    player.__dirtyPosition = true;
    player.position.z = 0;        
  }
The first guard clause ensure that the function only applies if the player's Z position has already been pushed out. The second ensures that the player is falling.

With that, I again see lots of collisions falling down, but am still free to move up without hindrance:


That seems like a good stopping point for tonight. Up tomorrow, I would like to see if I can get screen wrapping to work. This seems prohibitively hard—especially in a world in which physics prevails. Still, I need to give it a shot.


Day #641

Thursday, January 24, 2013

Bouncy Platforms with Physijs

‹prev | My Chain | next›

I have a nice start on a new game in 3D Game Programming for Kids. This is actually a 2D game in the vein of Doodle Jump, thought it still benefits from WebGL and definitely needs Physijs in addition to the prettiness of Three.js. So far I have my player falling from the sky and hitting platforms. Today I hope to add some controls to the player and give it a little bounce.

The controls are easy enough. I have done this elsewhere in the book. In this case, I handle only the left and right arrow keys:
  document.addEventListener("keydown", function(event) {
    var code = event.keyCode;
    if (code == 37) left();  // left arrow
    if (code == 39) right(); // right arrow
  });
The left() and right() functions are simple wrappers that move the player in the positive or negative X direction:
  function left()  { move(-100); }
  function right() { move(100); }
This level of indirection is a bit tricky in a kids book. Really, I could have called the move() function directly in the event handler with the appropriate argument. I do think it makes sense to keep the indirection when there are more controls (up, down, jump, etc.). It just reads better for kids (and me), though there is more typing. In the Basic that I coded from as a kid, they never would have used indirection. Then again, does Basic even have functions? I make a mental note to never look that up and move on...

The move() function is tasked with changing the X velocity, but not the Y velocity. So I grab the downward speed into a local variable and update the speed accordingly:
  function move(x) {
    var velocity_y = player.getLinearVelocity().y;
    player.setLinearVelocity(
      new THREE.Vector3(x, velocity_y, 0)
    );
  }
That gives me the desired left-right controls. Now I need the player to bounce when it hits the platforms. It seems half a lifetime ago (in reality roughly 250 posts back), but I have done bounciness in Physijs before. The version of the library that I am using is a few months old. Still, the wiki page includes the same information about bouncy materials. So I update both my player and my platforms to be bouncy:
  function makePlatform() {
    var box = new Physijs.BoxMesh(
      new THREE.CubeGeometry(100, 20, 10),
      Physijs.createMaterial(
        new THREE.MeshNormalMaterial(), 0.2, 1.0
      ),
      0
    ); 
    // ...
  }
  // ...
  function makePlatform() {
    var box = new Physijs.BoxMesh(
      new THREE.CubeGeometry(100, 20, 10),
      Physijs.createMaterial(
        new THREE.MeshNormalMaterial(), 0.2, 1.0
      ),
      0
    ); 
The bounciness factor comes from the third number in the Physijs.createMaterial() function. I am using 1.0 for maximum bounciness. And that does the trick—I now have a nice, bouncy ball that I can move back and forth with arrow keys:


Unfortunately, I think I spot a flaw. The 1.0 bounciness is perfect bounciness—the player bounces away with the same speed that it hits (only in the opposite direction). In other words, I am never going to add velocity to move up to a goal in space.

This is not my only problem as I need to figure out a way to allow the player to pass through platforms when moving up, but bounce when going down. I will pick back up tomorrow trying to answer those two questions.

(the code so far)


Day #640

Wednesday, January 23, 2013

Simple 2D Physics in Three.js

‹prev | My Chain | next›

I have been toying with the idea of including a simple game like Doodle Jump in 3D Game Programming for Kids. Admittedly, it is not 3D, but it still seems like it might be fun. I just don't know how easy it would be for kids to code. There is one way to find out...

First up, I need to add physics to the game, which means Physijs in addition to the usual Three.js. I do not have a template for this in my fork of the code editor. I may need to do that before the final version of the book. For now, I manually add the <script> tag:
<body></body>
<script src="http://gamingJS.com/Three.js"></script>
<script src="http://gamingJS.com/physi.js"></script>
<script src="http://gamingJS.com/ChromeFixes.js"></script>
And the normal phsysijs settings in the actual code:
  // Physics settings
  Physijs.scripts.ammo = 'http://gamingJS.com/ammo.js';
  Physijs.scripts.worker = 'http://gamingJS.com/physijs_worker.js';

  // This is where stuff in our game will happen:
  var scene = new Physijs.Scene({ fixedTimeStep: 2 / 60 });
  scene.setGravity(new THREE.Vector3( 0, -100, 0 ));
The numbers in there are just some that seem to move things well.

Next, I do something a little different—I use an orthographic camera for better 2D presentation:
  var width = window.innerWidth,
      height = window.innerHeight;
  var camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1e6 );
  camera.position.z = 500;
  scene.add(camera);
For that to work, I have to swap out the usual least-common-denominator canvas camera for the WebGL equivalent:
  var renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
If I end up including this in the book, I will have to check this out with a perspective camera and the canvas renderer to see if it is worth having kids without WebGL attempt this.

With that, I am ready to start adding my player and the platforms. I start with a function to randomly place platforms:
  function makePlatform() {
    var box = new Physijs.BoxMesh(
      new THREE.CubeGeometry(100, 10, 1),
      new THREE.MeshNormalMaterial()
    ); 

    box.position.set(
      (width/2 - width * Math.random()) * 0.75, 
      (height/2 - height * Math.random()),
      0
    );
    
    return box;
  }
Nothing too fancy there. I make the usual Phsysijs box mesh so that the player can interact with the platform. I will have to add bounce to it later, but this should be a good start. The width and height variables come from the calculations that used the browser's width and height to build the camera.

I call this function manually four times:
  scene.add(makePlatform());
  scene.add(makePlatform());
  scene.add(makePlatform());
  scene.add(makePlatform());
Which gives me platforms:


I do similar work to create a player in the shape of a disc, placing it at the top of the screen and giving it a little horizontal shove:
  var player = new Physijs.ConvexMesh(
    new THREE.CylinderGeometry(20, 20, 1, 24),
    new THREE.MeshNormalMaterial()
  );
  
  player.position.set(100 + width/-2, height/2, 0);
  player.rotation.x = Math.PI/2;
  scene.add(player);
  player.setLinearVelocity(
    new THREE.Vector3(10, 0, 0)
  );
With that, I am ready to animate my code:
  function animate() {
    requestAnimationFrame(animate);
    scene.simulate(); // run physics
    renderer.render(scene, camera);
  }
  animate();
In addition to the usual Three.js animation, I also have to tell Phsysijs to do its thing, which it does, but... I forgot to peg the platforms. The player and the platforms fall under the influence of Phsyijs gravity. To fix, I add a third parameter, zero, to the platforms' BoxMesh constructor to tell the physics engine that these platforms are not affected by gravity:
  function makePlatform() {
    var box = new Physijs.BoxMesh(
      new THREE.CubeGeometry(100, 10, 1),
      new THREE.MeshNormalMaterial(),
      0
    ); 

    box.position.set(
      (width/2 - width * Math.random()) * 0.75, 
      (height/2 - height * Math.random()),
      0
    );
    
    return box;
  }
This reminds me that I also need to constrain the player from rotating or from moving in-or-out of the screen (this is a 2D game):
  player.setAngularFactor(new THREE.Vector3( 0, 0, 1 )); // only rotate around the Z axis
  player.setLinearFactor(new THREE.Vector3( 1, 1, 0 )); // only move on X and Y axis
With that, I have my player falling and hitting platforms. There is still much to be done, but this will serve as a good stopping point. I'm glad that I have not forgotten all of this. It does help to have the pre-beta copy of the book around to refresh my memory on some of this stuff as well.

(the code so far)


Day #639

Tuesday, January 22, 2013

Abusing the Three.js clone method

‹prev | My Chain | next›

Last night I was able to get two Three.js renderers displaying on the same screen. The approach was highly manual—recreating the objects, cameras and positions from the first scene in the second. Tonight, I hope to automate some of that.

I am still working on the animating the phases of the Moon—both from above the Solar System and from the Earth:


When I first tackled simultaneous rendering of multiple cameras, I found that the clone() method was not working well for me. Now that I no longer have multiple things going wrong, I look a little closer at clone(). First off, it is not a public method so I probably should not be using it. It is defined in Object3D, but does not appear in the documentation. Secondly, the API is a bit weird (unsurprising in that it is only meant for internal use).

By default, clone() creates Object3D objects, not new instances of whatever is being cloned. This gets position an similar items, so it presumably is used for cloning size, orientation, and position of things, but not much else. In other words, it is of little use to me in this case. Just to be clear, this is a private method so I have no cause to complain.

Anyhow, to verify that I have an approximate grasp on what is going on here, I clone the light from the main scene into the overlay scene. Instead of relying on the vanilla clone() which is going to return an Object3D instead of the desired DirectionalLight, I supply clone() with an argument—an instance of DirectionalLight:
  var directionalLight = directionalLight.clone(new THREE.DirectionalLight());
  scene2.add( directionalLight );
This does the trick, except that the colors of the Moon are not copied in this approach:


(the Moon is a dull grey instead of white)

This is to be expected since clone() only knows to copy Object3D properties.

So it seems that, if I want what I consider a clone, then I need to do a little prototype hacking:
  THREE.DirectionalLight.prototype.clone = function(){
     var copy = THREE.Object3D.prototype.clone.call(this, new THREE.DirectionalLight());
     copy.color = this.color;
     copy.intensity = this.intensity;
     return copy;
  };
  
  scene2.add(directionalLight.clone());
With this updated clone() in place, I now get the nice, bright Moon that I expected:


I would have to do something similar to get the planet and Moon meshes to clone. I am not sure if that is worth the effort at this point. I do not really need a general purpose solution, especially with all of the other outstanding work needed in 3D Game Programming for Kids. Still, stubborness may win out, in which case I could be back at this tomorrow.


Day #638

Monday, January 21, 2013

Multiple Renderers in Three.js

‹prev | My Chain | next›

I ended yesterday's attempt at getting multiple Three.js scenes working with no visual output and a rather obnoxious error:
WebGL: CONTEXT_LOST_WEBGL: loseContext: context lost
Today I think to check the actual source of the exception and find:


Actually, that is not a big deal. This is just caused when I drop a preview page so that a new one can be created. To be sure, I need to get rid of this—kids working through 3D Programming for Kids should not have to see this. But really, this is OK—it is likely something new in Chrome 26.

But that still leaves me with no visual output in my Phases of the Moon project. From yesterday, I am currently attempting to clone everything from my first scene into my second scene:
  // ...
  var moon_orbit = new THREE.Object3D();
  moon_orbit2 = moon_orbit.clone();
  earth.add(moon_orbit);
  earth2.add(moon_orbit2);
  moon_orbit.add(moon);
  moon.position.set(0, 100, 0);
  //...
I ended yesterday by trying to comment everything out relating to the first scene just to get the new "picture-in-picture" overlay scene rendered. While fiddling around with re-enabling the main scene, I start taking notice of the litany of error messages that Three.js is spewing my way. Although there are many, they may actually be telling me something useful:
WebGL: INVALID_OPERATION: useProgram: object not from this context
WebGL: INVALID_OPERATION: uniformMatrix4fv: location is not from current program
WebGL: INVALID_OPERATION: uniform3f: location not for current program
WebGL: INVALID_OPERATION: uniform1f: location not for current program
WebGL: INVALID_OPERATION: uniform3fv: location is not from current program
...
This would seem as though I am including something from the first scene in the second, which is a no-no.

To test this theory out, I delete all of the scene-2 code. I again have the Moon revolving around the Earth in the main scene with an empty picture-in-picture overlay:


This time, I start by adding nothing but new objects to the scene that will go in that overlay:
  var scene2 = new THREE.Scene();

  var renderer2 = new THREE.WebGLRenderer();
  renderer2.setSize(250, 250);
  pip.appendChild(renderer2.domElement);
  
  var directionalLight = new THREE.DirectionalLight( 0xffffff, 5 ); 
  directionalLight.position.set( -1, 0, 0 ); 
  scene2.add( directionalLight );


  var cam2 = new THREE.OrthographicCamera( -125, 125, 125, -125, 1, 1e6 );
  cam2.position.set(0,0,500);
  
  var surface = new THREE.MeshPhongMaterial({ambient: 0x1a1a1a, color: 0xffffff});
  var planet = new THREE.SphereGeometry(15, 30, 25);
  var moon2 = new THREE.Mesh(planet, surface);
  
  scene2.add(moon2);

  function animate2() {
    requestAnimationFrame(animate2);
    renderer2.render(scene2, cam2);
  }
  animate2();
And... it works!

I now have two different WebGL renderers sending two different scenes to two different DOM elements:


If I try to replace the completely new moon2 definition with yesterday's moon.clone(), then I again get the "not from this context/program" WebGL errors. So cloning is out, which makes it much more difficult, thought not impossible to get two different views.

I take a little time to recreate the Moon's orbit around the Earth and the Earth's positioning relative to the center of the Solar System. With that, I am ready to copy the rotation of the Moon's orbit from the main animation thread:
  function animate2() {
    requestAnimationFrame(animate2);
    renderer2.render(scene2, earth_cam2);
    moon_orbit2.rotation.set(0, 0, moon_orbit.rotation.z);
  }
  animate2();
And I have an accurate picture-in-picture representation of my scene. Two different scenes, two different cameras showing at the same time:


The runnable, editable code that does this is available.


Day #637

Sunday, January 20, 2013

Still Can't Get Two Three.js Scenes Rendered

‹prev | My Chain | next›

I failed to get two Three.js renderers to display to the same screen yesterday. I tried multiple renderers, multiple cameras, different kinds of renderers, and different kinds of cameras. None of it worked. But there was one thing I did not try: multiple scenes. I have no idea if that is any more possible than multiple cameras and renderers, but there is one way to find out.

Unfortunately, it does not quite work out. I am still unsure if it is possible, but I definitely do not have it working. At some point during my refactoring, I started getting "WebGL: CONTEXT_LOST_WEBGL: loseContext: context lost" errors and no longer saw any screen output at all.

My approach was to clone or recreate just about every aspect of the previous screen, so I ended up with a lot of code along the lines of:
  var moon_orbit = new THREE.Object3D();
  moon_orbit2 = moon_orbit.clone();
  earth.add(moon_orbit);
  earth2.add(moon_orbit2);
  moon_orbit.add(moon);
  moon.position.set(0, 100, 0);

  var moon2 = moon.clone();
  moon_orbit2.add(moon2);
  moon_orbit.add(earth_cam);
  earth_cam.rotation.set(Math.PI/2, 0, 0);
  earth_cam2.rotation.set(Math.PI/2, 0, 0);
  moon_orbit2.add(earth_cam2);
With Earth-prime and Earth-2, I have something of a Crisis of Infinite Earths on my hands. Only the problem seems to be that I have lost the Earth, the Moon, the Sun and everything else.

I should probably cut my losses here and move onto to other games and such for Gaming JavaScript. Then again, I really feel like this ought to be possible so I may give it one more shot tomorrow.


Day #636

Saturday, January 19, 2013

Can't Render a Three.js Scene in Two Places

‹prev | My Chain | next›

I am done with the Moon simulator chapter in 3D Game Programming for Kids and am ready to move onto other things (like more actual games). It occurs to me, however, that I do not know how to allow two cameras to be active at the same time in Three.js. I was able to do this king of thing in CubicVR.js / Gladius, but am not even sure it is possible in Three.js.

In the chapter, I include some side-by-side comparisons of the Moon's position and its phase:


Those screenshots are labor intensive—I need to screen capture each (and resize the print scale of each). This would not be something that I would have the kids do, but it would be great to have the Moon's phase superimposed on the overhead shot.

Unfortunately, as best I can tell, there is no way to accomplish this in Three.js. The render that spits the scene out onto canvas, takes only one camera:
  function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
  }
Three.js does include a CombinedCamera. Sadly this does not do what I hope—it just makes it easy to switch back and forth between a perspective camera and an orthographic camera.

I might not be entirely out of luck though. Just because Three.js renders are restricted to a single camera does not necessarily mean that I am restricted to a single renderer.

So I add a "picture-in-picture" element to the screen:
  var pip = document.createElement('div');
  pip.style.width = 250;
  pip.style.height = 250;
  pip.style.position = 'absolute';
  pip.style.backgroundColor = 'black';
  pip.style.borderRadius = "5px";
  pip.style.border = '2px solid white';
  pip.style.padding = "0px 20px";
  pip.style.left = "50px";
  pip.style.top = "25px";
  document.body.appendChild(pip);
Then I create a second renderer and add its domElement to the picture-in-picture:
  var renderer2 = new THREE.WebGLRenderer();
  renderer2.setSize(250, 250);
  pip.appendChild(renderer2.domElement);
This is just like the regular renderer, except that I append the renderer's DOM element to a specific element instead of appending it to the document body. I also note that the aspect ratio that I will have is 1:1, so I redefine the "Earth Cam" to have a 1:1 aspect ratio:
  //var earth_cam = new THREE.PerspectiveCamera(45, aspect_ratio, 1, 1e6);
  var earth_cam = new THREE.PerspectiveCamera(45, 1, 1, 1e6);
With that, I am ready to try double rendering in Three.js. I add the second renderer to my animate() method:
  function animate() {
    requestAnimationFrame(animate);
    renderer2.render(scene, earth_cam);
    renderer.render(scene, camera);
    // ...
  }
Unfortunately, that does not work. I only get the new camera, with the rest of the scene completely blank:


In addition to the visual evidence, Three.js spits out a lot of warnings telling me that I am doing something very wrong:
THREE.WebGLRenderer 52 Three.js:15545
THREE.WebGLRenderer 52 Three.js:15545
WebGL: INVALID_OPERATION: useProgram: object not from this context Three.js:20282
WebGL: INVALID_OPERATION: uniformMatrix4fv: location is not from current program Three.js:20298
WebGL: INVALID_OPERATION: uniform3f: location not for current program Three.js:20743
WebGL: INVALID_OPERATION: uniform1f: location not for current program
...
I try moving the second renderer out into its own animate2() function:
  function animate2() {
    requestAnimationFrame(animate2);
    renderer2.render(scene, earth_cam);
  }
  animate2();
But this has no effect. I still only see a single camera's output and get lots of warnings in the JavaScript console. I try rendering the second camera just a single time. I also try switching the renderer to a CanvasRendered. All have no effect.

It seems that I simply cannot render the same scene to two different renderers. If anyone has any thoughts, the non-worky code is up. In the meantime, I will sleep on it and perhaps revisit tomorrow.


Day #635

Friday, January 18, 2013

Going Grey in Three.js Space Simulations (sigh)

‹prev | My Chain | next›

I was bummed when my editor reminded me that 3D Gaming JavaScript for Kids will need to support greyscale devices. Well, devices and print, but who buys print anymore? Well some people do apparently. Anyhow...

My pretty space simulations:


End up looking like this in greyscale:


Dang it. The Sun and the Moon look OK, but Earth is barely visible. Looks like I need to make a complete greyscale version of each game. Fuuuuuuu....

Well, hopefully I won't have to do each game, but I definitely need to do the space-based games. And I suppose that I do not need it completely grey—just the Earth. But I do need a way to toggle the color back and forth so that I can have exact duplicates for each picture that I take.

So I add yet another key handler:
 document.addEventListener("keydown", function(event) {
    var code = event.which || event.keyCode;

    if (code == 67) changeCamera(); // C
    if (code == 32) changeCamera(); // Space
    if (code == 71) colorToggle(); // G
    if (code == 80) pause = !pause; // P
    if (code == 49) speed = 1; // 1
    if (code == 50) speed = 2; // 2
    if (code == 51) speed = 10; // 3
    event.preventDefault();
  });
For the actual toggle, I check for all white in the Three.js Mesh:
  function colorToggle() {
    var c = earth.material.color.clone();
    if (c.r == 1 && c.g == 1 && c.b ==1) earth.material.color.setRGB(0,0,0.8);
    else earth.material.color.setRGB(1,1,1);
  }
That seems to do the trick. Now I can take a picture of a Third Quarter Moon from the Earth:


Then take a color snapshot from above:


And quickly toggle to greyscale:


I think that'll have to be the solution that I use for this and just about any image that does not look right in color. But mostly I am going to work to keep games that look OK in greyscale.


Day #634

Thursday, January 17, 2013

Better Orbits with Three.js Frames of Reference

‹prev | My Chain | next›

Yesterday, I was able to pick back up with Three.js by coding up a simulation of the Moon travelling around the Earth. I eased myself back in by copying a simulation of the Earth passing Mars in their respective orbits. The result was a working solution, but perhaps not the best solution. Today I hope to see if I can do better.

In the Mars simulation, I positioned both Earth and Mars with absolute X & Y coordinates, with the Sun at (0,0). I accomplished this with simple sines and cosines primarily because I needed those absolute coordinates to calculate the orientation of the Earth camera pointing at Mars. Also, I like tormenting kids with trig in 3D Game Programming for Kids.

Anyhow, for the Moon simulator, I repeated the absolute X & Y coordinates for the Moon, though I offset them by the Earth's position to give the appearance of orbiting. It worked, but that's not an effective 3D programming technique. The proper way to do this is create a new earth-centric frame of reference for the Moon. In Three.js, this is done by creating an empty Object3D object and adding it to the Earth:
  var moon_orbit = new THREE.Object3D();
  earth.add(moon_orbit);
I then add the Moon to this frame of reference and move the Moon 100 units away from the center of this new frame of reference:
  moon_orbit.add(moon);
  moon.position.set(0, 100, 0);
The order of those two operations does not matter in Three.js, but it is easier to follow as shown.

It occurs to me that I can stick the earth-cam-looking-at-the-moon into this same frame of reference. When this frame of reference turns to give the Moon the semblance of an orbit, the camera will turn right along with it:
  moon_orbit.add(earth_cam);
  earth_cam.rotation.set(Math.PI/2, 0, 0);
Rotating this frame of reference is pretty simple. In my animate() function, I update the rotation around the Z axis (pointing out of the screen) a tiny bit on each call:
  function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);

    if (pause) return;
    time = time + speed;
    var e_angle = time * 0.001;
    earth.position.set(350* Math.cos(e_angle), 350* Math.sin(e_angle), 0);

    var m_angle = time * 0.01;
    moon_orbit.rotation.set(0, 0, m_angle);
  }  
That is a darn sight easier to read than the sine and cosine for the Earth. It also much improves on setting the X & Y coordinates as I was doing yesterday:
    // Ew.
    moon.position.set(
      100* Math.cos(m_angle) + earth.position.x, 
      100* Math.sin(m_angle) + earth.position.y, 
      0
    );
Most importantly, this is another example that I can use to teach kids about frame of reference in the book.

Obviously, I can improve upon the Earth's orbit as well, but I may leave it as-is in the book to make the transition to or from the Mars simulator easier. That or I could leave it as a challenge for the more industrious readers.

The end result code and simulation are available in editable format. Once the code is hidden, the controls are: C (or Space) to toggle the camera, P to pause, and 1, 2, or 3 to change the speed of the simulation.

This stuff is starting to come back to me. I make pick up with an entirely new game tomorrow. Or I may add planet and satellite rotation to the mix.


Day #633

Wednesday, January 16, 2013

The Earth and the Moon in Three.js

‹prev | My Chain | next›

With deadlines looming, it is high time to get back to Gaming JavaScript (actually it looks as though it will be called 3D Game Programming for Kids). I am bit out of practice, so I pick back up with a variation of an earlier exercise in the book. Instead of simulating planetary motion, I am going to have kids simulate the Moon's revolution around the Earth.

I copy the Mars simulation to a "Moon Phases" project and start by renaming the mars variable to moon. The Moon's position over the course of time is going to be a circle around the Earth's position in the Solar System. So, in the Three.js animate() function, I express this with simple sines and cosines:
  function animate() {
    requestAnimationFrame(animate);
    var t = clock.getElapsedTime();

    /* set the earth position first */   

    // Now set the moon
    var m_angle = t * 0.2;
    moon.position.set(
      100* Math.cos(m_angle) + earth.position.x, 
      100* Math.sin(m_angle) + earth.position.y, 
      0
    );

    /* position various cameras */
        
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
  
  animate();
That actually does the trick. I now have the Earth revolving around the Sun and the Moon revolving around the Earth:


And, thanks to the camera controls that I added to the Mars simulator, I can switch back and forth between above-the-solar-system view and from-the-earth view:


As can be seen, a waning crescent Moon is what you get when the Moon is almost in between the Earth and the Sun. Only it's a little hard to see in the simulator because I lack a pause button. So I add one (the "P" key), along with speed controls (1, 2, and 3):
  document.addEventListener("keydown", function(event) {
    var code = event.which || event.keyCode;

    if (code == 65) camera = above_cam; // A
    if (code == 69) camera = earth_cam; // E
    if (code == 77) camera = earth_cam; // M
    if (code == 80) pause = !pause; // P
    if (code == 49) speed = 1; // 1
    if (code == 50) speed = 2; // 2
    if (code == 51) speed = 10; // 3
    event.preventDefault();
    return false;
  });
To make those work, particularly the pause button, I need to drop my reliance on the Three.js clock time to calculate the Earth's position in it orbit:
  function animate() {
    requestAnimationFrame(animate);
    
    var t = clock.getElapsedTime();
    
    var e_angle = t * 0.8;
    earth.position.set(250* Math.cos(e_angle), 250* Math.sin(e_angle), 0);
    // ...
  }
The problem here is that the Three.js clock keeps ticking even if the pause button is pressed. Then, when the pause button is unpressed, the Earth and Moon jump way ahead in their orbit.

So instead, I introduce an independent time variable:
  var time = 0,
      speed = 1,
      pause = false;
  function animate() {
    requestAnimationFrame(animate);

    if (pause) return;
    time = time + speed;
    var e_angle = time * 0.001;
    earth.position.set(350* Math.cos(e_angle), 350* Math.sin(e_angle), 0);
    // ...
  }
This new time tracking variable is only updated if the pause button has not been pressed. And, if it is updated, it is increased by the current speed.

The end result seems pretty solid and can be seen in the ICE editor. It is 110 lines and mostly based on the previous solar system code, so kids ought to be able to handle it.

Tomorrow, I may give it a shot positioning the Moon relative to the Earth's frame of reference rather than offsetting it from the Earth's position. If I recall correctly, there was difficulty orienting the camera when doing this. Then again it has been a while so it's definitely worth revisiting.


Day #632