psxsdk/examples/mandel/mandel.c

348 lines
7.2 KiB
C

/*
* Simple fractal generator for the PlayStation
*
* by Giuseppe Gatta
* Placed in the Public Domain.
*
* Consider enabling compiler optimizations, for instance this code is much faster
* if you add -O3 to the gcc command line.
*
* The fractals are rendered at a 160x120 resolution in an array in memory,
* then this array is uploaded to video memory and handled as 8-bit image data.
* We render at 160x120 to speed rendering up because the PlayStation is a slow machine
* for doing this kind of thing.
*
* A sprite primitive is used to display the image data on the screen, and the primitive
* is scaled twice horizontally and vertically to cover the 320x240 screen with a 160x120
* image.
*
* The fractal drawing code by default also exploits the simmetry of the Mandrelbot
* and Tricorn sets to further speed rendering up (2x). To have a real rendering of
* the Burning Ship fractal, though, you must disable simmetry because that fractal
* does not have this property.
*
* This is the description of the controls of this program:
*
* SELECT - Toggle palette (Red->Green->Blue->Yellow->Violet->Red->...)
* START - Render fractal
* X - Toggle type of fractal to render (Mandelbrot->Tricorn->Burning Ship->Mandelbrot->...)
* O - Toggle simmetry (on the screen as 'S')
* LEFT - Decrease multibrot power (displayed on the screen as 'M')
* RIGHT - Increase multibrot power
* UP - Decrease number of maximum iterations (on the screen as 'I')
* DOWN - Increase number of maximum iterations
*/
#include <psx.h>
#include <stdio.h>
unsigned char fractal_image_data[120][160];
unsigned int prim_list[0x4000];
unsigned short clut_buf[256];
volatile int frac_x=0, frac_y=0;
enum
{
FRAC_MANDELBROT, // Normal mandelbrot
FRAC_TRICORN,
FRAC_BSHIP,
FRAC_TF,
FRAC_NOSIMM = 0x200,
};
/*
* Code based on http://reocities.com/CapeCanaveral/5003/mandel.htm
*/
void mandrelbrot_int(int width, int height, int maxI, int type, int M)
{
#define FIXSIZE 25
#define mul(a,b) ((((long long)a)*(b))>>FIXSIZE)
#define fixpt(a) ((int)(((a)*(1<<FIXSIZE))))
#define integer(a) (((a)+(1<<(FIXSIZE-1)))>>FIXSIZE)
int x0,y0,x1,y1,p,q,xn;
double xmin=-2.5,ymin=-1.5,xmax=1.5,ymax=1.5,xs,ys;
int i,x,y,t;
int m;
xs=(xmax-xmin)/width;
ys=(ymax-ymin)/height;
t = type & 0xff;
for (y=0;y<((type&FRAC_NOSIMM)?height:(height/2));y++)
{
for (x=0;x<width;x++)
{
p=fixpt(xmin+x*xs); // c_re
q=fixpt(ymin+y*ys);
xn=0;
x0=0;
y0=0;
i=0;
while ((mul(xn,xn)+mul(y0,y0))<fixpt(4) && ++i<maxI)
{
switch(t)
{
case FRAC_BSHIP:
if(x0<0)x0=-x0;
if(y0<0)y0=-y0;
break;
case FRAC_TRICORN:
y0=-y0;
break;
}
x1=x0;
y1=y0;
for(m=1;m<M;m++)
{
xn=mul(x0,x1) - mul(y0,y1);
y0=mul(y0,x1) + mul(x0,y1);
x0=xn;
}
x0=xn;
x0+=p;
y0+=q;
}
if (i==maxI) i=1;
fractal_image_data[y][x] = (i*256)/maxI;
if(!(type & FRAC_NOSIMM))
fractal_image_data[(height-1)-y][x] = (i*256)/maxI;
}
}
}
volatile int display_is_old = 1;
volatile int time_counter = 0;
void prog_vblank_handler()
{
display_is_old = 1;
time_counter++;
}
int frac_type = FRAC_MANDELBROT;
unsigned int frac_maxI = 64;
int frac_M=2;
const char *frac_type_string[] =
{ "Mandelbrot",
"Tricorn",
"BurningShip"};
GsSprite frac_sprite;
int main()
{
int x, dbuf=0;
int waspal=0;
int wastype=0;
int wasrender=0;
int wassimm=0;
int wasMm=0,wasMp=0, wasIm=0, wasIp=0;
int rendering_time = -1;
unsigned short padbuf;
PSX_InitEx(PSX_INIT_CD);
GsInit();
GsSetList(prim_list);
GsClearMem();
GsSetVideoMode(320, 240, EXAMPLES_VMODE);
GsLoadFont(768, 0, 768, 256);
SetVBlankHandler(prog_vblank_handler);
// Fill & upload CLUTs
// Red CLUT
for(x = 0; x < 256; x++)
clut_buf[x] = ((x*4)>255?255:x*4)>>3;
LoadImage(clut_buf, 512, 256, 256, 1);
while(GsIsDrawing());
// Green CLUT
for(x = 0; x < 256; x++)
clut_buf[x] = (((x*4)>255?255:x*4)>>3)<<5;
LoadImage(clut_buf, 512, 257, 256, 1);
while(GsIsDrawing());
// Blue CLUT
for(x = 0; x < 256; x++)
clut_buf[x] = (((x*4)>255?255:x*4)>>3)<<10;
LoadImage(clut_buf, 512, 258, 256, 1);
while(GsIsDrawing());
// Yellow CLUT
for(x = 0; x < 256; x++)
{
clut_buf[x] = ((x*4)>255?255:x*4)>>3;
clut_buf[x]|= clut_buf[x]<<5;
}
LoadImage(clut_buf, 512, 259, 256, 1);
while(GsIsDrawing());
// Violet CLUT
for(x = 0; x < 256; x++)
{
clut_buf[x] = ((x*4)>255?255:x*4)>>3;
clut_buf[x]|= clut_buf[x]<<10;
}
LoadImage(clut_buf, 512, 260, 256, 1);
while(GsIsDrawing());
frac_sprite.tpage = 5;
frac_sprite.attribute = COLORMODE(COLORMODE_8BPP);
frac_sprite.u = 0;
frac_sprite.v = 0;
frac_sprite.cx = 512;
frac_sprite.cy = 256;
frac_sprite.x = 0;
frac_sprite.y = 0;
frac_sprite.w = 160;
frac_sprite.h = 120;
frac_sprite.r = frac_sprite.g = frac_sprite.b = NORMAL_LUMINOSITY;
frac_sprite.scalex = SCALE_ONE*2;
frac_sprite.scaley = SCALE_ONE*2;
time_counter = 0;
mandrelbrot_int(160, 120, frac_maxI, frac_type, frac_M);
rendering_time = time_counter * (GsScreenM==VMODE_NTSC?17:20);
LoadImage(fractal_image_data, 320, 0, 80, 120);
while(GsIsDrawing());
while(1)
{
if(display_is_old)
{
dbuf=!dbuf;
GsSetDispEnvSimple(0, dbuf?0:256);
GsSetDrawEnvSimple(0, dbuf?256:0, 320, 240);
GsSortCls(0,0,0);
GsSortSprite(&frac_sprite);
GsPrintFont(0, 0, "Type: %s", frac_type_string[(frac_type&0xff)]);
GsPrintFont(0, 8, "I=%d, M=%d, S=%c", frac_maxI, frac_M,
frac_type&FRAC_NOSIMM?'N':'Y');
GsPrintFont(0, 16, "Time: %d.%03d", rendering_time / 1000, rendering_time % 1000);
GsDrawList();
while(GsIsDrawing());
PSX_ReadPad(&padbuf, NULL);
if((padbuf & PAD_SELECT) && !waspal)
{
frac_sprite.cy++;
if(frac_sprite.cy>260)
frac_sprite.cy = 256;
waspal=1;
}
if((padbuf & PAD_CROSS) && !wastype)
{
frac_type++;
if((frac_type & 0xff) >= FRAC_TF)
frac_type&=~0xff;
wastype=1;
}
if((padbuf & PAD_START) && !wasrender)
{
GsPrintFont(0, 224, "Rendering...");
GsDrawList();
while(GsIsDrawing());
dbuf=!dbuf;
GsSetDispEnvSimple(0, dbuf?0:256);
GsSetDrawEnvSimple(0, dbuf?256:0, 320, 240);
time_counter = 0;
mandrelbrot_int(160, 120, frac_maxI, frac_type, frac_M);
rendering_time = time_counter * (GsScreenM==VMODE_NTSC?17:20);
LoadImage(fractal_image_data, 320, 0, 80, 120);
while(GsIsDrawing());
wasrender=1;
}
if((padbuf & PAD_CIRCLE) && !wassimm)
{
frac_type ^= FRAC_NOSIMM;
wassimm=1;
}
if((padbuf & PAD_LEFT) && !wasMm)
{
if(frac_M>2)frac_M--;
wasMm=1;
}
if((padbuf & PAD_RIGHT) && !wasMp)
{
frac_M++;
wasMp=1;
}
if((padbuf & PAD_UP) && !wasIm)
{
if(frac_maxI>1)frac_maxI--;
wasIm=1;
}
if((padbuf & PAD_DOWN) && !wasIp)
{
frac_maxI++;
wasIp=1;
}
if(!(padbuf & PAD_SELECT))
waspal=0;
if(!(padbuf & PAD_CROSS))
wastype=0;
if(!(padbuf & PAD_START))
wasrender=0;
if(!(padbuf & PAD_CIRCLE))
wassimm=0;
if(!(padbuf & PAD_LEFT))
wasMm=0;
if(!(padbuf & PAD_RIGHT))
wasMp=0;
if(!(padbuf & PAD_UP))
wasIm=0;
if(!(padbuf & PAD_DOWN))
wasIp=0;
display_is_old=0;
}
}
return 0;
}