/**************************************************************************
  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	: display.c
  Description	: Routines d'affichage
**************************************************************************/

#include <stdio.h>
#include <gl.h>
#include "types.h"
#include "macros.h"
#include "interface.h"
#include "misc.h"
#include "rad.h"
#include "display.h"

float dis2_4D(VECTOR_4D p1, VECTOR_4D p2)
/**************************************************************************
  But	: Calcule la distance au carre entre deux points en coordonnees 
	  homogenes
  Entree: p1, p2    : les points
  Sortie: la distance au carre
**************************************************************************/
{
  VECTOR pp1, pp2, v;
  float  iw;

  iw = 1.0/p1[3];
  V_scale(pp1, iw, p1);
  iw = 1.0/p2[3];
  V_scale(pp2, iw, p2);
  V_sub(v, pp2, pp1);
  
  return V_dot(v, v);
}

void trans_bbox(VECTOR bbox[2], VECTOR_4D tbbox[8], SCENE_OP scene)
/**************************************************************************
  But	: Transforme les huits points de la bounding box passee dans leur
	  coordonnees uniformes
  Entree: bbox	    : bbox originale
	  tbbox	    : les huits points transformes
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  int	    i;
  VECTOR    local;

  for(i = 0; i < 8; i++)
    {
      V_init(local, bbox[((i>>2)&1)][0],
		    bbox[((i>>1)&1)][1],
		    bbox[(i&1)][2]);
      M4D_transform_point_4D(tbbox[i], local, scene->state.visu.world2unif);
    }
}

BOOLEAN visible(OBJECT_OP object, SCENE_OP scene)
/**************************************************************************
  But	: Verifie si l'objet est visible.
  Entree: object    : l'objet
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  VECTOR_4D points[8];
  int	    i, j;

  trans_bbox(object->bbox, points, scene);

  for(i = 0; i < 3; i ++)
    {
      BOOLEAN out = TRUE;

      for(j = 0; (j < 8) && out; j++)
	out = out && (points[j][i] > points[j][3]);

      if (out)
	return FALSE;		/* Completement a cote (en dehors) */

      out = TRUE;

      for(j = 0; (j < 8) && out; j++)
	out = out && (points[j][i] < -points[j][3]);

      if (out)
	return FALSE;		/* Completement en dehors */
    }

  return TRUE;			/* Completement a l'interieur */
}

int p_visible(TRIANGLEP tri, SCENE_OP scene)
/**************************************************************************
  But	: Verifie si le polygone est visible.
  Entree: tri	    : le polygone	  
	  scene	    : la scene
  Sortie: Nombre de niveaux de subdivisions a afficher
	  0 -> polygone invisible
	  1 -> seulement le polygone
	  etc..
**************************************************************************/
{
  VECTOR_4D points[3];
  int	    i, j;
  float	    maxd, dis2;

  for(i = 0; i < 3; i++)
    M4D_transform_point_4D(points[i],
			   tri->tri.poly.vertex[i].point,
			   scene->state.visu.world2unif);

  for(i = 0; i < 3; i ++)
    {
      BOOLEAN out = TRUE;

      for(j = 0; (j < 3) && out; j++)
	out = out && (points[j][i] > points[j][3]);

      if (out)
	return 0;		/* Completement a cote (en dehors) */

      out = TRUE;

      for(j = 0; (j < 3) && out; j++)
	out = out && (points[j][i] < -points[j][3]);

      if (out)
	return 0;		/* Completement en dehors */
    }

  maxd = 0.0;
  dis2 = dis2_4D(points[0], points[1]);
  maxd = MAX(dis2, maxd);
  dis2 = dis2_4D(points[0], points[2]);
  maxd = MAX(dis2, maxd);
  dis2 = dis2_4D(points[1], points[2]);
  maxd = MAX(dis2, maxd);

  if (maxd <= min_disp2_size)
    return 1;
  else
    return (1 + (int)(flog(maxd/min_disp2_size)/flog(M_E)));
}

int  disp_tri(OBJECT_OP	    object,
	      TRIANGLEP	    tri,
	      SCENE_OP	    scene,
	      SELECTIONP    select,
	      BOOLEAN	    tex_on,
	      int	    cur_level,
	      int	    max_level)
