/*
 * shells.c - Creates a shell using Pickover's generation method (see IEEE
 *     CG&A November 1989).  One light source.  This thing tends to bring ray
 *     tracers to their knees (lots of overlapping primitives, many quite
 *     tiny) - it's meant as a study of a realistic yet pathological database.
 *
 *     There are various additional characteristics which can be diddled with
 *     for different shell shapes (most pretty unrealistic, but there you go).
 *     See Pickover's article for more information.  Do "shells -?" to see the
 *     additional parameters which can be varied (listed at the end).
 *
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * Modified:  Antonio Costa, INESC
 *     Changed lib_get_opts to shells_get_opts
 *     Corrected bug with light source definition
 *     Changed several vars from COORD4 to COORD3
 *
 * Size factor determines the number of objects output.
 *	Total objects = 180*(2**SF) spheres
 *
 *	Size factor	# spheres    # squares
 *	     1		    360           1
 *	     2		    720           1
 *	     3		   1440		  1
 *	     4		   2880		  1
 *	     5 		   5760		  1
 */

#include <stdio.h>
#include <math.h>
#include "def.h"
#include "lib.h"

/* These may be read from the command line */
static int size_factor      = 5;
static int raytracer_format = OUTPUT_RT_DEFAULT;
static int output_format    = OUTPUT_CURVES;


#ifdef OUTPUT_TO_FILE
static FILE * stdout_file = NULL;
#else
#define stdout_file stdout
#endif /* OUTPUT_TO_FILE */


static	double	fgamma = 1.0 ;	/* 0.01 to 3 */
static	double	alpha = 0.0 ;	/* > 1 - 1.1 is good */
static	double	beta = -2.0 ;	/* ~ -2 */
static	double	a = 0.15 ;	/* exponent constant */
static	double	k = 1.0 ;	/* relative size */

void	shells_show_usage()
{
    show_gen_usage() ;
    fprintf(stderr, "-a alpha - alpha value (0 to 1.1 is good)\n");
    fprintf(stderr, "-b beta - beta value (-2 is good)\n");
    fprintf(stderr, "-g gamma - gamma value (0.01 to 3 is good)\n");
    fprintf(stderr, "-e exponent - exponent value (0.15 is good)\n");
}

int	shells_get_opts( argc, argv, p_size, p_rdr, p_curve )
int	argc ;
char	*argv[] ;
int	*p_size, *p_rdr, *p_curve ;
{
int num_arg ;
int val ;
double fval ;

    num_arg = 0 ;

    while ( ++num_arg < argc ) {
	if ( (*argv[num_arg] == '-') || (*argv[num_arg] == '/') ) {
	    switch( argv[num_arg][1] ) {
		case 'g':	/* gamma */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%lf", &fval ) ;
			if ( fval < 0.0 ) {
			    fprintf( stderr,
				    "bad gamma value %lf given\n",fval);
			    shells_show_usage();
			    return( TRUE ) ;
			}
			fgamma = fval ;
		    } else {
			fprintf( stderr, "not enough args for -g option\n" ) ;
			shells_show_usage();
			return( TRUE ) ;
		    }
		    break ;
		case 'a':	/* alpha */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%lf", &alpha ) ;
		    } else {
			fprintf( stderr, "not enough args for -a option\n" ) ;
			shells_show_usage();
			return( TRUE ) ;
		    }
		    break ;
		case 'b':	/* beta */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%lf", &beta ) ;
		    } else {
			fprintf( stderr, "not enough args for -b option\n" ) ;
			shells_show_usage();
			return( TRUE ) ;
		    }
		    break ;
		case 'e':	/* exponent selection */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%lf", &a ) ;
		    } else {
			fprintf( stderr, "not enough args for -e option\n" ) ;
			shells_show_usage();
			return( TRUE ) ;
		    }
		    break ;

		case 's':	/* size selection */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%d", &val ) ;
			if ( val < 1 ) {
			    fprintf( stderr,
				    "bad size value %d given\n",val);
			    shells_show_usage();
			    return( TRUE ) ;
			}
			*p_size = val ;
		    } else {
			fprintf( stderr, "not enough args for -s option\n" ) ;
			shells_show_usage();
			return( TRUE ) ;
		    }
		    break ;
		case 'c':	/* true curve output */
		    *p_curve = OUTPUT_CURVES ;
		    break ;
		case 't':	/* tessellated curve output */
		    *p_curve = OUTPUT_PATCHES ;
		    break ;
		case 'r':	/* renderer selection */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%d", &val ) ;
			if ( val < OUTPUT_VIDEO || val >= OUTPUT_DELAYED ) {
			    fprintf( stderr,
				    "bad renderer value %d given\n",val);
			    shells_show_usage();
			    return( TRUE ) ;
			}
			*p_rdr = val ;
		    } else {
			fprintf( stderr, "not enough args for -r option\n" ) ;
			shells_show_usage();
			return( TRUE ) ;
		    }
		    break ;
		default:
		    fprintf( stderr, "unknown argument -%c\n",
			    argv[num_arg][1] ) ;
		    shells_show_usage();
		    return( TRUE ) ;
	    }
	} else {
	    fprintf( stderr, "unknown argument %s\n",
		    argv[num_arg] ) ;
	    shells_show_usage();
	    return( TRUE ) ;
	}
    }
    return( FALSE ) ;
}

