3D ATST rendered in WebGL

On a quiet Sunday afternoon I actually managed to render one of my old 3D models in WebGL. And I didn’t even have to deal with WebGL at all, thanks to Three.js.

The model I wanted to is an ATST, originally created for my Domino Star Wars project.

I had the model exported as a 3DS file, which I then imported into Blender, scaled down a bit, then exported to Three.js’ ASCII JSON format. Figuring out how to use the Blender plugin took a bit of digging (documented into a new readme). From there I took one of the existing examples and replaced the loaded model with my own. And tada, a rotating 3D ATST, in your browser, if it supports WebGL


3D ATST demo

If that doesn’t render for you, here’s a screenshot:

This isn’t all too interesting yet, but an important first step.

For more WebGL resources, see my WebGL Links post.

WebGL Links

Here are some resources on WebGL, the OpenGL implementation that now runs in current Firefox and Chrome browsers.

  • There’s a WebGL specification, and as usual, its intended for implementors of the spec, not for users of the API. Though at least this spec as actual usage examples, so if you’re looking for a specific detail, this could be a good last resort.
  • An okay Getting Started tutorial let’s you render, BEHOLD, a square. It makes use of vertex and fragment shaders, making the whole thing fairly complicated, but hey, its hardware accelerated and you’ll learn a lot of the low level basics.
  • A lot more resources can be found on Planet WebGL, there I just found this cool X-Wing demo.
  • Another site decicated to learning WebGL, The WebGL Cookbook.
And with that, a jump to WebGL libraries or “3d engines”, on various high or low levels:
  • GLGE claims to be “WebGL for the lazy”
  • CopperLicht is just a “JavaScript 3D Engine”
  • three.js isn’t  focussed soley on WebGL, as it can also output to canvas and svg; it has a focus on being “for dummies”, so might be the best starting point
  • lightgl.js is yet another approach, being more low-level (without a scene graph! whatever that means).
Related to the above three.js is probably, in some way, ThreeNodes.js, which is kind of an IDE for WebGL.
One of these days I hope to convert my old 3D models to some format that I can load into one of these engines and bring them to your browser…

Don Hertzfeld – Rejected and Billy’s Balloon

These are two of my all-time favorite cartoons that I found on the intertubes long before YouTube existed, and that I wish all my friends would know so that they’d get the references and in-jokes. So here they are, the awesome work for Don Hertzfeld, starting with “Rejected”:

And, while a very different beast, also very worth watching on its own, Billy’s Balloon:

:D *winkwink* :D *wink back*

Vector math basics to animate a bouncing ball in JavaScript

Vector math is pretty much essential when you want to do any kind of physics simulation, be it as simple as a bouncing ball. While my goal originally was to implement a flocking simulation (like birds flying close to each other, but not too close), the lack of math skills led me to build a bouncing ball simulation first.

At the same time, I wanted to see what Khan Academy is all about. Turns out they have lessons on vector math, but there’s very little on vectors specifically. There are two lessons on vector basics, where this second one is a lot more practical. There are also two exercises, one for addition of vectors, one for scaling. Both are pretty easy and you should be done within a minute if you watched the video.

With that knowledge under your belt, let’s look at some practical application, the aforementioned bouncing ball. To start, take a look at the demo and play around with it, there are some instructions at the top.

You can find the source code for that demo on GitHub, here is the main file for the bouncing balls demo. I’m not going to discuss the Point and Vector classes that this uses, though you should take a look. They just implement adding and scaling of vectors, and calculating a vector based on two points. Let’s walk through the code:

var GRAVITY = new Vector(0, 9.81);
var FRICTION = 0.85;
var world = {
	x1: 0,
	y1: 0
};
$(window).resize(function() {
	world.x2 = $(window).width();
	world.y2 = $(window).height();
}).trigger("resize");

This defines two constants, GRAVITY and FRICTION, which we’ll use later to affect simulated objects. GRAVITY is a vector pointing downwards, where the second component represents the 9.81 meters per second, while the first component is zero. FRICTION is used in collisions later, and completely arbitrary.

The world object is also used in collision detection, and represents the dimensions of our 2d world. It starts at 0/0 in the left top corner, and ends in the right bottom corner. We bind a resize handler on window to update this calculation, that way collisions happen within the browser window, no matter how big it currently is.

Next up is the definition of our Ball class:

function Ball() {
	this.position = new Point(200, 200);
	this.output = $("<div>").addClass("dot").appendTo("body");
	this.velocity = new Vector(-5, 0);
}
Ball.prototype = {
	remove: function() {
		this.output.remove();
	},
	move: function() {
		// apply gravity
		this.velocity = this.velocity.add(GRAVITY.scale(0.1));

		// collision detection against world
		if (this.position.y > world.y2) {
			this.velocity.x2 = -this.velocity.x2 * FRICTION;
			this.position.y = world.y2;
		} else if (this.position.y < world.y1) {
			this.velocity.x2 = -this.velocity.x2 * FRICTION;
			this.position.y = world.y1;
		}
		if (this.position.x < world.x1) {
			this.velocity.x1 = -this.velocity.x1 * FRICTION;
			this.position.x = world.x1;
		} else {
			if (this.position.x > world.x2) {
				this.velocity.x1 = -this.velocity.x1 * FRICTION;
				this.position.x = world.x2;
			}
		}

		// update position
		this.position.x += this.velocity.x1;
		this.position.y += this.velocity.x2;

		// render
		this.output.css({
			left: this.position.x,
			top: this.position.y
		});
	}
};

