/**************************************************************************
  Copyright (C) 1992 Guy Moreillon
  All rights reserved.

  This software may be freely copied, modified, and redistributed
  provided that this copyright notice is preserved on all copies.

  You may not distribute this software, in whole or in part, as part of
  any commercial product without the express consent of the authors.

  There is no warranty or other guarantee of fitness of this software
  for any purpose.  It is provided solely "as is".
**************************************************************************/
/**************************************************************************

				RAD
		       (Interactive Radiosity)
		    	    Version 1.0
			       1992		    	    
		    	    
		  
		    	  Guy Moreillon
		    	  
**************************************************************************/

/**************************************************************************
  Fichier	: rinit.c
  Description	: Fonctions d'initialisation des donnees pour le calcul
**************************************************************************/

#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include "types.h"
#include "macros.h"
#include "rad.h"
#include "misc.h"
#include "radio.h"
#include "ray.h"
#include "rinit.h"

#define MIN_WIDTH	0.05
#define INIT_FOVY	500
#define INIT_NEAR	0.1
#define INIT_FAR	10000.0

float	tev_props[] = { TV_MODULATE, TV_NULL };

void prep_object(OBJECT_OP  object,
		 OBJECT_OP  dad,
		 VECTOR	    bbox0,
		 VECTOR	    bbox1)
/**************************************************************************
  But	: Prepare l'objet passe en parametre: defini sa boite englobante,
	  son treillis 3D.
  Entree: object:	l'objet
	  dad:		le pere de l'objet
	  bbox0, bbox1:	la boite englobante (2 points 3D)
  Sortie: neant
**************************************************************************/
{
  OBJECT_O  *obj;
  VECTOR    bboxmin, bboxmax, bboxi, bboxa;
  int	    i, j;

  object->dad = dad;

  V_init(bboxi,  HUGE,  HUGE,  HUGE);
  V_init(bboxa, -HUGE, -HUGE, -HUGE);

  switch (object->type) {
    case assembly_type :
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
        {
	  prep_object(obj, object, bboxmin, bboxmax);
	  V_min(bboxi, bboxi, bboxmin);
	  V_max(bboxa, bboxa, bboxmax);
        };
      V_copy(object->bbox[0], bboxi);
      V_copy(object->bbox[1], bboxa);
      break;
    case composite_type :
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
        {
	  prep_object(obj, object, bboxmin, bboxmax);
	  V_min(bboxi, bboxi, bboxmin);
	  V_max(bboxa, bboxa, bboxmax);
        };
      V_copy(object->bbox[0], bboxi);
      V_copy(object->bbox[1], bboxa);
      break;
    case pure_type :
      for(i = 0; i < object->object.pure.nb_polys; i++)
	for(j = 0; j < 3; j++)
	  {
	    V_min(bboxi, bboxi,
		  object->object.pure.polys[i].tri.poly.vertex[j].point);
      	    V_max(bboxa, bboxa,
		  object->object.pure.polys[i].tri.poly.vertex[j].point);
	  };

      for(i = 0; i < 3; i++)
	if (bboxi[i] == bboxa[i])
	  {
	    bboxi[i] -= MIN_WIDTH;
	    bboxa[i] += MIN_WIDTH;
	  };

      V_copy(object->bbox[0], bboxi);
      V_copy(object->bbox[1], bboxa);

      object->object.pure.grid = NULL;

      nb_tri += object->object.pure.nb_polys;
      nb_patch = nb_tri;
      break;
    default :
      Bye_bye("Unknown object type !\n");
  };

  V_copy(bbox0, bboxi);
  V_copy(bbox1, bboxa);
}

void map_on(TRIANGLEP	shoot,
	    float 	s,
	    float 	t,
	    VECTOR	p,
	    float 	*a,
	    float 	*b,
	    float 	*c)
/**************************************************************************
  But	: Place un point dans la patch specifiee
  Entree: shoot	    : la patch
	  p	    : le point (rendu)
	  a, b, c   : les valeurs pour calculer la B associee a ce point
  Sortie: neant
**************************************************************************/
{
  float     aa, bb, cc, tt;
  VECTOR    sc, pt;

  register  VERTEXP   vertex = shoot->tri.poly.vertex;

  tt = fsqrt(t);

  aa = 1.0 - tt; 
  bb = (1.0 - s) * tt;
  cc = s * tt;

  V_interp(p, aa, vertex[0].point,
	      bb, vertex[1].point);
  V_add_mul(p, p, cc, vertex[2].point);

  V_sub(pt, p, vertex[0].point);

  *a = V_dot(pt, shoot->tri.poly.sa);
  *b = V_dot(pt, shoot->tri.poly.sb);
  
  V_add(sc, shoot->tri.poly.sa, shoot->tri.poly.sb);
  *c = 1.0 - V_dot(pt, sc);
}

