March 29, 2024, 08:55:19 AM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


Dos fireworks example

Started by sapero, July 31, 2006, 10:45:44 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

sapero

July 31, 2006, 10:45:44 AM Last Edit: July 31, 2006, 11:00:29 AM by sapero
This demo uses some simple physics to simulate an explosion with a particle system. It looks pretty awesome considering the small amount of code used for the particle system. You can press the spacebar to keep making new explosions which makes this a fun demo to play with.

One method - BlurScreen - is wrapped by much faster standard assembly code. For directx window 320x200 (also original size) - this wrapper is not so important.
This example uses 8 bit color depth and custom color palette: while->yellow->red

note: instruction cmovc can be unsupported on older processors, here is replacement (replace cmovc eax,ecx with ...):
jnc .nocy
mov eax,ecx
.nocy:

///////////////////////////////////////////////////////////
//ÂÃ,  Particle Explosion in DirectX 5
//ÂÃ,  based on Tom Hammersley's particle code for DOS
///////////////////////////////////////////////////////////

import int ZeroMemory alias RtlZeroMemory(void *Destination,int Length);
import int MoveMemory alias RtlMoveMemory(void *Destination,void *Source,int Length);
#define PC_NOCOLLAPSEÂÃ,  ÂÃ, 0x04

// D E F I N E S //////////////////////////////////////////

// change partition direction if partition reaches left, right or top border
//#define BORDER_COLLISIONS

// Defines for video mode
#define SCREEN_WIDTHÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, 800//320ÂÃ,  ÂÃ,  ÂÃ, // size of screen
#define SCREEN_HEIGHTÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  600//200
#define SCREEN_BPPÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, 8ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  // update code if changed! (8bpp=UCHAR)
#define SCREEN_SIZEÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  (SCREEN_WIDTH * SCREEN_HEIGHT)

// divide 10000 particles to 4 banks
#define MAX_PARTICLESÂÃ,  ÂÃ,  ÂÃ,  20000
#define PARTICLE_BANKSÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, 8
#define PARTICLE_BANK_SIZE (MAX_PARTICLES/PARTICLE_BANKS)



// S T R U C T U R E S /////////////////////////////////////

struct PARTICLE_STRUCT
{
float x;ÂÃ,  ÂÃ,  ÂÃ, // horizontal position
float y;ÂÃ,  ÂÃ,  ÂÃ, // vertical position
float dirx;ÂÃ,  // horizontal move direction
float diry;ÂÃ,  // vertical move direction
intÂÃ,  ÂÃ, color;
intÂÃ,  ÂÃ, life;ÂÃ,  // life time
}


#typedefÂÃ,  UCHAR unsigned byte

struct PALETTEENTRY // from wingdi.inc
{
ÂÃ,  ÂÃ,  unsigned byte peRed;
ÂÃ,  ÂÃ,  unsigned byte peGreen;
ÂÃ,  ÂÃ,  unsigned byte peBlue;
ÂÃ,  ÂÃ,  unsigned byte peFlags;
}


class CFireworks : C2DScreen
{
C2DSurfaceÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, *m_back ;ÂÃ,  // dd primary surface
PALETTEENTRYÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  m_palette[256];ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, // color m_palette
UCHARÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  *m_VideoBuffer ; // primary video buffer
UCHARÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  *m_MemoBackBuffer; // double buffer (system RAM)
CDirectInputÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  m_keyboard;

intÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, m_ytable[SCREEN_HEIGHT], i;
PARTICLE_STRUCTÂÃ,  ÂÃ,  *m_particles;
floatÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, m_gravity;
floatÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, m_energy;
intÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, m_particlescnt;
intÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, m_particleLife;
intÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, m_particleLifeLimit; // if reached: particle.color--
intÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, m_particlesBank;ÂÃ,  ÂÃ,  ÂÃ, // bank index for new explosion RandomExplosion()

declare CFireworks()
{
m_gravity = 0.07f;
m_energyÂÃ,  = 10.0f;
m_particlescnt = PARTICLE_BANK_SIZE; // MAX_PARTICLES = PARTICLE_BANK_SIZE * PARTICLE_BANKS
m_particleLife = 150; // depends on FPS
m_particleLifeLimit = 140;
m_particlesBank = 0;
m_MemoBackBuffer = null; // one memory block:ÂÃ,  ÂÃ, m_MemoBackBuffer | m_particles
}
declare _CFireworks()
{
if m_MemoBackBuffer delete m_MemoBackBuffer;
}

declare Init(),int;
declare Shutdown();
declare Main(),int;
declare CreateFirePalette();
declare Explosion(int x, int y);
declare RandomExplosion();
declare DrawParticle(PARTICLE_STRUCT *aparticle);
declare DrawParticles();
declare MoveParticles();
declare BlurScreen();
declare WaitForVsync();
declare ShowDoubleBuffer();

declare virtual OnTimer(int nIDEvent),int
{
StopTimer(1);
RandomExplosion();
StartTimer(rand(200, 2000)); // average explosion frequency
}
}



