/*
======================================================================
tetra.c

Ernie Wright  02 Jun 05

This version has been modified to fit your TV.
====================================================================== */

/*
 * 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
 *
 * 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 "lwmod.h"
#include "def.h"
#include "lib.h"

static  int     size_factor;
static char *spdName = "Tetra";


/* Create tetrahedrons recursively */
static 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 ;

    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 ) {
            if (depth==size_factor)
               if ( lwMonitorStep( 1 ))
                  return;
            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 ) ;
               }
            }
         }
      }
    }
}


void spdTetra( int sf )
{
    double  lscale;
    COORD3  back_color, tetra_color ;
    COORD3  from, at, up ;
    COORD4  center_pt, light ;


    size_factor = sf;

    /* 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);

    /*
     * For raytracers that don't scale the light intensity,
     * we will do it for them
     */
    #define NUM_LIGHTS    1
    lscale = 1.0 / sqrt(NUM_LIGHTS);

    /* output light source */
    SET_COORD4( light, 2.0, -18.0, -5.0, lscale) ;
    lib_output_light( light ) ;

    /* output tetrahedron color - red */
    SET_COORD3( tetra_color, 1.0, 0.2, 0.2 ) ;
    lib_output_color(spdName, 1, 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 ) ;
    lwMonitorBegin( spdName, "Creating tetra geometry.", 7 );
    create_tetra( size_factor, center_pt ) ;
    lwMonitorDone();
}
