Sunday, September 30, 2012

Remove Falling Letters

‹prev | My Chain | next›

I continue working with my learn-to-type Three.js. So far, I have letters falling. More precisely, I have a letter falling—"f". To start the game just the home keys, "f" and "j" should fall. So I create a nextLetter() function to supply a random next letter:
  function nextLetter() {
    var pool = ['f', 'j'];
    return pool[Math.floor(Math.random() * pool.length)];
  }
To determine whether or not I have hit the right key, I am going to need to know the list of currently falling letters. As of last night, I am not storing the letter—just writing it in a texture for a Three.js sprite. So, instead of maintaining a list of sprites that are falling, I maintain a list of objects that includes the letter and the sprite:
  function addLetter() {
    var letter = nextLetter();
    var canvas = document.createElement('canvas');
    canvas.width = canvas.height = 50;
    var context = canvas.getContext('2d');
    context.font = '48px Arial';
    context.fillText(letter, 0, 40);

    var texture = new THREE.Texture(canvas);
    texture.needsUpdate = true;
    var sprite = new THREE.Sprite({
      map: texture,
      useScreenCoordinates: false
    });
    scene.add(sprite);
    sprite.position.set(0.5* window.innerWidth - Math.random() * window.innerWidth, window.innerHeight/2,0);
    meteors.push({
      letter: letter,
      sprite: sprite
    });
  }
I still worry that this is too long a function for kids to type and I can see no obvious way to shorten it up. Still, I will leave that problem for another day.

For now, I add a document listener to remove the falling letters:
  document.addEventListener('keypress', function(event) {
    for (var i=0; i<meteors.length; i++) {
      if (String.fromCharCode(event.keyCode) == meteors[i].letter) {
        scene.remove(meteors[i].sprite);
        
        var rest = meteors.slice(i+1);
        meteors = meteors.slice(0, i).concat(rest); 
        console.log(meteors);
        continue;
      }
    };
  });
I am pretty excited to find a legitimate use of the continue keyword (to break out of the for loop). That aside, the only interesting bit in there is the call to String.fromCharCode to compare the keycode of the keypress event with the falling letters/meteors. And that seems to work fairly well.

While String.fromCharCode is interesting, I am again frustrated in my efforts to make a simple coding example for kids by JavaScript's lack of a remove() method on arrays. Here, I muck with slices and concat to simulate the effect, but I would really prefer to avoid that kind of thing. Next thing I know, I'll be teaching kids on the wonders of fencepost problems.

Thwarted, at least for the time being, in my efforts to come up with a simpler way of doing this, I call it a night here.

Code so far


Day #525

Saturday, September 29, 2012

Falling Letters

‹prev | My Chain | next›

Oh, what the hey? Or is that what the hay?

Anyway, why not make a learn-to-type game? I spent the last few days going into text sprites in excruciating detail. I had done so simply for my own edification, but perhaps there is something more immediate that I can do with that knowledge. And it might make a good intro game for Gaming JavaScript.

I leave my center marker in place, just as a point of reference while I am building things:
  var wrapper = new THREE.MeshNormalMaterial();
  var shape = new THREE.SphereGeometry(10);
  var center_marker = new THREE.Mesh(shape, wrapper);
  scene.add(center_marker);
For the overall structure of the game, I will store a list of letters in an array, then animate and run through steps of the game:
  var letters = [];  
  
  animate();
  gameStep();