/**************************************************************************
  But	: Dessine le polygone tri, eventuellement en wireframe egalement
  Entree: object    : l'objet
	  tri	    : le polygone
	  scene	    : la scene
	  select    : la selection courante
	  tex_on    : TRUE si l'objet a une texture
	  cur_level : niveau courant de recursion
	  max_level : niveau max de recursion
  Sortie: le nombre de triangles effectivement affiches
**************************************************************************/
{
  int	    i, j;
  int	    nb_tri_disp = 0;

  if (tri->type == poly_type)
    {
      if (tri->tri.poly.sons != NULL)
	{
          int p_d = p_visible(tri, scene);

	  if (p_d == 0)
	    return 0;

	  if (p_d > 1)
            return disp_tri(object,
			    tri->tri.poly.sons,
			    scene, select, tex_on, 2, p_d);
	}

      if (tri == select->tri)
	{
	  VECTOR sel_color;

	  nb_tri_disp++;

	  V_init(sel_color, 1.0, 1.0, 1.0);

	  setpattern(NOPATTERN);
	  bgnclosedline();
          for(i = 0; i < 3; i++)
	    {
	      c3f(sel_color);
	      v3f(tri->tri.poly.vertex[i].point);
	    };
	  endclosedline();
	  setpattern(PATTERN);

	  bgnpolygon();
          for(i = 0; i < 3; i++)
	    {
	      switch(i) {
		case 0:
		  V_init(sel_color, 1.0, 0.3, 0.3);
		  break;
		case 1:
		  V_init(sel_color, 0.3, 1.0, 0.3);
		  break;
		case 2:
		  V_init(sel_color, 0.3, 0.3, 1.0);
		  break;
		default:
		  break;
	      }
	      c3f(sel_color);
	      v3f(tri->tri.poly.vertex[i].point);
	    };
	  endpolygon();
	}
      else
        {
	  nb_tri_disp++;

	  bgnpolygon();
          for(i = 0; i < 3; i++)
	    {
    	      VECTOR    rgb;
	      if (tex_on)
		{
		  V_interp(rgb,
			   object->object.pure.ro,
			   tri->tri.poly.vertex[i].rB,
			   object->object.pure.E,
			   object->object.pure.color);
				
		  clip_rgb(rgb, rgb);
		}
	      else
	        clip_rgb(rgb, tri->tri.poly.vertex[i].B);
	      c3f(rgb);
	      v3f(tri->tri.poly.vertex[i].point);
	    };
	  endpolygon();
        };
    }
  else
    for(j = 0; j < 4; j++)
      if ((tri->tri.subdiv.elements[j].sons != NULL) &&
	  (cur_level < max_level))
	nb_tri_disp += disp_tri(object,
				tri->tri.subdiv.elements[j].sons,
				scene, select, tex_on, cur_level+1, max_level);
      else
        {
	  VERTEXP   array = tri->tri.subdiv.dad->tri.poly.vertex;
	  VECTOR    sel_color;

	  nb_tri_disp++;

	  bgnpolygon();
	  for(i = 0; i < 3; i++)
	    {
	      if ((select->elem == &(tri->tri.subdiv.elements[j])) &&
		  (select->tri == tri->tri.subdiv.dad))
		{
		  switch(i) {
		    case 0:
		      V_init(sel_color, 1.0, 0.3, 0.3);
		      break;
		    case 1:
		      V_init(sel_color, 0.3, 1.0, 0.3);
		      break;
		    case 2:
		      V_init(sel_color, 0.3, 0.3, 1.0);
		      break;
		    default:
		      break;
		  }
		}
	      else
		{
		  if (tex_on)
		    {
		      V_interp(sel_color,
			       object->object.pure.ro,
			       array[tri->tri.subdiv.elements[j].vertex[i]].rB,
			       object->object.pure.E,
			       object->object.pure.color);
				
		      clip_rgb(sel_color, sel_color);
		    }
		  else
		   clip_rgb(sel_color, array[tri->tri.subdiv.elements[j].vertex[i]].B);
		}
	      c3f(sel_color);
	      v3f(array[tri->tri.subdiv.elements[j].vertex[i]].point);
	    };
	  endpolygon();

	  if (select->tri == tri->tri.subdiv.dad)
	    {
	      V_init(sel_color, 1.0, 1.0, 1.0)
	      setpattern(NOPATTERN);
	      bgnclosedline();
	      for(i = 0; i < 3; i++)
		{
		  c3f(sel_color);
		  v3f(array[tri->tri.subdiv.elements[j].vertex[i]].point);
		};
	      endclosedline();
	      setpattern(PATTERN);
	    };
        };

  return nb_tri_disp;
}

int disp_obj(OBJECT_OP object, SCENE_OP scene, SELECTIONP select)
/**************************************************************************
  But	: Dessine l'objet object, avec un pattern si necessaire
  Entree: object    : l'objet
	  select    : la selection courante
  Sortie: le nombre de triangles affiches
**************************************************************************/
{
  static float	    ss_props[3] = {0.0, 0.0, 0.0};

  BOOLEAN	tex_on = FALSE;
  OBJECT_OP	obj;
  register int	i, nb_tri_disp = 0;

  if (!visible(object, scene))
    return;

  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	nb_tri_disp += disp_obj(obj, scene, select);
      break;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	nb_tri_disp += disp_obj(obj, scene, select);
      break;
    case pure_type:
      if (select->pure == object)
	setpattern(PATTERN);
      else if ((object->object.pure.texmap.index > 0) && tex_yes)
	{
	  long	    mm;
	  Matrix    transform;
	  float	    s = object->object.pure.texmap.scale,
		    w = (float)scene->textures[object->object.pure.texmap.index].width,
		    h = (float)scene->textures[object->object.pure.texmap.index].height;

	  texgen(TX_S, TG_LINEAR, object->object.pure.texmap.s);
	  texgen(TX_S, TG_ON, NULL);
	  texgen(TX_T, TG_LINEAR, object->object.pure.texmap.t);
	  texgen(TX_T, TG_ON, NULL);
	  texbind(TX_TEXTURE_0, object->object.pure.texmap.index);
	  scrsubdivide(SS_DEPTH, ss_props);
	  tevbind(TV_ENV0, 1);
	  mm = getmmode();
	  mmode(MTEXTURE);
	  M4D_init(transform,
		   s*h/w, 0.0, 0.0, 0.0,
		   0.0, s, 0.0, 0.0,
		   0.0, 0.0, s, 0.0,
		   0.0, 0.0, 0.0, s);
	  loadmatrix(transform);
	  mmode(mm);
	  tex_on = TRUE;
	}

      for(i = 0; i < object->object.pure.nb_polys; i++)
	nb_tri_disp += disp_tri(object,
			        &(object->object.pure.polys[i]),
			        scene, select, tex_on, 1, 2);

      texbind(TX_TEXTURE_0, TX_NULL);
      scrsubdivide(SS_OFF, ss_props);
      setpattern(NOPATTERN);
      break;
    default:
      break;
  }

  return nb_tri_disp;
}

