
		    ͻ 
	             TERRAFORMER        
		             TRAINER          
		       ͻ   SERIES    ͼ 
			 ͼ 

				 Ŀ
				   Volume 6 
				 


		        by Jazm / Terraformer 


			     *** FRACTAL PLASMAS ***



=============================================================================
INTRODUCTION
------------

	Howdy all!  After seeing my BBS intro, TPA_FRAC, a few people have
asked me how to create fractal plasmas, so while Kiwidog is coding our sound
player for (hopefully) Assembly '95, I'll be taking over the reigns for this
trainer.  Originally, this trainer was going to be a joint effort by Codex
Hammer (Craig Agule) and myself with CH covering realtime plasmas and me doing
the precalculated fractal plasmas, but due to free time restrictions, I'll be
flying solo on this one.
	As formentioned, this trainer will show you the basics of fractal
plasma generation.  The idea behind it is fairly simple if you know what
"recursive" means and how to implement it.  After that, it's just a simple
exercise in getting pixels, averaging values, and putting pixels.  Piece of
cake, right?
	I didn't comment the code at all because there wouldn't be enough room
for everything I'd have to say, so there is an explanation of the example code
(TFTRAIN6.PAS) at the end of this file.  I tried to leave as much assembly as
possible out of the code so that it can be read easier...just in case you
haven't read all of the trainers in the Terraformer series. :)

=============================================================================
RECURSIVE?!
-----------

	...yeah, recursive.  That's the basic premise for the whole thing.
Recursion (for our pruposes) pretty much means that you call a function or
procedure within itself.  That may seem kinda stupid, if not impossible, for
someone who hasn't seen the power of recursion, but in fact, it is extremely
powerful (example: quicksort algo).  Here's a very basic example of a
recursive procedure.

	procedure countdown(x : integer);
	begin
  	  write(x, ' ');
  	  if x > 0 then
    	    countdown(x-1);
	end;

	begin  {--Main Code--}
  	  countdown(5);
	end.

Here is what the output would look like:

5 4 3 2 1 0

	Countdown is originally called in the main part of the code passing 5
to the procedure.  The procedure receives the parameter into x and writes it
to the screen, and because x (or 5 now) is greater that 0, countdown is called
FROM INSIDE ITSELF, but this time passing x-1 (or 4) as the parameter.  This
process continues until x=0 and then it has nowhere to go, so it exits.  If
you don't understand this example, stare at it for a while and trace the
numbers out until you get it.
	Okay, now that you know how the above example works, there is one
thing that needs to be said about recursive routines...they must have a trap
door!  Before a procedure/function calls itself, there should be an IF
statement, a REPEAT UNTIL loop, or a WHILE loop.  The reason for this is that
if you didn't test for some limit, the routine would continue to calls itself
until the stack overflows.
	Well, now that you've seen a very pointless example, let's see how
recursivity can be used practically.  How bout a factorial (n!) program...

	function factorial(x : integer) : integer;
	begin
  	  if x = 0 then
    	    factorial := 1            		{ 0! = 1 }
  	  else
    	    factorial := x * factorial(x-1);    { n! = n * (n-1)! }
	end;

	Once you understand the above example, you've pretty much got
recursion locked and it's time to go on to easier and more graphically related
stuff.

=============================================================================
MAKING BOXES OUT OF BOXES
-------------------------

	Here we go, this is the meat of the trainer right here.  To create a
fractal plasma, you need to be able to read pixels, find a midpoint value
between two points, and write a pixel...all recursively.  Throw in a couple of
random numbers to mix things up and you have yourself an interesting pattern
known as a plasma.
	What we need is to fill an area, a box, with pixels that relate to their
neighboring pixels.  This is almost easier done than said.  Let's start out with
our box, or rather just the 4 pixels that limit our box.  We'll start out by
putting a pixel in each corner with a random color value.

	A   ...   B
	.         .
	.	  .
	.	  .
	C   ...   D

	That was easy.  Now we're going to need to set ourselves up for
recursion.  This is where getting, averaging, and putting pixels comes into
play.  Lets find the midpoints of the 4 lines around the box.

	A   .a.   B
	.         .
	b	  c
	.	  .
	C   .d.   D

	We have the positions, now what colors do we fill them with?  Well,