///////////////////////////////////////////////////////////
// W I N M A I N //////////////////////////////////////////
///////////////////////////////////////////////////////////

global sub main()
{
CFireworks *pf = new(CFireworks, 1);
if (pf)
{
if pf->Init()
{
pf->StartTimer(1000);
// enter main event loop
while (!pf->m_keyboard.KeyDown(DIK_ESCAPE))
{
pf->Main();
}
}
else messagebox(0,"CFireworks::Init failed","");
pf->Shutdown();
delete pf;
}
}

///////////////////////////////////////////////////////////
// WINX GAME PROGRAMMING CONSOLE FUNCTIONS ////////////////
///////////////////////////////////////////////////////////
//
// These functions are coded DOS style!ÂÃ,  Oh yeah!
//
///////////////////////////////////////////////////////////

CFireworks::Init(),int
{
int index;

// This function is where you do all the initialization for your game


// Allocate memory for m_particles and double buffer
m_MemoBackBuffer = new(byte, (MAX_PARTICLES * sizeof(PARTICLE_STRUCT)) + SCREEN_SIZE);
m_particles = m_MemoBackBuffer + SCREEN_SIZE;
if (!m_MemoBackBuffer) return false;

if CreateFullScreen(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP) < 0 return false;
SetCursor(CS_CUSTOM,0);
SetFont("Verdana", 8, 800);

// Set up DirectInput
m_keyboard.Init(this);

// Create the primary surface!
m_back = GetBack();
if (!m_back) return false;

// Create a 256 color m_palette with a good set of fire colors
CreateFirePalette();

// Do first explosion
Explosion(SCREEN_WIDTH/2.0, SCREEN_HEIGHT/2.0);

// return success
return true;
}

///////////////////////////////////////////////////////////

CFireworks::Shutdown()
{
CloseScreen();
}

///////////////////////////////////////////////////////////
// G A M EÂÃ,  ÂÃ,  M A I N /////////////////////////////////////
///////////////////////////////////////////////////////////

CFireworks::Main(),int
{
// This is the workhorse of your game
// All the calls for your main game loop go here!

m_back->Fill(0);
DrawParticles();
MoveParticles();

// Without this the explosion looks like a pixel explosion
// rather than a real fire explosion
BlurScreen();

// Tell DirectDraw we are accessing video memory
if m_back->Lock()
{
// Alias a pointer to the surface memory for easy access
m_VideoBuffer = m_back->m_DDSD.lpSurface;

// Synchronize to vertical retrace to eliminate flicker
// and lock the frame rate.
WaitForVsync();

// Copy double buffer into video memory
ShowDoubleBuffer();

BackPen(0);
DrawMode(TRANSPARENT);
FrontPen(0xFFFFFF);
WriteText(0,0,sprint("gravitation:",m_gravity," (left,right)"));
WriteText(0,14,sprint("explosion energy:",m_energy," (up,down)"));
WriteText(0,28,sprint("explosion size:",m_particlescnt*100/PARTICLE_BANK_SIZE,"% (page up,down)"));
FrontPen(0x00FFFF);
WriteText(0,42,"SPACE - fireÂÃ,  ÂÃ,  ESC - quit");

// Tell DirectDraw we are done accessing video memory
m_back->Unlock();
Flip();

if (m_keyboard.KeyDown(DIK_LEFT) && (m_gravity > 0.0)) m_gravity -= 0.0025;
if (m_keyboard.KeyDown(DIK_RIGHT)) m_gravity += 0.0025;

if (m_keyboard.KeyDown(DIK_DOWN) && (m_energy > 0.0)) m_energy -= 0.01;
if (m_keyboard.KeyDown(DIK_UP)) m_energy += 0.1;

if (m_keyboard.KeyDown(DIK_PRIOR) && (m_particlescnt < PARTICLE_BANK_SIZE)) m_particlescnt += 10;;
if (m_keyboard.KeyDown(DIK_NEXT) && (m_particlescnt)) m_particlescnt -= 10;
}
// Do another explosion if the user pressed the spacebar
if m_keyboard.KeyDown(DIK_SPACE)
RandomExplosion();

// return success
return(1);

} // end Game_Main