This defines a Ball constructor, which initializes a new Ball at some arbitrary position, with some velocity to the left. It also creates a simple DOM element that we use for output.

The prototype of Ball has two methods. The remove method just removes the DOM element, which we use for cleanup. The move method is much more intersting: It gets called for each ‘tick’ of our animation loop, so we use it to update the current velocity, look for collisions, update the current position and render the result. Step by step:

this.velocity = this.velocity.add(GRAVITY.scale(0.1));

This adds GRAVITY to the balls velocity. While GRAVITY has a real world value, we need to adapt it to our pixel-based dimension. Doing this in every tick causes the ball to accelerate downwards, or when moving upwards, to deccelarate. With this alone our ball would start falling, but never stop. That’s where the next block comes in, the collision detection:

if (this.position.y > world.y2) {
	this.velocity.x2 = -this.velocity.x2 * FRICTION;
	this.position.y = world.y2;
} else if (this.position.y < world.y1) {
	this.velocity.x2 = -this.velocity.x2 * FRICTION;
	this.position.y = world.y1;
}
if (this.position.x < world.x1) {
	this.velocity.x1 = -this.velocity.x1 * FRICTION;
	this.position.x = world.x1;
} else {
	if (this.position.x > world.x2) {
		this.velocity.x1 = -this.velocity.x1 * FRICTION;
		this.position.x = world.x2;
	}
}

Here we compare the current position of our ball to the dimensions of the world. For each direction, there’s a check if the call is beyond the limit, if so, it inverts the velocity for that direction, while applying FRICTION. This causes the ball to bounce back slightly slower then it was before, simulating very primitive friction. To avoid glitches, where the ball goes beyond the world dimensions and doesn’t come back, the position gets updated to move it back inside the defined limits.

Now that we’ve updated the velocity (and fixed the position in case of a collision), we can update the resulting position and output it:

// update position
this.position.x += this.velocity.x1;
this.position.y += this.velocity.x2;

// render
this.output.css({
	left: this.position.x,
	top: this.position.y
});

This adds the velocity components to the position of the ball, then uses inline styles to update the position in the DOM.

Next we’ll look at the setup and animation loop:

var balls = [];
balls.push(new Ball());

// animation loop
setInterval(function() {
	balls.forEach(function(ball) {
		ball.move();
	});
}, 25);

Here we create an array of balls and add one initial Ball. Then start an interval to at 25ms, which should give us about 40 frames per second (fps). To get more smooth 60fps, we’d have to go down to 16.5ms, which would also be even more CPU intensive then this becomes with lots of balls.

Inside the interval, we just loop through all balls and call the move method for each. In a proper game engine, this loop would separate the position updates from the rendering to ensure that, when frames get dropped, the game itself doesn’t slow down.

Up next, we’ve got the code to add new balls, with user controlled initial velocity:

var start;
$(document).mousedown(function(event) {
	start = new Point(event.pageX, event.pageY);
}).mouseup(function(event) {
	var end = new Point(event.pageX, event.pageY);
	var ball = new Ball();
	ball.position = end;
	ball.velocity = start.relative(end).scale(0.2);
	ball.move();
	balls.push(ball);
});

Here we bind mousedown and mouseup events, each time creating a Point object from the pageX and pageY event properties. In the mouseup handler, we then use the end point as the starting position for the new Ball object. Using Point’s relative method, we calculate a vector between those two points, scale it down and use it as the velocity for the new ball. That way, you can just click anywhere to add a new ball, or click, drag and let go to create one with intial velocity based on the drag. To get the ball animated along with the others, its added to the balls array.

With that we’re almost at the end. The last piece just clears all balls when pressing Escape:

$(document).keyup(function(event) {
	if (event.keyCode === 27) {
		balls.forEach(function(ball) {
			ball.remove();
		});
		balls.splice(0, balls.length);
	}
});

And that’s it! Thanks to Vector, we’ve got a pretty sane implementation, and a good starting point for further improvements. And there’s lots of potential:

  • Better friction simulation: Currently balls keep bouncing pretty often, they don’t slow down as much as they should after loosing some height.
  • More collision detection: Detecting collisions with other balls, the mouse or other objects would make the whole thing a lot more interesting.
  • Better collision detection: Currently collision detection just happens against a fixed position, not the actual ball’s dimensions. Taking the (rounded) borders into account would make things quite a bit more complicated, but also more realistic.
  • More moving objects with other shapes: Currently there’s just pixels bouncing around, even though they’re rendering as balls. Adding square objects both animated and static could make things a lot more interesting.
  • 3D: Moving from a 2D to a 3D simulation involves adding another component to both the Vecotor and Point class, and would add that third dimensions to each calculation, making especially the collision detection, already the most complex part, even more complex.

With this, I’ll get back to working on the basics for my flocking simulation.