/* particle.c - Particle system - Don Yang (uguu.org) v1.22 (9/19/98) Parameters: PARTICLE [8] [c] [g] [d] [q] [s] [w] [f] [b] number Number of particles (required parameter) 8 Use 8253 timer instead of VSync c Set delay dots color r = red g = green b = blue (default) c = cyan m = magenta y = yellow k = black (short trail) w = white g Enable delay dots gamma correction d Disable starfield q Disable squares s Disable sphere w Disable wave f Disable fireworks b Disable butterflies Use the "+" switch to solo a particle system. e.g. PARTICLE 1000 d+ runs only the starfield system with 1000 particles. For Microsoft C 6.0 only. 8253 vs VSync: VSync guarantees no tearing in graphics, and provides the smoothest animation if the particle system can be drawn at 70fps. If the time it takes to draw one frame is a little over 1/70 seconds, VSync will be missed and you get 35fps :( Often some routines are faster than others, so you get 70fps sometimes and 35fps all other times (very jerky stuff) 8253 does not rely on a periodical pulse like VSync, it simply guarantees that there will be a certain (constant?) minimal delay between frames. 8253 timed animation is always faster than VSync, but VSync looks nicer and is more reliable. You can set your own frame rate by adjusting the TICKS_PER_FRAME constant in particle.h, and enable 8253 timer when running this program. Lower value increases frame rate. Gamma: The effect of gamma correction is very subtle, since the main particle colors are not changed. Particle systems should appear brighter, but the difference is only noticeable in sphere particle system. This stuff is very processor intensive! Remember it's a lot of multiplies and divides per particle, plus lots of random number generation, memory/port access and stuff. Most calculations are done with signed 32bit integers. It would really help if MSC compiles 386 objects :P Really, this was a program more suited for C++ too. Memory costs (bytes per particle): Basic rendering: 6 Starfield: 6 Squares: 6 Sphere: 6 Wave: 4 Firework: 12 Butterfly: 6 The program itself takes 111K to execute. Firework system requires an additional 16 bytes for each firework (roughly 250 particles per firework). Butterfly requires an additional 34 bytes per butterfly (about 125 particles per butterfly), plus about 1K of scratch space. Program not really optimized but hey, I got my Pentium II ;) Morphing slows everything down while enable 8253 speeds things up (unpredictably). Because particles are not Z-sorted, high particle counts will make certain particle systems (except wave) will look obscured :P BTW: Those numbers that you see in parenthesis during start up, they are 8253 ticks of the initialization time for each particle system. It's not really informative or accurate, but just so that you know which particle system tooks the most cycles to start :) 08/29/98: main InitScreen 08/30/98: UninitScreen Blit SetPalette InitPalette ClearScreen ClearScreen2 DoParticles Render 08/31/98: MorphParticles Factor 09/01/98: DoSimpleParticles 09/03/98: ReadTimer Blit2 09/09/98: main: Fireworks DoParticles: fireworks DoSimpleParticles: fireworks MorphParticles: splitted function DoParticles: random start 09/10/98: Timed initialization 09/11/98: Rotate 09/12/98: ParseCommandLine 09/13/98: main: butterflies DoParticles: butterflies DoSimpleParticles: butterflies MorphParticles: butterfiles ParseCommandLine: butterflies 09/15/98: InitPalette: gamma correction ParseCommandLine: gamma correction main: gamma correction */ /********************************* Header **********************************/ /* Includes */ #include #include #include #include #include #include #include #include #include"particle.h" #include"butterfl.h" #include"firework.h" #include"sphere.h" #include"squares.h" #include"stars.h" #include"wave.h" /* Globals */ unsigned char _far buffer[64000]; unsigned char _far palette[768]; unsigned char _far *Screen; unsigned int _far RowOffset[200]; int _far SineTable[360]; unsigned long FrameCount; int _far *ParticleX; int _far *ParticleY; int _far *ParticleZ; int NumberOfParticles; int StarSystem; int SquareSystem; int SphereSystem; int WaveSystem; int FireworkSystem; int ButterflySystem; int Use8253, StartTime; int UseGamma; char DelayDotsColor = 'B'; /* Prototypes */ void Blit(void); void Blit2(void); void ClearScreen(void); void ClearScreen2(void); void DoParticles(void); void DoSimpleParticles(void); void InitPalette(void); void InitScreen(void); void MorphParticles(int OldSystem, int NewSystem, int frame, int target); void ParseCommandLine(int argc, char *argv[]); unsigned int ReadTimer(void); void Render(void); void SetPalette(unsigned char _far *Palette, int NumberOfColors); void UninitScreen(void); /********************************** main ***********************************/ void _cdecl main(int argc, char *argv[]) { clock_t t; int i; unsigned int s; /* Startup */ /* Initialize command line */ if( argc < 2 ) { printf(TITLE "\n\n" "Usage: Particle [8] [c] [g] [d] [q] [s] [w] [f] [b]\n\n" "number Number of particles (required).\n" "8 Use 8253 PIT instead of VSync.\n" "c Set delay dots color:\n" " r = red g = green b = blue (default)\n" " c = cyan m = magenta y = yellow\n" " k = black w = white\n" "g Enable delay dots gamma correction.\n\n" "d Disable starfield.\n" "q Disable squares.\n" "s Disable sphere.\n" "w Disable wave.\n" "f Disable fireworks.\n" "b Disable butterflies.\n" "+ Solo particle system (e.g. 'PARTICLE 100 d+')\n\n" "Maximum particles = %d\n" "Minimum particles = %d\n", MAX_PARTICLES, MIN_PARTICLES); exit(EXIT_SUCCESS); } printf(TITLE "\n\nInitializing!\n\n"); /* Initialize particles */ NumberOfParticles = atoi(argv[1]); if( NumberOfParticles < MIN_PARTICLES || NumberOfParticles > MAX_PARTICLES ) { printf("Invalid particle count: %d\n" "Maximum = %d\n" "Minimum = %d\n", NumberOfParticles, MAX_PARTICLES, MIN_PARTICLES); exit(EXIT_FAILURE); } else { printf("Number of particles = %d\n", NumberOfParticles); } ParticleX = _fcalloc((size_t)NumberOfParticles, sizeof(int)); ParticleY = _fcalloc((size_t)NumberOfParticles, sizeof(int)); ParticleZ = _fcalloc((size_t)NumberOfParticles, sizeof(int)); if( ParticleX == NULL || ParticleY == NULL || ParticleZ == NULL ) { printf("Not enough memory.\n"); _ffree(ParticleX); _ffree(ParticleY); _ffree(ParticleZ); exit(EXIT_FAILURE); } /* Seed random number */ srand((unsigned)time(NULL)); /* Initialize sine table */ for(i = 0; i < 360; i++) SineTable[i] = (int)(SINE_SCALE * sin(i * PI / 180)); ParseCommandLine(argc, argv); if( Use8253 ) printf("8253 enabled.\n"); else printf("8253 disabled.\n"); if( UseGamma ) printf("Gamma correction enabled.\n"); else printf("Gamma correction disabled.\n"); if( StarSystem ) { s = ReadTimer(); if( InitStars() ) /* (stars.c) */ { s -= ReadTimer(); StarSystem = 0; printf("Starfield not available.\n"); } else { s -= ReadTimer(); printf("Starfield initialized (%u).\n", s); } } else { printf("Starfield disabled.\n"); } if( SquareSystem ) { s = ReadTimer(); if( InitSquares() ) /* (squares.c) */ { s -= ReadTimer(); SquareSystem = 0; printf("Squares not available.\n"); } else { s -= ReadTimer(); printf("Squares initialized (%u).\n", s); } } else { printf("Squares disabled.\n"); } if( SphereSystem ) { s = ReadTimer(); if( InitSphere() ) /* (sphere.c) */ { s -= ReadTimer(); SphereSystem = 0; printf("Sphere not available.\n"); } else { s -= ReadTimer(); printf("Sphere initialized (%u).\n", s); } } else { printf("Sphere disabled.\n"); } if( WaveSystem ) { s = ReadTimer(); if( InitWave() ) /* (wave.c) */ { s -= ReadTimer(); WaveSystem = 0; printf("Wave not available.\n"); } else { s -= ReadTimer(); printf("Wave initialized (%u).\n", s); } } else { printf("Wave disabled.\n"); } if( FireworkSystem ) { s = ReadTimer(); if( InitFireworks() ) /* (firework.c) */ { s -= ReadTimer(); FireworkSystem = 0; printf("Fireworks not available.\n"); } else { s -= ReadTimer(); printf("Fireworks initialized (%u).\n", s); } } else { printf("Fireworks disabled.\n"); } if( ButterflySystem ) { s = ReadTimer(); if( InitButterflies() ) /* (butterfl.c) */ { s -= ReadTimer(); ButterflySystem = 0; printf("Butterflies not available.\n"); } else { s -= ReadTimer(); printf("Butterflies initialized (%u).\n", s); } } else { printf("Butterflies disabled.\n"); } i = StarSystem + SquareSystem + SphereSystem + WaveSystem + FireworkSystem + ButterflySystem; if( i == 0 ) { printf("No particle systems available.\n"); _ffree(ParticleX); _ffree(ParticleY); _ffree(ParticleZ); exit(EXIT_FAILURE); } if( i > 1 ) printf("\n%d particle systems", i); else printf("\n1 particle system"); printf("\n\nPress any key to start", i); i = getch(); if( i == 0 ) i = getch(); /* Initialize display */ InitScreen(); /* Draw particles */ FrameCount = 0; t = clock(); DoParticles(); t = clock() - t; /* Cleanup */ UninitScreen(); _ffree(ParticleX); _ffree(ParticleY); _ffree(ParticleZ); if( StarSystem ) UninitStars(); /* (stars.c) */ if( SquareSystem ) UninitSquares(); /* (squares.c) */ if( SphereSystem ) UninitSphere(); /* (sphere.c) */ if( WaveSystem ) UninitWave(); /* (wave.c) */ if( FireworkSystem ) UninitFireworks(); /* (firework.c) */ if( ButterflySystem ) UninitButterflies(); /* (butterfl.c) */ printf("%luf / %lums = %2.2ffps\n", FrameCount, t, FrameCount / ((float)t / CLOCKS_PER_SEC)); exit(EXIT_SUCCESS); } /* main() */ /********************************** Blit *********************************** Dump screen buffer to video memory, synchronized using VSync. * * IN: (global) Screen = pointer to screen buffer. * OUT: (global) FrameCount updated. */ void Blit(void) { _asm { cld push ds push si push di ; Point DS:SI at screen buffer mov ax, word ptr [Screen+2] mov si, word ptr [Screen] mov ds, ax ; Point ES:DI at video memory mov ax, 0a000h xor di, di mov es, ax ; Wait for vertical synchronization mov dx, 3dah WaitVSync0: in al, dx test al, 8 jnz WaitVSync0 WaitVSync1: in al, dx test al, 8 jz WaitVSync1 ; copy buffers mov cx, 32000 rep movsw pop di pop si pop ds } FrameCount++; } /* Blit() */ /********************************* Blit2 *********************************** Dump screen buffer to video memory, synchronized using 8253 PIT. * * IN: (global) Screen = pointer to screen buffer. * (global) StartTime = starting frame time. * OUT: (global) FrameCount updated. * (global) StartTime updated. */ void Blit2(void) { unsigned int t; /* Wait for 8253 time */ t = 0; do { _asm { ; Read finish time cli ; Latch 8253 timer mov dx, 43h xor al, al out dx, al jmp $+2 ; Read 8253 timer mov dx, 40h in al, dx mov ah, al jmp $+2 in al, dx xchg ah, al mov dx, StartTime add t, dx sub t, ax ; Read next start time ; Latch 8253 timer mov dx, 43h xor al, al out dx, al jmp $+2 ; Read 8253 timer mov dx, 40h in al, dx mov ah, al jmp $+2 in al, dx xchg ah, al sti mov StartTime, ax } } while( t < TICKS_PER_FRAME ); _asm { cld push ds push si push di ; Point DS:SI at screen buffer mov ax, word ptr [Screen+2] mov si, word ptr [Screen] mov ds, ax ; Point ES:DI at video memory mov ax, 0a000h xor di, di mov es, ax ; copy buffers mov cx, 32000 rep movsw pop di pop si pop ds } FrameCount++; } /* Blit2() */ /******************************* ClearScreen ******************************* Clear off-screen buffer. * * IN: (global) Screen points at screen buffer. * OUT: None. */ void ClearScreen(void) { _asm { cld push di ; Point ES:DI at screen buffer mov ax, word ptr [Screen+2] mov di, word ptr [Screen] mov es, ax ; Write to screen xor ax, ax mov cx, 32000 rep stosw pop di } } /* ClearScreen() */ /****************************** ClearScreen2 ******************************* Clear screen buffer, delay dot style. * * IN: (global) Screen points at screen buffer. * OUT: None. */ void ClearScreen2(void) { _asm { cld push ds push si push di ; Point ES:DI and DS:SI at screen buffer mov ax, word ptr [Screen+2] mov di, word ptr [Screen] mov es, ax mov ds, ax mov si, di mov cx, 64000 DelayDotsLoop: lodsb ; AL = DS:SI, SI++ test al, 0fh jz BlankDot dec al stosb ; ES:DI = AL, DI++ loop DelayDotsLoop jmp EndDelayDots BlankDot: xor al, al stosb ; ES:DI = AL, DI++ loop DelayDotsLoop EndDelayDots: pop di pop si pop ds } } /* ClearScreen2() */ /******************************* DoParticles ******************************* Draw particles until key pressed. * * IN: (global) StarSystem = starfield particle system status. * (global) SquareSystem = square particle system status. * (global) SphereSystem = sphere particle system status. * (global) WaveSystem = wave particle system status. * (global) FireworkSystem = firework particle system status. * (global) ButterflySystem = butterfly particle system status. * (global) Use8253, StartTime = timer status. * OUT: None. */ void DoParticles(void) { int i, ParticleSystem, NewSystem, ParticleTime, TargetTime; /* Check for single particle system */ if( StarSystem + SquareSystem + SphereSystem + WaveSystem + FireworkSystem + ButterflySystem == 1 ) { DoSimpleParticles(); return; } /* Multiple particle systems */ do { NewSystem = rand() % SYSTEM_COUNT + 1; if( NewSystem == M_STAR && StarSystem == 0 ) NewSystem = 0; if( NewSystem == M_SQUARE && SquareSystem == 0 ) NewSystem = 0; if( NewSystem == M_SPHERE && SphereSystem == 0 ) NewSystem = 0; if( NewSystem == M_WAVE && WaveSystem == 0 ) NewSystem = 0; if( NewSystem == M_FIREWORK && FireworkSystem == 0 ) NewSystem = 0; if( NewSystem == M_BUTTERFLY && ButterflySystem == 0 ) NewSystem = 0; } while( NewSystem == 0 ); ParticleSystem = NewSystem; ParticleTime = 0; TargetTime = PHASE_TIME; NewSystem = 0; /* Prepare timer */ if( Use8253 ) StartTime = ReadTimer(); /* Loop until key pressed */ while( !kbhit() ) { /* Clear screen */ #ifdef USE_DELAY_DOTS ClearScreen2(); #else ClearScreen(); #endif /* Get particle coordinates */ if( NewSystem ) { MorphParticles(ParticleSystem, NewSystem, ParticleTime, TargetTime); } else { if( ParticleSystem == M_STAR ) { GetAllStarParticles(); /* (stars.c) */ TranslateStars(); /* (stars.c) */ } if( ParticleSystem == M_SQUARE ) { GetAllSquareParticles(); /* (squares.c) */ TranslateSquares(); /* (squares.c) */ } if( ParticleSystem == M_SPHERE ) { GetAllSphereParticles(); /* (sphere.c) */ TranslateSphere(); /* (sphere.c) */ } if( ParticleSystem == M_WAVE ) { GetAllWaveParticles(); /* (wave.c) */ TranslateWave(); /* (wave.c) */ } if( ParticleSystem == M_FIREWORK ) { GetAllFireworkParticles(); /* (firework.c) */ TranslateFireworks(); /* (firework.c) */ } if( ParticleSystem == M_BUTTERFLY ) { GetAllButterflyParticles(); /* (butterfl.c) */ TranslateButterflies(); /* (butterfl.c) */ } } /* Draw particles */ Render(); /* Redraw screen */ if( Use8253 ) Blit2(); else Blit(); /* Change phase */ ParticleTime++; if( NewSystem ) { if( ParticleTime == TargetTime ) { ParticleSystem = NewSystem; ParticleTime = 0; NewSystem = 0; TargetTime = rand() % TIME_ERR + PHASE_TIME; } } else { if( ParticleTime == TargetTime ) { ParticleTime = 0; do { NewSystem = rand() % SYSTEM_COUNT + 1; if( (NewSystem == M_STAR && StarSystem == 0) || (NewSystem == M_SQUARE && SquareSystem == 0) || (NewSystem == M_SPHERE && SphereSystem == 0) || (NewSystem == M_WAVE && WaveSystem == 0) || (NewSystem == M_FIREWORK && FireworkSystem == 0) || (NewSystem == M_BUTTERFLY && ButterflySystem == 0) ) NewSystem = ParticleSystem; } while( NewSystem == ParticleSystem ); TargetTime = rand() % TIME_ERR + MORPH_TIME; } } } /* Cancel keystroke */ i = getch(); if( i == 0 ) i = getch(); } /* DoParticles() */ /*************************** DoSimpleParticles ***************************** Draw single particle system until key pressed. * * IN: (global) StarSystem = starfield particle system status. * (global) SquareSystem = square particle system status. * (global) SphereSystem = sphere particle system status. * (global) WaveSystem = wave particle system status. * (global) FireworkSystem = firework particle system status. * (global) ButterflySystem = butterfly particle system status. * (global) Use8253, StartTime = timer status. * OUT: None. */ void DoSimpleParticles(void) { int k; /* Prepare 8253 timer */ if( Use8253 ) StartTime = ReadTimer(); if( StarSystem ) { while( !kbhit() ) { /* Clear screen */ #ifdef USE_DELAY_DOTS ClearScreen2(); #else ClearScreen(); #endif /* Get particles */ GetAllStarParticles(); /* (stars.c) */ TranslateStars(); /* (stars.c) */ /* Draw particles */ Render(); if( Use8253 ) Blit2(); else Blit(); } } if( SquareSystem ) { while( !kbhit() ) { /* Clear screen */ #ifdef USE_DELAY_DOTS ClearScreen2(); #else ClearScreen(); #endif /* Get particles */ GetAllSquareParticles(); /* (squares.c) */ TranslateSquares(); /* (squares.c) */ /* Draw particles */ Render(); if( Use8253 ) Blit2(); else Blit(); } } if( SphereSystem ) { while( !kbhit() ) { /* Clear screen */ #ifdef USE_DELAY_DOTS ClearScreen2(); #else ClearScreen(); #endif /* Get particles */ GetAllSphereParticles(); /* (sphere.c) */ TranslateSphere(); /* (sphere.c) */ /* Draw particles */ Render(); if( Use8253 ) Blit2(); else Blit(); } } if( WaveSystem ) { while( !kbhit() ) { /* Clear screen */ #ifdef USE_DELAY_DOTS ClearScreen2(); #else ClearScreen(); #endif /* Get particles */ GetAllWaveParticles(); /* (wave.c) */ TranslateWave(); /* (wave.c) */ /* Draw particles */ Render(); if( Use8253 ) Blit2(); else Blit(); } } if( FireworkSystem ) { while( !kbhit() ) { /* Clear screen */ #ifdef USE_DELAY_DOTS ClearScreen2(); #else ClearScreen(); #endif /* Get particles */ GetAllFireworkParticles(); /* (firework.c) */ TranslateFireworks(); /* (firework.c) */ /* Draw particles */ Render(); if( Use8253 ) Blit2(); else Blit(); } } if( ButterflySystem ) { while( !kbhit() ) { /* Clear screen */ #ifdef USE_DELAY_DOTS ClearScreen2(); #else ClearScreen(); #endif /* Get particles */ GetAllButterflyParticles(); /* (butterfl.c) */ TranslateButterflies(); /* (butterfl.c) */ /* Draw particles */ Render(); if( Use8253 ) Blit2(); else Blit(); } } /* Clear keystroke */ k = getch(); if( k == 0 ) k = getch(); } /* DoSimpleParticles() */ /********************************* Factor ********************************** Calculate rectangular particle distribution. * * IN: n = number to factor. * row, column, extra = pointer to return values. * OUT: row = number of horizontal rings. * column = number of particles per ring. * extra = leftover particles. */ void Factor(int n, int *row, int *column, int *extra) { double s; int e, i, x, y, c, m; s = sqrt((double)n); e = (int)(s * SPHERE_ERROR + 1); m = n; for(i = 0; i < e; i++) { if( i % 2 ) y = (int)s - (i + 1) / 2; else y = (int)s + i / 2; x = n / y; c = n - (x * y); if( c < m ) { m = c; *row = y; *column = x; *extra = c; } } } /* Factor() */ /******************************* InitPalette ******************************* Initialize color palette. * * IN: (global) DelayDotsColor, UseGamma = color options. * (global) palette[] = palette buffer. * OUT: None. */ void InitPalette(void) { static unsigned char Gamma[64] = { 0, 10, 14, 17, 19, 21, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63}; int z, i, x; /* Upper nibble = Z gradient (brighter for closer Z) */ if( DelayDotsColor == 'K' ) { for(z = 0; z < 16; z++) { x = 63 * (15 - z) / 15; for(i = 0; i < 4; i++) { palette[(z * 16 + i) * 3] = 0; palette[(z * 16 + i) * 3 + 1] = 0; palette[(z * 16 + i) * 3 + 2] = 0; palette[(z * 16 + i + 4) * 3] = 0; palette[(z * 16 + i + 4) * 3 + 1] = 0; palette[(z * 16 + i + 4) * 3 + 2] = 0; palette[(z * 16 + i + 8) * 3] = 0; palette[(z * 16 + i + 8) * 3 + 1] = 0; palette[(z * 16 + i + 8) * 3 + 2] = 0; palette[(z * 16 + i + 12) * 3] = (unsigned char)(x * i / 3); palette[(z * 16 + i + 12) * 3 + 1] = (unsigned char)(x * i / 3); palette[(z * 16 + i + 12) * 3 + 2] = (unsigned char)(x * i / 3); } } } else if( DelayDotsColor == 'W' ) { for(z = 0; z < 16; z++) { x = 63 * (15 - z) / 15; for(i = 0; i < 16; i++) { palette[(z * 16 + i) * 3] = (unsigned char)(x * i / 15); palette[(z * 16 + i) * 3 + 1] = (unsigned char)(x * i / 15); palette[(z * 16 + i) * 3 + 2] = (unsigned char)(x * i / 15); } } } else { for(z = 0; z < 16; z++) { x = 63 * (15 - z) / 15; /* Lower nibble = intensity gradient (for delay dots) */ for(i = 0; i < 8; i++) { if( DelayDotsColor == 'R' || DelayDotsColor == 'M' || DelayDotsColor == 'Y' ) { palette[(z * 16 + i) * 3] = (unsigned char)(x * i / 8); palette[(z * 16 + i + 8) * 3] = (unsigned char)x; } else { palette[(z * 16 + i) * 3] = 0; palette[(z * 16 + i + 8) * 3] = (unsigned char)(x * i / 7); } if( DelayDotsColor == 'G' || DelayDotsColor == 'C' || DelayDotsColor == 'Y' ) { palette[(z * 16 + i) * 3 + 1] = (unsigned char)(x * i / 8); palette[(z * 16 + i + 8) * 3 + 1] = (unsigned char)x; } else { palette[(z * 16 + i) * 3 + 1] = 0; palette[(z * 16 + i + 8) * 3 + 1] = (unsigned char)(x * i / 7); } if( DelayDotsColor == 'B' || DelayDotsColor == 'C' || DelayDotsColor == 'M' ) { palette[(z * 16 + i) * 3 + 2] = (unsigned char)(x * i / 8); palette[(z * 16 + i + 8) * 3 + 2] = (unsigned char)x; } else { palette[(z * 16 + i) * 3 + 2] = 0; palette[(z * 16 + i + 8) * 3 + 2] = (unsigned char)(x * i / 7); } } } } if( UseGamma ) { for(i = 0; i < 768; i += 48) { for(x = 0; x < 45; x++) palette[i + x] = Gamma[palette[i + x]]; } } SetPalette(palette, 256); } /* InitPalette() */ /******************************* InitScreen ******************************** Initialize graphics mode. * * IN: (global) buffer[] = screen buffer. * OUT: (global) Screen points at buffer[]. * (global) RowOffset[] = row offsets. */ void InitScreen(void) { int i; /* Make pointer to off-screen buffer */ Screen = buffer; /* Calculate row offsets */ for(i = 0; i < 200; i++) RowOffset[i] = (199 - i) * 320; /* Initialize hardware */ _asm { ; Set mode 13 (320x200x256) mov ax, 13h int 10h ; Set active page 0 mov ax, 500h int 10h ; Hide cursor mov ah, 1 mov cx, 2000h int 10h } InitPalette(); ClearScreen(); } /* InitScreen() */ /***************************** MorphParticles ****************************** Blend particle systems. * * IN: OldSystem = initial particle system. * NewSystem = target particle system. * frame = current stage. * target = target stage. * (global) NumberOfParticles = number of particles. * OUT: (global) (ParticleX[], ParticleY[], ParticleZ[]) updated. */ void MorphParticles(int OldSystem, int NewSystem, int frame, int target) { /* Get/move source particles */ if( OldSystem == M_STAR ) { GetAllStarParticles(); /* (stars.c) */ TranslateStars(); /* (stars.c) */ } else if( OldSystem == M_SQUARE ) { GetAllSquareParticles(); /* (squares.c) */ TranslateSquares(); /* (squares.c) */ } else if( OldSystem == M_SPHERE ) { GetAllSphereParticles(); /* (sphere.c) */ TranslateSphere(); /* (sphere.c) */ } else if( OldSystem == M_WAVE ) { GetAllWaveParticles(); /* (wave.c) */ TranslateWave(); /* (wave.c) */ } else if( OldSystem == M_FIREWORK ) { GetAllFireworkParticles(); /* (firework.c) */ TranslateFireworks(); /* (firework.c) */ } else /* OldSystem == M_BUTTERFLY */ { GetAllButterflyParticles(); /* (butterfl.c) */ TranslateButterflies(); /* (butterfl.c) */ } /* Morph/move target particles */ if( NewSystem == M_STAR ) { MorphStars(frame, target); /* (stars.c) */ TranslateStars(); } else if( NewSystem == M_SQUARE ) { MorphSquares(frame, target); /* (squares.c) */ TranslateSquares(); } else if( NewSystem == M_SPHERE ) { MorphSphere(frame, target); /* (sphere.c) */ TranslateSphere(); } else if( NewSystem == M_WAVE ) { MorphWave(frame, target); /* (wave.c) */ TranslateWave(); } else if( NewSystem == M_FIREWORK ) { MorphFireworks(frame, target); /* (firework.c) */ TranslateFireworks(); } else /* NewSystem == M_BUTTERFLY */ { MorphButterflies(frame, target); /* (butterfl.c) */ TranslateButterflies(); } } /* MorphParticles() */ /**************************** ParseCommandLine ***************************** Process command line. * * IN: argc = argument count. * argv[] = argument strings. * OUT: (global) Use8253 is updated. * (global) StarSystem is updated. * (global) SquareSystem is updated. * (global) SphereSystem is updated. * (global) WaveSystem is updated. * (global) FireworkSystem is updated. * (global) ButterflySystem is updated. * (global) DelayDotsColor is updated. * (global) UseGamma is updated. */ void ParseCommandLine(int argc, char *argv[]) { int i; StarSystem = 1; SquareSystem = 1; SphereSystem = 1; WaveSystem = 1; FireworkSystem = 1; ButterflySystem = 1; Use8253 = 0; UseGamma = 0; for(i = 2; i < argc; i++) { switch( toupper(argv[i][0]) ) { case '8': Use8253 = 1; break; case 'C': switch( toupper(argv[i][1]) ) { case 'R': case 'G': case 'B': case 'C': case 'M': case 'Y': case 'K': case 'W': DelayDotsColor = (char)toupper( argv[i][1] ); break; default: printf("Unrecognized color option: %s\n", argv[i]); DelayDotsColor = 'B'; } break; case 'G': UseGamma = 1; break; case 'D': if( argv[i][1] == '+' ) { StarSystem = 1; SquareSystem = 0; SphereSystem = 0; WaveSystem = 0; FireworkSystem = 0; ButterflySystem = 0; } else { StarSystem = 0; } break; case 'Q': if( argv[i][1] == '+' ) { StarSystem = 0; SquareSystem = 1; SphereSystem = 0; WaveSystem = 0; FireworkSystem = 0; ButterflySystem = 0; } else { SquareSystem = 0; } break; case 'S': if( argv[i][1] == '+' ) { StarSystem = 0; SquareSystem = 0; SphereSystem = 1; WaveSystem = 0; FireworkSystem = 0; ButterflySystem = 0; } else { SphereSystem = 0; } break; case 'W': if( argv[i][1] == '+' ) { StarSystem = 0; SquareSystem = 0; SphereSystem = 0; WaveSystem = 1; FireworkSystem = 0; ButterflySystem = 0; } else { WaveSystem = 0; } break; case 'F': if( argv[i][1] == '+' ) { StarSystem = 0; SquareSystem = 0; SphereSystem = 0; WaveSystem = 0; FireworkSystem = 1; ButterflySystem = 0; } else { FireworkSystem = 0; } break; case 'B': if( argv[i][1] == '+' ) { StarSystem = 0; SquareSystem = 0; SphereSystem = 0; WaveSystem = 0; FireworkSystem = 0; ButterflySystem = 1; } else { ButterflySystem = 0; } break; default: printf("Unrecognized option: %s.\n", argv[i]); } } } /* ParseCommandLine() */ /******************************* ReadTimer ********************************* Latch 8253 PIT and read timer value. * * IN: None. * OUT: Returns timer value (~2386 ticks per ms). */ unsigned int ReadTimer(void) { unsigned int r; _asm { cli ; Latch timer mov dx, 43h xor al, al out dx, al jmp $+2 ; Read timer mov dx, 40h in al, dx mov ah, al jmp $+2 in al, dx xchg ah, al sti mov r, ax } return r; } /* ReadTimer() */ /********************************* Render ********************************** Draw particles. * * IN: (global) (ParticleX[], ParticleY[], ParticleZ[]) = particles. * (global) NumberOfParticles = number of particles. * (global) buffer[] = screen buffer. * (global) RowOffset = row offset. * OUT: None. */ void Render(void) { int i; long sx, sy; unsigned char c; unsigned int offset; for(i = 0; i < NumberOfParticles; i++) { /* Project particles */ if( ParticleZ[i] < -SPACE_L || ParticleZ[i] > SPACE_L ) continue; sx = ProjectX(ParticleX[i], ParticleZ[i]); if( sx < 0 || sx >= 320 ) continue; sy = ProjectY(ParticleY[i], ParticleZ[i]); if( sy < 0 || sy >= 200 ) continue; /* Set color */ c = (unsigned char)GetColor(ParticleZ[i]); /* Draw pixel */ offset = (unsigned int)(RowOffset[sy] + sx); buffer[offset] = c; } } /* Render() */ /********************************* Rotate ********************************** Rotate coordinates. * * IN: (sx, sy) = pointer to source coordinates. * (tx, ty) = pointer to target coordinates. * count = number of coordinates. * angle = rotation angle. * OUT: (tx, ty) overwritten. */ void Rotate(int _far *sx, int _far *sy, int _far *tx, int _far *ty, int count, int angle) { int x, i; long c, s; c = Cosine(angle); s = Sine(angle); if( sx == tx ) { /* Writing to same array */ for(i = 0; i < count; i++) { x = (int)(c * sx[i] / SINE_SCALE - s * sy[i] / SINE_SCALE); ty[i] = (int)(c * sy[i] / SINE_SCALE + s * sx[i] / SINE_SCALE); tx[i] = x; } } else { /* Writing to different arrays */ for(i = 0; i < count; i++) { tx[i] = (int)(c * sx[i] / SINE_SCALE - s * sy[i] / SINE_SCALE); ty[i] = (int)(c * sy[i] / SINE_SCALE + s * sx[i] / SINE_SCALE); } } } /* Rotate() */ /******************************* SetPalette ******************************** Set VGA palette. * * IN: Palette = pointer to palette. * NumberOfColors = number of colors to set. * OUT: None. */ void SetPalette(unsigned char _far *Palette, int NumberOfColors) { _asm { ; Point ES:DX at palette mov ax, word ptr [Palette+2] mov dx, word ptr [Palette] mov es, ax ; Set palette mov ax, 1012h xor bx, bx mov cx, NumberOfColors int 10h } } /* SetPalette() */ /****************************** UninitScreen ******************************* Restore text mode. * * IN: None. * OUT: None. */ void UninitScreen(void) { _asm { ; Set text mode mov ax, 3 int 10h ; Set active page 0 mov ax, 500h int 10h ; Set default cursor shape mov ah, 1 mov cx, 607h int 10h } } /* UninitScreen() */