/*
 * balls.c - Create a set of shiny spheres, with each sphere blooming sets of
 *     9 more spheres with 1/3rd radius.  None of the spheres are clipped.  A
 *     square floor polygon is added.  Three light sources.
 *
 * Version:  2.2 (11/17/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of objects output.
 *     Total spheres = sum of n=0,SF of (9**SF).
 *
 *     SIZE_FACTOR	# spheres	# squares
 *	    1		    10		     1
 *	    2		    91		     1
 *	    3		   820		     1
 *
 *	    4		  7381		     1
 */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "def.h"
#include "lib.h"

/* These may be read from the command line */
static int size_factor      = 1;
static int raytracer_format = OUTPUT_POVRAY;
static int output_format    = OUTPUT_CURVES;

static	COORD4	objset[9] ;

/*
 * Output the parent sphere, then output the children of the sphere.
 * Uses global 'objset'.
 */
void
output_object(depth, center, direction)
   int depth;
   COORD4 *center, *direction;
{
    double  angle;
    COORD4  axis, z_axis;
    COORD4  child_pt, child_dir;
    MATRIX  mx;
    long    num_vert;
    double  scale;


    /* output sphere at location & radius defined by center */
    lib_output_sphere(center, output_format);

    /* check if children should be generated */
    if (depth > 0) {
	--depth ;

	/* rotation matrix to new axis from +Z axis */
	if ( direction->z >= 1.0 ) {
	    /* identity matrix */
	    lib_create_identity_matrix(mx);
	    }
	else if ( direction->z <= -1.0 ) {
	    lib_create_rotate_matrix(mx, Y_AXIS, PI);
	    }
	else {
	    SET_COORD(z_axis, 0.0, 0.0, 1.0);
	    CROSS(axis, z_axis, (*direction));
	    lib_normalize_coord3(&axis);
	    angle = acos((double)DOT_PRODUCT(z_axis, (*direction)));
	    lib_create_axis_rotate_matrix(mx, &axis, angle);
	    }

	/* scale down location of new spheres */
	scale = center->w * (1.0 + direction->w ) ;

	for ( num_vert = 0 ; num_vert < 9 ; ++num_vert ) {
	    lib_transform_coord(&child_pt, &objset[num_vert], mx);
	    child_pt.x = child_pt.x * scale + center->x ;
	    child_pt.y = child_pt.y * scale + center->y ;
	    child_pt.z = child_pt.z * scale + center->z ;
	    /* scale down radius */
	    child_pt.w = center->w * direction->w ;
	    SUB3_COORD( child_dir, child_pt, (*center) ) ;
	    child_dir.x /= scale ;
	    child_dir.y /= scale ;
	    child_dir.z /= scale ;
	    child_dir.w = direction->w ;
	    output_object(depth, &child_pt, &child_dir);
	}
    }
}

/* Create the set of 9 vectors needed to generate the sphere set. */
/* Uses global 'objset' */
void
create_objset()
{
    COORD4  axis, temp_pt, trio_dir[3];
    double  dist;
    MATRIX  mx;
    long    num_set, num_vert;

    dist = 1.0 / sqrt((double)2.0);

    SET_COORD4(trio_dir[0], dist, dist,   0.0, 0.0);
    SET_COORD4(trio_dir[1], dist,  0.0, -dist, 0.0);
    SET_COORD4(trio_dir[2],  0.0, dist, -dist, 0.0);

    SET_COORD(axis, 1.0, -1.0, 0.0);
    lib_normalize_coord3(&axis);
    lib_create_axis_rotate_matrix(mx, &axis,
	                          asin((double)(2.0/sqrt((double)6.0))));

    for (num_vert=0;num_vert<3;++num_vert) {
	lib_transform_coord(&temp_pt, &trio_dir[num_vert], mx);
	COPY_COORD(trio_dir[num_vert], temp_pt);
        }

    for (num_set=0;num_set<3;++num_set) {
	lib_create_rotate_matrix(mx, Z_AXIS, num_set*2.0*PI/3.0);
	for (num_vert=0;num_vert<3;++num_vert) {
	    lib_transform_coord(&objset[num_set*3+num_vert],
				&trio_dir[num_vert], mx);
	    }
        }
}