For starters, animation is just the usual request animation:
  function animate() {
    requestAnimationFrame(animate);
    
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
In the game loop, I will add a new letter every three seconds and continuous update the location of the falling letters:
  var step = 0;
  function gameStep() {
    if ((step % (3*60)) == 0) {
      addLetter();
    }
  
    lettersFall();
    
    step++;  
    setTimeout(gameStep, 1000 / 60); // process the game logic
                                    // at a target 60 FPS.    
  }
To add letters, I create a new sprite for each new letter. As I found the past few days, this is a bit involved (create canvas, create 2D drawing context, draw letter, make texture from canvas, use in Three.js):
  function addLetter() {
    var canvas = document.createElement('canvas');
    var size = 50;
    canvas.width = size;
    canvas.height = size;
    var context = canvas.getContext('2d');
    context.textAlign = 'center';
    context.font = '48px Arial';
    context.fillText("f", size/2, size);

    var texture = new THREE.Texture(canvas);
    texture.needsUpdate = true;
    var sprite = new THREE.Sprite({
      map: texture,
      transparent: true,
      useScreenCoordinates: false
    });
    scene.add(sprite);
    sprite.position.set(0.5* window.innerWidth - Math.random() * window.innerWidth ,window.innerHeight/2,0);
    letters.push(sprite);
  }
At the very end of that function, I add the sprite to the scene and to the letters array. This allows me to continuously update the position of the letters in the lettersFall() function:
  function lettersFall() {
    letters.forEach(function(letter) {
      letter.position.y = letter.position.y - 1;
    });
  }
And that seems to do the trick:


I am unsure if creating a new canvas element for each letter is wise, but it seems to work for now. Then again, that might be a prohibitive amount of typing for a kids game. I'll sleep on that thought and pick back up tomorrow.

Day #524

Friday, September 28, 2012

Positioning Canvas Text on Three.js Sprites

‹prev | My Chain | next›

I don't quite have text sprites in Three.js working to my satisfaction. I can imagine using these things in games so that "+10" replaces a captured game element. The "+10" could then slowly rise above the spot where it was awarded until it fades quickly away. The problem in this scenario is that I am not confident that I could place text exactly where it needs to go.

From list night, I am working with the words "Game Over". Since this phrase is obviously longer than it is tall, I specify the canvas dimension accordingly:
  var canvas = document.createElement('canvas');
  canvas.width = 250;
  canvas.height = 100;
  var context = canvas.getContext('2d');
  context.font = '48px Arial';
  context.fillText("Game Over", 0, 50);
I draw the words in the 2D context of canvas. I make the font size 48 pixels high, which means that I have to shift the words down a little more than 48 pixels from the top so that I can see them.

Unfortunately, using this canvas element as a Three.js texture doesn't quite work. The text is stretched tall:


My problem is that textures in Three.js need to be squares. If they are not squares, Three.js will stretch them until they fit. This explains the undesired appearance of my "Game Over" text. It also tells me how to fix it—make the canvas width and height the same:
  var canvas = document.createElement('canvas');
  canvas.width = 250;
  canvas.height = 250;
  var context = canvas.getContext('2d');
  context.font = '48px Arial';
  context.fillText("Game Over", 0, 50);
This results in a much better looking text sprite:


I still think I need help placing the text correctly in the scene. I am already using scene (not canvas) coordinates by virtue of setting the sprite's useScreenCoordinates property to false. If I make a ball with a radius of 100 at the center of the screen, then try to place the text 100 above the center, then my text ought to be immediately on top of the ball. The result, however is:


My text is a little too high there. My problem is that the text is placed at the very top of the canvas. If I want to move the text baseline to the middle, I need only shift the distance from the top by half the height:
  var canvas = document.createElement('canvas');
  var size = 250;
  canvas.width = size;
  canvas.height = size;
  var context = canvas.getContext('2d');
  context.font = '48px Arial';
  context.fillText("Game Over", 0, size/2);
That gives me the nicely placed text:


That is not quite a robust solution however. If I decrease the font size of "Game Over", the vertical position is still good, but now the horizontal alignment is off:


Per MDN, there is a textAlign property for the canvas context that supports a value of "center". That sounds perfect. Unfortunately, it is less than perfect:


The textAlign property is not within the bounding box of the canvas. Rather, it specifies how text is drawn from the specified coordinates. In this case, my coordinates are zero pixels over and size/2 pixels down the canvas:
  context.fillText("Game Over", 0, size/2);
The text is centered on that x coordinate of zero, which is good—only I need the x coordinate to be in the center of the canvas. In other words, I want size/2 for the x position as well:


So the important points for this exercise—at least the points that confused me at first—are that canvas, like any other Three.js texture, needs to be a square and centering text on that canvas takes a bit of work.

Final version of the code

Day #523

Thursday, September 27, 2012

Three.js Text Textures

‹prev | My Chain | next›

I was able to get 3D text added to a Three.js scene yesterday. Today I would like to add 2D text. The 3D text works fairly well, but I may not always need 3D. Also I am not quite sure what the font parameter.

I have seen a couple of examples in which a canvas is used to create a texture, so I am going to attempt to get that to work. The canvas and context:
  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');
I can then use fillText() to enter some text:

  context.fillText("Game Over", 10, 10);
Now, I can create a texture and try to add that to a Three.js Mesh:

  var texture = new THREE.Texture(canvas);
  var cover = new THREE.MeshBasicMaterial({map: texture});
  var shape = new THREE.PlaneGeometry(100, 100);
  var banner = new THREE.Mesh(shape, cover);
  banner.rotation.x = Math.PI/2;
  banner.position.y = -100;
  scene.add(banner);
But, I only get a grey box:


I have seen that before. I need to tell Three.js that something needs an update:
  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');
  context.fillText("Game Over", 10, 10);
  var texture = new THREE.Texture(canvas);
  texture.needsUpdate = true;
  var cover = new THREE.MeshBasicMaterial({map: texture});
  var shape = new THREE.PlaneGeometry(100, 100);
  var banner = new THREE.Mesh(shape, cover);
  banner.rotation.x = Math.PI/2;
  banner.position.y = -100;
  scene.add(banner);
But then the mesh disappears completely.

After fiddling quite a bit, it turns out that my problem is the lack of dimension on the canvas element:
  var canvas = document.createElement('canvas');
  canvas.width = 100;
  canvas.height = 50;
  var context = canvas.getContext('2d');
  context.fillText("Game Over", 0, 10);
  //context.font = '48px';
  var texture = new THREE.Texture(canvas);
  texture.needsUpdate = true;
  var cover = new THREE.MeshBasicMaterial({map: texture});
  var shape = new THREE.PlaneGeometry(100, 100);
  var banner = new THREE.Mesh(shape, cover);
  banner.rotation.x = Math.PI/2;
  banner.position.y = -100;
  scene.add(banner);
With that, I get the "Game Over" texture:


I need to fiddle with the size a little more to get this working just right, but this is a decent start.

Last up, I switch from a plane to a sprite:
  var canvas = document.createElement('canvas');
  canvas.width = 60;
  canvas.height = 20;
  var context = canvas.getContext('2d');
  context.fillText("Game Over", 0, 10);

  var texture = new THREE.Texture(canvas);
  texture.needsUpdate = true;
  var sprite = new THREE.Sprite({
    map: texture,
    transparent: true,
    useScreenCoordinates: false
  });
  sprite.position.set(0,-150,0);
  scene.add(sprite);  
That actually works well:


I am still not quite sure about the ideal canvas dimensions to get this to show, but I can fiddle with that another day.

Day #522

Wednesday, September 26, 2012

Three.js TextGeometry and the Missing Font

‹prev | My Chain | next›

Tonight I am going to take a look at another of the many, many features of Three.js that I know nothing about: TextGeometry. I can not explain it, but I fear that text geometry is going to prove difficult for me. I have no reason to expect this—for all I know it will work like anything else in Three.js—but still, I fear it.

To overcome said fear, I start with an empty project in Mr Doob's Code Editor. In there, I try to add TextGeometry to the scene, treating TextGeometry as I would any other Three.js Geometry:
  var shape = new THREE.TextGeometry("Game Over");
  var wrapper = new THREE.MeshNormalMaterial({color: 0x00ff00});
  var words = new THREE.Mesh(shape, wrapper);
  scene.add(words);
Sadly, I see nothing and find the following in the JavaScript console:
Uncaught TypeError: Cannot read property 'normal' of undefined
Most (all?) of the examples on the Three.js site make use of a "helvetiker" font that appears to be defined in JavaScript. Since the font is specified in the TextGeometry's options argument, I would not expect this to be required, but I cannot think of anything else to try.

So I add a <script> tag to pull in the helvetiker font from the Three.js site:
<script src="http://mrdoob.github.com/three.js/examples/fonts/helvetiker_regular.typeface.js"></script>
Next, I add the helvetiker font to the TextGeometry constructor:
  var shape = new THREE.TextGeometry("Game Over", {font: 'helvetiker'});
  var wrapper = new THREE.MeshNormalMaterial({color: 0x00ff00});
  var words = new THREE.Mesh(shape, wrapper);
  scene.add(words);
With that, I finally get my text:


I am confused as to why an attribute in the options object seems to be required. I think I am likely missing something obvious. I will puzzle through this a bit more and ask in the IRC channel tomorrow, if I am unable to come up with a satisfactory explanation.

Regardless, I have it working. I must have tried this at some point earlier, which would explain my initial concerns. Happily, I was able to get a little further tonight.

The Code.

Day #521

Tuesday, September 25, 2012

Particles, Sprites, and Particle Systems

‹prev | My Chain | next›

Last night, I found that Three.js Particles belong in the Canvas Renderer, while Particle Systems belong in WebGL Renderers. This begs the question where do Sprites go?'

Well, it turns out that Sprites go in WebGL renderers along with Particle Systems and serve the same function as Particles. Interestingly (more precisely, confusingly), Sprites take similar constructor arguments to particle materials, but are positioned like a canvas element. That is, Sprites are positioned from the top left corner of the canvas element with y increasing downwards:
  var map = THREE.ImageUtils.loadTexture('/images/purple_fruit_monster.png');
  map.needsUpdate=true;
  
  var sprite = new THREE.Sprite({
    map: map,
    transparent: true
  });
  sprite.position.set(200,150,-10000000);
  scene.add(sprite);
An X position of 200 would put the sprite to the right of the center position, but it barely moves the sprite from the left of the viewport:

(the ball marks the center of 3D space)

Not only are the x-y coordinates canvas-based, but the z coordinate (negative ten million) is completely ignored.

Happily, if the sprite is meant to be a part of the 3D scene rather than an overlay, Three.js provides the useScreenCoordinates property. Setting useScreenCoordinates to false lets me place the sprite directly in the scene with the usual 3D coordinates (including the Z position):


As for the particle system, I would still like to know how to move individual particles within the system. I can make the entire system rotate in a simple animation. But if I try to move a single particle within that system, the particle sits frozen:
  function animate() {
    requestAnimationFrame(animate);
    
    monsters.rotation.set(0, monsters.rotation.y + 0.01, 0);
    points.vertices[0].set(0, points.vertices[0].y + 0.1, 0);
    
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
  
  animate();
It turns out that I need to specify the verticesNeedUpdate property in order to make this work:
  function animate() {
    requestAnimationFrame(animate);
    
    monsters.rotation.set(0, monsters.rotation.y + 0.01, 0);
    points.vertices[0].set(0, points.vertices[0].y + 0.1, 0);
    points.verticesNeedUpdate = true;
    
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
Further experimentation reveals that, if I set the sortParticle property on the points, then I do not need to specify verticesNeedUdpate and I get rid of some clipping that occurs occasionally as otherwise transparent parts of the particles pass over each other:


The final implementation then becomes:
  var wrapper = new THREE.ParticleBasicMaterial({
    size: 256,
    map: map,
    transparent: true
  });
  var points = new THREE.Geometry();
  points.vertices.push(new THREE.Vector3(0, 0, 250));
  points.vertices.push(new THREE.Vector3(200, 0, 250));
  points.vertices.push(new THREE.Vector3(-200, 0, 250));
  var monsters = new THREE.ParticleSystem(points, wrapper);
  monsters.sortParticles = true;
  scene.add(monsters);
  
  function animate() {
    requestAnimationFrame(animate);
    
    monsters.rotation.set(0, monsters.rotation.y + 0.01, 0);
    points.vertices[0].set(0, points.vertices[0].y + 0.1, 0);
    
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
  
  animate();
With that, one particle slowly rise as the other two rotate about the center of 3D space:


I think that about does it for Sprites, Particles, and Particle Systems in Three.js. At least until I stumble across one more thing that I cannot quite get to work.

Final version of the code


Day #520

Monday, September 24, 2012

Particles vs Particle Systems in Three.js

‹prev | My Chain | next›

Up tonight, I hope to figure out what the difference is between a particle system and a particle in Three.js. On the surface, I would hazard a guess that a particle system is comprised of lots of individual particles (I'm good at deductive reasoning like that), but what might I be able to do with a bunch of individual particles that I cannot do with a particle system?

To answer that, I create a new, blank project in Mr Doob's Code Editor. In there, I add a particle:
  var wrapper = new THREE.ParticleBasicMaterial({size: 100});
  var particle = new THREE.Particle(wrapper);
  particle.position.set(0,0,400);
  scene.add(particle);
Particle systems require a geometry to specify where the individual particles are. Single particles only require a material—the position can be set directly on the particle. Except...

That does not work. I do not see the particle at all and I cannot figure out why.

I am able to create a particle system with a single particle using the same material:
  var points = new THREE.Geometry();
  points.vertices.push(new THREE.Vector3(0, 0, 400));
  var monsters = new THREE.ParticleSystem(points, wrapper);
  scene.add(monsters);
Eventually, I happen across a bug that was wondering the same thing. It turns out that particles are for the canvas renderer only. If I switch the renderer:
var renderer = new THREE.CanvasRenderer();
Then I can even load my fruit monster image as the particle's texture:
  var wrapper = new THREE.ParticleBasicMaterial({
    size: 10,
    color: 0xFFFFFF,
    map: THREE.ImageUtils.loadTexture('/images/purple_fruit_monster.png'),
    transparent: true
  });
  var particle = new THREE.Particle(wrapper);
  particle.position.set(0,0,40);
  scene.add(particle);
With that, I see my fruit monster "particle":


It seems from the same bug that particles are going away (to be renamed Sprites). That was a bit confusing, but problem solved.

As for the particle system, I have a good deal of difficulty getting the image to show. I keep seeing nothing but grey squares:


In the end, I find that I have to mark the image texture as needing an update:
  var map = THREE.ImageUtils.loadTexture('/images/purple_fruit_monster.png');
  map.needsUpdate=true;
  
  var wrapper = new THREE.ParticleBasicMaterial({
    size: 256,
    map: map,
    transparent: true
  });

  var points = new THREE.Geometry();
  points.vertices.push(new THREE.Vector3(0, 0, 450));
  points.vertices.push(new THREE.Vector3(200, 0, 250));
  points.vertices.push(new THREE.Vector3(-200, 0, 250));
  var monsters = new THREE.ParticleSystem(points, wrapper);
  scene.add(monsters);


Particles proved a little more challenging than expected, but I think I have them sorted out.



Day #519

Sunday, September 23, 2012

Rotating Pixie Dust in Three.js

‹prev | My Chain | next›

I have a reasonably nifty looking Three.js / Physijs tilt-a-board game, now complete with a starry background:


I used a Three.js particle system for the starry background, which was my first experience with the particles. Tonight, I would like to see if I can use particles to make some "pixie dust" around the goal (the center of the tilt-a-platform).

I try adding a dust "system" in a randomly dispersed cylinder:
    var pixies = new THREE.Geometry();
    for (var i=0; i<200; i++) {
      var angle = 2*Math.PI * Math.random();
      pixies.vertices.push(new THREE.Vector3(
        25*Math.cos(angle),
        200*Math.random()-100,
        25*Math.sin(angle)
      ));
    }
    var pixie_shape = new THREE.ParticleBasicMaterial({color: 0x009900});
    var pixie_dust = new THREE.ParticleSystem(pixies, pixie_shape);
    scene.add(pixie_dust);
This results in a not entirely satisfactory look:


I think I might want to switch to plain-old particles rather than a particle system to achieve my desired effect. But for tonight, I will end by rotating the entire pixie dust system with every game step:
  function gameStep() {  
    if (ball.position.y < -100) resetBall();
    
    pixie_dust.rotation.set(0, pixie_dust.rotation.y + 0.01, 0);
    
    setTimeout(gameStep, 1000 / 60); // process the game logic
                                    // at a target 60 FPS.    
  } 
That actually looks somewhat nice-ish. As I said, I will probably mess with regular particles tomorrow. Unless something else distracts me.


Day #518

Saturday, September 22, 2012

Three.js Particle Stars

‹prev | My Chain | next›

Up tonight, I plan to add a star field to the background of my Three.js / Physijs tilt-a-board game.

I think that the Three.js ParticleSystem is the way to go for this.

    // Starfield    
    var stars = new THREE.Geometry();
    for (var i=0; i<1000; i++) {
      stars.vertices.push(new THREE.Vector3(
        1e3 * Math.random() - 5e2,
        1e3 * Math.random() - 5e2,
        -1e2
      ));
    }
    var star_stuff = new THREE.ParticleBasicMaterial();
    var star_system = new THREE.ParticleSystem(stars, star_stuff);
    scene.add(star_system);
A particle field gets a single geometry with as many vertices as I want points. In this case, I add 1000 vertices to create 1000 stars in the background. I randomize the x & y coordinates throughout the field within ±500 of the origin. For the z position, I just put the stars really far away. Lastly, I use a basic particle material. If I do not do so, then the ParticleSystem will choose its own color—and it seems to favor a color that is hard to see against the black of space.

With that, I have my space-age tilt-a-board game:


That turned out to be surprisingly easy to get started with. For some reason particles seemed much harder to grasp until I actually tried it.


Day #317

Friday, September 21, 2012

Flashing Spotlight on Victory

‹prev | My Chain | next›

I remain dissatisfied with the goal marker in my Three.js / Physijs tilt-a-board game. The spotlight almost works:


But it seems odd just starting thin air like that. I could add a light fixture of limited sophistication, but that would still look odd suspended in mid-air.

Tonight, I try a pale, transparent light that shines through the game platform:
    goalLight = new THREE.Mesh(
      new THREE.CylinderGeometry(20, 20, 1000),
      new THREE.MeshPhongMaterial({
        opacity: 0.15,
        transparent:true,
        shininess: 0,
        ambient: 0xffffff,
        emissive: 0xffffff
      })
    );
    scene.add(goalLight);
This results in a light that extends the entire height of the screen:


That's still not ideal—it might be better if the visible "light" had a blur to it. Or maybe became square after passing through the center of the platform. No matter, I will worry about that another day.

For now, I would simply like to change the light to indicate a game win. I already have a goal object. I had been logging to console when the game was won. Instead, I now tell it to flash the goal light in salute of victory:
    score.addEventListener('collision', function() {
      flashGoalLight();
      resetBall();
    });
The flash light function merely switches between two states for the light:
  function flashGoalLight(remaining) {
    if (typeof(remaining) == 'undefined') remaining = 9;
    
    if (goalLight.material.opacity == 0.4) {
      goalLight.material.ambient.setRGB(1,1,1);
      goalLight.material.emissive.setRGB(1,1,1);
      goalLight.material.color.setRGB(1,1,1);
      goalLight.material.opacity = 0.15;
    }
    else {
      goalLight.material.ambient.setRGB(1,0,0);
      goalLight.material.emissive.setRGB(1,0,0);
      goalLight.material.color.setRGB(1,0,0);
      goalLight.material.opacity = 0.4;
    }

    if (remaining > 0) {
      setTimeout(function() {flashGoalLight(remaining-1)}, 500);
    }
  }
The result is the goal "spotlight" flipping between white and red:


I may fiddle with that some more. Then again, with the deadline for Gaming JavaScript fast approaching, I may move onto more pressing matters.


Day #516

Thursday, September 20, 2012

Visible Spotlight

‹prev | My Chain | next›

I would like to make the goal of my Three.js / Physijs game a bit more obvious. My first thought is to shine a spotlight on the center of the game board:
    var goalLight = new THREE.SpotLight( 0x0000ff , 20 );
    goalLight.castShadow = true;  
    goalLight.position.set( 0, 33, 0 );
    scene.add( goalLight );
The result is... less than satisfying:


I can see the spotlight, but it is not obvious. I try a red instead of the blue, but, if anything, it is harder to see:


If I cannot make the spotlight obvious, perhaps I can fake it with a transparent "light cone":
    cone = new THREE.Mesh(
      new THREE.CylinderGeometry(20, 100, 100),
      new THREE.MeshPhongMaterial({
        opacity: 0.1,
        perPixel:true
      })
    );
    scene.add(cone);
But the opacity setting seemingly has no effect, leaving me with a big, opaque cone in my game:


Ah, curse me for a fool. Three.js will ignore the opacity setting unless I enable the transparent setting. I do so and I change the "light cone" to be red:
    var cone = new THREE.Mesh(
      new THREE.CylinderGeometry(3, 25, 100),
      new THREE.MeshPhongMaterial({
        color: 0xff0000,
        ambient: 0xff0000,
        opacity: 0.33,
        transparent:true
      })
    );
    cone.position.y = 50;
    scene.add(cone);
This results in:


I can live with that—at least for tonight. I am not 100% sold on this approach so I may revisit.

(the game so far)

Day #515

Wednesday, September 19, 2012

Shadows and Compound Physijs Objects

‹prev | My Chain | next›

I went to all sorts of trouble to include shadow support in the spotlight on my Three.js / Physijs tilt-board game. And yet, no shadow:


Well, to be perfectly honest, I didn't go to that much trouble. I copied and pasted most of it:
    var spotLight = new THREE.SpotLight( 0xffffff ); 
    spotLight.position.set( -250, 250, 250 );  
    spotLight.castShadow = true;  
    spotLight.shadowMapWidth = 1024; 
    spotLight.shadowMapHeight = 1024;  
    spotLight.shadowCameraNear = 500; 
    spotLight.shadowCameraFar = 4000; 
    spotLight.shadowCameraFov = 30; 
    scene.add( spotLight );
Still, with all of that shadow stuff, I ought to actually have a shadow. However, more is needed for Three.js to cast a shadow. First up, I need to tell Three.js that my game ball casts a shadow and that the game platform receives the shadow (the shadow is cast onto the platform):
    platform.receiveShadow = true;
// ...
    ball.castShadow = true;
I still do not see a shadow.

Ah, silly me, I forgot to tell the renderer that I want it to cast shadows:
    renderer.shadowMapEnabled = true;
But even after that, I still do not have shadows. Under normal circumstances this ought to be enough to cast shadows in Three.js. It turns out that I am suffering from not one, but two self-inflicted wounds.

First, I cargo-coded the spotlight shadow definition without paying it much attention. A closer inspection reveals that I must specify a "near" shadow casting point that is between the spotlight and the ball. Since the spotlight is 300 pixels or so away from the center of the platform, I need to reduce the shadowCameraNear setting:
    var spotLight = new THREE.SpotLight( 0xffffff ); 
    spotLight.position.set( -250, 250, 250 );  
    spotLight.castShadow = true;  
    spotLight.shadowMapWidth = 1024; 
    spotLight.shadowMapHeight = 1024;  
    spotLight.shadowCameraNear = 250; 
    spotLight.shadowCameraFar = 750; 
    spotLight.shadowCameraFov = 30; 
    scene.add( spotLight );
I also up the shadowCameraFar property for good measure—there is no reason to cast shadows beyond 750 pixels away from the spotlight since the platform is only 200 pixels wide.

The second problem is that setting the receiveShadow property on the compound Physijs platform object has no effect. I have to set that property on each of the beams that comprise the platform:
    var beam = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 200),
      material,
      0
    );
    beam.receiveShadow = true;
    
    var beam2 = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 200),
      material
    );
    beam2.position.x = 75;
    beam2.receiveShadow = true;
    beam.add(beam2);
    // ....
Once I have those two issues addressed, I have my shadow:


All of this is more than a tiny hassle, though I can certainly understand the necessity of it all. Shadow casting is described in the Three.js documentation as an expensive operation, so it makes sense that the renderer needs to be explicitly told to do it. Similarly, it makes sense to keep the calculations to a minimum, so any way that can constrain the calculations would be a good thing. That said, it would be nice if it were possible to do this without all of the ceremony.

Regardless, the effect is nice, making the trouble worthwhile. That will do for a stopping point tonight. Tomorrow, I will add some visual feedback that a winning score was made.

(The game so far)


Day #514

Tuesday, September 18, 2012

Pretty, Shiny Materials for Three.js Games

‹prev | My Chain | next›

I have not done a fantastic job of keeping the code in my tilt-a-board game small. I cleaned it up and generally tried to make it shorter yesterday and it went from 140 to 150 lines of code. I still might eke out some lines of code savings another time. For today, I am going to see what I can do about making this Three.js / Physijs game a little slicker.

I think this would likely end up being a separate chapter in Gaming JavaScript—cool things to make a working game more visually appealing. Since it's for kids, I'll probably call it "make your games look awesome" or something like that. Let's see if I can actually do that.

I think I'll try to avoid things like bump maps and the like. It is certainly possible to create visually stunning scenes with them, but I am unsure if the amount of effort is worth the payoff. That's something to consider another day. For now, I will try to get enough bang from the map-less buck.

I start by simply making the background color black:
    renderer = initRenderer(0x000000);
// ...

  function initRenderer(bgColor) {
   var renderer = renderingStrategy();

   renderer.setSize(window.innerWidth, window.innerHeight);
   renderer.setClearColorHex(bgColor);
   document.body.appendChild(renderer.domElement);

   return renderer;
  }

  function renderingStrategy() {
    if (Detector.webgl) return new THREE.WebGLRenderer({antialias: true});
   if (Detector.canvas) return new THREE.CanvasRenderer();
   return undefined;
  }
I will add some stars later, but first want to improve the material used for both the ball an the game board. Both are as basic as Three.js allows:


I start by adding some lighting to the scene. Without lighting, Three.js just assumes an ambient white light everywhere. I tone down the ambient light with a grey, then add a point light behind the board and a spot light to the side:
    // Lighting
    scene.add(new THREE.AmbientLight(0x999999));

    var back_light = new THREE.PointLight(0xffffff, 0.9);
    back_light.position.set(100, 100, -200);
    scene.add(back_light);
 
    var spotLight = new THREE.SpotLight( 0xffffff ); 
    spotLight.position.set( -250, 250, 250 );  
    spotLight.castShadow = true;  
    spotLight.shadowMapWidth = 1024; 
    spotLight.shadowMapHeight = 1024;  
    spotLight.shadowCameraNear = 500; 
    spotLight.shadowCameraFar = 4000; 
    spotLight.shadowCameraFov = 30; 
    scene.add( spotLight );
The lighting itself is not as important as the various color options in the materials. For instance, I set the ball as follows:
    ball = new Physijs.SphereMesh(
      new THREE.SphereGeometry(10),
      new THREE.MeshPhongMaterial({
        color: 0x000000,
        shininess: 100.0,
        ambient: 0xff0000,
        emissive: 0x111111,
        specular: 0xbbbbbb
      })
    );
    resetBall();
    scene.add(ball);
The color in a Phong material is not the intrinsic color as we normally think of it—that is what the ambient color does. The color in the Three.js Phong material describes how diffuse the lighting will be (0xffffff would be low contrast). I want things to be as shiny as possible, so I disable the diffuse color entirely. Then I crank up the shininess factor. I want my game ball's intrinsic color to be red, so I set the ambient color to red. I do not want the ball to emit light. Lastly, I want the reflections from the shine to be really bright, so I crank up the specular color.


After doing similar for the game board, the result is a shiny looking ball and game board:


I can live with that. I still need to add some stars to simulate space and maybe some fog and a spotlight from below. But I will play with those tomorrow.

[the code so far]


Day #513

Monday, September 17, 2012

Three.js Code Clean-Up

‹prev | My Chain | next›

I have my little Three.js / Physijs tilt-a-game at 140 lines of code (code editor). This seems good for a Gaming JavaScript entry. Still I might like to get it a little smaller, a little cleaner, and possibly add a bit more 3D "feel" to it.

I start, by trying to eliminate a fairly annoying bug. At some point in the last few days, I have introduced something that spews a lot of Three.js errors output on the JavaScript console. The error output starts with:
ERROR: 0:26: 'undefined' : syntax error 
 Three.js:325
precision highp float;
#define MAX_DIR_LIGHTS 0
#define MAX_POINT_LIGHTS 0
#define MAX_SPOT_LIGHTS 0
#define MAX_SHADOWS 0

#define SHADOWMAP_SOFT

uniform mat4 viewMatrix;
uniform vec3 cameraPosition;
undefined Three.js:325
WebGL: INVALID_VALUE: attachShader: no object or object deleted /code-editor/ (1):1
ERROR: 0:62: 'undefined' : syntax error
Even more console errors and warnings are logged until Chrome has finally had enough:
...
WebGL: INVALID_OPERATION: useProgram: program not valid /code-editor/ (1):1
.WebGLRenderingContext: GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 0 /code-editor/ (1):1
WebGL: too many errors, no more errors will be reported to the console for this context. /code-editor/ (1):1
I suspect that I have added an invalid mesh or two to my scene. By process of elimination, I track all of that output down to last night's scoring mesh:
    // Scoring 
    var score = new Physijs.ConvexMesh(
      new THREE.PlaneGeometry(20, 20),
      new THREE.Material
    );
    score.position.x = 50;
    score.position.y = -75;
    scene.add(score);
    
    score.addEventListener('collision', function() {
      console.log('collision!');
      scene.remove(score);
    });
Commenting those lines out (indeed, just commenting out scene.add(score)) eliminates the errors. This turns out to be caused by my use of the superclass, Three.Material. I had used that as a cheap way to get a transparent mesh. It seems the more proper way of doing so is with the opacity setting of MeshBasicMaterial:
    // Scoring 
    var score = new Physijs.ConvexMesh(
      new THREE.PlaneGeometry(20, 20),
      new THREE.MeshBasicMaterial({opacity: 0.0})
    );
    score.position.x = 50;
    score.position.y = -75;
    scene.add(score);
With those errors eliminated and a bunch of code cleaned up, I notice something else odd: I can no longer get the ball to roll when I tilt the platform. Even if I tilt the platform 90°:


This one, I cannot figure out. It seems that this was accidentally working previous because I had given the platform an initial tilt. I restore it:
    scene.add(ground);
    ground.__dirtyRotation = true;
    ground.rotation.x = -0.01;
Restoring this allows the tilt controls to again have an effect on the ball:


I suspect that I am doing something wrong here. It may be the use of __dirtyRotation—both here and in the subsequent tilt() function used by the game controls:
  function tilt(dir, amount) {
    ground.__dirtyRotation = true;
    ground.rotation[dir] = ground.rotation[dir] + amount;
  }
And yes, I feel dirty for using this, but it does the trick. Mostly.

I finish up by attempting to clean up the platform generation. First off, I rename it as platform instead of ground. I am unable to clean up the beam definitions much. There is no copy()/clone() in Three.js yet, so I am stuck with defining each beam manually. I can move that into its own function, if nothing else:
  function makePlatform() {
    var beam = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 200),
      new THREE.MeshBasicMaterial({color: 0x7CFC00}),
      0
    );
    
    var beam2 = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 200),
      new THREE.MeshBasicMaterial({color: 0x7CFC00})
    );
    beam2.position.x = 75;
    beam.add(beam2);
    //...
  }
