/**************************************************************************
  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".
**************************************************************************/
/**************************************************************************

			     PREPRAD
		     (Scene preparation program)
		    	    Version 1.0
			       1992		    	    
		    	    
		  
		    	  Guy Moreillon
		    	  
**************************************************************************/

/**************************************************************************
  Fichier	: init.c
  Description	: Routines d'initialisation de la scene
**************************************************************************/

#include <stdio.h>
#include <assert.h>

#include <types.h>
#include <macros.h>
#include <misc.h>
#include "preprad.h"
#include "ray.h"
#include "init.h"

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

float	tev_props[] = { TV_MODULATE, TV_NULL };

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_tev()
/**************************************************************************
  But	: Initialise l'environement de texture
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  tevdef(1, 2, tev_props);
  tevbind(TV_ENV0, 1);
}

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_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;
 
  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	init_pures_in_obj(obj, scene);
      break;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	init_pures_in_obj(obj, scene);
      break;
    case pure_type:
      init_tex_map(object, scene);
      for(i = 0; i < object->object.pure.nb_polys; i++)
	{
	  object->object.pure.polys[i].dad = object;
	  object->object.pure.polys[i].tri.poly.samples = NULL;
	}
      break;
    default:
      break;
  }
}

void prep_object(OBJECT_OP  object,
		 OBJECT_OP  dad,
		 VECTOR	    bbox0,
		 VECTOR	    bbox1)
/**************************************************************************
  But	: Prepare l'objet passe en parametre: defini sa boite englobante
  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;
      break;
    default :
      Bye_bye("Unknown object type !\n");
  };

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

void init_scene(SCENE_OP scene)
/**************************************************************************
  But	: Initialise la scene
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  int	    i;
  VECTOR    v_dum, pos, point, s_bboxi, s_bboxa;
  VECTOR    v_dum5, v_dum6, bbi, bba;
  VECTOR    ex, ey, ez;

  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;

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

  M4D_transform_point(bbi, s_bboxi, scene->state.visu.mod);
  M4D_transform_point(bba, s_bboxa, scene->state.visu.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(scene->state.visu.viewing,
	    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(scene->state.visu.viewing,
	  scene->state.visu.viewing,
	  scene->state.visu.imod);

  M4D_invert(scene->state.visu.viewing, scene->state.visu.viewing);

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

  init_tev();
}

