Previous: 2.a Adjusting the particle creation rate
The second part of the script which could be improved is that which chooses the initial velocity of the particles. When you look at real-life fountains (whether pyrotechnic or water fountains, for instance) you observe that the spreading of sparks or water is roughly circular around the point where it gushes. In the present case, if we were to mark on the ground the points where the particles fall, they would not draw a nice circle around the model, but a nice square instead! This is because the components along the x and y axes of the initial velocity vector we use to launch the particles, follow equations (2.1) and (2.2) which, as we will see in lesson 5, define a square. The resulting trajectories therefore follow the same pattern. Though this is not too striking or bothersome in the present case where we look at the fountain from the side, in other instances it could be visually rather 'amateurish'.
The solution is to draw the sparks' initial velocities in a vertical cone directed toward the sky. As we will see in lesson 7, the easiest way to do this is by using spherical coordinates. For now, we will just write down the equations without going into all the details and see what they produce in level lesson2.wed (yes, I hate it too when textbook do this! But bare with me for now...). Here is the new code:
var theta; var phi; var speed; ... speed = 10 + 40*key_shift; ... while(my.skill1<=n) { // compute velocity theta = random(45); phi = random(360); velocity.x = speed*sin(theta)*cos(phi); velocity.y = speed*sin(theta)*sin(phi); velocity.z = speed*cos(theta); // create particles ... }
We first select a value for the variable speed which represents the magnitude of the initial velocity vector of the sparks (see equation (1.7)): 10 or 50 depending if the 'shift' key is pressed down or not. Then we choose angles theta and phi which specify the orientation in space of vector velocity. theta and phi are the analogs of the elevation and azimuth angles, respectively, used to define the position of the sun in WED: theta gauges how vertical velocity is, while phi defines its orientation in the (xy) plane. The values chosen here assure that we pick velocity in a vertical cone of opening 2*45 = 90 degrees directed straight upwards (see lesson 7 for more details). This way of defining velocity illustrates how sometimes it is more practical to specify separately the length and orientation of a vector (as mentioned in lesson 1).
With these changes, the code is now slightly more compact, easier to read, and our fountain looks alright even when looked from above.
Well ...... not exactly alright actually... If we look closely both at the minimal and our newly improved particle sources, and toggle the resolution (and the frame rate), we notice a strange thing: the particles do not seem to come out of the tip of the fountain but somewhere above it! And the effect gets more noticeable when the frame rate drops or if we increase the initial velocity of the sparks. Another mystery to solve...
I could not find any explanation in the 3DGS documentation but my guess is that, when we call the effect() function with a non-zero velocity, the engine creates the particle not at the position we specify in effect() but instead at that position shifted by time*velocity. That's weird. Why is that?
As described in the Tuesday lesson of the C-Script tutorial, the vector velocity of an entity 'connects' the positions of that entity at some instant t and at the subsequent instant t+time as follows:
my.x(t+time) = my.x(t) + time*velocity. (2.6)
We all know that if we travel for a half-hour at the speed of 100 km/hour, then we will end up 0.5*100 = 50 km from where we were before. Equation (2.6) is just the generalization of this fact using vectors: if the particle is at my.x(t) at some time t, then it will be at a new location a distance time*|velocity| away in the direction of the vector velocity at instant t+time (since we traveled during time ticks).
Therefore, going back to the problem at hand, it seems that 3DGS, instead of creating the particles at the position my.x of the fountain's tip and moving them at the next frame, rather creates them directly at this next position: it already sets them 'in motion' before being displayed for the first time. This is why the gap between the tip of the fountain and the cloud of sparks increases when the frame rate drops ( time increases) or when the initial velocity increases. Both augment the size of the vector time*velocity and therefore of the discrepancy.
This is not a big problem however now that we understand what is going on: we just have to subtract that same amount time*velocity from the initial position where we create our sparks so that they appear at the fountain's tip:
var position; ... while(my.skill1<=n) { // compute velocity ... // move the particles a "step" behind position.x = my.x - time*velocity.x; position.y = my.y - time*velocity.y; position.z = my.z - time*velocity.z; // create particles effect(improved_part,1,position.x,velocity.x); my.skill1 += 1; }
To do this, we introduced a new vector position which we set as follows:
position.x = my.x - time*velocity (2.7)
and use it as position to create the sparks. The sparks now appear at the fountain's tip. :-) This completes the modifications we will do on the action of the fountain.