/*
======================================================================
tree.c

Ernie Wright  02 Jun 05

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

/*
 * tree.c - Creates a tree using Aono & Kunii's generation method.
 *      (See IEEE CG&A May 1984).  A square polygon is placed beneath the
 *      tree to act as a field.  Seven light sources.
 *
 * Author:  Eric Haines
 *
 * size_factor determines the number of objects output.
 *      Total objects = 2**(SF+1)-1 cones and spheres + 1 square polygon.
 *
 *      size_factor     # spheres          # cones      # squares
 *           1               3                 3             1
 *           2               7                 7             1
 *           3              15                15             1
 *
 *          11            4095              4095             1
 */

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


static int size_factor;
static int output_format = 0;
static char *spdName = "Tree";

/* the following affect the shape of the tree */
#define BR_ANGLE_0              40.0
#define BR_ANGLE_1              25.0
#define BR_CONTR_0              0.65
#define BR_CONTR_1              0.70
#define BR_DIAMETER             0.67
#define DIV_ANGLE               140.0
#define WIDTH_HEIGHTH_RATIO     0.15

static  MATRIX  Rst_mx[2] ;

/* grow tree branches recursively */
static void
grow_tree(cur_mx, scale, depth)
MATRIX cur_mx;
double scale;
int depth;
{
    int i;
    COORD3 vec;
    COORD4 apex, base;
    MATRIX new_mx;

    /* output branch */
    SET_COORD3( vec, 0.0, 0.0, 0.0 ) ;
    lib_transform_point( base, vec, cur_mx ) ;
    base[W] = scale * WIDTH_HEIGHTH_RATIO ;

    SET_COORD3( vec, 0.0, 0.0, 1.0 ) ;
    lib_transform_point( apex, vec, cur_mx ) ;
    apex[W] = base[W] * BR_DIAMETER ;

    lib_output_cylcone( base, apex, output_format ) ;
    lib_output_sphere( apex, output_format ) ;

    if ( depth > 0 ) {
      --depth ;
      if ( lwMonitorStep( 1 ))
         return;

      for ( i = 0 ; i < 2 ; ++i ) {
         lib_matrix_multiply( new_mx, Rst_mx[i], cur_mx ) ;
         grow_tree( new_mx, scale * BR_DIAMETER, depth ) ;
      }
    }
}

/*
 * Set up matrices for growth of each branch with respect to the
 * parent branch, then grow each branch.
 */
static void
create_tree()
{
    int i;
    double branch_angle, branch_contraction, divergence;
    MATRIX ident_mx, temp1_mx, temp2_mx, tempr_mx, tempst_mx;

    for ( i = 0 ; i < 2 ; ++i ) {
      if ( i == 0 ) {
         branch_angle = BR_ANGLE_0 ;
         divergence = 90.0 ;
         branch_contraction = BR_CONTR_0 ;
      } else {
         branch_angle = BR_ANGLE_1 ;
         divergence = DIV_ANGLE + 90.0 ;
         branch_contraction = BR_CONTR_1 ;
      }

      /* rotate along X axis by branching angle */
      lib_create_rotate_matrix( temp1_mx, X_AXIS, branch_angle*PI/180.0 ) ;

      /* rotate along Z axis by divergence angle */
      lib_create_rotate_matrix( temp2_mx, Z_AXIS, divergence*PI/180.0 ) ;

      lib_matrix_multiply( tempr_mx, temp1_mx, temp2_mx ) ;

      /* include translation of branch, scaled */
      lib_create_identity_matrix( tempst_mx ) ;
      tempst_mx[0][0] = branch_contraction;
      tempst_mx[1][1] = branch_contraction;
      tempst_mx[2][2] = branch_contraction;
      tempst_mx[3][2] = 1.0;

      /* concatenate */
      lib_matrix_multiply( Rst_mx[i], tempr_mx, tempst_mx ) ;
    }

    /* set up initial matrix */
    lib_create_identity_matrix( ident_mx ) ;
    grow_tree( ident_mx, 1.0, size_factor ) ;
}


void spdTree( int sf )
{
    COORD3 field[4];
    COORD3 from, at, up;
    COORD3 back_color, tree_color;
    COORD4 light;
    double lscale;


    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, 4.5, 0.4, 2.0 ) ;
    SET_COORD3( at, 0.0, 0.0, 1.5 ) ;
    SET_COORD3( up, 0.0, 0.0, 1.0 ) ;
    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    7
    lscale = 1.0 / sqrt(NUM_LIGHTS);

    /* output light source */
    SET_COORD4( light, -5.0, 5.0, 50.0, lscale ) ;
    lib_output_light( light ) ;
    SET_COORD4( light, 30.0, -30.0, 30.0, lscale ) ;
    lib_output_light( light ) ;
    SET_COORD4( light, -40.0, -30.0, 20.0, lscale ) ;
    lib_output_light( light ) ;
    SET_COORD4( light, 10.0, 30.0, 40.0, lscale ) ;
    lib_output_light( light ) ;
    SET_COORD4( light, -30.0, 40.0, 10.0, lscale ) ;
    lib_output_light( light ) ;
    SET_COORD4( light, 50.0, 25.0, 20.0, lscale ) ;
    lib_output_light( light ) ;
    SET_COORD4( light, -10.0, -60.0, 30.0, lscale ) ;
    lib_output_light( light ) ;

    /* output field polygon - green */
    SET_COORD3( back_color, 0.2, 0.7, 0.2 ) ;
    lib_output_color(spdName, 1, back_color, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0);
    SET_COORD3( field[0],  50.0,  50.0, 0.0 ) ;
    SET_COORD3( field[1], -50.0,  50.0, 0.0 ) ;
    SET_COORD3( field[2], -50.0, -50.0, 0.0 ) ;
    SET_COORD3( field[3],  50.0, -50.0, 0.0 ) ;
    lib_output_polygon( 4, field ) ;

    /* set up tree color - brown */
    SET_COORD3( tree_color, 0.55, 0.4, 0.2 ) ;
    lib_output_color(spdName, 2, tree_color, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0);

    /* create tree */
    lwMonitorBegin( spdName, "Creating tree.", 1 << size_factor );
    create_tree();
    lwMonitorDone();
}
