/**************************************************************************
  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	: misc.c
  Description	: Fonctions diverses
**************************************************************************/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <malloc.h>
#include "types.h"
#include "macros.h"
#include "rad.h"
#include "load_image.h"
#include "misc.h"

#define MAX_CLIP	0.9999
#define MIN_CLIP	0.0001
#define ONE_THIRD	0.3333333333
#define	MS_CONST	0.2
#define DEG2RAD		0.017453

float	texprops[] = {	TX_MINFILTER, TX_MIPMAP_BILINEAR,
			TX_MAGFILTER, TX_BILINEAR,
			TX_WRAP, TX_REPEAT, TX_NULL };


void *arr_realloc(void	*array,
		  int	cur,
		  int	*tot,
		  int	el_size,
		  int	blck_sz)
/**************************************************************************
  But	: Alloue cur places dans le tableau array.
	  Il s'agit de cur places en tout dans le tableau, les places deja
	  occupees y comprises
	  Ne fait rien si il reste de la place dans le tableau, sinon
	  alloue un bloc de blck_sz elements de plus.
  Entree: array :   le tableau
	  cur :	    le nombre d'elements qui doivent etre disponibles au retour
  	  tot :	    le pointeur sur le nombre total de place dans le tableau
	  el_size : la taille d'un element
	  blck_sz : le nombre d'element pour un bloc
  Sortie: le pointeur sur la nouvelle zone memoire
**************************************************************************/
{
  void  *n_array;

  if (cur > *tot)
    {
      *tot += blck_sz;
      if (*tot > blck_sz)
        n_array = (void *)realloc(array, (*tot) * el_size);
      else
	n_array = (void *)malloc((*tot) * el_size);
      assert(n_array);

      return n_array;
    };
  
  return array;
}

void get_tot_size_obj(OBJECT_OP object, float *ts, int *tp)
/**************************************************************************
  But	: Calcule la somme des tailles de tous les poygones d'un objet
  Entree: object    : l'objet
	  ts	    : la taille totale
	  tp	    : le nombre total de cotes additionnes
  Sortie: neant
**************************************************************************/
{
  OBJECT_OP obj;
  TRIANGLEP polys;
  VECTOR    ea, eb, ab;
  float	    nea, neb, nab, tot;
  int	    tot_pol, i;

  *ts = 0.0;
  *tp = 0;

  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	{
	  get_tot_size_obj(obj, &tot, &tot_pol);
	  *ts += tot;
	  *tp += tot_pol;
	}
      break;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	{
	  get_tot_size_obj(obj, &tot, &tot_pol);
	  *ts += tot;
	  *tp += tot_pol;
	}
      break;
    case pure_type:
      polys = object->object.pure.polys;
      for(i = 0; i < object->object.pure.nb_polys; i++)
	{
	  VERTEXP   vertex = polys[i].tri.poly.vertex;

	  V_sub(ea, vertex[0].point, vertex[2].point);
	  V_sub(eb, vertex[1].point, vertex[2].point);
	  V_sub(ab, vertex[0].point, vertex[1].point);
  
	  nea = V_norm(ea);
	  neb = V_norm(eb);
	  nab = V_norm(ab);
  
	  *ts += nea + neb + nab;
	  *tp += 3;
	}
      break;
    default:
      break;
  }
}

