All pastes #399705 Raw Edit

pixelmachine

public text v1 · immutable
·published 2007-03-18 04:52 UTC
rendered paste body
/*

** PixelMachine

** v 0.1.20070220

**

** A ray-traced 3D renderer by SuperJer

**

** Email = superjer@superjer.com

** Web   = http://www.superjer.com/

**

** You can do whatever you like with this code, I don't care,

** just please recognize me as the original author. :)

**

*/





#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <math.h>

#include <memory.h>

#include <time.h>



#include "collision.h"





#define TERDIV  8    // terrain divisions (powers of 2 ONLY!)

#define TERSECS 8    // sections per division

                     // this will work out to (TERDIV*TERSECS+1)^3 cubes!!



#define BSIZE (TERDIV*TERSECS+1)

#define SQSIZE (1000.0/(double)BSIZE)

#define MULTIS 2     // level of multisampling (makes rendering n^2 times slower!!)



#define W 200        // output image size

#define H 150

#define FRAMES 1     // frames of animation



#define XY 0

#define XZ 1

#define YZ 2



#define SCATTER 0.00003



#define SUNS 2



#define SPHERES 100







double terrain[BSIZE][BSIZE];















unsigned char *img = NULL;

int w;

int h;

int multis;

int frames;

COLOR blocks[BSIZE][BSIZE][BSIZE] = {0};



double blocksize = 1000.0/((double)BSIZE);



V cam;

V tar;

double tarw;

double pixpitch;

double subpitch;



V sun[SUNS];

SPHERE sphere[SPHERES];





void savebmp( const char *filename, int w, int h );

void preview( int w, int h );

void render( int w, int h, bool quiet );

COLOR &raytrace( COLOR &color, const V &cam, const V &ray, bool lighttest );









int main(int argc,char **argv)

