Thursday, 18 August 2016

Making "Ad Astra" (2/3)


Scene 1 : Flyby


The opening scene makes the most use of the Map module, this is a little wrapper around the Map function that adds variables for movement over time, and checking boundries for resetting the map's position.  By stacking these up I could have some parallax layers to set the scene.  The stars use the Vector Bob module on a mesh of random X,Y,Z points, with a very slight rotation on one axis so it's direction gradually moves from head-on to the side-view. 

ilkke supplied some great assets for this, a full-size space background across several maps, the detailed space freighter and finally our Ate Bit logo.  This is also the first time you see the triangular design style ilkke went with on the backgrounds to each scene.

If you're unfamiliar with the term Vector Bobs, it's basically a mesh of points sorted in Z order and rendered with sprites.  To add more depth you can use different sized sprites based on the Z ordering.  I think the first time a lot of us saw this effect was the Afterburner arcade game's title screen.

In the early days of the Amiga demoscene it was quite a popular effect, probably the most famous is in the Red Sector Megademo.

Scene 2: Space Flight


So now we use the Vector Bobs again with classic bubble sprites, and bring in a filled spaceship to fly over it while a planet hovers into view behind.

Amusingly this is the first software triangle routine I've ever done, so it was fun looking up old tutorials to find out how to best approach it.   Ones I used were this for the triangle splitting algorithm and this old one from Cubic & $eeN on backface culling.  I did end up with a couple of bugs in the draw which were temporarily fixed with Rectfill to cover them up.  I did mean to go back and fix it properly but I was running out of time so that stayed in, it didn't seem to impact the speed much.  Anyway, something to work on next time.

For sorting both bobs and tris I used a Comb Sort, I think I did a literal translation of the psuedo code from here in the end, short and fast enough for what I needed.  I did have a faster version in the demo which shared the Z position and index in one token, it had a hard-limit of 511 items (not really a problem for the scenes I wanted to do) but had some problems with really close Z positions that I didn't notice until late in the day.  As I didn't have time to fix that up and the scenes were still in a frame without it I swapped back to the older two token version instead.   The one in the demo puts out about 150 bobs in a frame, the faster one was hitting about 190.   Here's a gif of the fast one at full capacity, just under 3 frames sorting 511 bobs:


Scene 3 : Scroller

This keeps the previous elements and adds a sine scrolling logo over the top.

The logo is one long map, when displayed the visible area is split into vertical strips of 1x16 in size, and each one of these has an independent Y sine position which is stored in an array.  Strips are pixel scrolled left, and once 8 pixels have been covered the pixel position is reset and the X position of the map draw increases by one so that the map scrolls across the screen.  

To do the sine effect the last element in the Y array has a new sine position calculated, then the whole array is shifted to the left every frame so it's constantly Y disting across the whole visible map.

Scene 4 : Planetfall



This uses the map and vector modules again (actually all screens use the Map module in some way), the only new thing here is the Plasma module which is used for the cloud effect.

The first effect I coded on Pico-8 was a standard Plasma, to make things a little smoother I did it as a 4x4 pixel effect in sprites, plotting the sprites for the map every 4 pixels in X + Y direction:



The Plasma module has a few extra inputs for max sprites to use, area and also what resolution to use.  Amusingly while it can still do the 4x4 mode I only use it in normal 8x8 sprite resolution in the demo.

Scene 5 : Landscape


The landscape is in the style of classic 'voxel map' effects like the famous Mars demo from 1993.  Obviously it's a lot less capable than any of those but it does use a height-map (displayed in sprites so I could have a bit of a gradient effect with the lighting) but can only move in map shifts, no rotation or camera movement is possible.   The Z distance calculation is far more like one you'd do for a classic racing game effect than proper 3d too.

The lighting is calculated based on the sun sprite's X + Y position relative to the displayed sprite position. But it's one of those 'demoscene calculations' where you tweak it to look alright rather than basing it on any real-world formulas.  As we're not shifting the map position left or right it's actually mirrored in the middle to save a bit of cpu time, but with slightly different lighting calculations for each half so it's not immediately obvious.

To finish the scene off I asked ilkke to do a cockpit overlay (which looks great), this is shifted up and down on the same sine calculation as the sun sprite.  In the background of the scene a couple of Rectfills draw the sky and horizon, which also moves on the sun's sine wave.

This is the only effect that goes over a 'frame', or at least goes over the stat(1) output whch is what I was using to test each screen during development.

I put an earlier version of this scene up on my twitter (below), but I think the new version looks a bit with the gradient sprites.  Still lots of room for improvement though.




Making "Ad Astra" (3/3)

Scene 6 : Ship flyover



This re-uses most of what we've already seen, with the only new effect being the Water module which is only 5 lines of code.  This uses memcpy to copy part of the screen line by line to another part of the screen but in reverse order.  To get the ripple effect I run a sine calculation in the copy loop and add the result to the line to copy, the start positions of the sine calculation are stored outside the loop and updated on each new call. This way you get the distortion of lines being copied out of order, but because the sine is starting from a moving constant you get a gradual movement of the distortion vertically up the area, making it seem much more like waves.   After this point I use it in pretty much all the scenes because it was fast and was a nice way to fill the spare space with action.