float get_min_size(SCENE_OP scene)
/**************************************************************************
  But	: Calcule la taille minimale que peut atteindre un polygone
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  float tot = 0.0;
  int	tot_pol = 0;

  get_tot_size_obj(scene->scene, &tot, &tot_pol);

  return (tot/(float)tot_pol * MS_CONST);
}

void i_poly(TRIANGLE	*poly,
	    VECTOR	c,
	    VECTOR	a,
	    VECTOR	b)
/**************************************************************************
  But	: Initialise le polygone
  Entree: poly	: le polygone
  	  a,b,c	: les trois coins
  	  n1, n2, n3 :	les normes des 3 cotes
  Sortie: neant
**************************************************************************/
{
  float  naxb, dum;
  VECTOR ea, eb, ab, axb, bxa, vdum;
  POLYP	 pol = &(poly->tri.poly);

  pol->nb_v   = 3;
  pol->nb_tot = 0;
  pol->vertex = (VERTEXP)arr_realloc(pol->vertex, pol->nb_v,
				     &(pol->nb_tot), sizeof(VERTEX),
				     VERT_BLOCK_SIZE);
  assert(pol->vertex);

  V_copy(pol->vertex[0].point, c);
  V_copy(pol->vertex[1].point, a);
  V_copy(pol->vertex[2].point, b);

  pol->vertex[0].id = 2.0;
  pol->vertex[1].id = 3.0;
  pol->vertex[2].id = 4.0;

  V_sub(ea, a, c);
  V_sub(eb, b, c);
  V_sub(ab, a, b);
  
  V_cross(axb, ea, eb);
  naxb = V_norm(axb);
  dum = 1.0/(naxb * naxb);
  pol->surface = naxb*0.5;
  V_scale(pol->normal, (1.0/naxb), axb);
  pol->d = -V_dot(pol->normal, pol->vertex[0].point);

  V_cross(pol->sa, eb, axb);
  V_scale(pol->sa, dum, pol->sa);

  V_cross(bxa, eb, ea);
  V_cross(pol->sb, ea, bxa);
  V_scale(pol->sb, dum, pol->sb);

  V_null(pol->vertex[0].B);
  V_null(pol->vertex[1].B);
  V_null(pol->vertex[2].B);
  V_null(pol->vertex[0].dB);
  V_null(pol->vertex[1].dB);
  V_null(pol->vertex[2].dB);
  V_null(pol->vertex[0].rB);
  V_null(pol->vertex[1].rB);
  V_null(pol->vertex[2].rB);

  pol->sons = NULL;
};

void init_poly(TRIANGLE	*poly,
	       VECTOR	a,
	       VECTOR	b,
	       VECTOR	c)
/**************************************************************************
  But	: Prepare les donnees pour l'initialisation du polygone
  Entree: poly	: le polygone
  	  a,b,c	: les trois coins
  Sortie: neant
**************************************************************************/
{
  VECTOR e[3];
  float  n[3];

  poly->type = poly_type;

  V_sub(e[0], b, a);
  V_sub(e[1], c, a);
  V_sub(e[2], c, b);
  n[0] = V_norm(e[0]);
  n[1] = V_norm(e[1]);
  n[2] = V_norm(e[2]);
  
  if ((n[0] > n[1]) && (n[0] > n[2]))
    i_poly(poly, c, a, b);
  else if (n[1] > n[2])
    i_poly(poly, b, c, a);
  else
    i_poly(poly, a, b, c);
};

void M4D_mul(Matrix res, Matrix left, Matrix right)
/**************************************************************************
  But	: Multiple left par right et met le resultat dans res
  Entree: res	: la matrice resultante
	  left  : la matrice gauche
	  right	: la matrice droite
  Sortie: neant
**************************************************************************/
{
  register int i, j, k;
  Matrix   product;

  for (i = 0; i < 4; ++i) {
    for (j = 0; j < 4; ++j) {
      product[i][j] = 0.0;
      for (k = 0; k < 4; ++k) {
	product[i][j] += ((double)left[i][k]*(double)right[k][j]);  
      }
    }
  }

  M4D_copy(res, product);
};

void M4D_transform_point(VECTOR res, VECTOR vector, Matrix matrix)
/**************************************************************************
  But	: Multiplie le vecteur par la matrice
  Entree: res	    : le vecteur resultant
	  vector    : le vecteur
	  matrix    : la matrice
  Sortie: neant
**************************************************************************/
{
  float	 inv, local[4];

  local[0] = 	   matrix[0][0] * vector[0] +
                   matrix[1][0] * vector[1] +
                   matrix[2][0] * vector[2] +
		   matrix[3][0];

  local[1] = 	   matrix[0][1] * vector[0] +
                   matrix[1][1] * vector[1] +
                   matrix[2][1] * vector[2] +
		   matrix[3][1];

  local[2] = 	   matrix[0][2] * vector[0] +
                   matrix[1][2] * vector[1] +
                   matrix[2][2] * vector[2] +
		   matrix[3][2];

  local[3] = 	   matrix[0][3] * vector[0] +
                   matrix[1][3] * vector[1] +
                   matrix[2][3] * vector[2] +
		   matrix[3][3];

  inv = 1.0/local[3];
  V_init(res, local[0]*inv,
	      local[1]*inv,
	      local[2]*inv);
};