{

    int i,j,k;

    int w,h;

    char str[80];

    unsigned seed = (unsigned)-1;



    w = W;

    h = H;

    multis = MULTIS;

    frames = FRAMES;



    for(i=1; i<argc; i++)

    {

        if( argv[i][0]=='-' ) switch( argv[i][1] )

        {

        case 'w': w = atoi(argv[i]+2); break;

        case 'h': h = atoi(argv[i]+2); break;

        case 'm': multis = atoi(argv[i]+2); break;

        case 'v': frames = atoi(argv[i]+2); break;

        case '-':

            if( !strcmp(argv[i],"--help") )

            {

                printf( "Usage: [OPTION]... [SEED]\nRender ray-traced 3D images generated randomly with seed number SEED.\n\n  option default description\n  -wNUM  %7d Set image output width to NUM\n  -hNUM  %7d Set image output height to NUM\n  -mNUM  %7d Set multisampling level to NUM (level 2 recommended)\n  -vNUM  %7d Render a sequence of NUM images for a video\n",W,H,MULTIS,FRAMES );

                exit(0);

            }

        default:

            fprintf( stderr, "Halt! -%c isn't one of my options!\n", argv[i][1] );

            exit(-1);

        }

        else if( seed==(unsigned)-1 )

        {

            seed = atoi(argv[i]);

            if( !seed )

            {

                fprintf( stderr, "Halt! SEED ought to be a positive number, not %s\n", argv[i] );

                exit(-1);

            }

        }

        else

        {

            fprintf( stderr, "Halt! I'm confused by cmd line argument #%d: %s\n", i, argv[i] );

            exit(-1);

        }

    }



    if( seed==(unsigned)-1 )

        seed = (unsigned)time(NULL);

    //seed = 1171997907;	// this one is pretty good!

    srand(seed);

    



    FILE *f;

    f = fopen("seed.txt","w");

    fprintf(f,"%d",seed);

    fclose(f);

    







    //generate terrain

    printf("Generating...\n");

    int div = TERDIV;

    for(i=0;i<BSIZE;i+=div)

        for(j=0;j<BSIZE;j+=div)

        {

            terrain[i][j] = (double)(rand()%1000)*0.5-200.0;

        }

    while( div>1 )

    {

        for(i=div/2;i<BSIZE;i+=div)

            for(j=div/2;j<BSIZE;j+=div)

            {

                terrain[i][j] = (terrain[i-div/2][j-div/2] + terrain[i-div/2][j+div/2] + terrain[i+div/2][j-div/2] + terrain[i+div/2][j+div/2])*0.25;

            }



        for(i=0;i<BSIZE;i+=div/2)

            for(j=0;j<BSIZE;j+=div/2)

            {

                if( (i+j)%div )

                {

                    if( i==0 )

                        terrain[i][j] = (                      terrain[i+div/2][j] + terrain[i][j-div/2] + terrain[i][j+div/2])*0.33333;

                    else if( i==BSIZE-1 )

                        terrain[i][j] = (terrain[i-div/2][j]                       + terrain[i][j-div/2] + terrain[i][j+div/2])*0.33333;

                    else if( j==0 )

                        terrain[i][j] = (terrain[i-div/2][j] + terrain[i+div/2][j]                       + terrain[i][j+div/2])*0.33333;

                    else if( j==BSIZE-1 )

                        terrain[i][j] = (terrain[i-div/2][j] + terrain[i+div/2][j] + terrain[i][j-div/2]                      )*0.33333;

                    else

                        terrain[i][j] = (terrain[i-div/2][j] + terrain[i+div/2][j] + terrain[i][j-div/2] + terrain[i][j+div/2])*0.25;

                }

            }



        div /= 2;

    }





	// represent terrain as those stupid cubes (this is dumb I KNOW!)

    for(k=0; k<BSIZE; k++)

        for(i=0; i<BSIZE; i++)

            for(j=0; j<BSIZE; j++)

                

                if( k==0 )

                {

                    blocks[i][j][k].r = (double)(rand()%30)/255.0 + 0.50;

                    blocks[i][j][k].g = (double)(rand()%40)/255.0 + 0.40;

                    blocks[i][j][k].b = (double)(rand()%20)/255.0 + 0.05;

                    blocks[i][j][k].a = 1.0; //(double)(rand()%100+1)*0.01;

                }

                else if( i>(BSIZE*2)/5 && i<(BSIZE*3)/5 && j>(BSIZE*2)/5 && j<(BSIZE*3)/5 ) // shiny area!

                {

                    if( k<BSIZE/5 )

                        blocks[i][j][k] = blocks[i][j][k-1];

                    else

                    {

                        blocks[i][j][k].r = 0.0;

                        blocks[i][j][k].g = 0.0;

                        blocks[i][j][k].b = 0.0;

                        blocks[i][j][k].a = 0.0;

                    }

                }

                else if( terrain[i][j] >= k*(1000.0/(double)BSIZE ) )

                {

                    blocks[i][j][k] = blocks[i][j][k-1];

                }

                else

                {

                    blocks[i][j][k].r = 0.0;

                    blocks[i][j][k].g = 0.0;

                    blocks[i][j][k].b = 0.0;

                    blocks[i][j][k].a = 0.0;

                }





    sun[0].x = (double)(rand()%1500) - 250.0;

    sun[0].y = (double)(rand()%1500) - 250.0;

    sun[0].z = (double)(rand()%700) + 100.0;



    for(i=0; i<SUNS; i++)

    {

        sun[i].x = sun[0].x + ((double)i)*0.4;

        sun[i].y = sun[0].y + ((double)i)*0.3;

        sun[i].z = sun[0].z + ((double)i)*0.2;

    }



    cam.x = (double)(rand()%900) + 50.0;

    cam.y = (double)(rand()%900) + 50.0;

    cam.z = terrain[int(cam.x/SQSIZE)][int(cam.y/SQSIZE)] + (double)(rand()%150) + 50.0;



    tar.x = (double)(rand()%900) + 50.0;

    tar.y = (double)(rand()%900) + 50.0;

    tar.z = cam.z - (double)(rand()%400);



    for(i=0; i<SPHERES; i++)

    {

        sphere[i].center.x = (double)(rand()%10000)*0.1;

        sphere[i].center.y = (double)(rand()%10000)*0.1;

        sphere[i].center.z = (double)(rand()%3500)*0.1;

        sphere[i].radius = (double)(rand()%10000)*0.005;

        sphere[i].color.a = 1.0;

        sphere[i].color.r = (double)(rand()%255)/255.0;

        sphere[i].color.g = (double)(rand()%255)/255.0;

        sphere[i].color.b = (double)(rand()%255)/255.0;

    }



    sphere[0].center.x = (double)(BSIZE*SQSIZE/2);

    sphere[0].center.y = (double)(BSIZE*SQSIZE/2);

    sphere[0].center.z = (double)(40*SQSIZE);

    sphere[0].radius = 30.0;







    V lin;

    subtract( lin, tar, cam );

    normalize( lin, 2.0 );





    printf("Rendering...\n");

    for(i=0;i<frames;i++)

    {

        render(61,22,true);

        preview(61,22);



/**/

        render(w,h,false);



        printf("Writing frame %.4d ...\n",i);

        if( frames > 1 )

            sprintf(str,"./video/frame%.4d.bmp",i);

        else

            sprintf(str,"./img.bmp");

        savebmp(str,w,h);

/**/    



        subtract( cam, cam, lin );

        tar.z -= 0.85;

        cam.z -= 0.9;



        for(j=0;j<SPHERES;j++)

        {

            switch( j%3 )

            {

            case 2: sphere[j].center.z += 1.1; break;

            case 1: sphere[j].center.z -= 1.1; break;

            case 0: break;;

            }

        }

    }



    free(img);

}







