

Fiery Cube screensaver
======================


fierycube screensaver version 1.0
Copyright  2001,  Harrison Artifex.  All rights reserved.

www.hxa7241.org | 2001-08-22T00:00:00Z




description
-----------

windows screensaver simulating a spinning flaming cube
(a little programming exercise to remind me about c++, windows, 
graphics and speed, after a couple of years doing java server side 
stuff.)




requirements
------------

* Windows 95 or later, or Windows NT 3.5 or later
* Pentium I or equivalent AMD, or later
* probably about 4M ram
* 100K disk space
* screen resolutions 640 * 480 upwards and color modes 8bit to 32bit




installation
------------

1) copy FieryCube.scr to wherever
2) right-click it, select 'install'

or to view, double-click FieryCube.scr (screensavers are just normal 
executables)




acknowledgements
----------------

* written in ANSI C++ for Win32
* edited with TextPad: www.textpad.com
* compiled with borland c++ 5.5.1 for win32, free command line 
  compiler: www.borland.com
* screensaver basis by lucian wischik:  www.wischik.com/scr
* some inspiration, and the color palette, from hugo elias: 
  freespace.virgin.net/hugo.elias
* cube concept from peter walser: www2.active.ch/~proxima
* random number generator from numerical recipes: www.nr.com
* fp to int conversion from chris hecker




implementation notes
--------------------

the code is in four separate 'packages':
- the root directory of windows things
- saverSupport: containing a mini framework for bitmap-oriented 
  windows screensavers (depends on win32)
- saverInstance: containing fiery cube specific material (depends on 
  saverSupport and general)
- general: containing some general utils (no dependencies)

    saverSupport                saverInstance
         |                        /        \
       win32                saverSupport   general

the outer facade is saverSupport/WinSaverGenerator which offers an 
interface of one function 'drawFrame' to be called at each timestep. 
the central classes are WinSaverGenerator and ImageGenerator. the 
former wraps the latter so that frame drawing code can be separated 
into windows and non-windows parts. both are derived from and 
specialised to make a specific application - WinSaverGeneratorFire 
and ImageGeneratorFire for fiery cube. these four classes are at the 
top of their packages, having a one-way uses/dependency relation on 
the other classes in their packages.

    WinSaverGenerator                     WinSaverGeneratorFire
           |      \                         /               |
           |   ImageGenerator    WinSaverGenerator      ImageGeneratorFire
           |                                             /      |       |
     WinBackBuffer                           ImageGenerator    cube    turbulence
           |
        WinDib

the main runtime structure goes like this:
      wndProc
            WinSaverGeneratorFire::drawFrame
                  WinSaverGenerator::drawFrame
                        ImageGeneratorFire::drawNextFrame
                              Turbulence::makeWarpMap
                              Turbulence::warpImage
                              ImageGeneratorFire::updraft
                              Cube::updateAndDraw
                              ImageGeneratorFire::coolAndHeat
                              Blurs::gatherSquare3
                              ImageGeneratorFire::writeColor
                        WinBackBuffer.blitToCenter

for the fire modelling, i like to think of it as a semi physically 
based discrete fluid dynamics simulation. no, really... the grid is 
the pixels, containing heat values. at each timestep new heat is 
added and heat is spread to adjacent pixels, cools a little, is 
stirred around by a vector field and moved upwards. the inconsistency 
is the warp: its an arbitrarilly chosen noise shape rather than being 
calculated from the smaller scale simulation elements. but thats the 
paradigm of computer graphics - a crafted mixture of maths and 
physics, and aesthetically based forms. its not properly simulating 
the navier-stokes equations, but its fast and it looks good. 

the simulation is scaleable to any size, almost - the two things 
holding it back are the spreading, which needs a parameterised blur, 
and the updraft, which also needs a parameterised blur. they can be 
done later sometime maybe. at present it maxes out at 600 pixels 
height - if you have a fast enough machine that is - otherwise when 
it starts it measures the frame rate and reduces the resolution until 
the frame rate reaches 45ish or failing that 22ish. (max frame rate 
is clamped to 50)

i developed this at first on an olde worlde 486, so speed was the 
main concern for all design decisions. its gone through a few 
iterations and i think its pretty honed now, aside from using 
assembly (i dont have an assembler to work with the borland compiler 
so inline asm wasnt readily available). of course i didnt have a good 
way of profiling, just ::GetTickCount, so there may be room for 
improvement based on better measurements.

the main speed problem overall was drawing to the screen: partly the 
pixel replication scaling, and partly the windows blit. not using 
windows stretch blit was faster because only a fraction of the color 
mode translation is necessary, simple integer scaling is used, and 
its possible to skip blocks of pixels that havent changed. its still 
slower than it really should be though. maybe a solution would be to 
use directdraw, but the fastest screen mode might not look good on 
various panel monitors, so the user should still have a choice, and 
so the code still has to handle different screen sizes (unless 
directx provides access to a super stretch blit or something - im not 
familiar with it yet).

the main compute drain inside the simulation was the warp. at first, 
i calculated the displacement for each pixel and stored it in an 
array, then ran through this each frame to get the source pixel for 
each target pixel. (also, every 4 frames a new warp map is made by 
'rotating' the array values along a random amount. the warp map for 
the other 3 frames is a linear interpolation between.) the main warp 
routine turned out to be rather slow, even though it was just a 
simple 14 line loop doing not much. it looked, on examination, like a 
memory problem: using too much, not fitting in the cache(s). so i 
changed the warp routine to a texture mapping form, reading the 
displacements from grid points and interpolating between. bigger 
complex code, but much smaller warp map arrays, and much faster. it 
might be worth trying generating all displacements algorithmically 
like the perlin noise concept. its still the main time drain, so it 
could be improved.

the second main compute drain was the spreading/blurring. this was 
improved by changing the main data structure to an array of bytes to 
store the heat values, which then allowed a simd style technique 
processing 4 pixels at once in the blur loops, and also similarly in 
heat and cool. its usually the default choice to use 32 bit types to 
store things, based on the knowledge that dwords are the cpus natural 
size and manipulating smaller types has an overhead. but the really 
slow factor is the memory access, and using 4 times the amount of 
cache cant be good. that and the simd opportunities mean the default 
choice perhaps ought to be not to use any more bits than necessary.


(changes made to minimal.cpp:
- disabled TSaverSettings::WriteConfigRegistry so the registry isnt 
  written to
- added a unhandledExceptionFilterFunction definition (to suppress 
  display of the 'illegal operation' alert box)
- adjusted SaverWindowProc WM_CREATE handler to set an unhandled 
  exception handler
- changed SaverWindowProc WM_TIMER handler to do the main job of 
  creating and calling WinSaverGeneratorFire
- adjusted the button/key-down handler to pass on key presses
- commented-out MessageName)




special keys: 
  'f' - runtime stats
  'g' - decrease resolution
  'h' - increase resolution