////////////////////////////////////////////////////////////////
// F U N C T I O N S ///////////////////////////////////////////
////////////////////////////////////////////////////////////////

CFireworks::CreateFirePalette()
{
// First clear out all the entries, defensive programming
ZeroMemory(m_palette,256*sizeof(PALETTEENTRY));

// Here, we make a nice m_palette, that fades from red->yellow->white,
// giving the usual spread of colors seen in an explosion

// Set first 64 colors to shades of red

for (int i=0; i<64; i++)
{
m_palette[i].peFlags = PC_NOCOLLAPSE;
m_palette[i].peRed = (i << 2);
m_palette[i].peGreen = 0;
m_palette[i].peBlue = 0;
}

// Set next 64 colors to shades of yellow and orange

for (i=64; i<128; i++)
{
m_palette[i].peFlags = PC_NOCOLLAPSE;
m_palette[i].peRed = 255;
m_palette[i].peGreen = (i - 64) << 2;
m_palette[i].peBlue = 0;
}

// Set last 128 colors to yellowish-white

for (i=128; i<256; i++)
{
m_palette[i].peFlags = PC_NOCOLLAPSE;
m_palette[i].peRed = 255;
m_palette[i].peGreen = 255;
m_palette[i].peBlue = (i - 128) << 1;
}

// set the new palette
IDirectDrawPalette *lpddpal;
if !m_lpDD->CreatePalette(DDPCAPS_8BIT | DDPCAPS_INITIALIZE | DDPCAPS_ALLOW256, m_palette,&lpddpal,NULL)
{
GetFront()->m_lpDDS->SetPalette(lpddpal);
lpddpal->Release();
}

// Store coordinates of the start of each row in a table

for (i=0; i<SCREEN_HEIGHT; i++)
{m_ytable[i] = i*SCREEN_WIDTH;}

}



/////////////////////////////////////////////////////////////////

CFireworks::RandomExplosion()
{
Explosion(RND(SCREEN_WIDTH-20)+10, RND(SCREEN_HEIGHT-20)+10);
}

/////////////////////////////////////////////////////////////////

CFireworks::Explosion(int x,int y)
{
intÂÃ,  i;
float d_len, lx, ly, dist;

m_particlesBank++;
if (m_particlesBank >= PARTICLE_BANKS)
m_particlesBank = 0;

int startPartition = (m_particlesBank * PARTICLE_BANK_SIZE);
int lastPartitionÂÃ,  = (startPartition + m_particlescnt-1);

for (i=startPartition; i<lastPartition; i++)
{
PARTICLE_STRUCT *part = *m_particles[i];
// Set coordinates for origin of explosion

if (!part->life) // renew only unused particles
{
part->x = x;
part->y = y;
part->life = rand(1, m_particleLife&32767);

// Set the x and y directions to either a positive or negative
// i.e. right/left or up/down

part->dirx = RND(3.0) - 1.5;
part->diry = RND(3.0) - 1.5;

// Set the energy (power) of the explosion

dist = RND(m_energy);

// Store x,y directions in temp variables

lx = part->dirx;
ly = part->diry;

// Use distance formula

d_len = sqrt(lx*lx + ly*ly);

if (d_len == 0.0)
d_len = 0.0
else
d_len = 1.0 / d_len;

// We need to normalize the vector to give us a direction
// vector, then find a random length of the vector, to give
// a nice, non-uniform distribution

part->dirx *= (d_len*dist);
part->diry *= (d_len*dist);

// Set PARTICLE_STRUCT to brightest color

part->color = 255;
}
ÂÃ,  ÂÃ,  ÂÃ, }
}

////////////////////////////////////////////////////////////////