void M4D_transform_point_4D(VECTOR_4D res, VECTOR vector, Matrix matrix)
/**************************************************************************
  But	: Multiplie le vecteur par la matrice
  Entree: res	    : le vecteur resultant (4D)
	  vector    : le vecteur
	  matrix    : la matrice
  Sortie: neant
**************************************************************************/
{
  res[0] = 	   matrix[0][0] * vector[0] +
                   matrix[1][0] * vector[1] +
                   matrix[2][0] * vector[2] +
		   matrix[3][0];

  res[1] = 	   matrix[0][1] * vector[0] +
                   matrix[1][1] * vector[1] +
                   matrix[2][1] * vector[2] +
		   matrix[3][1];

  res[2] = 	   matrix[0][2] * vector[0] +
                   matrix[1][2] * vector[1] +
                   matrix[2][2] * vector[2] +
		   matrix[3][2];

  res[3] = 	   matrix[0][3] * vector[0] +
                   matrix[1][3] * vector[1] +
                   matrix[2][3] * vector[2] +
		   matrix[3][3];
};

BOOLEAN M4D_invert(Matrix inverse, Matrix mat)
/**************************************************************************
  But	: Calcule l'inverse d'une matrice 4D
	  Code par Greg Ward, tire de mat4.c, Radiance
  Entree: inverse   : la matrice inversee
	  mat	    : la matrice
  Sortie: TRUE si l'inverse existe, FALSE sinon
**************************************************************************/
{
#define SWAP(a,b,t) (t=a,a=b,b=t)

	register int i,j,k;
	register double temp;
	Matrix	 tmp;

	M4D_copy(tmp, mat);
	M4D_identity(inverse);

	for(i = 0; i < 4; i++) {
		/* Look for row with largest pivot and swap rows */
		temp = 0; j = -1;
		for(k = i; k < 4; k++)
			if(_ABS(tmp[k][i]) > temp) {
				temp = _ABS(tmp[k][i]);
				j = k;
				}
		if(j == -1)	/* No replacing row -> no inverse */
			return(FALSE);
		if (j != i)
			for(k = 0; k < 4; k++) {
				SWAP(tmp[i][k],tmp[j][k],temp);
				SWAP(inverse[i][k],inverse[j][k],temp);
				}

		temp = tmp[i][i];
		for(k = 0; k < 4; k++) {
			tmp[i][k] /= temp;
			inverse[i][k] /= temp;
			}
		for(j = 0; j < 4; j++) {
			if(j != i) {
				temp = tmp[j][i];
				for(k = 0; k < 4; k++) {
					tmp[j][k] -= tmp[i][k]*temp;
					inverse[j][k] -= inverse[i][k]*temp;
					}
				}
			}
		}
	return(TRUE);

#undef SWAP
}

void clip_rgb(VECTOR	co,
	      VECTOR	ci)
/**************************************************************************
  But	: Coupe une couleur RGB pour que ses composantes soient
	  comprisent entre 0.0 et 1.0, tout en guardant sa teinte et son
	  intensite. Sorti de la literature.
  Entree: co :	la couleur resultante
	  ci :	la couleur originale
  Sortie: neant
**************************************************************************/
{
  VECTOR    diff;
  float     ac, dm, tm;

  if ((ci[0] < 0.0) || (ci[0] > 1.0) ||
      (ci[1] < 0.0) || (ci[1] > 1.0) ||
      (ci[2] < 0.0) || (ci[2] > 1.0))
    {
      ac = (ci[0] + ci[1] + ci[2]) * ONE_THIRD;
      if (ac <= MIN_CLIP)
	co[0] = co[1] = co[2] = 0.0;
      else if (ac >= MAX_CLIP)
	co[0] = co[1] = co[2] = 1.0;
      else
	{
	  V_init(diff, ci[0] - ac, ci[1] - ac, ci[2] - ac);
	  dm = 1.0;
	  if (ci[0] > 1.0)
	    {
	      if ((tm = (MAX_CLIP - ac)/diff[0]) < dm)
		dm = tm;
	    }
	  else if (ci[0] < 0.0)
	    {
	      if ((tm = (MIN_CLIP - ac)/diff[0]) < dm)
		dm = tm;
	    };

	  if (ci[1] > 1.0)
	    {
	      if ((tm = (MAX_CLIP - ac)/diff[1]) < dm)
		dm = tm;
	    }
	  else if (ci[1] < 0.0)
	    {
	      if ((tm = (MIN_CLIP - ac)/diff[1]) < dm)
		dm = tm;
	    };

	  if (ci[2] > 1.0)
	    {
	      if ((tm = (MAX_CLIP - ac)/diff[2]) < dm)
		dm = tm;
	    }
	  else if (ci[2] < 0.0)
	    {
	      if ((tm = (MIN_CLIP - ac)/diff[2]) < dm)
		dm = tm;
	    };

	  V_scale(co, dm, diff);
	  V_init(co, ac+co[0], ac+co[1], ac+co[2]);
	};
      return;
    }
  else
    V_copy(co, ci);
}

