Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
#### Fire The fire effect is a really cool trick that, when performed properly, can create a very convincing illusion of fire. This effect is well documented both on the Web and in various books on graphics programming, and has been around for quite some time. It is a very computationally intensive effect, but it can be highly optimized and runs quite fast when implemented in assembly code. There are numerous ways of implementing this effect, but we’ll examine one that produces a nice effect at decent speeds. Basic Theory The first thing we need to accomplish is creating an appropriate palette. For an effective fire, we’ll create a palette that ranges from white in the upper slots (255-192), through yellow and orange, to red, and finally to black in the lowest slots (63-0). We could adjust the palette range somewhat and put some blues in, to simulate a cooler flame, or perhaps add some grays at the end, which would effectively simulate smoke. This effect involves moving pixels that already exist in a surface. Therefore, we need to create a new surface that will contain the image of the fire. Since we’ll be reading values from this surface, we’ll place it in system memory. This algorithm is a CPU hog, so even though we’ll create a surface that’s the same size as the screen, we’ll only use a small part of it for the effect. In our example, we’ve defined a starting and ending vertical position as well as a starting and ending horizontal position, which will confine the effect to the rectangle defined by these coordinates. Now for the real work. Starting at the first row of pixels (defined by our starting vertical position) in the fire effect surface, we begin iterating through each column (as defined by our starting and ending horizontal position). We retrieve the current value of this pixel, subtract a random amount from this number, and then place the new value in the row of pixels above our current row. In effect, this produces an animation of pixels moving upwards on the screen. Additionally, we add a random number between –1 and 1 to move the pixels left or right in a chaotic manner. Figure 11-13: Flaming pixels Relative to the colors we defined in our palette, the pixel colors will slowly move from white, through yellow and red, and finally to black as they move upward due to the subtraction of a random value from their current color. This produces a very effective illusion of flames. At each frame of the animation, we’ll need to place some random pixel values on the bottom row of the effect to give the algorithm some starting values. Implementing this effect is very simple, as illustrated by the following example. Listing 11-8: The flame effect
    const

       .

       .

       .

      {define the rectangular area for the effect}

      XSTART = 200;

      XEND   = 440;

      YSTART = 100;

      YEND   = 400;

      {define the random decay value}

      DECAY  = 6;

    type

       .

       .

       .

      TBytePtr = array[0..0] of Byte;

      PBytePtr = ^TBytePtr;

    var

       .

       .

       .

      {the interfaces for the primary and backbuffer surfaces}

      FPrimarySurface,

      FBackBuffer,

      FireSurface: IDirectDrawSurface4;

      {the palette interface}

      FPalette: IDirectDrawPalette;

    implementation

       .

       .

       .

    procedure TfrmDXAppMain.FormActivate(Sender: TObject);

    var

       .

       .

       .

      {hold the palette colors}

      PalColors: TColorTable;

      iCount: Integer;

      {this procedure stuffs the RGB values into the appropriate color table entry}

      procedure SetPalEntry(Idx, Red, Green, Blue: Integer);

      begin

        PalColors[Idx].R := Red;

        PalColors[Idx].G := Green;

        PalColors[Idx].B := Blue;

        PalColors[Idx].A := 0;

      end;

    begin

       .

       .

       .

      {define the colors for the palette. this will produce a spread from white

       to yellow/orange to red, fading to black}

      for iCount := 0 to 63 do

      begin

        SetPalEntry(iCount, iCount shl 2, 0, 0);

        SetPalEntry(64+iCount, 255, iCount shl 2, 0);

        SetPalEntry(128+iCount, 255, 255, iCount shl 2);

        SetPalEntry(192+iCount, 255, 255, 255);

      end;

      {create the actual palette}

      FDirectDraw.CreatePalette(DDPCAPS_8BIT or DDPCAPS_ALLOW256, @PalColors,

                                FPalette, nil);

      {attach the palette to the primary surface}

      FPrimarySurface.SetPalette(FPalette);

      {create the offscreen buffer for the fire. we’re going to be modifying this

       constantly, so we’ll place it in system memory}

      FillChar(DDSurface, SizeOf(TDDSurfaceDesc2), 0);

      DDSurface.dwSize   := SizeOf(TDDSurfaceDesc2);

      DDSurface.dwFlags  := DDSD_CAPS or DDSD_HEIGHT or DDSD_WIDTH;

      DDSurface.dwWidth  := DXWIDTH;

      DDSurface.dwHeight := DXHEIGHT;

      DDSurface.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or

                                  DDSCAPS_SYSTEMMEMORY;

      {initialize the surface}

      DXCheck( FDirectDraw.CreateSurface(DDSurface, FireSurface, nil) );

      {initialize the offscreen buffer to all black}

      ColorFill(FireSurface, 0, nil);

       .

       .

       .

    end;

       .

       .

       .

    procedure TfrmDXAppMain.DrawSurfaces;

    var

      SrcRect: TRect;

      SrfcDesc: TDDSurfaceDesc2;

      iCol, iRow: Integer;

      FireSrfcPtr,

      BottomLine: PBytePtr;

      CurPixel: Byte;

    begin

      {begin by locking the fire surface buffer}

      FillChar(SrfcDesc, SizeOf(TDDSurfaceDesc2), 0);

      SrfcDesc.dwSize := SizeOf(TDDSurfaceDesc2);

      FireSurface.Lock(nil, SrfcDesc, DDLOCK_WAIT, 0);

      FireSrfcPtr := PBytePtr(Longint(SrfcDesc.lpSurface)+

                              (YSTART*SrfcDesc.lPitch));

      {we’ll also need a pointer to the bottom line of the rectangular

       area for the effect}

      BottomLine := PBytePtr(Longint(SrfcDesc.lpSurface)+(YEND*SrfcDesc.lPitch));

      {initialize some new pixels in the fire}

      for iCol := XSTART to XEND do

        BottomLine^[iCol] := 255-Random(32);

      {perform the actual flame calculations}

      for iRow := YSTART to YEND do

      begin

        for iCol := XSTART to XEND do

        begin

          {retrieve the pixel from this column}

          CurPixel := FireSrfcPtr^[iCol];

          {if the pixel is less than the decay value or is at the far ends of

           the effect area, blacken it out...}

          If (CurPixel < Decay) or (iCol = XSTART) or (iCol = XEND) then

            {draw a black pixel on the row above the current one}

            PBytePtr(Longint(FireSrfcPtr)-SrfcDesc.lPitch)^[iCol] := 0

          else

            {...otherwise, draw a new pixel on the row above the current one,

             either directly above or to either side of the current pixel, but

             subtract a random value from its color. this causes the color of the

             pixel to slowly move from white, through yellow and red, to black}

            PBytePtr(Longint(FireSrfcPtr)-SrfcDesc.lPitch)^[iCol+Random(3)-1] :=

                     CurPixel - Random(DECAY);

        end;

        {move to the next row in the fire surface}

        Inc(FireSrfcPtr, SrfcDesc.lPitch);

      end;

      {unlock the buffer}

      FireSurface.Unlock(nil);

      {finally, blit the fire surface to the back buffer to make it visible}

      SrcRect := Rect(0, 0, DXWIDTH, DXHEIGHT);

      FBackBuffer.BltFast(0, 0, FireSurface, SrcRect, DDBLTFAST_WAIT);

    end;
Previous | Table of Contents | Next

Products | Contact Us | About Us | Privacy | Ad Info | Home Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.