/*
 * tetra.c - Create a tetrahedral pyramid.  This environment is based on the
 *	scene used by Glassner ("Space Subdivision for Fast Ray Tracing," IEEE
 *	CG&A, October 1984) and Kay & Kajiya ("Ray Tracing Complex Scenes,"
 *	SIGGRAPH '86 Proceedings) for testing their ray tracers.
 *	One light source.
 *
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * Note:  the view and light positions are the same (after transformation to
 *	a different set of world coordinates) as used by Kay & Kajiya,
 *	courtesy of Tim Kay.  For some reason, the number of shadow rays
 *	generated is different (Kay gets 34K, I get 46K).  One light source.
 *
 * size_factor determines the number of polygons output.
 *	Total triangular polygons = 4**SF
 *
 *	size_factor	# triangles
 *	     1		     4
 *	     2		    16
 *	     3		    64
 *
 *	     6		  4096
 */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>	/* atoi */
#include "def.h"
#include "drv.h"	/* display_close() */
#include "lib.h"

static	int	size_factor = 6 ;
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 */


/* Create tetrahedrons recursively */
void
create_tetra( depth, center )
    int depth ;
    COORD4 center ;
{
    int num_face, num_vert, swap, vert_ord[3] ;
    int x_dir, y_dir, z_dir ;
    COORD3 face_pt[3], obj_pt[4] ;
    COORD4 sub_center ;

    PLATFORM_MULTITASK();

    if ( depth <= 1 ) {
	/* Output tetrahedron */

	/* find opposite corners of a cube which form a tetrahedron */
	for ( num_vert = 0, x_dir = -1 ; x_dir <= 1 ; x_dir += 2 ) {
	    for ( y_dir = -1 ; y_dir <= 1 ; y_dir += 2 ) {
		for ( z_dir = -1 ; z_dir <= 1 ; z_dir += 2 ) {
		    if ( x_dir*y_dir*z_dir == 1 ) {
			obj_pt[num_vert][X] =
					center[X] + (double)x_dir * center[W] ;
			obj_pt[num_vert][Y] =
					center[Y] + (double)y_dir * center[W] ;
			obj_pt[num_vert][Z] =
					center[Z] + (double)z_dir * center[W] ;
			++num_vert ;
		    }
		}
	    }
	}

	/* find faces and output */
	for ( num_face = 0 ; num_face < 4 ; ++num_face ) {
	    /* output order:
	     *   face 0:  points 0 1 2
	     *   face 1:  points 3 2 1
	     *   face 2:  points 2 3 0
	     *   face 3:  points 1 0 3
	     */
	    for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
		vert_ord[num_vert] = (num_face + num_vert) % 4 ;
	    }
	    if ( num_face%2 == 1 ) {
		swap = vert_ord[0] ;
		vert_ord[0] = vert_ord[2] ;
		vert_ord[2] = swap ;
	    }

	    for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
		COPY_COORD3( face_pt[num_vert], obj_pt[vert_ord[num_vert]] ) ;
	    }
	    lib_output_polygon( 3, face_pt ) ;
	}
    }

    else {
	/* Create sub-tetrahedra */

	/* find opposite corners of a cube to form sub-tetrahedra */
	for ( x_dir = -1 ; x_dir <= 1 ; x_dir += 2 ) {
	    for ( y_dir = -1 ; y_dir <= 1 ; y_dir += 2 ) {
		for ( z_dir = -1 ; z_dir <= 1 ; z_dir += 2 ) {
		    if ( x_dir*y_dir*z_dir == 1 ) {
			sub_center[X] =
				center[X] + (double)x_dir * center[W] / 2.0 ;
			sub_center[Y] =
				center[Y] + (double)y_dir * center[W] / 2.0 ;
			sub_center[Z] =
				center[Z] + (double)z_dir * center[W] / 2.0 ;
			sub_center[W] = center[W] / 2.0 ;

			create_tetra( depth-1, sub_center ) ;
		    }
		}
	    }
	}
    }
}

int
main(argc,argv)
    int argc ;
    char *argv[] ;
{
    COORD3  back_color, tetra_color, light ;
    COORD3  from, at, up ;
    COORD4  center_pt ;

    PLATFORM_INIT(SPD_TETRA);

    /* Start by defining which raytracer we will be using */
    if ( lib_get_opts( argc, argv, &size_factor, &raytracer_format,
	    &output_format ) ) {
	return 0;
    }
#ifdef OUTPUT_TO_FILE
    /* no stdout, so write to a file! */
    if (raytracer_format == OUTPUT_VIDEO)
	stdout_file = stdout;
    else
	stdout_file = fopen("Tetra.out","w");
#endif /* OUTPUT_TO_FILE */
    lib_set_output_file(stdout_file);

    if (raytracer_format == OUTPUT_RTRACE ||
	raytracer_format == OUTPUT_PLG)
	lib_set_raytracer(OUTPUT_DELAYED);
    else
	lib_set_raytracer(raytracer_format);

    /* 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, 1.022846, -3.177154, -2.174512 ) ;
    SET_COORD3( at, -0.004103, -0.004103, 0.216539 ) ;
    SET_COORD3( up, -0.816497, -0.816497, 0.816497 ) ;
    lib_output_viewpoint( from, at, up, 45.0, 1.0, 1.0, 512, 512);

    /* output light source */
    SET_COORD4( light, 1.876066, -18.123936, -5.000422, 1.0 ) ;
    lib_output_light( light ) ;

    /* output tetrahedron color - red */
    SET_COORD3( tetra_color, 1.0, 0.2, 0.2 ) ;
    lib_output_color(NULL, tetra_color, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0);

    /* compute and output tetrahedral object */
    SET_COORD4( center_pt, 0.0, 0.0, 0.0, 1.0 ) ;
    create_tetra( size_factor, center_pt ) ;

    /* Make sure everything is cleaned up */
    if (raytracer_format == OUTPUT_RTRACE ||
	raytracer_format == OUTPUT_PLG) {
	lib_set_raytracer(raytracer_format);
	lib_flush_definitions();
    }

#ifdef OUTPUT_TO_FILE
    /* no stdout, so close our output! */
    if (stdout_file)
	fclose(stdout_file);
#endif /* OUTPUT_TO_FILE */
    if (raytracer_format == OUTPUT_VIDEO)
	display_close(1);

    PLATFORM_SHUTDOWN();

    return EXIT_SUCCESS ;
}