void find_shooting_p(SHOOT_PP shoot, SCENE_OP scene, OBJECT_OP object)
/**************************************************************************
  But	: Trouve la premiere shooting patch dans l'objet, et rend le numero
	  du dernier objet pure visite
  Entree: shoot	    : la shooting patch rendue
	  scene	    : la scene
	  object    : l'objet a traiter
  Sortie: le numero du dernier objet pure visite
**************************************************************************/
{
  OBJECT_OP obj;
  int	    i;
  float	    E;

  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	find_shooting_p(shoot, scene, obj);
      break;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	find_shooting_p(shoot, scene, obj);
      break;
    case pure_type:
      for(i = 0; i < object->object.pure.nb_polys; i++)
	{
	  E = object->object.pure.polys[i].tri.poly.surface *
	      ExCp(object->object.pure.E, object->object.pure.color);
	  if (_ABS(E) > _ABS(shoot->E))
	    {
	      shoot->E     = E;
	      shoot->patch = &(object->object.pure.polys[i]);
	      shoot->o	   = object->object.pure.id;
	      shoot->p	   = i;
	    };
	}
      break;
    default:
      break;
  }
}

char *get_short_name(STR name)
/**************************************************************************
  But	: Rend la derniere partie du nom d'un fichier (apres le dernier '/')
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  char *last;

  last = strrchr(name, '/');
  if (last != NULL)
    return (char *)(&(last[1]));
  else
    return name;
}

int add_texture(SCENE_OP scene)
/**************************************************************************
  But	: Recopie la texture se trouvant a l'emplacement 0 du tableau des
	  textures a la fin du tableau (rajoute une texture)
  Entree: scene	    : la scene recevant la texture
  Sortie: l'indice de la nouvelle texture dans l'ENVIRONEMENT
**************************************************************************/
{
  scene->textures[scene->state.nb_tex] = scene->textures[0];
  scene->textures[0].name[0] = 0;
  scene->textures[0].image   = NULL;

  texdef2d(scene->state.nb_tex,
	   4,
	   scene->textures[scene->state.nb_tex].width,
	   scene->textures[scene->state.nb_tex].height,
	   scene->textures[scene->state.nb_tex].image,
	   7,
	   texprops);

  scene->state.nb_tex++;
  assert(scene->state.nb_tex <= MAX_TEX);

  return (scene->state.nb_tex-1);
}

int new_texture(STR filename, SCENE_OP scene)
/**************************************************************************
  But	: Charge une nouvelle texture en memoire et la stocke dans le
	  tableau des textures
  Entree: filename  : le nom du fichier contenant la texture
	  scene	    : la scene recevant la texture
  Sortie: l'indice de la nouvelle texture dans l'ENVIRONEMENT
**************************************************************************/
{
  int i;

  for(i = 1; i < scene->state.nb_tex; i++)
    if (strcmp(get_short_name(filename),
	       get_short_name(scene->textures[i].name)) == 0)
      return i;

  if (strrchr(filename, '/') == NULL)
    {
      strcpy(scene->textures[scene->state.nb_tex].name, tex_path);
      strcat(scene->textures[scene->state.nb_tex].name, "/");
      strcat(scene->textures[scene->state.nb_tex].name, filename);
    }
  else
    strcpy(scene->textures[scene->state.nb_tex].name, filename);
  scene->textures[scene->state.nb_tex].image =
  load_image(scene->textures[scene->state.nb_tex].name,
	     &(scene->textures[scene->state.nb_tex].width),
	     &(scene->textures[scene->state.nb_tex].height),
	     scene->textures[scene->state.nb_tex].meancolor);

  if (scene->textures[scene->state.nb_tex].image == NULL)
    return (-1);

  texdef2d(scene->state.nb_tex,
	   4,
	   scene->textures[scene->state.nb_tex].width,
	   scene->textures[scene->state.nb_tex].height,
	   scene->textures[scene->state.nb_tex].image,
	   7,
	   texprops);

  scene->state.nb_tex++;
  assert(scene->state.nb_tex <= MAX_TEX);

  return (scene->state.nb_tex-1);
}