main(argc,argv)
int	argc ;
char	*argv[] ;
{
double	r,angle ;
long	i, steps ;
COORD3	back_color, obj_color ;
COORD3	from, at, up ;
COORD4	light ;
COORD4	sphere;

    PLATFORM_INIT(SPD_SHELLS);

    /* Start by defining which raytracer we will be using */
    if ( shells_get_opts( argc, argv,
			&size_factor, &raytracer_format, &output_format ) ) {
	return EXIT_FAIL;
    }
    if ( lib_open( raytracer_format, "Shells.out" ) ) {
	return EXIT_FAIL;
    }

    /* output background color - UNC sky blue */
    /* NOTE: Do this BEFORE lib_output_viewpoint(), for display_init() */
    SET_COORD3( back_color, 0.078, 0.361, 0.753 ) ;
    lib_output_background_color( back_color ) ;

    /* output viewpoint */
    SET_COORD3( from, -6.0, -60.0, 35.0 ) ;
    SET_COORD3( at, 0.0, 8.0, -15.0 ) ;
    SET_COORD3( up, 0.0, 0.0, 1.0 ) ;
    lib_output_viewpoint( from, at, up, 45.0, 1.0, 0.5, 512, 512 ) ;

    /* output light sources */
    SET_COORD4( light, -100.0, -100.0, 100.0, 1.0 ) ;
    lib_output_light( light ) ;

    /* set up sphere color */
    SET_COORD3( obj_color, 1.0, 0.8, 0.4 ) ;
    lib_output_color( NULL, obj_color, 0.0, 0.8, 0.2, 0.5, 5.0, 0.0, 1.0 ) ;

    steps = 180.0 * pow( 2.0, (double)size_factor ) ;
    for ( i = -steps*2/3; i <= steps/3 ; ++i ) {
	PLATFORM_MULTITASK();

	angle = 3.0 * 6.0 * PI * (double)i / (double)steps ;
	r = k * exp( a * angle ) ;
	sphere[X] = r * sin( angle ) ;
	sphere[Y] = r * cos( angle ) ;
	if ( alpha > 0.0 ) {
	    /* alternate formula: z = alpha * angle */
	    sphere[Z] = alpha * angle ;
	} else {
	    sphere[Z] = beta * r ;
	}
	sphere[W] = r / fgamma ;
	lib_output_sphere( sphere, output_format ) ;
    }

    lib_close();

    PLATFORM_SHUTDOWN();
    return EXIT_SUCCESS;
}