void savebmp( const char *filename, int w, int h )

{

    int i;

    FILE *f;

    int filesize = 54 + 3*w*h;



    unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0};

    unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};

    unsigned char bmppad[3] = {0,0,0};



    bmpfileheader[ 2] = (unsigned char)(filesize    );

    bmpfileheader[ 3] = (unsigned char)(filesize>> 8);

    bmpfileheader[ 4] = (unsigned char)(filesize>>16);

    bmpfileheader[ 5] = (unsigned char)(filesize>>24);



    bmpinfoheader[ 4] = (unsigned char)(       w    );

    bmpinfoheader[ 5] = (unsigned char)(       w>> 8);

    bmpinfoheader[ 6] = (unsigned char)(       w>>16);

    bmpinfoheader[ 7] = (unsigned char)(       w>>24);

    bmpinfoheader[ 8] = (unsigned char)(       h    );

    bmpinfoheader[ 9] = (unsigned char)(       h>> 8);

    bmpinfoheader[10] = (unsigned char)(       h>>16);

    bmpinfoheader[11] = (unsigned char)(       h>>24);



    //f = fopen("img.raw","wb");

    //fwrite(img,3,w*h,f);

    //fclose(f);



    f = fopen(filename,"wb");

    fwrite(bmpfileheader,1,14,f);

    fwrite(bmpinfoheader,1,40,f);

    for(i=0; i<h; i++)

    {

        fwrite(img+(w*(h-i-1)*3),3,w,f);

        fwrite(bmppad,1,(4-(w*3)%4)%4,f);

    }

    fclose(f);

}









void preview( int w, int h )

{

    int i,j;

    int brightness,min,max,step;



    max = 0;

    min = 768;



    for(j=0; j<h; j++)

    {

        for(i=0; i<w; i++)

        {

            brightness = img[(i+j*w)*3+0] + img[(i+j*w)*3+1] + img[(i+j*w)*3+2];

            if( brightness>max )

                max = brightness;

            if( brightness<min )

                min = brightness;

        }

    }



    step = (max-min)/11;



    for(j=0; j<h; j++)

    {

        for(i=0; i<w; i++)

        {

            brightness = img[(i+j*w)*3+0] + img[(i+j*w)*3+1] + img[(i+j*w)*3+2];

            if( brightness>min+step*10 )

                printf("#");

            else if( brightness>min+step*9 )

                printf("@");

            else if( brightness>min+step*8 )

                printf("8");

            else if( brightness>min+step*7 )

                printf("$");

            else if( brightness>min+step*6 )

                printf("?");

            else if( brightness>min+step*5 )

                printf("%%");

            else if( brightness>min+step*4 )

                printf("*");

            else if( brightness>min+step*3 )

                printf(";");

            else if( brightness>min+step*2 )

                printf(",");

            else if( brightness>min+step*1 )

                printf("`");

            else

                printf(" ");

        }

        printf("\n");

    }

}







void render( int w, int h, bool quiet )

