                      Phobos' Turbo Pascal Demo Tutors
                           [C++ Update by Karma]
                               Tutor 5 - Fire

4/2/97

      Hi, this is a conversion of Phobos Pascal tutes to C++.
      I've converted all of his pascal code into C++ but I have left
      the original text unaltered.  So I take *no* credit for this
      tutorial, so you still have to mail questions to phobos ;-)
      Anyway the code was written and tested in Borland C++ 3.1
      Any comments by me will be in [ ]. -Karma-

Introduction
-------------
Hello everyone!  Well, tutor five is finally here.  This is the third time
this tutor has been written.  The other times I used Smooth Editor.  A nice
little text editor I found.  Unfortunately it was VERY buggy but it took me a
while to find them.  When I went to save the completed tutor it did not save
correctly and I lost all but the first line of the document!  Argh!  Needless
to say Smooth Editor has been banished from my hard disk forever.
[Yippie!! Smooth editor is gone forever!]

I'm sorry it's been such a long time since the last tutor, I've been
busy/lazy.  Hopefully I'll be able to keep up to a tutor a week from now on
though!
[Does that mean I have to convert these _every_ week?? :) ]

Okay, slight change of plan for this tutor, it was originally going to be
about virtual screens but I'm going to put that on hold until next week.
Today we are going to do something much for fun - Fire!
[Where!, where!! - Oh sorry, I thought you mean't a real fire!!]

Fire Theory
------------
When Denthor covered fire, he did it in assembler.  Not much good for us
Pascal coders, so here is a nice little fire routine, asm free!

The way that the fire routine works is surprisingly simple.  So simple
infact, it's amazing it works at all!  Lets think about what fire is like.
It has a start point, usually the ground, then from there it goes upwards in
flames, cooling as it does so.  Okay so from that we can build our palette...

The Palette
------------
I'm sure you will agree, the base 256 palette isn't very suitable for fire.
For this reason, we must add some fiery colours - whites, yellows, oranges
and reds.  The way that we want to set our palette up is cold colours at the
start (blacks and reds) and hot colours at the end (yellows and whites).
As you know, the screen is only 200 pixels in height, so for simplicity, we
will only use the first 200 colours in the palette.

The flames
-----------
The colour of each pixel is worked out by the average colour of the four
below it.  Confused?  Here's a diagram:

                                  x  <---- The pixel we're talking about

                               1  2  3    <---- The pixels we average out.

                                  4

So in relation to our pixel coords (x,y), we add together (x-1,y+1) +
(x,y+1) + (x+1,y+1) + (x,y+2) and then divide by 4.  This gives us the
heat (colour) which we put in our pixel.  Simple no?

Okay, we also need somewhere for the fire to start.  In our case, we'll use
the bottom of the screen or 199 on the y axis.  This is the hottest part of
the fire, so it should use colours high up in our palette.  Yellows and
whites are best.

The last thing we need to do is decrease the heat of the flames as the fire
cools down and gets further from the source.  All we do for this is decrease
the colour value of each pixel by one each time.

How we code all this
---------------------
Okay, by now you should have a pretty good idea of how the fire routine
works.  No doubt you've also tried the source code.  It would be a good idea
if you had a go at coding the fire routine yourself now.  You've got all the
info you need, and it's a much more satisfying experience if you actually
work out how to code something _yourself_!

This is how we set up our palette :

---------------------------------------------------------------

[Pascal]

PROCEDURE palette;
  VAR loop,r,g,b : BYTE;
BEGIN
  r := 0; g := 0; b := 0;                         {give r,g,b initial value}
  FOR loop := 0 TO 200 DO pal (loop,0,0,0);       {colour the palette black}
  FOR loop := 50 TO 99 DO BEGIN         {increase red from colours 50 to 99}
    pal (loop,r,g,b);
    INC (r);
  END;
  FOR loop := 100 TO 149 DO BEGIN    {increase green from colours 100 to 149}
    pal (loop,r,g,b);                           {red and green makes yellow!}
    INC (g);
  END;
  FOR loop := 150 TO 199 DO BEGIN     {increase blue from colours 150 to 199}
    pal (loop,r,g,b);
    INC (b);
  END;
END;

[C++]

void palette() {
 byte loop, r, g, b;                             
  r = 0; g = 0; b = 0;                           // give r,g,b initial value
   for (loop=0;loop<201;loop++) pal(loop,0,0,0); // colour the palette black
    for (loop=50;loop<100;loop++) {    // increase red from colours 50 to 99
      pal(loop,r,g,b);
       r++;
     }
    for (loop=100;loop<150;loop++) { //increase green from colours 100 to 149
      pal(loop,r,g,b);                         // red and green makes yellow!
       g++;
     }
    for (loop=150;loop<200;loop++) { // increase blue from colours 150 to 199
     pal(loop,r,g,b);
      b++;
    }
 }
-----------------------------------------------------------------

This code is a little larger than it really needs to be.  I could have made
one loop instead of having four.  However, I though it would be easier to
understand this way, so there you go.  I make the entire palette black to
start with, this tidies it up a bit.  Like cleaning your brushes I suppose. ;)
I think the rest of the code is fairly straight forward.  Look at the earlier
tutors for more stuff about the palette and loops.

