Table of content

Previous: 7.b Making stationary spheres

c. Building moving spheres

Ok, so we see now how to construct spheres with particles. However, these spheres do not move at all so far. How can we set them in motion? We want each sphere to rotate on itself, and also the red sphere to move around the blue one.

This is where things get a little trickier. Let us take for example the blue sphere. By running level lesson7.wed, we see that to give the impression that the blue sphere is rotating around the vertical direction (there is no sphere, right?), we need to move each little blue particle along a horizontal circle around the imaginary axis of rotation of the sphere. Further, the height and radius of that circle depends on where that particle is on the sphere: if it is close to the north pole, the circle is small and high. If the particle is near the equator, the circle will be lower and much larger. In fact, just as each particle has a unique position on the sphere specified by angles theta and phi, it also has a unique trajectory when the sphere rotates.

Hmmmmm....... What should we do then. Here is the code for the blue sphere. I'll explain it in a second.

First, we slightly modify action a_spheres to create the "new" blue particles:

var theta;
var phi;
...
action a_spheres
{
	...
	// build the blue sphere 
	theta = 0;
	phi = 0;
	while(theta<=180)
	{
		while(phi<=360)
		{
			phi += 10;
			// create blue particle with parameters (theta,phi)
			effect(blue_square,1,nullvector,nullvector);
			wait(1);
		}
		theta += 10;
		phi = 0;			
	}
	...
}

and here is the new function of the blue particles, which are now rotating around the z axis:

function blue_square()
{
	if (my.lifespan==80)
	{ 
		...
		// remember theta and phi
		my.skill_a = theta;
		my.skill_b = phi;
		...
	}
	
	my.lifespan = 70;				// particle never dies
	...
		// only turns around center at 5 degrees/tick
		// with radius radius_spheres
		my.x = radius_spheres*
		                cos(my.skill_b+5*total_ticks)*
		                sin(my.skill_a);
		my.y = radius_spheres*
		                sin(my.skill_b+5*total_ticks)*
		                sin(my.skill_a);
		my.z = radius_spheres*
		                cos(my.skill_a);
	...
}

Ok, so here is how it works, step-by-step. Let us take the very first iteration of the loop: theta = 0 and phi = 0 and we execute the instruction from the code above,

effect(blue_square,1,nullvector,nullvector);

which creates a blue particle. However, note that contrary to the case of stationary spheres (see 7.b above), we do not specify the position of the particle in the effect() statement. So why did we compute theta and phi and how does the script know where to put that particle? We have to look for the answer in function blue_square(). Right at the beginning of the code we see

// remember theta and phi
my.skill_a = theta;
my.skill_b = phi;

We are therefore storing, right at particle creation and in the function of the particle itself, the values of the angles theta and phi when that particular particle was created, and which are specific to it: here theta = 0 and phi = 0, so skill_a = 0 and skill_b = 0. To make this possible, we defined theta and phi as global variables above (outside of action a_spheres - see manual for a definition of global variables). This way, their value can be set in action a_spheres and then accessed by function blue_square(). We have however to store their values right away in skill variables because 1) theta and phi will change values when the next particle is created, and 2) their values define uniquely the position, and most importantly, the trajectory of the present particle.

This is the same trick we used in the level of lesson 5 for the polar coordinates. We drew a new initial velocity vel_rocket for each missile which was fire by the death star. The first thing we did in the action a_rocket of the missile, was to store the components of this vector in skill variables so that the missile kept going in the same direction even when a new value of the vel_rocket vector is generated.

In the present case, the following lines of code from function blue_square(),

		// only turns around center at 5 degrees/tick
		// with radius radius_spheres
		my.x = radius_spheres*
		                cos(my.skill_b+5*total_ticks)*
		                sin(my.skill_a);
		my.y = radius_spheres*
		                sin(my.skill_b+5*total_ticks)*
		                sin(my.skill_a);
		my.z = radius_spheres*
		                cos(my.skill_a);

define the position of the particle at any time. Indeed, by comparing them with equations (7.1), (7.2) and (7.3), we see that they put the particle on a sphere of radius radius_spheres, at 'latitude' and 'longitude' theta and phi (held in variables skill_a and skill_b), and make it turn around the z axis with an angular velocity of 5 degrees/tick (thanks to the factor 5*total_ticks - see discussion in lessons 5 and 6). In the present case, with theta = phi = 0, the particle does not have to move since it sits on the north pole (which is stationnary for the blue sphere since it rotates around the z axis). :-) But you get the idea, I hope.

Now, by repeating the same process for all the other values of theta between 0 and 180, and phi between 0 and 360, we create a whole cloud of particles moving in space as if they were sitting on a rotating sphere. So, to sum up, action a_spheres only creates the particles representing the spheres and produces values of theta and phi for each of them. These are then passed on and memorized in the function of the corresponding particle, where they are used to create there the equation of motion of that blue square, properly placing it and moving it in space so that it appears to be part of a rotating sphere.

We do the same thing for the red particles forming the red sphere, except that we choose a smaller radius, radius_spheres/2, for it. Also, we put the red sphere in motion around the blue one using the same trick as in lesson 5:

		my.x = red_sphere_center.x +
		         (radius_spheres/2)*
		         cos(my.skill_b-5*total_ticks)*
		         sin(my.skill_a);
		my.y = red_sphere_center.y +
		         (radius_spheres/2)*
		         sin(my.skill_b-5*total_ticks)*
		         sin(my.skill_a);
		my.z = red_sphere_center.z +
		         (radius_spheres/2)*
		         cos(my.skill_a);

As we did in equations (5.8) and (5.9), we add to the position of each particle a vector red_sphere_center. This vector has length orbit_radius = 250, it rotates in space with angular velocity 3 degrees/tick and it is updated in action a_spheres:

		// move the center of the red sphere around blue sphere
		red_sphere_center.x = my.x + orbit_radius*cos(3*timer+90);
		red_sphere_center.y = my.y + orbit_radius*sin(3*timer+90);
		red_sphere_center.z = my.z;
		timer += time;

This operation therefore translates, simultaneously and at each frame, all the particles making up the red sphere so that the latter appears to orbit around the blue sphere.

"Big deal!" you might think. "You did all this mathematical mumbo jumbo just to get a couple of clouds of light dots which look like rotating spheres??? I can get that same effect in no time with a model, a skin with dots on it and an overlay flag!!!".

Yes, so far, you're completely right. However, what is nice about mathematical equations is that you can change parameters at will. You can even do this dynamically and get some really nice effects this way. To go beyond what simple models can do, I added a few lines of codes to functions blue_square() and red_square() to change the pattern in which the two spheres appear on screen.

For the blue sphere, instead of creating the particles on the object's surface right away, I create them instead with a large random radius value. I then let this radius decrease quickly until it reaches the value radius_spheres. This is done using the variable my.skill_c for the coordinate r (see script for details). The result is that now blue particles circle the sphere like bees before settling onto it surface.

For the red sphere, I did the same thing with the initial height of the red particles rather than their radius. Using my.skill_d to raise the z coordinate of the red particles, they now appear near the ceiling and then drop and drape themselves neatly into place around the sphere.

The overall final effect is a cloud of blue particles which converges and covers the blue sphere from top to bottom, and slices of red spheres dropping from the ceiling and converging to form a unified object. Just try THIS with models and skins. ;-) By playing around with the lesson7.wdl script, you can make all sorts of cool effects very easily. I only presented here the two simplest.

Whew! Hard lesson... Just one more and we are done here...

Next: Lesson 8. Putting it all together: the BIG one