I call it a night here and return to writing Gaming JavaScript. Tomorrow, I will get started on some of the more interesting Three.js scene properties that I have not used much yet (fog, illumination, particles). That should punch up the game a bit.


Day #512

Sunday, September 16, 2012

Physijs Collisions without the Collision

‹prev | My Chain | next›

I rather like my little tilt-a-plane game. It is only 100 lines of code, but demonstrates some nice 3D concepts without too much Three.js / Physijs craziness. The hole in the middle was a little harder than desirable, but still, this might a good sample for Gaming JavaScript.

I need to figure out how to score a win. That is, when the ball falls through the hole, I need to mark the game as won. So, I need something that will register a collision. Any old Physijs shape should do, but I start with a plane:
    // Scoring 
    var score = new Physijs.ConvexMesh(
      new THREE.PlaneGeometry(200, 200),
      new THREE.Material
    );
    score.position.y = -15;
    scene.add(score);
The lack of a third dimension of a plane seems perfect for an object that is intended only to register another object passing it.

But, as I suspected, this is a solid object that prevents my ball from passing through:


I could almost live with this behavior: stopping the game mid-fall is reasonable—perhaps even desirable given the simplicity. For my own edification, I would like to know how to register a collision event without affecting physics. That is, I would like to register the ball falling through the hole, but not interrupt the downward trajectory.