int
main(argc,argv)
   int argc ;
   char *argv[] ;
{
    COORD4  back_color, obj_color ;
    COORD4  backg[5], bvec, light ;
    COORD4  from, at, up ;
    COORD4  center_pt, direction ;
    double  radius, lscale;
    char    *scolor, *bcolor;

   /* Start by defining which raytracer we will be using */
   switch (argc) {
      case 4:
	 output_format = atoi(argv[3]);
      case 3:
	 raytracer_format = atoi(argv[2]);
      case 2:
	 size_factor = atoi(argv[1]);
	 break;
      defaut:
	 break;
      }

   if (raytracer_format == OUTPUT_RTRACE ||
       raytracer_format == OUTPUT_PLG)
      lib_set_raytracer(OUTPUT_DELAYED);
   else
      lib_set_raytracer(raytracer_format);

   lib_set_output_file(stdout);
   lib_set_raytracer(raytracer_format);
   lib_set_polygonalization(3, 3);

   /* set radius of sphere which would enclose entire object */
   radius = 1.0 ;

   /* output viewpoint */
   SET_COORD(from, 2.1, 1.3, 1.7);
   SET_COORD(at, 0.0, 0.0, 0.0);
   SET_COORD(up, 0.0, 0.0, 1.0);
   lib_output_viewpoint(&from, &at, &up, 45.0, 1.0, 0.01, 512, 512);

   /* output background color - UNC sky blue */
   SET_COORD(back_color, 0.078, 0.361, 0.753);
   lib_output_background_color(&back_color);

   /* For raytracers that don't scale the light intensity, we will do
      it for them */
   lscale = (raytracer_format == OUTPUT_NFF ||
	     raytracer_format == OUTPUT_RTRACE ? 1.0 : 1.0 / sqrt(3.0));

   /* output light sources */
   SET_COORD4(light, 4.0, 3.0, 2.0, lscale);
   lib_output_light(&light);
   SET_COORD4(light, 1.0, -4.0, 4.0, lscale);
   lib_output_light(&light);
   SET_COORD4(light, -3.0, 1.0, 5.0, lscale);
   lib_output_light(&light);

   /* output floor polygon - beige */
   SET_COORD(back_color, 1.0, 0.75, 0.33);
   lib_output_color(NULL, &back_color, 0.2, 0.8, 0.0, 0.0, 0.0, 0.0, 1.0);
   bvec.x = bvec.y = radius * 12.0;
   bvec.z = -radius / 2.0;
   SET_COORD(backg[0],  bvec.x,  bvec.y, bvec.z);
   SET_COORD(backg[1], -bvec.x,  bvec.y, bvec.z);
   SET_COORD(backg[2], -bvec.x, -bvec.y, bvec.z);
   SET_COORD(backg[3],  bvec.x, -bvec.y, bvec.z);
   SET_COORD(backg[4],  bvec.x,  bvec.y, bvec.z);
   lib_output_polygon(4, backg);

   /* set up object color - mirrored */
   SET_COORD(obj_color, 1, 1, 1);
   lib_output_color(NULL, &obj_color, 0.0, 0.2, 0.8, 0.3, 5.0, 0.0, 1.0);

   /* create set of spawned points */
   create_objset();

   /* compute and output object */
   SET_COORD4(center_pt, 0.0, 0.0, 0.0, radius / 2.0);
   SET_COORD4(direction, 0.0, 0.0, 1.0, 1.0/3.0);
   output_object(size_factor, &center_pt, &direction);

   /* Make sure everything is cleaned up */
   if (raytracer_format == OUTPUT_RTRACE ||
       raytracer_format == OUTPUT_PLG) {
      lib_set_raytracer(raytracer_format);
      lib_flush_definitions();
      }
}
