Files
ZSNES/zsnes/src/burn.c
2003-03-16 23:40:07 +00:00

303 lines
6.5 KiB
C

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#ifndef M_PI
#ifndef PI
#define M_PI 3.1415926535897932384626433832795
#else
#define M_PI PI
#endif
#endif
/*
Hi guys, try this, use it in your code, but please credit
Frank Jan Sorensen Alias:Frank Patxi (fjs@lab.jt.dk) for the
fireroutine.
*/
/*
Hi again, guys!
If you use this code, please also credit me, Joachim Fenkes, 'cause I added
the following speedups:
-Replaced one tiny loop with a faster Move(...) (not much speedup)
-Wrote the main display loop in 100% assembler, including a faster random
number generator (the RNG is only a more or less optimized version of
Borland's generator (see MEGARAND.ASM), but with the advantage of the
ultimate crash if you call it normally :-)
-Changed version number into 1.10 (this isn't a speedup, but necessary :-)
*/
/*
Bcoz of the knowledge that reading from videocards is much slower than
writing to them, I changed some things to write and read from/to a pointer
and put the result with 32-Bit moves to the screen
Also I added now a much more faster randommer.
The result of this change is more than 3 times fast than before
Stefan Goehler
Please credit me!
...
to JF: your bug is fixed!
*/
/*
Oops, silly me, I removed all of the assembly code, so I can let the compiler
have at it. Also makes it more portable... even though I am doing this to add
to a project that is very non-portable.
*/
#define BUF_WIDTH 288
#define BUF_HEIGHT 224
const int rootrand = 20; /* Max/Min decrease of the root of the flames */
const int decay = 5; /* How far should the flames go up on the screen? */
/* This MUST be positive - JF */
const int miny = 0; /* Startingline of the flame routine.
(should be adjusted along with MinY above) */
const int smooth = 1; /* How descrete can the flames be?*/
const int minfire = 50; /* limit between the "starting to burn" and
the "is burning" routines */
const int xstart = 0; /* Startingpos on the screen, should be divideable by 4 without remain!*/
const int xend = 287; /* Guess! */
const int width = 1 + 287; /* +xend-xstart; Well- */
const int maxcolor = 110; /* Constant for the MakePal procedure */
const int fireincrease = 3; /*3 = Wood, 90 = Gazolin*/
typedef struct colorvalue
{
unsigned char r, g, b;
} colorvalue;
typedef colorvalue vgapalettetype[256];
void hsi2rgb(double h, double s, double i, struct colorvalue *c)
/*Convert (Hue, Saturation, Intensity) -> (RGB)*/
{
double t;
double rv, gv, bv;
t = h;
rv = 1 + s * sin(t - 2 * M_PI / 3);
gv = 1 + s * sin(t);
bv = 1 + s * sin(t + 2 * M_PI / 3);
t = 255.999 * i / 2;
{
c->r = floor(rv * t);
c->g = floor(gv * t);
c->b = floor(bv * t);
}
}
void genpal()
{
int i;
vgapalettetype pal;
memset(pal, 0, 3);
for( i=1; i <= maxcolor; i ++)
hsi2rgb(4.6-1.5*i/maxcolor,(double)(i)/maxcolor,(double)(i)/maxcolor,&pal[i]);
for( i=maxcolor; i <= 255; i ++)
{
pal[i]=pal[i-1];
{
struct colorvalue *with = &pal[i];
if (with->r<255) with->r += 1;
if (with->r<255) with->r += 1;
if ((~i & 1) && (with->g<215)) with->g += 1;
if ((~i & 1) && (with->b<255)) with->b += 1;
}
}
}
int started = 0;
#if 0 // emulating Turbo Pascal
unsigned int randseed;
const unsigned modulus = 2147483647;
const unsigned factor = 397204094;
void Randomize()
{
randseed = time(NULL);
}
unsigned int randint(unsigned range)
{
randseed = randseed * factor % modulus;
return range ? randseed % range : 0;
}
double randreal()
{
randseed = randseed * factor % modulus;
return (double)randseed / modulus;
}
int rand1(int r) /* Return a random number between -R And R*/
{
int result;
result=randint(r*2+1)-r;
return result;
}
#else
#define Randomize()
#define randint(a) (rand() % (a))
#define randreal() (((double)rand()) / ((double)RAND_MAX))
#define rand1(a) ((randint(a*2+1))-a)
#endif
unsigned char flamearray[BUF_WIDTH];
int morefire;
extern unsigned char *vidbuffer;
/* damn, this seems like such a waste */
static unsigned char pt[BUF_WIDTH * BUF_HEIGHT];
#if 0
int burn_init()
{
int i;
if (flamearray) return 1;
flamearray = (unsigned char *) malloc(BUF_WIDTH);
for( i=xstart; i <= xend; i++)
flamearray[i]=0;
Randomize();
morefire=1;
genpal();
return 0;
}
void burn_shutdown()
{
if (flamearray)
{
free(flamearray);
flamearray = NULL;
}
}
#endif
void DrawBurn()
{
int i,j;
int x,p;
int v;
if (!started)
{
started = 1;
for( i=xstart; i <= xend; i++)
flamearray[i]=0;
Randomize();
morefire=1;
memset(pt, 0, BUF_HEIGHT * BUF_WIDTH);
/* genpal(); */
}
/*
for (x=0; x < BUF_WIDTH*BUF_HEIGHT; x++)
{
i = pt[x];
j = vidbuffer[x] << 2;
if (i > j) pt[x] = i;
else pt[x] = (i + j) >> 1;
}
*/
/* Put the values from FlameArray on the bottom line of the screen */
memcpy(pt+((BUF_HEIGHT-1)*BUF_WIDTH)+xstart,flamearray, width);
/* This loop makes the actual flames */
for( i=xstart; i <= xend; i++)
for( j=miny; j <= (BUF_HEIGHT-1); j ++)
{
v = pt[j*BUF_WIDTH + i];
if ((v==0) ||
(v<decay) ||
(i<=xstart) ||
(i>=xend))
pt[(j-1)*BUF_WIDTH + i] = 0;
else
pt[((j-1)*BUF_WIDTH) + (i-(randint(3)-1))] = v - randint(decay);
}
/*Match?*/
if (randint(150)==0)
memset(flamearray + xstart + randint(xend-xstart-5),255,5);
/*This loop controls the "root" of the
flames ie. the values in FlameArray.*/
for( i=xstart; i <= xend; i++)
{
x=flamearray[i];
if (x<minfire) /* Increase by the "burnability"*/
{
/*Starting to burn:*/
if (x>10) x += randint(fireincrease);
}
else
/* Otherwise randomize and increase by intensity (is burning)*/
x += rand1(rootrand)+morefire;
if (x>255) x=255; /* X Too large ?*/
flamearray[i]=x;
}
/* Pour a little water on both sides of
the fire to make it look nice on the sides*/
/*
for( i=1; i <= width / 8; i ++)
{
x=floor(sqrt(randreal())*width/8);
flamearray[xstart+x]=0;
flamearray[xend-x]=0;
}
*/
/*Smoothen the values of FrameArray to avoid "descrete" flames*/
p=0;
for( i=xstart+smooth; i <= xend-smooth; i++)
{
x=0;
for( j=-smooth; j <= smooth; j++) x += flamearray[i+j];
flamearray[i] = x / ((smooth << 1) + 1);
}
for (x=0; x < BUF_WIDTH*BUF_HEIGHT; x++)
{
i = vidbuffer[x];
j = pt[x] >> 3;
if (j > i) vidbuffer[x] = j;
else vidbuffer[x] = ((i + j) >> 1) + 1;
}
}