My first try is a collision event listener that immediately removes the "score" plane from the scene:
    score.addEventListener('collision', function() {
      console.log('collision!');
      scene.remove(score);
    });
And, amazingly, that works. When the ball falls through, it keeps falling without any interruption:


Just to make sure that I am not missing something, I comment out the scoring plane's removal. I still see the collision event but the ball comes to rest on the scoring plane:


I really like that solution. It makes sense and it is very short—perfect for Gaming JavaScript. I add some tilting controls and call it a night.

View game in the code editor.


Day #511

Saturday, September 15, 2012

Grouping Three.js Objects to Make a Hole

‹prev | My Chain | next›

I found yesterday that my initial attempt at making holes in Three.js / Physijs shapes was ill-founded. As explained in comments by Chandler Prall, using ThreeCSG to subtract "holes" will not work—at least not yet.

So tonight, I try an alternative approach. Specifically, I will leave a hole in the center and build stuff around it. I start with small plane/thin cubes:
    var g1 = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 50),
      new THREE.MeshBasicMaterial({color: 0x7CFC00}),
      0
    );
    g1.position.x = 40;
    scene.add(g1);
After positioning four of these, I have a square hole:


Recalling that grouping Physijs objects involves adding them to the grouping object before the the grouping object is added to the scene, I redo the ground object:
    ground = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 200),
      new THREE.MeshBasicMaterial({color: 0x7CFC00}),
      0
    );
    
    var g2 = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 200),
      new THREE.MeshBasicMaterial({color: 0x7CFC00}),
      0
    );
    g2.position.x = 75;
    ground.add(g2);

    //...
    scene.add(ground);