The Plasma module is used again here to add some subtle ripples on the water surface. I'm quite happy with how that turned out because with the small window size it creates some quite diverse shapes in the water.  With ilkke's great map layer work behind and in front of the water I think this was quite effective in the end.

Scene 7 : Shaded vectorbobs



Here we use the Vector Bobs again on a 5x5 cube.  The lighting is again calculated by the bob's X+Y position against the Sun sprite's X+Y position.  It averages this out so that the X + Y results are always a positive.  If both X+Y results are under a certain threshold (a simple way of saying the bob is close enough to the sun to be lit) then we do some division on both values to end up with a final result that is somewhere in the spectrum of bob sprites in the bank.  I think we had 8 lighting values in the end and as long as they're in the right sprite order bobs closest to the sun will get the brightest colours.

To give the calculation a bit more of a 3d appearance the X + Y are divided by different amounts (half for Y) and I use the bob's Z position as a divider to give it more of a spread across the cube surface.   These are, of course, 'demoscene calculations' in that you keep tweaking until you get what you want for the effect.

Anyway I was quite happy with this one, that lighting spread was my main aim and it gives the right appearance even if the maths are very far from a proper lighting model.

Scene 8 : Vertical Rasters


Another oldskool effect, I was quite happy to see that this can be done in nearly the same way I've done it on 8-bit machines before.  Basically you have a 1-pixel high horizontal strip which is plotted onto each vertical line of the screen.  For each vertical line you plot into it your vertical bar graphic at a sine calculation.  You then update the sine calculation, wait for the next vertical line and plot your bar graphic into it again.  You continue doing this for the whole screen and because you're plotting into the same pixel line of graphics without clearing it you end up with the 3d snakey effect above.  

In the pico-8 version I have a one-pixel high strip of sprites which is plotted into as above and then I use the map command to redraw it one pixel down each time.  Simpler than doing it on a Vic-20 obviously. :) 

To enhance it a bit I use the Map X position to give me some wobbly sine offsets, and I do the loop in reverse order so it comes out upside down.   That's very easy on here where you're not chasing the raster beam down the screen, but it's also a homage to the one in LDA STAin by Clarence/Chorus which was a complete headscratcher for me when it came out.   He then went one better in Rocketry by drawing them in reverse Z order, amazing stuff.

Finally as I'm plotting into a map I can make use of transparency and have some bubble sprites behind the rasters to add depth, the horizon also bobs up and down using Rectfill and the raster sine position calculation.  Everything else in this scene is handled by the Map module and Water so I can hide the logo behind it when I need to.

Scene 9 : Blenk vectors



Classic blenk vectors, I think the first of these I saw was in Enigma by Phenomena though those were centrally lit.  This uses no new modules but adds a moving sun sprite.  The lighting calculation is much simpler for this and only uses the sun's X position relative to each triangle's X position.  There is no threshold check, it just divides down to a nicely useable number for an array of the lighting colours: darkest to brightest. 



So that's all the vector routines in the demo.  One I didn't get to use was an extra mode which combined wireframe with filled to have a psuedo-drawn effect.  The wireframe has a random offset from the origin each frame to make it look more sketched.  As it didn't fit in to the design I didn't remove the triangle joins which would make this look neater.

Scene 10 : Mirror bobs


So here is the final screen, this almost didn't get in because we'd started running out of memory in the blenk vector part.  To claw it back I turned all the init functions into one big unwieldy if..else list, generalised some variables and finally hard-coded some movement routines only to the parts they're used in. (like the shade bobs and blenk vector)

In this part the vector bobs are plotted to the same origin position on an empty screen, which is then copied to a 4x4 block of sprites in one of the banks.  The screen is then cleared and 4 map strips are drawn onto the screen next to each other, these strips contain the same sprites we just plotted into, so we get a mirror copy of the vector bobs all over the screen.  To add variety the map strips are offset by 1 y map position each and are longer than the display so they can be shifted vertically and then reset every 4 map blocks for a seamless moving background.  I put an earlier version of this on my twitter which got quite a few likes, so thanks for that:

  
As there was some cpu time left I put the logo scroller over it for the finale.  I think this part is still running in a frame, stat(1) says yes but it does look like it staggers occasionally, maybe that's just the draw update.

Transitions

In most scenes there's a transition at the start and end, this just uses Rectfill and each of the X+Y Start and End points can have a number added or subtracted to give different fills and reveals. (with different colours)  I hard-coded the timing for when these start and end (start is literally at the start of a new screen, end is a few beats into the last pattern of the part) which worked ok here but isn't that flexible obviously.

Music

One rule of thumb for doing music for demos (and trailers too) is to have the music first, and time your scenes to it.  But personally if I'm coding the demo as well the last thing I'm thinking about is writing the music.  One obvious flaw with writing it last is you are stuck to the timing of the demo which usually won't be as bar friendly as you'd like.   You'll notice the music in the demo doesn't really follow much of a song structure because the scenes aren't very even in length.

One thing I will say though is make use of soundfx in your music.  Having the ship engine whooshes and odd portamento effects really helped with dynamics. (One thing I should have put in was a 'Shake' module for shifting the camera around, but I got around some of that using sound fx)  This is still something that doesn't happen very often even in modern PC demos and I don't really understand why.

End

Thanks for reading, hope you enjoyed watching the demo.  Massive thanks to ilkke for all his support working on this.