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

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

void disp_bbox(OBJECT_OP object)
/**************************************************************************
  But	: Affiche la bounding box d'un objet
  Entree: object    : l'objet
  Sortie: neant
**************************************************************************/
{
  VECTOR	dum;

  if (Marked(object))
    {
      cpack(0x0000aa);
      linewidth(2);
    }
  else
    cpack(0x006060);

  bgnline();
    V_copy(dum, object->bbox[0]);
    v3f(dum);
    dum[0] = object->bbox[1][0];
    v3f(dum);
    dum[1] = object->bbox[1][1];
    v3f(dum);
    dum[2] = object->bbox[1][2];
    v3f(dum);
    dum[0] = object->bbox[0][0];
    v3f(dum);
    dum[1] = object->bbox[0][1];
    v3f(dum);
    dum[2] = object->bbox[0][2];
    v3f(dum);
    dum[1] = object->bbox[1][1];
    v3f(dum);
    dum[0] = object->bbox[1][0];
    v3f(dum);
  endline();
  bgnline();
    dum[1] = object->bbox[0][1];
    v3f(dum);
    dum[2] = object->bbox[1][2];
    v3f(dum);
    dum[1] = object->bbox[1][1];
    v3f(dum);
  endline();
  bgnline();
    dum[1] = object->bbox[0][1];
    v3f(dum);
    dum[0] = object->bbox[0][0];
    v3f(dum);
  endline();
  bgnline();
    dum[1] = object->bbox[1][1];
    v3f(dum);
    dum[2] = object->bbox[0][2];
    v3f(dum);
  endline();

  linewidth(1);
}

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

int nb_tri_disp;

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 */
}

BOOLEAN p_visible(TRIANGLEP tri, SCENE_OP scene)
/**************************************************************************
  But	: Verifie si le polygone est visible.
  Entree: tri	    : le polygone	  
	  scene	    : la scene
  Sortie: TRUE si le polygone est visible, FALSE sinon
**************************************************************************/
{
  VECTOR_4D points[3];
  int	    i, j;

  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 FALSE;		/* 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 FALSE;		/* Completement en dehors */
    }

  return TRUE;
}

void disp_tri(OBJECT_OP	    object,
	      TRIANGLEP	    tri,
	      BOOLEAN	    select,
	      BOOLEAN	    d_norm,
	      SCENE_OP	    scene,
	      BOOLEAN	    tex_on)
/**************************************************************************
  But	: Dessine le polygone tri
  Entree: object    : l'objet
	  tri	    : le polygone
	  select    : TRUE si l'objet pere est selectionne
	  d_norm    : TRUE si il faut afficher la normale
	  scene	    : la scene
	  tex_on    : TRUE si l'objet a une texture
  Sortie: neant
**************************************************************************/
{
  VECTOR    red, blue;
  int	    i, j;

  if (!p_visible(tri, scene))
    return;

  V_init(blue, 0.0, 0.0, 1.0)
  V_init(red, 1.0, 0.0, 0.0)

  nb_tri_disp++;

  bgnpolygon();
  for(i = 0; i < 3; i++)
    {
      VECTOR    rgb;

      if (tri->tri.poly.samples)
	c3f(red);
      else if (select)
	c3f(blue);
      else
        n3f(tri->tri.poly.normal);
      v3f(tri->tri.poly.vertex[i].point);
    };
  endpolygon();

  if (d_norm && (BOOLEAN)tri->tri.poly.samples)
    {
      VECTOR center, end;
      VECTOR green;

      V_init(green, 0.0, 1.0, 0.0);

      V_add(center, tri->tri.poly.vertex[0].point,
		    tri->tri.poly.vertex[1].point);
      V_add(center, center,
		    tri->tri.poly.vertex[2].point);
      V_scale(center, 0.33333, center);

      V_add(end, center, tri->tri.poly.normal);

      bgnline();
        c3f(green);
	v3f(center);
	c3f(green);
	v3f(end);
      endline();
    }
}

void disp_obj(OBJECT_OP object,
	      BOOLEAN select,
	      BOOLEAN hide,
	      BOOLEAN d_bbox,
	      BOOLEAN d_norm,
	      BOOLEAN tex_yes,
	      SCENE_OP scene)
/**************************************************************************
  But	: Dessine l'objet object
  Entree: object    : l'objet
	  select    : TRUE si le pere est selectionne
	  hide	    : TRUE si le pere est cache
	  d_bbox    : TRUE si il faut dessiner la bounding box
	  d_norm    : TRUE si il faut afficher les normales
	  tex_yes   : TRUE si les textures sont disponibles
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  static BOOLEAN    tex_on = FALSE;
  static float	    ss_props[3] = {0.0, 0.0, 0.0};

  BOOLEAN	mark = Marked(object), hidden = Hidden(object) || hide;
  float		matcol[5];
  OBJECT_OP	obj;
  register int	i;

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

  if (hidden)
    setpattern(PATTERN);
  else
    setpattern(NOPATTERN);

  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	disp_obj(obj, (select || mark), hidden, d_bbox, d_norm, tex_yes, scene);
      break;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	disp_obj(obj, (select || mark), hidden, d_bbox, d_norm, tex_yes, scene);
      break;
    case pure_type:
      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;
	}

      matcol[0] = DIFFUSE;
      matcol[1] = 0.9*object->object.pure.color[0];
      matcol[2] = 0.9*object->object.pure.color[1];
      matcol[3] = 0.9*object->object.pure.color[2];
      matcol[4] = LMNULL;
      lmdef(DEFMATERIAL, 1, 0, matcol);

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

      if (tex_on)
	{
	  texbind(TX_TEXTURE_0, TX_NULL);
	  scrsubdivide(SS_OFF, ss_props);
	  tex_on = FALSE;
	}
      break;
    default:
      break;
  }

  setpattern(NOPATTERN);

  if (d_bbox)
    disp_bbox(object);
}