void find_avg_normal(OBJECT_OP object, VECTOR res)
/**************************************************************************
  But	: Calcule la normale moyenne de l'objet
  Entree: object    : l'objet
	  res	    : le resultat
  Sortie: neant
**************************************************************************/
{
  VECTOR tot;
  int	 i;

  V_null(tot);
  for(i = 0; i < object->object.pure.nb_polys; i++)
    V_add_self(tot, object->object.pure.polys[i].tri.poly.normal);

  V_scale(res, (1.0/(float)object->object.pure.nb_polys), tot);
}

void init_tex_map(OBJECT_OP object, SCENE_OP scene)
/**************************************************************************
  But	: Initialise les donnees de mapping de texture
  Entree: object    : l'objet
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  VECTOR    dir, ex, ey;

  if (object->object.pure.texmap.index < 0)
    {
      object->object.pure.texmap.scale = 1.0;
      return;					/* Pas de texture */
    }

  V_copy(dir, object->object.pure.texmap.s);
  V_copy(ey,  object->object.pure.texmap.t);

  if ((dir[0] == 0.0) && (dir[1] == 0.0) && (dir[2] == 0.0))
    {
      find_avg_normal(object, dir);
      if ((dir[0] == 0.0) && (dir[1] == 0.0) && (dir[2] == 0.0))
	{
	  V_init(dir, 1.0, 1.0, 1.0);
	  printf("Warning ! Default texture projection applied\n");
	}
    }

  if ((dir[0] == ey[0]) && (dir[1] == ey[1]) && (dir[2] == ey[2]))
    {
      V_init(dir, 1.0, 1.0, 1.0);
      V_init(ey,  0.0, 1.0, 0.0);
      printf("Warning ! Default texture projection applied\n");
    }

  V_normalize(dir, dir);
  V_cross(ex, dir, ey);
  V_normalize(ex, ex);
  V_cross(ey, ex, dir);
  V_normalize(ey, ey);

  V_copy(object->object.pure.texmap.s, ex);
  V_copy(object->object.pure.texmap.t, ey);
  object->object.pure.texmap.s[3] = -V_dot(ex, object->bbox[0]);
  object->object.pure.texmap.t[3] = -V_dot(ey, object->bbox[0]);
  V_copy(object->object.pure.color,
	 scene->textures[object->object.pure.texmap.index].meancolor);
}

void init_samples(TRIANGLE *tri)
/**************************************************************************
  But	: Initialise les points d'echantillonage d'un polygone
  Entree: tri	: le polygone
  Sortie: neant
**************************************************************************/
{
  register int i, j;

  tri->tri.poly.samples = (SAMPLE_ARR *)malloc(sizeof(SAMPLE_ARR));
  assert(tri->tri.poly.samples);
  for(i = 0; i < NB_SAMPLES; i++)
    for(j = 0; j < NB_SAMPLES; j++)
      {
	map_on(tri,
	       ((float )i + 0.5)/(float )NB_SAMPLES,
	       ((float )j + 0.5)/(float )NB_SAMPLES,
	       tri->tri.poly.samples->array[i][j].point,
	       &(tri->tri.poly.samples->array[i][j].a),
	       &(tri->tri.poly.samples->array[i][j].b),
	       &(tri->tri.poly.samples->array[i][j].c));
	tri->tri.poly.samples->array[i][j].el = NULL;
	if (tri->tri.poly.sons != NULL)
	  find_subdiv(tri->tri.poly.sons,
		      &(tri->tri.poly.samples->array[i][j]));
      };
}