CFireworks::DrawParticle(PARTICLE_STRUCT *aparticle)
{
if (aparticle->life)
{
aparticle->life--;
if (aparticle->color)
{
if (aparticle->life < m_particleLifeLimit) aparticle->color--;
}
else
{
aparticle->life = 0;
}
}
else
{
aparticle->color = 0;
}
// Round coordinates to integers

int x = (aparticle->x + 0.5);
int y = (aparticle->y + 0.5);

// Draw only if inside screen area

if ((x >= 0) && (x < SCREEN_WIDTH) && (y >=0) && (y < SCREEN_HEIGHT))
{*m_MemoBackBuffer[m_ytable[y] + x] = aparticle->color;}

}

/////////////////////////////////////////////////////////////////

CFireworks::DrawParticles()
{
PARTICLE_STRUCT *part = m_particles;

// Draw all particles
for (int i=0; i<MAX_PARTICLES; i++)
{
if (part->color) DrawParticle(part);
part+=sizeof(PARTICLE_STRUCT);
ÂÃ,  ÂÃ,  }
}

/////////////////////////////////////////////////////////////////

CFireworks::MoveParticles()
{
int i;
PARTICLE_STRUCT *part = m_particles;

for (i=0; i<MAX_PARTICLES; i++)
ÂÃ,  ÂÃ,  {
// Move particles according to vector direction

part->x += part->dirx;
part->y += part->diry;

// Check if the PARTICLE_STRUCT has collided with any of the screen
// top or bottom edges. If so, reflect the direction, and
// lose energy. If not, then just add m_gravity

if (part->y >= SCREEN_HEIGHT)
{
part->y = SCREEN_HEIGHT-1;
part->dirx /= 4;
part->diry = (-part->diry / 2);
}
else if (part->y < 1)
{
part->y = 1;
#ifdef BORDER_COLLISIONS
part->dirx /= 4;
part->diry = (-part->diry / 2);
#else
part->color = 0;
part->life = 0;
#endif
}
else
{
part->diry += m_gravity;
}

if (part->x < 0)
{
#ifdef BORDER_COLLISIONS
part->x = 1;
part->dirx = (-part->dirx / 2);
part->diry /= 4;
#else
part->x = 0;
part->color = 0;
part->life = 0;
#endif
}
else if (part->x >= SCREEN_WIDTH)
{
part->x = SCREEN_WIDTH-1;
#ifdef BORDER_COLLISIONS
part->dirx = (-part->dirx / 2);
part->diry /= 4;
#else
part->color = 0;
part->life = 0;
#endif
}

// If the PARTICLE_STRUCT is near the bottom of the screen, we
// make its color random, to simulate the effect of the
// PARTICLE_STRUCT dying, things catching fire, that kind of stuff

if (part->y >= (SCREEN_HEIGHT-20))
part->color = (rand(127)) + 128;

part+=sizeof(PARTICLE_STRUCT);
ÂÃ,  ÂÃ,  ÂÃ, }
}

/////////////////////////////////////////////////////////////////

CFireworks::BlurScreen()
{
_BlurScreen(m_MemoBackBuffer, m_ytable, SCREEN_WIDTH, SCREEN_HEIGHT);
/*
// this is very slow loop, used asm version

UCHAR *row1, *row2, *row3;
int x, y, color;

for (y=1; y<(SCREEN_HEIGHT-1); y++)
{
// Get values of pixel above and below the PARTICLE_STRUCT's row

row1 = *m_MemoBackBuffer[m_ytable[y - 1]];
row2 = *m_MemoBackBuffer[m_ytable[y]];
row3 = *m_MemoBackBuffer[m_ytable[y + 1]];

// Set value of next row's pixel to 0 to prevent sides of screen
// from staying ablaze

*row1 = 0; row1++;
*row2 = 0; row2++;
*row3 = 0; row3++;

// Take the average of the 4 pixels on each side of the current pixel
// and darken it a bit too

for (x=1; x<(SCREEN_WIDTH-1); x++)
{
color = ((*row1 + *row3 + *(UCHAR)(row2 - 1) + *(UCHAR)(row2 + 1)) >> 2) - 2;

if (color < 0)
color = 0;

// Set current pixel to calculated color

*row2 = color;
row1++;
row2++;
row3++;
}

// Set value of next row's pixel to 0 to prevent sides of screen
// from staying ablaze

*row1 = 0; row1++;
*row2 = 0; row2++;
*row3 = 0; row3++;
ÂÃ,  ÂÃ,  ÂÃ, }
*/
ZeroMemory(m_MemoBackBuffer,SCREEN_WIDTH);
}


