/* firework.c - Fireworks particle module - Don Yang (uguu.org) v1.0 (9/10/98) 09/10/98: InitFireworks UninitFireworkss GetAllFireworkParticles TranslateFireworks MorphFireworks GenerateFirework 09/11/98: GenerateFirework: splitted function GenerateRectangularFirework GenerateCircularFirework GenerateCubicFirework GenerateSphericalFirework */ /********************************* Header **********************************/ /* Includes */ #include #include #include"particle.h" #include"firework.h" /* Globals */ static int _far *FireEX; static int _far *FireEY; static int _far *FireEZ; static int _far *FireSX; static int _far *FireSY; static int _far *FireSZ; static size_t NumberOfFireworks; static FIRE _far *fire; /* Local prototypes */ void GenerateCircularFirework(size_t index, int x, int y, int z); void GenerateCubicFirework(size_t index, int x, int y, int z); void GenerateFirework(size_t index); void GenerateRectangularFirework(size_t index, int x, int y, int z); void GenerateSphericalFirework(size_t index, int x, int y, int z); /************************ GenerateCircularFirework ************************* Generate circular firework. Firework is generated by distributing * particles evenly on two circles, then rotate each circle randomly. * * IN: index = firework index. * (x, y, z) = core coordinates. * (global) fire[] = firework status. * OUT: (global) (FireSX[], FireSY[], FireSZ[]) updated. */ void GenerateCircularFirework(size_t index, int x, int y, int z) { int lon, lat; int i, ix, iy, a; ix = fire[index].count / 2; iy = fire[index].count - ix; /* Generate outer circle */ for(i = 0; i < ix; i++) { a = (int)(360L * i / ix); FireSX[fire[index].start + i] = \ (int)(3 * SPACE_W * Cosine(a) / SINE_SCALE / 4); FireSY[fire[index].start + i] = \ (int)(3 * SPACE_H * Sine(a) / SINE_SCALE / 4); FireSZ[fire[index].start + i] = 0; } /* Generate inner circle */ for(i = 0; i < iy; i++) { a = (int)(360L * i / iy); FireSX[fire[index].start + i + ix] = \ (int)(SPACE_W * Cosine(a) / SINE_SCALE / 2); FireSY[fire[index].start + i + ix] = \ (int)(SPACE_H * Sine(a) / SINE_SCALE / 2); FireSZ[fire[index].start + i + ix] = 0; } /* Rotate outer circle */ lon = rand() % 360; lat = rand() % 360; Rotate(&FireSY[fire[index].start], &FireSZ[fire[index].start], &FireSY[fire[index].start], &FireSZ[fire[index].start], ix, lat); /* (particle.c) */ Rotate(&FireSX[fire[index].start], &FireSZ[fire[index].start], &FireSX[fire[index].start], &FireSZ[fire[index].start], ix, lon); /* Rotate inner circle */ lon = rand() % 360; lat = rand() % 360; ix += fire[index].start; Rotate(&FireSY[ix], &FireSZ[ix], &FireSY[ix], &FireSZ[ix], iy, lat); Rotate(&FireSX[ix], &FireSZ[ix], &FireSX[ix], &FireSZ[ix], iy, lon); /* Add offset */ for(i = fire[index].start; i < fire[index].end; i++) { FireSX[i] += x; FireSY[i] += y; FireSZ[i] += z; } } /* GenerateCircularFirework() */ /************************* GenerateCubicFirework *************************** Generate cubic firework. Firework is generated by repeatedly selecting * one of 12 edges of a cube, then randomly distribute particles along * that edge. * * IN: index = firework index. * (x, y, z) = core coordinates. * (global) fire[] = firework status. * OUT: (global) (FireSX[], FireSY[], FireSZ[]) updated. */ void GenerateCubicFirework(size_t index, int x, int y, int z) { int lon, lat; int i, e; /* Generate corners */ i = fire[index].start; FireSX[i] = -3 * SPACE_W / 4; FireSX[i + 4] = 3 * SPACE_W / 4; FireSX[i + 1] = -3 * SPACE_W / 4; FireSX[i + 5] = 3 * SPACE_W / 4; FireSX[i + 2] = -3 * SPACE_W / 4; FireSX[i + 6] = 3 * SPACE_W / 4; FireSX[i + 3] = -3 * SPACE_W / 4; FireSX[i + 7] = 3 * SPACE_W / 4; FireSY[i + 2] = -3 * SPACE_H / 4; FireSY[i] = 3 * SPACE_H / 4; FireSY[i + 3] = -3 * SPACE_H / 4; FireSY[i + 1] = 3 * SPACE_H / 4; FireSY[i + 6] = -3 * SPACE_H / 4; FireSY[i + 4] = 3 * SPACE_H / 4; FireSY[i + 7] = -3 * SPACE_H / 4; FireSY[i + 5] = 3 * SPACE_H / 4; FireSZ[i] = -3 * SPACE_L / 4; FireSZ[i + 1] = 3 * SPACE_L / 4; FireSZ[i + 3] = -3 * SPACE_L / 4; FireSZ[i + 2] = 3 * SPACE_L / 4; FireSZ[i + 4] = -3 * SPACE_L / 4; FireSZ[i + 5] = 3 * SPACE_L / 4; FireSZ[i + 7] = -3 * SPACE_L / 4; FireSZ[i + 6] = 3 * SPACE_L / 4; /* Generate edges */ i += 8; while( i < fire[index].end ) { e = rand() % 12; if( e < 8 ) /* left/right */ { if( e < 4 ) FireSX[i] = -3 * SPACE_W / 4; else FireSX[i] = 3 * SPACE_W / 4; if( e % 2 ) /* vertical */ { if( e == 1 || e == 5 ) FireSZ[i] = 3 * SPACE_L / 4; else FireSZ[i] = -3 * SPACE_L / 4; FireSY[i] = Generate(rand(), 3 * SPACE_H / 4); } else /* horizontal */ { if( e == 0 || e == 4 ) FireSY[i] = 3 * SPACE_H / 4; else FireSY[i] = -3 * SPACE_H / 4; FireSZ[i] = Generate(rand(), 3 * SPACE_L / 4); } } else /* middle */ { if( e == 8 || e == 9 ) FireSY[i] = 3 * SPACE_H / 4; else FireSY[i] = -3 * SPACE_H / 4; if( e % 2 ) /* back */ FireSZ[i] = 3 * SPACE_L / 4; else /* front */ FireSZ[i] = -3 * SPACE_L / 4; FireSX[i] = Generate(rand(), 3 * SPACE_W / 4); } i++; } /* Rotate cube */ lon = rand() % 360; lat = rand() % 360; Rotate(&FireSY[fire[index].start], &FireSZ[fire[index].start], &FireSY[fire[index].start], &FireSZ[fire[index].start], fire[index].count, lat); /* (particle.c) */ Rotate(&FireSX[fire[index].start], &FireSZ[fire[index].start], &FireSX[fire[index].start], &FireSZ[fire[index].start], fire[index].count, lon); /* Add offsets */ for(i = fire[index].start; i < fire[index].end; i++) { FireSX[i] += x; FireSY[i] += y; FireSZ[i] += z; } } /* GenerateCubicFirework() */ /**************************** GenerateFirework ***************************** Generate target coordinates for single firework. * * IN: index = firework index. * (global) fire[] = firework status. * OUT: (global) (FireEX[], FireEY[], FireEZ[]) updated. */ void GenerateFirework(size_t index) { int i, x, y, z; /* Generate core coordinates */ x = Generate(rand(), SPACE_W); /* (particle.h) */ z = -(rand() % (SPACE_L / 2)); y = SPACE_H - rand() % (SPACE_H / FIREWORK_H_RATIO + 1); for(i = fire[index].start; i < fire[index].end; i++) { FireEX[i] = Generate(rand(), SPACE_W / FIREWORK_C_RATIO) + x; FireEY[i] = Generate(rand(), SPACE_H / FIREWORK_C_RATIO) + y; FireEZ[i] = Generate(rand(), SPACE_L / FIREWORK_C_RATIO) + z; } /* Generate shape coordinates */ i = rand() % 7; if( i == 0 ) { GenerateRectangularFirework(index, x, y, z); } else if( i == 1 ) { GenerateCircularFirework(index, x, y, z); } else if( i == 2 ) { GenerateCubicFirework(index, x, y ,z); } else { GenerateSphericalFirework(index, x, y, z); } } /* GenerateFirework() */ /*********************** GenerateRectangularFirework *********************** Generate rectangular firework. Particles are distributed evenly on a * plane, then the plane is rotated at random angles. * * IN: index = firework index. * (x, y, z) = core coordinates. * (global) fire[] = firework status. * OUT: (global) (FireSX[], FireSY[], FireSZ[]) updated. */ void GenerateRectangularFirework(size_t index, int x, int y, int z) { int lon, lat, extra; int i, ix, iy; /* Get particle distribution */ Factor(fire[index].count, &lat, &lon, &extra); /* (particle.c) */ /* Generate plane */ i = fire[index].start; for(ix = 0; ix < lat; ix++) { for(iy = 0; iy < lon; iy++) { FireSX[i] = (3 * SPACE_W * ix / lon / 2) - (3 * SPACE_W / 4); FireSY[i] = (3 * SPACE_H * iy / lat / 2) - (3 * SPACE_H / 4); FireSZ[i] = z; i++; } } /* Fill in extras */ while( extra ) { ix = rand() % (i - fire[index].start) + fire[index].start; FireSX[i] = FireSX[ix]; FireSY[i] = FireSY[ix]; FireSZ[i] = FireSZ[ix]; i++; extra--; } /* Rotate plane */ lon = rand() % 360; lat = rand() % 360; Rotate(&FireSY[fire[index].start], &FireSZ[fire[index].start], &FireSY[fire[index].start], &FireSZ[fire[index].start], fire[index].count, lat); /* (particle.c) */ Rotate(&FireSX[fire[index].start], &FireSZ[fire[index].start], &FireSX[fire[index].start], &FireSZ[fire[index].start], fire[index].count, lon); /* Add offset */ for(i = fire[index].start; i < fire[index].end; i++) { FireSX[i] += x; FireSY[i] += y; FireSZ[i] += z; } } /* GenerateRectangularFirework() */ /************************ GenerateSphericalFirework ************************ Generate spherical firework (default). Particles are distributed * randomly in spherical coordinate system using random phi and theta, * while rol stays constant. * * IN: index = firework index. * (x, y, z) = core coordinates. * (global) fire[] = firework status. * OUT: (global) (FireSX[], FireSY[], FireSZ[]) updated. */ void GenerateSphericalFirework(size_t index, int x, int y, int z) { int lon, lat; int i, r; /* Generate shape coordinates */ for(i = fire[index].start; i < fire[index].end; i++) { lon = rand() % 360; lat = rand() % 360; r = (int)((3 * SPACE_L / 4) * Cosine(lat) / SINE_SCALE); FireSX[i] = (int)(r * Cosine(lon) / SINE_SCALE) + x; FireSY[i] = (int)(r * Sine(lon) / SINE_SCALE) + y; FireSZ[i] = (int)((3 * SPACE_L / 4) * Sine(lat) / SINE_SCALE) + z; } } /* GenerateSphericalFirework() */ /************************* GetAllFireworkParticles ************************* Copy all particle coordinates. * * IN: (global) fire[] = firework status. * (global) (FireEX[], FireEY[], FireEZ[]) = explode coordinates. * (global) (FireSX[], FireSY[], FireSZ[]) = shape coordinates. * (particle.c) NumberOfParticles = number of particles. * OUT: (particle.c) (ParticleX[], ParticleY[], ParticleZ[]) = target. */ void GetAllFireworkParticles(void) { size_t f; int i; for(f = 0; f < NumberOfFireworks; f++) { if( fire[f].state == FIREWORK_RISE ) { for(i = fire[f].start; i < fire[f].end; i++) { ParticleX[i] = \ Morph(fire[f].sx, FireEX[i], fire[f].frame, fire[f].target); ParticleY[i] = \ Morph(-SPACE_H, FireEY[i], fire[f].frame, fire[f].target); ParticleZ[i] = \ Morph(fire[f].sz, FireEZ[i], fire[f].frame, fire[f].target); } } else if( fire[f].state == FIREWORK_EXPLODE ) { for(i = fire[f].start; i < fire[f].end; i++) { ParticleX[i] = \ Morph(FireEX[i], FireSX[i], fire[f].frame, fire[f].target); ParticleY[i] = \ Morph(FireEY[i], FireSY[i], fire[f].frame, fire[f].target); ParticleZ[i] = \ Morph(FireEZ[i], FireSZ[i], fire[f].frame, fire[f].target); } } else /* fire[f].state == FIREWORK_DECAY */ { for(i = fire[f].start; i < fire[f].end; i++) { if( rand() % fire[f].target < fire[f].frame ) { ParticleX[i] = fire[f].sx; ParticleY[i] = -SPACE_H; ParticleZ[i] = fire[f].sz; } else { ParticleX[i] = \ Morph(FireEX[i], FireSX[i], fire[f].frame, fire[f].target); ParticleY[i] = \ Morph(FireEY[i], FireSY[i], fire[f].frame, fire[f].target); ParticleZ[i] = \ Morph(FireEZ[i], FireSZ[i], fire[f].frame, fire[f].target); } } } } } /* GetAllFireworkParticles() */ /****************************** InitFireworks ****************************** Initialize particles. * * IN: (particles.c) NumberOfParticles = number of particles. * OUT: 0 = success. * (global) NumberOfFireworks = number of fireworks. * (global) fire[] = firework status. * (global) (FireEX[], FireEY[], FireEZ[]) = explode coordinates. * (global) (FireSX[], FireSY[], FireSZ[]) = shape coordinates. * 1 = not enough memory. */ int InitFireworks(void) { size_t f; int c, p; NumberOfFireworks = (NumberOfParticles + FIREWORK_P_RATIO / 2) / FIREWORK_P_RATIO; if( NumberOfFireworks == 0 ) NumberOfFireworks++; /* Allocate memory */ FireEX = _fcalloc((size_t)NumberOfParticles, sizeof(int)); FireEY = _fcalloc((size_t)NumberOfParticles, sizeof(int)); FireEZ = _fcalloc((size_t)NumberOfParticles, sizeof(int)); FireSX = _fcalloc((size_t)NumberOfParticles, sizeof(int)); FireSY = _fcalloc((size_t)NumberOfParticles, sizeof(int)); FireSZ = _fcalloc((size_t)NumberOfParticles, sizeof(int)); fire = _fcalloc(NumberOfFireworks, sizeof(FIRE)); if( FireEX == NULL || FireEY == NULL || FireEZ == NULL || FireSX == NULL || FireSY == NULL || FireSZ == NULL || fire == NULL ) { _ffree(FireEX); _ffree(FireEY); _ffree(FireEZ); _ffree(FireSX); _ffree(FireSY); _ffree(FireSZ); _ffree(fire); return 1; } /* Generate initial states */ c = NumberOfParticles / NumberOfFireworks; p = FIREWORK_TIME_R; for(f = 0; f < NumberOfFireworks; f++) { fire[f].start = c * f; if( f == NumberOfFireworks - 1 ) fire[f].end = NumberOfParticles; else fire[f].end = fire[f].start + c; fire[f].count = fire[f].end - fire[f].start; fire[f].sx = Generate(rand(), SPACE_W); /* (particle.h) */ fire[f].sz = Generate(rand(), SPACE_L); fire[f].state = FIREWORK_RISE; fire[f].frame = 0; fire[f].target = rand() % FIREWORK_ERR + p; p = fire[f].target; GenerateFirework(f); } return 0; } /* InitFireworks() */ /***************************** MorphFireworks ****************************** Blend particle systems. * * IN: frame = current morphing animation frame. * target = end animation frame. * (global) (FireEX[], FireEY[], FireEZ[]) = explode coordinates. * (global) (FireSX[], FireSY[], FireSZ[]) = shape coordinates. * (particle.c) NumberOfParticles = number of particles. * (particle.c) (ParticleX[], ParticleY[], ParticleZ[]) = source. * OUT: (particle.c) (ParticleX[], ParticleY[], ParticleZ[]) updated. */ void MorphFireworks(int frame, int target) { size_t f; int i, x, y, z; for(f = 0; f < NumberOfFireworks; f++) { if( fire[f].state == FIREWORK_RISE ) { for(i = fire[f].start; i < fire[f].end; i++) { x = Morph(fire[f].sx, FireEX[i], fire[f].frame, fire[f].target); y = Morph(-SPACE_H, FireEY[i], fire[f].frame, fire[f].target); z = Morph(fire[f].sz, FireEZ[i], fire[f].frame, fire[f].target); ParticleX[i] = Morph(ParticleX[i], x, frame, target); ParticleY[i] = Morph(ParticleY[i], y, frame, target); ParticleZ[i] = Morph(ParticleZ[i], z, frame, target); } } else if( fire[f].state == FIREWORK_EXPLODE ) { for(i = fire[f].start; i < fire[f].end; i++) { x = Morph(FireEX[i], FireSX[i], fire[f].frame, fire[f].target); y = Morph(FireEY[i], FireSY[i], fire[f].frame, fire[f].target); z = Morph(FireEZ[i], FireSZ[i], fire[f].frame, fire[f].target); ParticleX[i] = Morph(ParticleX[i], x, frame, target); ParticleY[i] = Morph(ParticleY[i], y, frame, target); ParticleZ[i] = Morph(ParticleZ[i], z, frame, target); } } else /* fire[f].state == FIREWORK_DECAY */ { for(i = fire[f].start; i < fire[f].end; i++) { if( rand() % fire[f].target < fire[f].frame ) { x = fire[f].sx; y = -SPACE_H; z = fire[f].sz; } else { x = Morph(FireEX[i], FireSX[i], fire[f].frame, fire[f].target); y = Morph(FireEY[i], FireSY[i], fire[f].frame, fire[f].target); z = Morph(FireEZ[i], FireSZ[i], fire[f].frame, fire[f].target); } ParticleX[i] = Morph(ParticleX[i], x, frame, target); ParticleY[i] = Morph(ParticleY[i], y, frame, target); ParticleZ[i] = Morph(ParticleZ[i], z, frame, target); } } } } /* MorphFireworks() */ /*************************** TranslateFireworks **************************** Move firework particles. * * IN: (global) fire[] = firework status. * (particle.c) NumberOfParticles = number of particles. * OUT: (global) state[], frame[], target[] updated. */ void TranslateFireworks(void) { size_t f; int i; /* Update state for each firework */ for(f = 0; f < NumberOfFireworks; f++) { fire[f].frame++; if( fire[f].state == FIREWORK_RISE ) { if( fire[f].frame == fire[f].target ) { fire[f].state = FIREWORK_EXPLODE; fire[f].frame = 0; fire[f].target = rand() % FIREWORK_ERR + FIREWORK_TIME_E; fire[f].sx = Generate(rand(), SPACE_W); fire[f].sz = Generate(rand(), SPACE_H); } } else { if( fire[f].state == FIREWORK_DECAY ) { for(i = fire[f].start; i < fire[f].end; i++) FireSY[i]--; } if( fire[f].frame == 3 * fire[f].target / 4 ) fire[f].state = FIREWORK_DECAY; if( fire[f].frame == fire[f].target ) { fire[f].state = FIREWORK_RISE; fire[f].frame = 0; fire[f].target = rand() % FIREWORK_ERR + FIREWORK_TIME_R; GenerateFirework(f); } } } } /* TranslateFireworks() */ /***************************** UninitFireworks ***************************** Uninitialize (reverses InitFireworks). * * IN: (global) fire[] = firework status. * (global) (FireEX[], FireEY[], FireEZ[]) = explode coordinates. * (global) (FireSX[], FireSY[], FireSZ[]) = shape coordinates. * OUT: None. */ void UninitFireworks(void) { _ffree(FireEX); _ffree(FireEY); _ffree(FireEZ); _ffree(FireSX); _ffree(FireSY); _ffree(FireSZ); _ffree(fire); } /* UninitFireworks() */