since we want the colors to relate to each other, how bout averaging the
pixels around it?  For example, the color value of 'a' would be the color
value of 'A' averaged with the color value of 'B'.  Now we just need to add 1
more pixel before we can say that this box is 'filled'...the middle pixel.  Do
this the same way...find the midpoint from either the pixels above and below
or the pixels to the left and right.  The color value of the middle pixel will
be the average of the color values of the 4 corner pixels together PLUS OR
MINUS a small random value that will prevent the filling routine from creating
an even fade.
	(*NOTE* Each pixel has a location value and a color value and
BOTH will need to be averaged in different places so make sure you keep them
straight.) You're screen now looks like this with a, b, c, d, and e being the
new points you just calculated.

	A   .a.   B
	.         .
	b    e  c
	.	  .
	C   .d.   D

	The box that you originally passed to the procedure is now considered
'filled'.  Here's where recursion comes into play.  As you can see, one pass
of this 'filling' procedure doesn't exactly cover the screen with pixles.  But
the same thing that made the points you just created can also fill the entire
box...if utilized correctly.  Notice that you sent the 4 corners of a box to
the procedure and with those 4 points, 5 new points were generated.  Well, by
making those 5 new points, didn't we also create the corners for 4 new boxes?
Now we have 4 boxes that can be 'filled': [A,a,b,e], [a,B,e,c], [b,e,C,d], and
[e,c,d,D].  If we send the points of box [A,a,b,e] to the filling procedure
recursively (from within itself), we end up with this.

	A  f  a     B
	g  j  h
	b  i  e   c

	C     d     D

	We now have 'filled' that box with the new points f, g, h, i, and j,
but wait, by doing that, we've created 4 MORE new boxes that need to be
filled.  So we send the corners of the upper left box to the procedure again.
We continue doing this until our trapdoor statement tells us that we're done.
But how DO we know when we're done?  The answer's simple, we only want to fill
a box when there is space between the corners, right?  So all we need to do is
write a simple IF statement to check whether or not there is space between any
2 adjacent corners.  We've now filled in all of the top-left boxes that have
been created...so now just send the corners of the top-right box, then the
bottom-left box, and finally the bottom-right box.
	One more note about about the characteristics of a recursive routine
must be noted here.  When a routine (procedure or function) is called from
within itself, it first pushes the values of the variables that it is using
onto the stack and THEN does it thing.  (*NOTE* If you don't know what the
stack is, you have 2 options.  1) Look it up somewhere else.  2) Just take my
word for it.)  The reason for this it to preserve the values of the variables
that were used it previous rescursive calls.  There is one disadvantage of
this though, the stack is only so big.  If you keep calling a routine from
within itself, it will continue to push variables onto the stack until it
overflows.  This has one effect on our code...the beginning box must not be too
big.  You can't just decide to fill a 256x256 box.  Our routine will use a
lot of variables.  We have 4 variables passed to the routine (the 4 corner
location values), we have 5 variables that will hold the location values of
the new points we generate, and we have 2 more for miscellaneous
purposes.  So every time our routine is called recursively, about 11 variables
are being pushed onto the stack.  This definitely limits the size of our
original box.  Take a look at the code and the explanation below and it
shouldn't be too hard to figure out.  It is much more complex than the
simple examples given above, but the basic concept of recursion is the same.
	Choosing box size IS important.  You need to satisfy 2 conditions in