void init_pures_in_obj(OBJECT_OP object, SCENE_OP scene)
/**************************************************************************
  But	: Initialise les objets pures d'un objet quelconque
  Entree: object    : l'objet
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  OBJECT_OP obj;
  int	    i;
 
  static int o = 0;

  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	init_pures_in_obj(obj, scene);
      o = 0;
      break;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	init_pures_in_obj(obj, scene);
      o = 0;
      break;
    case pure_type:
      o++;
      object->object.pure.id = o;
      init_tex_map(object, scene);
      for(i = 0; i < object->object.pure.nb_polys; i++)
	{
	  register  VERTEXP vertex = object->object.pure.polys[i].tri.poly.vertex;

	  object->object.pure.polys[i].dad = object;
	  V_scale(vertex[0].dB, object->object.pure.E, object->object.pure.color);
	  V_copy(vertex[1].dB, vertex[0].dB);
	  V_copy(vertex[2].dB, vertex[0].dB);

	  V_copy(vertex[0].B, vertex[0].dB);
	  V_copy(vertex[1].B, vertex[0].B);
	  V_copy(vertex[2].B, vertex[0].B);

	  V_null(vertex[0].rB);
	  V_null(vertex[1].rB);
	  V_null(vertex[2].rB);

	  vertex[0].update = FALSE;
	  vertex[1].update = FALSE;
	  vertex[2].update = FALSE;

	  init_samples(&(object->object.pure.polys[i]));
	}
      break;
    default:
      break;
  }
}

void init_tev()
/**************************************************************************
  But	: Initialise l'environement de texture
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  tevdef(1, 2, tev_props);
  tevbind(TV_ENV0, 1);
}

void build_i_viewing_m(Matrix rot, Matrix trans, Matrix viewing,
		       Matrix mod, Matrix imod,
		       VECTOR bboxi, VECTOR bboxa)
/**************************************************************************
  But	: Calcule les matrices de rotation et translation donnant
	  l'orientation et la position de l'observateur
  Entree: rot, trans: les matrices resultantes
	  viewing   : la matrice de viewing (rendue)
	  mod	    : la matrice de changement d'axe
	  imod	    : la matrice de changement d'axe inversee
	  pos	    : la position de l'observateur
	  point	    : le point d'interet
  Sortie: neant
**************************************************************************/
{
  VECTOR    v_dum, pos, point;
  VECTOR    bbi, bba;
  VECTOR    ex, ey, ez;

  M4D_transform_point(bbi, bboxi, mod);
  M4D_transform_point(bba, bboxa, mod);

  V_init(point,
	 (bbi[0]+bba[0])/2.0,
	 (bbi[1]+bba[1])/2.0,
	 (bbi[2]+bba[2])/2.0);
  V_init(pos, bbi[0], bba[1], bbi[2]);
  V_sub(v_dum, pos, point);
  v_dum[0] *= 3.0;
  v_dum[1] *= 3.0;
  v_dum[2] *= 3.0;
  V_add(pos, point, v_dum);

  V_sub(ez, pos, point);
  V_normalize(ez, ez);

  V_init(ey, 0.0, 1.0, 0.0);
  V_cross(ex, ey, ez);
  V_normalize(ex, ex);

  V_cross(ey, ez, ex);

  M4D_init(rot, ex[0],  ex[1],  ex[2], 0.0,
		ey[0],  ey[1],  ey[2], 0.0,
		ez[0],  ez[1],  ez[2], 0.0,
	       pos[0], pos[1], pos[2], 1.0);

  M4D_mul(rot, rot, imod);
  M4D_invert(viewing, rot);

  M4D_init(trans, 1.0, 0.0, 0.0, 0.0,
		  0.0, 1.0, 0.0, 0.0,
		  0.0, 0.0, 1.0, 0.0,
		  rot[3][0], rot[3][1], rot[3][2], 1.0);

  rot[3][0] = rot[3][1] = rot[3][2] = 0.0;
  rot[3][3] = 1.0;
}

void init_scene(SCENE_OP scene)
/**************************************************************************
  But	: Initialise les differentes variables de la scene et du programme
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  VECTOR    s_bboxi, s_bboxa;
  int	    i;

  for(i = 0; i < MAX_DEPTH; i++)
    scene->state.complex[i] = NULL;
  scene->state.o		= 1;
  scene->state.p		= 0;
  scene->state.pure		= NULL;
  scene->state.not_done		= TRUE;
  scene->state.shooting_p.E	= 0.0;
  scene->state.shooting_p.patch = NULL;
  scene->state.new_shoot.E	= 0.0;
  scene->state.new_shoot.patch	= NULL;
  scene->textures[0].name[0]	= 0;
  scene->textures[0].image	= NULL;

  if (!min_size_set)
    scene->state.min_size = get_min_size(scene);
				    /* Taille minimale pour une subdivision */
  scene->state.min_size_s = scene->state.min_size*scene->state.min_size;

  prep_object(scene->scene, NULL, s_bboxi, s_bboxa);

  build_i_viewing_m(scene->state.visu.rot,
		    scene->state.visu.trans,
		    scene->state.visu.viewing,
		    scene->state.visu.mod,
		    scene->state.visu.imod,
		    s_bboxi,
		    s_bboxa);

  scene->state.visu.fovy = INIT_FOVY;
  scene->state.visu.near = INIT_NEAR;
  scene->state.visu.far  = INIT_FAR;

  init_pures_in_obj(scene->scene, scene);

  init_grids(scene->scene);

  find_shooting_p(&(scene->state.shooting_p), scene, scene->scene);

  init_tev();
}