Okay and now the fun bit, the actual fire routine.

----------------------------------------------------------------------

[Pascal]

VAR flames : ARRAY [-5..325,130..200] OF BYTE;
    col    : BYTE;

PROCEDURE palette;
  VAR x,y,loop : INTEGER;
BEGIN
  FOR x := -5 TO 325 DO BEGIN                         {the length of the fire}
    FOR y := 120 TO 200 DO BEGIN                      {the height of the fire}
      col := flames[x-1,y+1] + flames[x,y+1] + flames[x+1,y+1] +
	flames[x,y+2]) DIV 4;                          {our averaging formula}
      IF y = 199 THEN col := RANDOM(125) + 75;       {creates the heat source}
      IF col <> 0 THEN DEC (col);                             {cools the fire}
      flames[x,y] := col;                    {puts the new value in the array}
      IF (x > 0) AND (x < 319) AND (y < 199) THEN putpixel (x,y,col,vga);
    END;                           {if the pixel fits on the screen, draw it!}
  END;
END;

[C++]

byte flames[325][70];
byte col;

void fire() {
  int x,y,loop;
   for (x=0;x<325;x++) {      // the length of the fire
    for (y=130;y<200;y++) {   // the height of the fire
      col=(flames[x-1][y+1] + flames[x][y+1] + flames[x+1][y+1] +
	flames[x][y+2]) / 4;  // our averaging formula
	 if (y == 199) col = (rand() % 126) + 75; // creates the heat source
	  if (col != 0) col--;               // cools the fire
	   flames[x][y] = col;        // puts the new value in the array
	    if ((x-5 > 0) && (x < 319) && (y < 199)) putpixel(x-5,y,col,vga);
	 }                     // if the pixel fits on the screen, draw it!
     }
 }

[ Read fire.cpp for an explaination about the array differences ]
------------------------------------------------------------------

Is that it?  Yep, that's all there is to it.  Here's what's going on.  You'll
notice that when we create our variables, we make them BYTEs instead of the
usual INTEGERs.  This is because BYTEs take up less memory than INTEGERs, and
because we won't be holding any value higher than 255 in them, it's okay to
use BYTEs.  Check out the info on data types for more details.  (Hi-light
the word INTEGER or BYTE and hit Ctrl-F1).

The reason our fire goes from -5 to 325 (note the screen is only 0 to 319) is
so we can get rid of the messed up bits that appear on the side of our fire.

There is a new function in there too - DEC. This, as you may have guessed is
like the oppersite to INC, it decreases the value.

Okay, I think that's about it!  Oh yeah, don't forget to put the RANDOMIZE
command at the start of the your program to make the random functions work!

Things to do
-------------
The fire routine I have given you here really is in it's barest form.  There
are many things you could do to improve it.  You could give it a better
palette, I think there is too much red in the current one.  You could make it
a weird colour like pink or green!  You could try and make the flames bigger.
You could use the fire routine to make letters or shapes!  The way this'd
work is you would have the start of the fire where you wanted to have your
letters.

In closing
-----------
Well, it looks like I finally got this tutor finished!  Thanks to everyone
for your comments on this tutor series, and the suggestions for future
tutors.  A special thank you to Antony Mills for giving me the idea (and original
theory) for this fire routine!


-----------------------------------------------------------------------------
                               C O N T A C T !

If you like\don't like these tutors or have any questions, comments,
suggestions, or anything, just email me!

                  Email Phobos :  ci.phobos@dial.pipex.com
      
                      Visit my Web Page on TP graphics : 
               http://dspace.dial.pipex.com/town/place/abi67/

-----------------------------------------------------------------------------

   Seeya next week and remember - SHARING KNOWLEDGE IS TO GAIN KNOWLEDGE!

[Karma signing off]