After mucking with the sizes some, I have the following object to support a ball:


Which means that I can tilt the entire playing surface to roll the ball in the hole:


That is a quick and dirty implementation of a hole, but it just might do for a Gaming JavaScript game.




Day #510

Friday, September 14, 2012

Can't Make Physijs Holes

‹prev | My Chain | next›

Tonight, I would like to explore the ability to add holes in Three.js / Physijs shapes. Specifically, I would like to see if it is possible / easy to create a hole in a plane.

I start with a normal Physijs plane and place a ball on the plane:

    // Ground...
    var ground = new Physijs.PlaneMesh(
      new THREE.PlaneGeometry(2e2, 2e2),
      new THREE.MeshBasicMaterial({color: 0x7CFC00})
    );
    scene.add(ground);
    
    // Ball ...
    ball = new Physijs.SphereMesh(
      new THREE.SphereGeometry(10),
   new THREE.MeshNormalMaterial
    );
    ball.position.y = 10;
    ball.position.x = 50;
    scene.add(ball);
In the code editor, this looks like:


As for making holes, there is the ThreeCSG add-on for Three.js. I use that to BSP meshes for the ground and a hole:
    var ground_mesh = new THREE.Mesh(
      new THREE.PlaneGeometry(2e2, 2e2)
    );
    var ground_bsp = new ThreeBSP( ground_mesh );
    
    var hole_geometry = new THREE.SphereGeometry( 20 );
    var hole_mesh = new THREE.Mesh( hole_geometry );
    var hole_bsp = new ThreeBSP( hole_mesh );

    var subtract_bsp = ground_bsp.subtract( hole_bsp );
Then I use the subtraction of the sphere from the plane to create a new Physijs object:
    var ground = new Physijs.ConvexMesh(
      // new THREE.PlaneGeometry(2e2, 2e2),
      subtract_bsp.toGeometry(),
      new THREE.MeshBasicMaterial({color: 0x7CFC00}),
      0
    );

    scene.add(ground);
Only this does not quite make a hole:


At best that is a minor bump. And when viewed from above, I cannot even see that much:


Even if I convert this into a regular Three.js object instead of Physijs:
var ground = subtract_bsp.toMesh( new THREE.MeshNormalMaterial )
I still get a divet instead of a hole:


I will try other shapes tomorrow—perhaps a 1 pixel thin cube might work—but so far this does not seem promising. If anyone has any ideas, the code far is here.


Day #509