order to avoid bugs.  1) You can't use up all of your memory with huge sized
boxes because of the above reason.  2) You have to choose a box size that is 2
raised to some power (i.e. 16, 32, 64, etc.) + 1.  This is because when you
find the midpoints, you're dividing by 2 and you want it to come out evenly.
The extra 1 added on is damage control.  That way, there is a whole number
exactly between the 2 points, but a trunc gets rid of the extra 1 after the
first box fill (if you don't understand that, just take my word for it).

=============================================================================
TRANSLATE THAT CODE PLEASE
--------------------------

	I decided to write this little section instead of commenting the
code because, if I tried to put everything that needs to be said into
the code, you'd be drowning in comments.  Unless you have a very good memory,
you may want to print out either this explanation or the code so you can make
references back and forth.

Procedures and Functions:
	videomode -- should be self explanatory
	waitforverticalretrace -- also, name says it all
	setpal -- you HAVE read Terraformer's other trainers, right?
	plas_pal -- makes a nifty little palette for the plasma
	putpixel -- duh!  (written in VERY simple terms)
	getpixel -- duh! again  (again, written in slowest fashion possible)
	min -- returns the smaller of 2 numbers
	fillbox -- our recusive procedure that does the dirty work

Global Variables:
	chaos -- the random number that is added or subtracted from the color
		 value of the middle point generated...play with it for
		 interesting results

The Main Procedure:
	Before we start into the fillbox() procedure, we need to put some
pixels at the corners of our original box.  That way we don't have to pass the
color value of those pixels to the procedure every time...we can just read
them from the screen.  I've used absolute mem addresses for the location
values instead of (x,y) coordinates to save some space.  Now, the fun part...

The Fillbox() Procedure:
	First, let's get all these variables straight.  Variables 'tl', 'tr',
'bl', and 'br' are the LOCATION values passed to the procedure...they stand
for top-left, top-right, bottom-left, and bottom-right respectively.
Variables 'top', 'left', 'right', 'bottom', and 'mid' are the LOCATION values
of the 5 pixels that are generated within the procedure.  Variables 'tlc',
trc', 'blc', 'brc', 'tc', 'bc', 'lc', 'rc', and 'mc' are the COLOR values of
the 9 pixels involved.  Variables 'temp' and 'sf' will be explained later.
	Now the code...
	- The first statement is our trapdoor statement that
checks if the corners are touching.
	- The next 5 lines generate the location
values of the 5 new pixels.  The formula used finds the midpoint of the 2
points written in parenthesis.  Remember, (x shr 1) is equivalent to
(x div 2).
	- The next 4 lines get the color values of the 4 corner pixels that we
passsed to the procedure.
	- We now come across 4 sets of 4 lines each that begin with:
"temp := getpixel...".  There are 4 sets of instructions that look similar and
basically do the same thing, but each for a different picture.  I won't
explain what these little blocks do any further than they get the color values
the correspond to the location values of 'top', 'left', 'right', and 'bottom'.
(Hey, you havta figure SOMETHING out on you own :> ).  Just remember, the more
randome numbers you throw in, the more interesting and diverse the plasma will
tend to be.
	- The next block of instructions generate the color of the middle
pixel.  This is basically where we get all of the diversity.  Because 'mc' is
a byte, we can easily add a random number to it and cause it to loop back to
zero (example: 255 + 4 = 3  when you're dealing with bytes).  So we actually
store the average of the color values of the other 4 new pixels plus a random
number (controlled by the variable 'chaos' set in the main procedure) to a
variable of type word.  This way, we can see if the value is greater than 255
(the limit of a byte) and if it is, we'll try again until the random number
allows a valid value (ie one that doesn't loop back to 0).
	- Now we put the 5 new pixels that we've just generated the location
and color values of.
	- Finally, we call fillbox() recursively to 'fill' the 4 new boxes
that we've created.

=============================================================================
HUH???
------

	That's about it.  This is the first trainer that I've written so I
apologize to anyone that gets totally lost, because my explaing skillz may be
a bit lacking.  I know how to do this stuff and it's hard not to assume that
someone reading this doesn't, but I tried.  If you're really lost, you can
contact me on The Programmer's Archive BBS (703-476-9015  email Jazm) or
Greenway Data Connect BBS (formerly Data Connection 703-506-8598  email James
Goodall) and I'll try my best to help you out.

=============================================================================
I'M DONE NOW
------------

	If you found this trainer helpful, great!  We would appreciate it if
you would greet Terraformer in you next production though.  We will continue
to release trainers such as this only if we see that it is being read.  So let
us know that you're out there.

	You can reach us via e-mail at any of the following addresses:

	kiwidog@mail.vt.edu -> Kiwidog.  Any mail may be sent here.

	rpope@muselab.ac.runet.edu -> Primal Scream.  Any business concerns
				      should be directed here.

	mdavistr@mail.vt.edu -> Damnation Alley.  Any questions regarding
				games or other non-technical questions can
				go here.

	Then of course there's always the tride-and-true SnailMail, which
although it's slow we don't mind it at all.  We will reply to all SnailMail
but it may take some time:

		Chris Hargrove
		Terraformer Systems Inc.
		1319 Grant St.
		Herndon, VA  22070

=============================================================================
MEMBERLIST OF TERRAFORMER
-------------------------

       Alias                    Real Name               Position
       ------------------------------------------------------------------
       Kiwidog                  Chris Hargrove          Coder / PR / Organizer
       Codex Hammer             Craig Agule             Coder
       Marmot                   Steve Possehl           Coder
       Necromancer              Felix Tan               Coder
       Jazm                     James Goodall           Coder
       Damnation Alley          Mitch Davister          GFX
       Spawn                    Eddy Simmons            GFX
       Lord Blanka the Black    Mark Sanders            Musician
       The Smeghead             Dave Oranchak           Musician
       Primal Scream            Robert Pope             Admin / PR / Musician

	If you would like Terraformer to work with you on a project such as
a game or demo, feel free to contact us anytime using the above methods.
Depending on our schedules we may or may not be able to contribute, but we'll
be happy to see what we can do.

=============================================================================
BYE
---

	See y'all at the next compo...

			Jazm / Terraformer

June 3, 1995	11:44 PM
=============================================================================
