The algorithms behind the stardust particles in the front view
The small particles of dust out there in space - which I've called "stardust" in this commentary, though I'm not sure what the official term is - is an essential way of making us feel like we are flying through space in our Cobra. Pulling back on the joystick and watching the stardust fly down and backwards is still a seriously immersive feeling. You really feel like those particles are slamming into your windshield as you shoot through space and into the ether.
The STARS1 routine looks after stardust in the front view. It is responsible for moving the stardust towards us according to our speed (so the dust rushes past us), and applying our current pitch and roll to each particle of dust, so the stardust moves correctly when we steer our ship.
The STARS6 routine processes stardust in the rear view, and is essentially the reverse of STARS1. Let's take a look at how STARS1 works.
The process in STARS1 breaks down into three stages:
- Moving the stardust towards us
- Applying roll to the stardust
- Applying pitch to the stardust
Let's look at these three stages in more detail. Throughout the calculations below, we disregard the low bytes from the angle calculations, just like in part 5 of MVEIT.
Note that each particle of stardust has its own (x, y, z) coordinate. Stardust coordinates are not true 3D space coordinates - when stardust is drawn, the z value is purely used to determine the size of the stardust dot that is displayed, and that's it. The dot is drawn at screen coordinate (x, y), so when thinking about stardust, we're really thinking about a 2D plane, with the z-coordinate only being used when moving the stardust towards us, and when determining how it should look.
Each of the coordinates is stored as 16-bit sign-magnitude value, as in (x_hi x_lo) and (y_hi y_lo). The coordinates for the Y-th particle of stardust are stored in (SX+Y SXL+Y), (SY+Y SYL+Y) and (SZ+Y SZL+Y) respectively. The origin for stardust particles is the centre of the screen, at the crosshairs.
Moving the stardust towards us
The following calculations move the stardust away from the centre of the screen by a distance proportionate to our speed, so dust that is further away from us (i.e. with a high value of z) moves by a smaller amount to create a sense of perspective.
These are the calculations:
1. q = 64 * speed / z_hi 2. z = z - speed * 64 3. y = y + |y_hi| * q 4. x = x + |x_hi| * q
We move the particle towards us by reducing the z-coordinate by the current speed - that's the easy part. We then calculate a factor q that determines how far we should move the stardust particle away from the centre point, and apply that factor by multiplying the x and y screen coordinates by (1 + q), which has the effect of making particles near the centre (small x and y) move less than particles near the edges (high x and y).
Essentially, this is using the fact that when we project 3D (x, y, z) coordinates onto a 2D screen, the calculation is essentially:
x_screen = x / z y_screen = y / z
so if we reduce z (i.e. move the particle near to us) then the x and y screen coordinates should increase by an inverse but proportional amount.
Applying roll to the stardust
The following calculations apply the current roll angle alpha to the stardust:
5. y = y + alpha * x / 256 6. x = x - alpha * y / 256
These are essentially the same as the roll equations from MVS4, which work in the same way when projected onto the 2D screen, as we can ignore the z axis when rolling.
Applying pitch to the stardust
The following calculations apply the current pitch angle beta to the stardust:
7. x = x + 2 * (beta * y / 256) ^ 2 8. y = y - beta * 256
The second one is essentially the same as the pitch equation from MVS4, just applied to the y-coordinate projected into 2D (i.e. divided by z).
The first one, though, is still a bit of a mystery. Removing this part of the calculation doesn't seem to affect the look of the stardust field, and with the maximum magnitude of beta being 8, and y always being less than 120, the maximum delta applied to x is:
2 * (beta * y / 256) ^ 2 = 2 * (8 * y / 256) ^ 2 = 2 * 64 * y^2 / 65536 = y^2 / 512 = 28
out of 256 pixels across the screen, and that's at the extremities of the screen and with full pitch. Perhaps this is a fisheye effect that makes space feel more curved when pitching is high? For now, I don't have an answer...