{

    int i,j,k,m,n;



    COLOR *color;

    color = (COLOR*)malloc(sizeof(COLOR)*(multis*multis));



    if( img )

        free( img );

    img = (unsigned char *)malloc(3*w*h);

    memset(img,0,sizeof(img));



    subtract( tar, tar, cam );

    normalize( tar, 1.0 );

    add( tar, tar, cam );



    tarw = 2.0;

    pixpitch = tarw/w;

    subpitch = pixpitch/((double)multis+1.0);



    V lin;

    lin.x = tar.x - cam.x;

    lin.y = tar.y - cam.y;

    lin.z = tar.z - cam.z;



    double di,dj;

    double r,g,b;

    int subgoal;



    subgoal = 0;



    for(i=0; i<w; i++)

    {

        for(j=0; j<h; j++)

        {

            di = (double)i;

            dj = (double)j;







            for(m=0; m<multis; m++)

                for(n=0; n<multis; n++)

                {

                    k = m + n*multis;



                    V wing;

                    wing.x =  lin.y;

                    wing.y = -lin.x;

                    wing.z = 0.0;



                    V head;

                    cross( head, lin, wing );



                    normalize( wing, (di-(double)w/2.0)*pixpitch + subpitch*m );

                    normalize( head, (dj-(double)h/2.0)*pixpitch + subpitch*n );



                    V ray;

                    ray.x = tar.x+wing.x+head.x - cam.x;

                    ray.y = tar.y+wing.y+head.y - cam.y;

                    ray.z = tar.z+wing.z+head.z - cam.z;



                    raytrace( color[k], cam, ray, false );

                }



            r = 0.0;

            g = 0.0;

            b = 0.0;



            for(k=0;k<(multis*multis);k++)

            {

                r += color[k].r;

                g += color[k].g;

                b += color[k].b;

            }



            r /= (double)(multis*multis);

            g /= (double)(multis*multis);

            b /= (double)(multis*multis);



            img[(i+j*w)*3+2] = (unsigned char)(255.0*r+0.5);

            img[(i+j*w)*3+1] = (unsigned char)(255.0*g+0.5);

            img[(i+j*w)*3+0] = (unsigned char)(255.0*b+0.5);



            //printf( "%3.3f %3.3f %3.3f\n", r, g, b );

            //getch();

        }

        

        if( !quiet )

            if( (int)(1000.0*(double)i/(double)w) >= subgoal )

            {

                subgoal += 20;

                if( subgoal%100==0 )

                    printf("%d",subgoal/10);

                else

                    printf(".");

                fflush(stdout);

            }

    }

    if( !quiet )

        printf("\n");

}









COLOR &raytrace( COLOR &color, const V &cam, const V &ray, bool lighttest )