/////////////////////////////////////////////////////////////////

CFireworks::WaitForVsync()
{
// this function waits for a vertical blank to begin

m_lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0);

}

/////////////////////////////////////////////////////////////////

CFireworks::ShowDoubleBuffer()
{
// This function copies the double buffer into the video buffer
// and takes the pitch into account for retarded video cards.
int pitch_off = 0;
int work_off = 0;
int pith = m_back->m_DDSD.lPitch;

for (int y=0; y<SCREEN_HEIGHT; y++)
{
// Copy double buffer into video memory one line at a time
MoveMemory(*m_VideoBuffer[pitch_off],*m_MemoBackBuffer[work_off],SCREEN_WIDTH);

// Move to the next line
pitch_off += pith;
work_offÂÃ,  += SCREEN_WIDTH;
}
}



sub _BlurScreen(byte *m_MemoBackBuffer,int *ytable,int screen_x,int screen_y)
{
int x[6]; // make room for local variables
#asm
%define m_MemoBackBuffer [ebp+8]
%define ytable [ebp+12]
%define screen_x [ebp+16]
%define screen_y [ebp+20]

%define XÂÃ,  ÂÃ,  ÂÃ, [ebp-4]
%define yÂÃ,  ÂÃ,  ÂÃ, [ebp-8]
%define color [ebp-12]
%define row1ÂÃ,  [ebp-16]
%define row2ÂÃ,  [ebp-20]
%define row3ÂÃ,  [ebp-24]


mov dword y,1
dec dword screen_x
;//dec dword screen_y

.for_y:
mov esi,ytable
mov edi,m_MemoBackBuffer
mov eax,y

sub eax,1
mov edx,[esi+4*eax] ;// ytable[y - 1]
lea edx,[edi+edx]ÂÃ,  ÂÃ, ;// m_MemoBackBuffer[edx]
mov row1,edx

add eax,1
mov edx,[esi+4*eax] ;// ytable[y]
lea edx,[edi+edx]ÂÃ,  ÂÃ, ;// m_MemoBackBuffer[edx]
mov row2,edx

add eax,1
mov edx,[esi+4*eax] ;// ytable[y + 1]
lea edx,[edi+edx]ÂÃ,  ÂÃ, ;// m_MemoBackBuffer[edx]
mov row3,edx

mov esi,row1
mov edi,row2
mov edx,row3
mov byte[esi],0
mov byte[edi],0
mov byte[edx],0

add dword row1,1
add dword row2,1
add dword row3,1

mov dword X,1
.for_x:

movÂÃ,  ÂÃ, esi,row2
movÂÃ,  ÂÃ, edi,row3
movzx eax,byte[esi-1]
movzx edx,byte[esi+1]
movzx ecx,byte[edi]
addÂÃ,  ÂÃ, eax,edx
addÂÃ,  ÂÃ, eax,ecx

movÂÃ,  ÂÃ, esi,row1
movzx edx,byte[esi]
addÂÃ,  ÂÃ, eax,edx

xorÂÃ,  ÂÃ, ecx,ecx
shrÂÃ,  ÂÃ, eax,2
subÂÃ,  ÂÃ, eax,2
cmovc eax,ecx

mov esi,row2
mov byte[esi],al
add dword row1,1
add dword row2,1
add dword row3,1

add dword X,1
mov eax,screen_x
cmp X,eax
jc .for_x

xor eax,eax
mov esi,row1
mov edi,row2
mov byte[esi],0
mov byte[edi],0
mov esi,row3
mov byte[esi],0

add dword row1,1
add dword row2,1
add dword row3,1


add dword y,1
mov eax,screen_y
cmp y,eax
jc .for_y
#endasm
}

kryton9

I got a message error(s) in compiling.

I did try the cmovc with the change too and got the same error. I am running on a new computer.

Parker

What errors are you getting? You shouldn't have compiling errors, only runtime errors if your computer doesn't support the instruction.

kryton9

I figured out somehow I messed up my aurora files. I got an error on code that worked before, so I am sure that is the case. I am going to reinstall aurora and will report back.

kryton9

Ok that was the problem. THat is my Aurora got messed up. I reinstalled and it is all fine now.

The program however, does cause my system to crash when it runs. I tried both the program both ways. Has anyone been able to run the example?

sapero

tested on two machines without any problems and exceptions (celeron 4 and pentium M centrino)