Secret Project   Leave a comment

Look what I did.

For the past several weeks I’ve been working on a secret coding project, which I can now reveal to the world! It’s a seasonal little thing, originally my entry for the Sekrit Forum advent calendar. You can download it from here.

It requires Windows (sorry), probably XP or above. It should run on pretty much any PC, but will try to throw more at faster machines. Simply extract the zip archive, keeping the directory structure intact, and run SekritAdvent.exe. If it doesn’t work, you may require one of:

The (tiny) Visual C++ 2008 SP1 runtimes, from here:
The (huge) latest (June 2010) DirectX Runtimes, from here:

I hope you enjoy it.

Below I’ll briefly (I should be icing cake and wrapping presents, as it’s twenty past eleven on Christmas Eve) explain how it works. But you should run it first, because otherwise you won’t be able to admire its effect unspoiled by preconception, and will never know true love.

It’s not desperately complicated, but I’m quite proud of it.

The lighting is done by making a window-sized texture, then rendering the sprite in circlelight.png to it using renderstates that make it add to what’s already there. The vertex colours of the sprite change the light’s colours as you’d expect.
To actually light the other objects, their vertices have lightmap texture co-ordinates calculated from their screen co-ordinates; you can see the maths in the two HLSL vertex shader files. This is modulated with the sprite’s colour and any other texture (in LitSprite’s case the colour and texture co-ordinates are passed as shader constants, and filled into the vertices by the graphics hardware, so it can always use the same four vertices; it never has to update its vertex buffer. I assume this is faster than calculating it all on the CPU and copying it back into video RAM every time a sprite’s rendered, but didn’t actually test it. I did it for fun.) to give the final lit colour at any given pixel.

The actual lights are rendered as the light glow above, plus three LitSprites; the main circle light – the first sprite in Sparks.png – which is rendered in pure white (modulated by the glow of any lights present), plus two of the other frames (possibly the same frame twice) in Sparks.png, which are rotated independently and rendered in the light’s colour. It alpha blends between one and the other over a few seconds, then picks another ‘glow’ sprite as the next in its sequence, repeating forever.

Of the snowflakes I’m particularly proud. The algorithm to generate them is simple, but gives either quarter or half a million variations (the code’s on the other PC, sorry!), each with 16 different alpha levels, which are then scaled according to a variable that controls how heavily it’s affected by the wind (ie. emulating distance from the camera).
The generation algorithm is the same, as you’d expect, for all six of the snowflake’s spokes. It works based on a 32-bit random number, first calculating whether they should be lines or triangles, the alpha value (the colour is pure white), and the lengths of the spokes. Each spoke then has four offshoots, which are each taken from 4 bits of the 32-bit number; 1 bit decides whether the offshoots go up (ie. the spoke angle plus or minus 60°) or down (spoke angle plus or minus 120°) and the remaining three give the length. Therefore each offshoot has fifteen possibilities; 0 long is the same in both directions, and it doesn’t bother adding any vertices in this case.
Each snowflake can have up to 108 vertices, with line-flakes having more than triangle-flakes (which have one more per spoke but one fewer per offshoot). Obviously it could have one-sixth as many vertices, rendered six times (fewer in the case of line flakes, as you could reflect the offshoots), but as the current method was simpler and, I suspect, faster (same number of vertices rendered overall, and Direct3D is allegedly slow at draw calls; again, I didn’t test it), I decided to go with the easy method.
The snowflakes are lightmapped similarly to the sprites – see snowflake.vsh for the HLSL code. They could actually have more offshoots; not all the 32-bits from the RNG are used, and originally 7 were planned (some of the spare bits are used for calculating the alpha level); but each extra is up to 24 vertices which slows it down quite a lot, and they also were simply too big and unwieldy.

The light switching is actually very simple; each can either fade or instantly blink between its chosen current and next colour, and next colours can be chosen randomly, in sequence (which is arbitrary, according to the order I typed them into an array), each can stay on its own current colour, or all can switch to the same colour. The first two modes last longer than the last two, to make it less likely that it seems to get stuck. Each light has its own fade-between-colour and fade-between-sparkle times, which’re random.

The letters, as previously mentioned, are simply lines of points interpolated between their vertices as worked out by me on a bit of paper, then used to create lights.

The snowflakes are blown about by a wind, which is similar to an old “plasma” algorithm; a couple of added sine-waves based on the snowflakes’ x and y co-ordinates in both vertical and horizontal directions, scaled and modulated by various random factors and each snowflake’s own distance-emulating “wind factor”. There’s a fudge acceleration added to vertical movement to prevent flakes being permanently blown upwards; they only vanish when they fall off the bottom of the screen, so eventually not doing that could crash the program.

There are a few debug keys: F1 shows some info, Enter changes the wind (this is quite subtle), Space changes the light mode, and plus and minus (on the numpad and maybe otherwise) control the time factor between 1/1024 and 1024 times normal.

I hope you liked it.


Posted 25 December 2011 by Colthor in Development

Tagged with , , , , ,

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s