{

    int i,k;

    double r,g,b,a;

    int blockx,blocky,blockz;

    V hit0,hit1,hit2;

    V hit;

    double p0,p1,p2;

    double p,potp;

    int side;

    int hitobject,pothitobject;



    side = XY;



    r = 0.0;

    g = 0.0;

    b = 0.0;

    a = 0.0;





    // spheres

    potp = 99999999.0;

    hitobject = -1;

    pothitobject = -1;

    for(i=0; i<SPHERES; i++)

    {

        V v;

        v = ray;

        CollideRaySphere(p0,cam,v,sphere[i].center,sphere[i].radius);



        if(p0!=NO_COLLISION && p0+0.00001 < potp)

        {

            pothitobject = i;

            potp = p0+0.00001;

        }

    }





    p = 0.0;

    k = 0;

    while( 1 )

    {

        // find current space block

        blockx = (int)((cam.x+p*ray.x)/blocksize);

        blocky = (int)((cam.y+p*ray.y)/blocksize);

        blockz = (int)((cam.z+p*ray.z)/blocksize);



        if( blockx<1 || blockx>BSIZE-1 || blocky<1 || blocky>BSIZE-1 || blockz<0 || blockz>BSIZE-1 || k>1000 || (lighttest && p>=1.0) )

        {

            if( !lighttest )

            {

                double dot = (ray.x*ray.x + ray.y*ray.y);

                double ang = fabs(dot/( sqrt(dot) * sqrt(ray.x*ray.x + ray.y*ray.y + ray.z*ray.z) ));

                ang *= ang;

                r = r + 0.5*ang*(1.0-a);

                g = g + 0.6*ang*(1.0-a);

                b = b + 1.0*ang*(1.0-a);

                a = 1.0;

            }

            break;

        }

        k++;



        // pickup color from current space block

        COLOR *c;

        if( hitobject>=0 )

            c = &sphere[hitobject].color;

        else

            c = &blocks[blockx][blocky][blockz];



        if( k>0 && (c->a>0.0 || hitobject>=0 ) )

        {

            if( lighttest )

            {

                color.r = 0.0;

                color.g = 0.0;

                color.b = 0.0;

                color.a = 0.7;

                return color;

            }



            // get incoming light/shadow

            V beam;

            COLOR light;

            COLOR finallight;



            finallight.r = 0.0;

            finallight.g = 0.0;

            finallight.b = 0.0;



            for(int m=0;m<SUNS;m++)

            {

                subtract( beam, sun[m], hit );

                raytrace( light, hit, beam, true );



                double falloff = 800000.0/(beam.x*beam.x + beam.y*beam.y + beam.z*beam.z);

                light.r = light.r*0.0+(1.0-light.a)*falloff;

                light.g = light.g*0.0+(1.0-light.a)*falloff;

                light.b = light.b*0.0+(1.0-light.a)*falloff;



                finallight.r += light.r;

                finallight.g += light.g;

                finallight.b += light.b;

            }



            finallight.r /= (double)SUNS;

            finallight.g /= (double)SUNS;

            finallight.b /= (double)SUNS;



            double sidefactor = (side==XY?1.0:(side==XZ?0.8:0.6));



            if( blockz==0 || hitobject>=0 || blockx>(BSIZE*2)/5 && blockx<(BSIZE*3)/5 && blocky>(BSIZE*2)/5 && blocky<(BSIZE*3)/5 ) //reflect!

            {

                V ray2 = ray;



                if( hitobject>=0 )

                {

                    V rad;



                    subtract( rad, hit, sphere[hitobject].center );

                    mirror( ray2, ray, rad );

                    ray2.x = -ray2.x;

                    ray2.y = -ray2.y;

                    ray2.z = -ray2.z;

                }

                else if( side==XY )

                    ray2.z = -ray.z;

                else if( side==XZ )

                    ray2.y = -ray.y;

                else 

                    ray2.x = -ray.x;



                //printf("%f",sin( (hit.z)*0.01 )*0.1 );



                double scatter;

                if( hitobject>=0 )

                    scatter = 0.0;

                else

                    scatter = SCATTER;



                ray2.x *= 1.0 + (double)(rand()%1000)*scatter; // + sin( (hit.z)*0.1 )*0.3;

                ray2.y *= 1.0 + (double)(rand()%1000)*scatter; // + cos( (hit.z)*0.1 )*0.3;

                ray2.z *= 1.0 + (double)(rand()%1000)*scatter; // + sin( (hit.x)*0.1 )*0.3;



                raytrace(color, hit, ray2, false);



                r = r + color.r*(1.0-a)*sidefactor*finallight.r;

                g = g + color.g*(1.0-a)*sidefactor*finallight.g;

                b = b + color.b*(1.0-a)*sidefactor*finallight.b;

                a = 1.0;



                break;

            }



            r = r + c->r*c->a*(1.0-a)*sidefactor*finallight.r;

            g = g + c->g*c->a*(1.0-a)*sidefactor*finallight.g;

            b = b + c->b*c->a*(1.0-a)*sidefactor*finallight.b;

            a += c->a*(1.0-a);



            if( a>=0.98 )

            {

                a = 1.0;

                break;

            }

        }



        // move to next space block

        hit0.x = (double)blockx*blocksize + (ray.x>0.0?blocksize:0.0);

        hit1.y = (double)blocky*blocksize + (ray.y>0.0?blocksize:0.0);

        hit2.z = (double)blockz*blocksize + (ray.z>0.0?blocksize:0.0);



        p0 = (hit0.x-cam.x)/(ray.x) + 0.00001;

        p1 = (hit1.y-cam.y)/(ray.y) + 0.00001;

        p2 = (hit2.z-cam.z)/(ray.z) + 0.00001;



        if( (int)((cam.x+p0*ray.x)/blocksize)==blockx && (int)((cam.y+p0*ray.y)/blocksize)==blocky && (int)((cam.z+p0*ray.z)/blocksize)==blockz )

            p0 = 99999.0;

        if( (int)((cam.x+p1*ray.x)/blocksize)==blockx && (int)((cam.y+p1*ray.y)/blocksize)==blocky && (int)((cam.z+p1*ray.z)/blocksize)==blockz )

            p1 = 99999.0;

        if( (int)((cam.x+p2*ray.x)/blocksize)==blockx && (int)((cam.y+p2*ray.y)/blocksize)==blocky && (int)((cam.z+p2*ray.z)/blocksize)==blockz )

            p2 = 99999.0;



        if( p0<p || p1<p || p2<p )

            break;



        if( p0<p1 && p0<p2 )

        {

            side = YZ;

            p = p0;

        }

        else if( p1<p2 )

        {

            side = XZ;

            p = p1;

        }

        else

        {

            side = XY;

            p = p2;

        }



        if( p>potp )

        {

            hitobject = pothitobject;

            side = XY;

            p = potp;

        }



        hit.x = (p-0.00002)*ray.x + cam.x;

        hit.y = (p-0.00002)*ray.y + cam.y;

        hit.z = (p-0.00002)*ray.z + cam.z;



        //printf( "%.4d %.4d %.4d\n", blockx, blocky, blockz );

    }



    if( r>1.0 ) r = 1.0;

    if( g>1.0 ) g = 1.0;

    if( b>1.0 ) b = 1.0;

    if( r<0.0 ) r = 0.0;

    if( g<0.0 ) g = 0.0;

    if( b<0.0 ) b = 0.0;



    color.r = r;

    color.g = g;

    color.b = b;

    color.a = a;



    return color;

}