/**************************************************************************
  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	: interface.c
  Description	: Routines de gestion de l'interface
**************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <gl.h>
#include <gl/device.h>
#include "types.h"
#include "macros.h"
#include "rad.h"
#include "radio.h"
#include "iosave.h"
#include "ioload.h"
#include "walkthru.h"
#include "display.h"
#include "modif.h"
#include "select.h"
#include "misc.h"
#include "load_image.h"
#include "forms.h"
#include "panels.h"
#include "picture.h"
#include "interface.h"

#ifdef SPACEBALL
#include "sbinteg.h"
#endif

extern	int load_tex(STR name, SCENE_OP scene);

#define CTRL_PAN_LABEL	"Control Panel"
#define RAD_PAN_LABEL	"Radiosity"
#define EDIT_PAN_LABEL	"Object Edit"
#define TEX_PAN_LABEL	"Texture Edit"
#define VISU_WIN_LABEL	"Visualization"

#define	HB_RED		1
#define	HB_BLUE		4

#define DEG2RAD(A)	((A)*0.017453)
#define RAD2DEG(A)	((A)/0.017453)

#define MIN_DISP_PIX	25
#define INIT_STEP	0.3
#define MAX_ANGLE	0.0872
#define	QUOTA_INIT	50

#define WIN3D_W		800
#define WIN3D_H		800

BOOLEAN	    not_quit = TRUE;
BOOLEAN	    win_redraw = FALSE, no_draw = FALSE;
BOOLEAN	    left_button = FALSE, mid_button = FALSE;
BOOLEAN	    state_saved = TRUE;
BOOLEAN	    delta_transform = FALSE;
BOOLEAN	    hold_computation = TRUE;
BOOLEAN	    left_mouse = FALSE, mid_mouse = FALSE;
STR	    cur_save_name;
STR	    tex_brow_path;
long	    visu, visu_w, visu_h;
long	    timer_freq;
long	    menu;
float	    center_x, center_y;
float	    ratio_alpha, ratio_beta;
float	    t_step = INIT_STEP, max_angle = MAX_ANGLE;
float	    min_disp2_size;
Mode	    mode = walkthru_mode;
Matrix	    rot_x_m, rot_y_m, trans_m, deltat_m;
MODIF	    cur_modif;
SCENE_OP    loc_scene;

unsigned short pattern[16] = {
      0x5555, 0xaaaa, 0x5555, 0xaaaa,
      0x5555, 0xaaaa, 0x5555, 0xaaaa,
      0x5555, 0xaaaa, 0x5555, 0xaaaa,
      0x5555, 0xaaaa, 0x5555, 0xaaaa};

void compute_visu(SCENE_OP	scene,
		  Matrix	rot_x,
		  Matrix	rot_y,
		  Matrix	trans,
		  Matrix	deltat,
		  BOOLEAN	dt);			/* Forward */
void ctrl_pan_cb(FL_OBJECT *fl_obj);			/* Forward */
void edit_pan_cb(FL_OBJECT *fl_obj);			/* Forward */
void tex_pan_cb(FL_OBJECT *fl_obj);			/* Forward */
void event_cb(short dev, short val);			/* Forward */

void init_draw_win(SCENE_OP scene)
/**************************************************************************
  But 	: Initialise la fenetre de visualisation
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  Matrix viewing;

  prefposition(0, WIN3D_W, getgdesc(GD_YPMAX)-WIN3D_H, getgdesc(GD_YPMAX));
  visu = winopen(VISU_WIN_LABEL);
  keepaspect(1, 1);
  winconstraints();

  menu = defpup("Mouse Mode %t| Walkthrough| Selection");

  zbuffer(TRUE);
  doublebuffer();
  shademodel(GOURAUD);
  backface(FALSE);
  subpixel(TRUE);
  RGBmode();
  lsetdepth(getgdesc(GD_ZMIN), getgdesc(GD_ZMAX));
  gconfig();
  mmode(MVIEWING);
  compute_visu(scene,
	       rot_x_m, rot_y_m, trans_m,
	       deltat_m, delta_transform);
  loadmatrix(scene->state.visu.viewing);
}

void init_ctrl_pan()
/**************************************************************************
  But 	: Initialise la fenetre de controle
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  fl_set_slider_bounds(Aperture_Slider, 5.0, 90.0);
  fl_set_slider_value(Aperture_Slider, 50.0);
  fl_set_slider_return(Aperture_Slider, FALSE);

  fl_set_slider_bounds(Translation_Slider, 0.1, 10.0);
  fl_set_slider_value(Translation_Slider, t_step);
  fl_set_slider_return(Translation_Slider, FALSE);

  fl_set_slider_bounds(Rotation_Slider, 1.0, 20.0);
  fl_set_slider_value(Rotation_Slider, RAD2DEG(max_angle));
  fl_set_slider_return(Rotation_Slider, FALSE);

  fl_set_slider_bounds(Update_Rate_Slider, 0.0, 15.0);
  fl_set_slider_value(Update_Rate_Slider, 7.0);
  fl_set_slider_return(Update_Rate_Slider, FALSE);

  fl_set_dial_bounds(Activity_Dial, 0.0, 1.0);
  fl_set_dial_value(Activity_Dial, 0.0);
  Activity_Dial->active = FALSE;

  if (hold_computation)
    fl_set_object_lcol(Hold_Button, HB_RED);
  else
    fl_set_object_lcol(Hold_Button, HB_BLUE);
}

void init_tex_pan()
/**************************************************************************
  But 	: Initialise la fenetre d'edition de texture
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  if (tex_path != NULL)
    strcpy(tex_brow_path, tex_path);
  else
    strcpy(tex_brow_path, ".");

/* ****** Is to be added in the panels.c file in the definition of
   ****** the Texture_Panel (it can't be generated automatically).
   ****** Copied here for safety.

  Texture_Pict =
    fl_add_free(FL_SLEEPING_FREE,
		(float)Texture_Box->x, (float)Texture_Box->y,
		(float)Texture_Box->w, (float)Texture_Box->h,
		"",
		picture_handle);
*/

  set_picture(NULL, 0, 0);
}

void init_edit_pan()
/**************************************************************************
  But 	: Initialise la fenetre d'edition
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  fl_set_slider_bounds(Red_Slider, 0.0, 1.0);
  fl_set_slider_bounds(Green_Slider, 0.0, 1.0);
  fl_set_slider_bounds(Blue_Slider, 0.0, 1.0);
}

void init_the_forms()
/**************************************************************************
  But 	: Initialize les paneaux
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  init_ctrl_pan();
  init_edit_pan();
  init_tex_pan();

  fl_qdevice(TIMER1);
  noise(TIMER1, (short)((float)timer_freq/7.0));

  fl_qdevice(LEFTMOUSE);
  fl_qdevice(MIDDLEMOUSE);
  fl_qdevice(RIGHTMOUSE);
  fl_qdevice(MOUSEX);
  fl_qdevice(MOUSEY);

#ifdef SPACEBALL
  if (!sbInit())
    printf("No Spaceball detected !!\n");
#endif

  fl_set_form_call_back(Control_Panel, ctrl_pan_cb);
  fl_set_form_call_back(Edit_Panel, edit_pan_cb);
  fl_set_form_call_back(Texture_Panel, tex_pan_cb);
  fl_set_event_call_back(event_cb);
}

void init_display(SCENE_OP scene)
/**************************************************************************
  But 	: Initialise l'interface et des variables
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  create_the_forms();

  init_the_forms();
  fl_deactivate_form(Radiosity_Panel);

  fl_set_form_position(Control_Panel,
		       getgdesc(GD_XPMAX) - Control_Panel->w,
		       getgdesc(GD_YPMAX) - Control_Panel->h);
  fl_show_form(Control_Panel, FL_PLACE_POSITION, TRUE, CTRL_PAN_LABEL);
  fl_set_form_position(Radiosity_Panel,
		       getgdesc(GD_XPMAX) - Radiosity_Panel->w,
		       Control_Panel->y   - Radiosity_Panel->h - 40);
  fl_set_form_position(Edit_Panel,
		       getgdesc(GD_XPMAX) - Edit_Panel->w,
		       Radiosity_Panel->y - Edit_Panel->h - 40);
  fl_set_form_position(Texture_Panel,
		       getgdesc(GD_XPMAX) - Texture_Panel->w,
		       Radiosity_Panel->y - Texture_Panel->h - 40);

  defpattern(PATTERN, 16, pattern);
  cur_modif.select.pure = NULL;
  cur_modif.select.tri  = NULL;
  cur_modif.select.elem = NULL;
  cur_modif.sel_tex	= -1;

  M4D_identity(rot_x_m);
  M4D_identity(rot_y_m);
  M4D_identity(trans_m);
  M4D_identity(scene->state.visu.mod);
  M4D_identity(scene->state.visu.viewing);

  init_draw_win(scene);
  win_redraw = TRUE;
}

void compute_visu(SCENE_OP	scene,
		  Matrix	rot_x,
		  Matrix	rot_y,
		  Matrix	trans,
		  Matrix	deltat,
		  BOOLEAN	dt)
/**************************************************************************
  But 	: Calcule la matrice de visualisation courante en fonction des
	  matrices de position et rotation et/ou de la matrice de delta-
	  transformation
  Entree: scene	    : la scene
	  rot_x	    : rotation atour de x
	  rot_y	    : rotation atour de y
	  trans	    : translation
	  deltat    : delta-transformation venant de la spaceball
	  dt	    : TRUE si deltat est valide
  Sortie: neant
**************************************************************************/
{
  float	    h_f, cot, fpn, fmn, ftn;
  Matrix    m;

  if (dt)
    {
      Matrix invdeltat;

      M4D_invert(invdeltat, deltat);
      M4D_mul(scene->state.visu.viewing, scene->state.visu.viewing, invdeltat);
      M4D_mul(scene->state.visu.view,
	      scene->state.visu.imod,
	      scene->state.visu.viewing);
      M4D_invert(scene->state.visu.rot, scene->state.visu.view);
      delta_transform = FALSE;
      M4D_identity(deltat);
    }
  else
    {
      M4D_mul(scene->state.visu.rot, scene->state.visu.rot, rot_y);
      M4D_mul(scene->state.visu.rot, rot_x, scene->state.visu.rot);
      M4D_mul(scene->state.visu.rot, trans, scene->state.visu.rot);
      M4D_mul(scene->state.visu.rot,
	      scene->state.visu.rot,
	      scene->state.visu.trans);
      M4D_invert(scene->state.visu.view, scene->state.visu.rot);
      M4D_mul(scene->state.visu.viewing,
	      scene->state.visu.mod,
	      scene->state.visu.view);
    }
  M4D_invert(scene->state.visu.iviewing, scene->state.visu.viewing);

  scene->state.visu.trans[3][0] = scene->state.visu.rot[3][0];
  scene->state.visu.trans[3][1] = scene->state.visu.rot[3][1];
  scene->state.visu.trans[3][2] = scene->state.visu.rot[3][2];
  scene->state.visu.rot[3][0] = 0.0;
  scene->state.visu.rot[3][1] = 0.0;
  scene->state.visu.rot[3][2] = 0.0;

  h_f = DEG2RAD(scene->state.visu.fovy/20.0);
  cot = cos(h_f)/sin(h_f);
  fpn = scene->state.visu.far + scene->state.visu.near;
  fmn = scene->state.visu.far - scene->state.visu.near;
  ftn = scene->state.visu.far * scene->state.visu.near;

  M4D_init(scene->state.visu.persp,
	     cot, 0.0,	          0.0,  0.0,
	     0.0, cot,	          0.0,  0.0,
	     0.0, 0.0,	   -(fpn/fmn), -1.0,
	     0.0, 0.0, -2.0*(ftn/fmn),  0.0);

  M4D_invert(scene->state.visu.ipersp, scene->state.visu.persp);

  M4D_mul(scene->state.visu.unif2world,
	  scene->state.visu.ipersp,
	  scene->state.visu.iviewing);
  M4D_mul(scene->state.visu.world2unif,
	  scene->state.visu.viewing,
	  scene->state.visu.persp);
}

void redraw_scene(SCENE_OP scene, long visu)
/**************************************************************************
  But 	: Redessine la scene
  Entree: scene	: la scene
	  visu	: la fenetre
  Sortie: neant
**************************************************************************/
{
  static    int	    count = 0;

  int		    n;
  short		    dum;

  int		    nb_tri_disp;
  Matrix	    viewing;
  static char	    string[30];
  static Matrix	    identity = { 1.0, 0.0, 0.0, 0.0,
				 0.0, 1.0, 0.0, 0.0,
				 0.0, 0.0, 1.0, 0.0,
				 0.0, 0.0, 0.0, 1.0 };

  winset(visu);
  reshapeviewport();
  czclear(0, getgdesc(GD_ZMAX));

  if (scene->scene != NULL)
    {
      getsize(&visu_w, &visu_h);
      min_disp2_size = (float)MIN_DISP_PIX * 2.0 / visu_w;
      min_disp2_size *= min_disp2_size;
      center_x = visu_w*0.5;
      center_y = visu_h*0.5;
      ratio_alpha = max_angle/visu_w;
      ratio_beta  = max_angle/visu_h;
      compute_visu(scene,
		   rot_x_m, rot_y_m, trans_m,
		   deltat_m, delta_transform);
      mmode(MPROJECTION);
      loadmatrix(scene->state.visu.persp);
      mmode(MVIEWING);
      loadmatrix(scene->state.visu.viewing);

      nb_tri_disp = disp_obj(scene->scene, scene, &(cur_modif.select));
      mmode(MPROJECTION);
      loadmatrix(identity);
      mmode(MVIEWING);
      loadmatrix(identity);
      cpack(0x00ffffff);
      cmov2(-1.0, -1.0);
      sprintf(string, "%d polygons", nb_tri_disp);
      charstr(string);
    };

  swapbuffers();

  win_redraw = FALSE;
}

void movement(long xpos, long ypos, BOOLEAN left, BOOLEAN mid)
/**************************************************************************
  But 	: Met a jour les matrices de position et d'orientation
  Entree: xpos, ypos:	la position de la souris dans la fenetre
	  left, mid:	l'etat des boutons de la souris
  Sortie: neant
**************************************************************************/
{
  float	dal, dbe, dx, dy;

  if (left || mid)
    {
      dx  = (float)xpos - center_x;
      dy  = (float)ypos - center_y;
      dal = ratio_alpha*dx;
      dbe = ratio_beta*dy;
      build_rotation_x(dbe, rot_x_m);
      build_rotation_y(dal, rot_y_m);

      if (left && mid)
        build_translation(-t_step, trans_m);
      else if ((!left) && mid)
        build_translation(t_step, trans_m);
    }

  win_redraw = TRUE;
  state_saved = FALSE;
};

void turn_on_edi(SCENE_OP scene, MODIFP modif)
/**************************************************************************
  But 	: Affiche la fenetre d'edition et cache celle des textures
  Entree: scene	    : la scene
	  modif	    : la modification courante
  Sortie: neant
**************************************************************************/
{
  if (Texture_Panel->visible)
    fl_hide_form(Texture_Panel);
  if (!Edit_Panel->visible)
    fl_show_form(Edit_Panel, FL_PLACE_POSITION, TRUE, EDIT_PAN_LABEL);
  modif->sel_tex = -1;

  win_redraw = TRUE;
}

int sel_rgb(struct dirent *de)
/**************************************************************************
  But 	: Selectionne les noms se terminant par .rgb
  Entree: de	: entree de repertoire
  Sortie: neant
**************************************************************************/
{
  char *ext;

  ext = strrchr(de->d_name, '.');
  if (ext == NULL)
    return FALSE;

  if (strcmp(ext, ".rgb") != 0)
    return FALSE;

  return TRUE;
}

void update_tex_brow()
/**************************************************************************
  But 	: Met a jour le browser de texture
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  int		i, nb_entry;
  struct dirent **namelist;

  fl_clear_browser(Texture_Name_Browser);

  nb_entry = scandir(tex_brow_path, &namelist, sel_rgb, alphasort);

  if (nb_entry < 0)
    {
      fl_show_message("Can't open", tex_brow_path, "for reading...");
      fl_qreset();
      return;
    }

  fl_freeze_object(Texture_Name_Browser);
  for(i = 0; i < nb_entry; i++)
    {
      char  *ext;
      STR   tmp;

      strcpy(tmp, namelist[i]->d_name);
      ext = strrchr(tmp, '.');
      ext[0] = '\0';
      fl_add_browser_line(Texture_Name_Browser, tmp);
      free(namelist[i]);
    }
  free(namelist);
  fl_unfreeze_object(Texture_Name_Browser);
}

void turn_on_tex(SCENE_OP scene, MODIFP modif)
/**************************************************************************
  But 	: Affiche la fenetre des textures et cache celle d'edition
  Entree: scene	    : la scene
	  modif	    : la modification courante
  Sortie: neant
**************************************************************************/
{
  if (Edit_Panel->visible)
    fl_hide_form(Edit_Panel);

  update_tex_brow();
  if (!Texture_Panel->visible)
    {
      fl_show_form(Texture_Panel, FL_PLACE_POSITION, TRUE, TEX_PAN_LABEL);
      fl_qreset();
    }

  win_redraw = TRUE;
}

void turn_off_all(SCENE_OP scene, MODIFP modif)
/**************************************************************************
  But 	: Efface toutes les fenetres de selection
  Entree: scene	    : la scene
	  modif	    : la modification courante
  Sortie: neant
**************************************************************************/
{
  if (Edit_Panel->visible)
    fl_hide_form(Edit_Panel);
  if (Texture_Panel->visible)
    fl_hide_form(Texture_Panel);
  if (Radiosity_Panel->visible)
    fl_hide_form(Radiosity_Panel);

  modif->sel_tex = -1;
}

void update_rad_win(SCENE_OP	scene,
		    MODIFP	modif)
/**************************************************************************
  But 	: Met a jour la fenetre d'affichage des radiosites
  Entree: scene	    : la scene
	  modif	    : la modification courante
  Sortie: neant
**************************************************************************/
{
  static BOOLEAN    init = TRUE;
  static SELECTION  last;

  STR		    str;

  if (init)
    {
      last.pure = NULL;
      last.tri  = NULL;
      last.elem = NULL;
      init = FALSE;
    }

  if ((last.tri != modif->select.tri) || (last.elem != modif->select.elem))
    {
      VECTOR B[3];
      
      if (modif->select.elem == NULL)
	{
	  V_copy(B[0], modif->select.tri->tri.poly.vertex[0].B);
	  V_copy(B[1], modif->select.tri->tri.poly.vertex[1].B);
	  V_copy(B[2], modif->select.tri->tri.poly.vertex[2].B);
	}
      else
	{
	  V_copy(B[0],
		 modif->select.tri->tri.poly.vertex[modif->select.elem->vertex[0]].B);
	  V_copy(B[1],
		 modif->select.tri->tri.poly.vertex[modif->select.elem->vertex[1]].B);
	  V_copy(B[2],
		 modif->select.tri->tri.poly.vertex[modif->select.elem->vertex[2]].B);
	}

      sprintf(str, "B (P1) : %7.5f %7.5f %7.5f", B[0][0], B[0][1], B[0][2]);
      fl_set_object_label(Rad_Red_Text, str);
      sprintf(str, "B (P2) : %7.5f %7.5f %7.5f", B[1][0], B[1][1], B[1][2]);
      fl_set_object_label(Rad_Green_Text, str);
      sprintf(str, "B (P3) : %7.5f %7.5f %7.5f", B[2][0], B[2][1], B[2][2]);
      fl_set_object_label(Rad_Blue_Text, str);
    }

  last = modif->select;

  if (!Radiosity_Panel->visible)
    fl_show_form(Radiosity_Panel, FL_PLACE_POSITION, TRUE, RAD_PAN_LABEL);
}

void update_tex_pan(SCENE_OP	scene,
		    MODIFP	modif)
/**************************************************************************
  But 	: Met a jour la fenetre de selection des textures
  Entree: scene	    : la scene
	  modif	    : la modification courante
  Sortie: neant
**************************************************************************/
{
  STR str;

  sprintf(str, "%f", modif->ro);
  fl_set_input(Tex_Reflectivity_Input, str);
  sprintf(str, "%f", modif->E);
  fl_set_input(Tex_Emissivity_Input, str);
  sprintf(str, "%f", 1.0/modif->scale);
  fl_set_input(Scale_Input, str);

  if (modif->sel_tex >= 0)
    set_picture(scene->textures[modif->sel_tex].image,
		scene->textures[modif->sel_tex].width,
		scene->textures[modif->sel_tex].height);
  else if (modif->cur_tex > 0)
    set_picture(scene->textures[modif->cur_tex].image,
		scene->textures[modif->cur_tex].width,
		scene->textures[modif->cur_tex].height);

  if (!Texture_Panel->visible)
    turn_on_tex(scene, modif);

  fl_redraw_object(Texture_Pict);
}

void update_edi_pan(SCENE_OP	scene,
		    MODIFP	modif)
/**************************************************************************
  But 	: Met a jour la fenetre d'edition d'objet
  Entree: scene	    : la scene
	  modif	    : la modification courante
  Sortie: neant
**************************************************************************/
{
  STR	str;

  sprintf(str, "%f", modif->ro);
  fl_set_input(Edit_Reflectivity_Input, str);
  sprintf(str, "%f", modif->E);
  fl_set_input(Edit_Emissivity_Input, str);
  fl_mapcolor(Color_Box->col1,
	      (short)(modif->select.pure->object.pure.color[0]*255),
	      (short)(modif->select.pure->object.pure.color[1]*255),
	      (short)(modif->select.pure->object.pure.color[2]*255));
  fl_redraw_object(Color_Box);
  fl_set_slider_value(Red_Slider,   modif->select.pure->object.pure.color[0]);
  fl_set_slider_value(Green_Slider, modif->select.pure->object.pure.color[1]);
  fl_set_slider_value(Blue_Slider,  modif->select.pure->object.pure.color[2]);

  if (!Edit_Panel->visible)
    turn_on_edi(scene, modif);
}

void selection(SCENE_OP scene, long xpos, long ypos)
/**************************************************************************
  But 	: Calcule les points de passage du rayon de visee dans l'espace
	  et appelle la routine de selection
  Entree: scene	    : la scene
	  xpos	    : la position en x de la souris dans la fenetre
	  ypos	    : la position en y de la souris dans la fenetre
  Sortie: neant
**************************************************************************/
{
  VECTOR p;

  p[0] = ((float)xpos - center_x)/(visu_w*0.5);
  p[1] = ((float)ypos - center_y)/(visu_h*0.5);

  select_obj(scene, &(cur_modif.select), p);

  if (cur_modif.select.pure != NULL)
    {
      V_copy(cur_modif.color, cur_modif.select.pure->object.pure.color);
      cur_modif.ro = cur_modif.select.pure->object.pure.ro;
      cur_modif.E  = cur_modif.select.pure->object.pure.E;
      cur_modif.cur_tex = cur_modif.select.pure->object.pure.texmap.index;
      cur_modif.scale   = cur_modif.select.pure->object.pure.texmap.scale;
      cur_modif.sel_tex = -1;

      update_rad_win(scene, &cur_modif);
      if (cur_modif.cur_tex > 0)
	update_tex_pan(scene, &cur_modif);
      else
        update_edi_pan(scene, &cur_modif);
    }
  else
    turn_off_all(scene, &cur_modif);

  win_redraw = TRUE;
}

BOOLEAN make_modif(SCENE_OP scene, MODIFP modif)
/**************************************************************************
  But 	: Calcule la difference d'emission engendree par la modification
	  et l'applique, pour autant que le shooting patch courant 
	  n'appartienne pas a l'objet qui doit etre modifie
  Entree: scene	    : la scene
	  modif	    : la modification
  Sortie: TRUE si la modification a ete faite, FALSE sinon
**************************************************************************/
{
  BOOLEAN proceed;

  if (scene->state.shooting_p.patch == NULL)
    proceed = !scene->state.not_done;
  else 
    proceed = modif->select.pure != scene->state.shooting_p.patch->dad;

  if (proceed)
    {
      int	i, j;
      VECTOR    new_emmtd, old_emmtd, dif_emmtd;
      VECTOR    new_rftor, old_rftor, new_rfltd, old_rfltd;
      TRIANGLEP t_array = modif->select.pure->object.pure.polys;
      SHOOT_P	shoot;

      shoot.E     = 0.0;
      shoot.patch = NULL;
      shoot.o     = -1;
      shoot.p     = -1;

      V_scale(old_emmtd, modif->select.pure->object.pure.E,
			 modif->select.pure->object.pure.color);
      V_scale(new_emmtd, modif->E, modif->color);
      V_sub(dif_emmtd, new_emmtd, old_emmtd);
      V_scale(old_rftor, modif->select.pure->object.pure.ro,
			 modif->select.pure->object.pure.color);
      V_scale(new_rftor, modif->ro, modif->color);
      for(i = 0; i < modif->select.pure->object.pure.nb_polys; i++)
        {
          VERTEXP	v_array = t_array[i].tri.poly.vertex;
	  float		new_E;

          for(j = 0; j < t_array[i].tri.poly.nb_v; j++)
	    {
	      VECTOR dif_rfltd;
    
	      V_mul(old_rfltd, v_array[j].rB, old_rftor);
	      V_mul(new_rfltd, v_array[j].rB, new_rftor);
	      V_sub(dif_rfltd, new_rfltd, old_rfltd);
	      V_add_self(v_array[j].dB, dif_emmtd);
	      V_add_self(v_array[j].dB, dif_rfltd);
	      V_add(v_array[j].B, new_emmtd, new_rfltd);
	    }

	  new_E = comp_newE(&(t_array[i]));
	  if (_ABS(new_E) > _ABS(shoot.E))
	    {
	      shoot.E	  = new_E;
	      shoot.patch = &(t_array[i]);
	      shoot.o	  = modif->select.pure->object.pure.id;
	      shoot.p	  = i;
	    }
	}
      modif->select.pure->object.pure.E = modif->E;
      modif->select.pure->object.pure.ro = modif->ro;
      V_copy(modif->select.pure->object.pure.color, modif->color);

      if (scene->state.not_done)
	{
	  if (_ABS(shoot.E) > _ABS(scene->state.new_shoot.E))
	    scene->state.new_shoot = shoot;
	}
      else
	if (_ABS(shoot.E) > _ABS(scene->state.shooting_p.E))
	  {
	    scene->state.shooting_p	 = shoot;
	    scene->state.new_shoot.E	 = 0.0;
	    scene->state.new_shoot.patch = NULL;
	    scene->state.new_shoot.o	 = -1;
	    scene->state.new_shoot.p	 = -1;
	    scene->state.not_done	 = TRUE;
	  };
    
      return TRUE;
    }
  else
    return FALSE;
}

void load(char *name, SCENE_OP scene)
/**************************************************************************
  But 	: Lit le fichier specifie
  Entree: name	: le nom du fichier
	  scene	: la scene lue
  Sortie: neant
**************************************************************************/
{
  BOOLEAN   yes = TRUE;
  short	    dum;
  int	    n;

  if (!state_saved)
    {
      yes = fl_show_question("The current scene hasn't been saved !",
			     "Are you sure you want to load a new scene ?",
			     "");
      fl_qreset();
    }

  if (yes)
    {
      FILE *file;

      if (name[strlen(name)-1] == 'd')
	{
          if ((file = fopen(name, "r")) != NULL)
	    if (!load_new_state(file, des_type, scene))
	      {
	        fl_show_message("Can't load file", name, "");
		fl_qreset();
		fclose(file);
		scene->scene = NULL;
		return;		  
	      }
	}
      else if (name[strlen(name)-1] == 'c')
	{
          if ((file = fopen(name, "rb")) != NULL)
	    if (!load_new_state(file, cal_type, scene))
	      {
	        fl_show_message("Can't load file", name, "");
		fl_qreset();
		fclose(file);
		scene->scene = NULL;
		return;		  
	      }
	}
      else
	{
	  fl_show_message("Can't load", name, "Not a RAD file");
	  fl_qreset();
	  fclose(file);
	  return;
	}
      
      fclose(file);
      strcpy(cur_save_name, name);
      cur_save_name[strlen(cur_save_name)-1] = 'c';

      win_redraw  = TRUE;
      state_saved = TRUE;
      mode	  = walkthru_mode;
      fl_set_slider_value(Aperture_Slider, (float)scene->state.visu.fovy/10.0);
      cur_modif.select.pure = NULL;
      cur_modif.select.tri  = NULL;
      cur_modif.select.elem = NULL;
      turn_off_all(scene, &cur_modif);
      empty_modif_queue();

      M4D_identity(rot_x_m);
      M4D_identity(rot_y_m);
      M4D_identity(trans_m);
      M4D_identity(deltat_m);
      delta_transform = TRUE;
      compute_visu(scene,
		   rot_x_m, rot_y_m, trans_m,
		   deltat_m, delta_transform);

      fl_qreset();

      win_redraw = TRUE;
    };
}

void bind_tex(SCENE_OP scene, MODIFP modif)
/**************************************************************************
  But 	: Attache une texture a un objet
  Entree: scene	    : la scene
	  modif	    : la modification courante
  Sortie: neant
**************************************************************************/
{
  VECTOR    dum, v;

  if ((modif->sel_tex < 0) && (modif->cur_tex < 0))
    return;

  if (modif->sel_tex == 0)
    modif->select.pure->object.pure.texmap.index = add_texture(scene);
  else if (modif->sel_tex > 0)
    modif->select.pure->object.pure.texmap.index = modif->sel_tex;

  modif->cur_tex = modif->select.pure->object.pure.texmap.index;
  modif->sel_tex = -1;

  V_copy(modif->color,
	 scene->textures[modif->select.pure->object.pure.texmap.index].meancolor);
  modif->select.pure->object.pure.texmap.scale = modif->scale;

  V_init(dum, 1.0, 0.0, 0.0);
  M4D_transform_point(v, dum, scene->state.visu.iviewing);
  V_sub(v, v, scene->state.visu.iviewing[3]);
  V_normalize(modif->select.pure->object.pure.texmap.s, v);
  modif->select.pure->object.pure.texmap.s[3] =
    -V_dot(modif->select.pure->object.pure.texmap.s,
	   modif->select.pure->bbox[0]);
  V_init(dum, 0.0, 1.0, 0.0);
  M4D_transform_point(v, dum, scene->state.visu.iviewing);
  V_sub(v, v, scene->state.visu.iviewing[3]);
  V_normalize(modif->select.pure->object.pure.texmap.t, v);
  modif->select.pure->object.pure.texmap.t[3] =
    -V_dot(modif->select.pure->object.pure.texmap.t,
	   modif->select.pure->bbox[0]);

  enq_modif(modif);
}

void update_cpu(int tq)
/**************************************************************************
  But 	: Met a jour l'affichage de l'activite de calcul
  Entree: tq	: Le temps de calcul alloue
  Sortie: neant
**************************************************************************/
{
  static int	tot_cpu = 0, sign = 1;
  float		val;
  
  tot_cpu += sign*tq;

  if (tot_cpu >= 1000)
    {
      tot_cpu = 1000;
      sign = -sign;
    }
  else if (tot_cpu <= 0)
    {
      tot_cpu = 0;
      sign = -sign;
    }

  val = ((float)tot_cpu)/1000.0;	    /* milisecondes */
  fl_set_dial_value(Activity_Dial, val);
}

int load_tex(STR name, SCENE_OP scene)
/**************************************************************************
  But 	: Charge une texture en memoire pour la visualisation, ou la trouve
	  parmi les textures utilisees
  Entree: name	    : le nom de fichier
	  scene	    : la scene
  Sortie: index de la texture dans le tableau des textures existantes
**************************************************************************/
{
  int i;

  if (scene->textures[0].image != NULL)
    {
      free(scene->textures[0].image);
      scene->textures[0].image = NULL;
    }

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

  scene->textures[0].image =
    load_image(name,
	       &(scene->textures[0].width),
	       &(scene->textures[0].height),
	       scene->textures[0].meancolor);

  if (scene->textures[0].image == NULL)
    {
      fl_show_message("Can't open image file", name, NULL);
      fl_qreset();
      return (-1);
    }

  strcpy(scene->textures[0].name, name);

  return 0;
}

void ctrl_pan_cb(FL_OBJECT *fl_obj)
/**************************************************************************
  But 	: Traite les evenements generes par le control panel
  Entree: fl_obj    : l'objet qui a emis l'evenement
  Sortie: neant
**************************************************************************/
{
  if (fl_obj == Load_Button)
    {
      static BOOLEAN	init = TRUE;
      static STR	dir, pat;

      char  *name;

      if (init)
	{
	  strcpy(dir, scene_path);
          strcpy(pat, "*.r[cd]");
	  init = FALSE;
	}

      name = fl_show_file_selector("Select a file",
				   dir, pat, "");
      fl_qreset();
      if (name)
	{
	  load(name, loc_scene);
	  strcpy(dir, fl_get_directory());
	}
    }
  else if (fl_obj == Save_Button)
    {
      char	*name;
      BOOLEAN	save_it;

      if (loc_scene->scene == NULL)
	return;

      name = fl_show_input("Enter la file name", cur_save_name);
      fl_qreset();

      if (name)
	{
          FILE *file;

	  state_saved = FALSE;

	  strcpy(cur_save_name, name);

          if ((file = fopen(cur_save_name, "rb")) != NULL)
	    {
	      save_it = fl_show_question("This file already exists:",
				         cur_save_name,
				         "Do you want do replace it ?");
	      fclose(file);
	    }
	  else
	    save_it = TRUE;

	  if (save_it)
	    {
	      if ((file = fopen(cur_save_name, "wb")) == NULL)
		fl_show_message("",
				"Can't open file for writing !!!\n",
				"");
	      else
		{
                  if (!save_state(file, loc_scene))
		    fl_show_message("", "Can't save file !!!\n", "");
	          else
		    state_saved = TRUE;
		  fclose(file);
		}
	    }

	  fl_qreset();
	}
    }
  else if (fl_obj == Hold_Button)
    {
      hold_computation = !hold_computation;
      if (hold_computation)
	fl_set_object_lcol(Hold_Button, HB_RED);
      else
	fl_set_object_lcol(Hold_Button, HB_BLUE);
    }
  else if (fl_obj == Quit_Button)
    {
      BOOLEAN yes = TRUE;

      if (loc_scene->scene == NULL)
	exit(0);

      if (!state_saved)
	{
          yes = fl_show_question("The current scene hasn't been saved !",
			         "Are you sure you want to quit ?",
			         "");
	  fl_qreset();
	}

      if (yes)
        exit(0);
    }
  else if (fl_obj == Update_Rate_Slider)
    {
      float val = fl_get_slider_value(Update_Rate_Slider);

      if (val == 0.0)
	no_draw = TRUE;
      else
	{
          noise(TIMER1, (short)((float)timer_freq/val));
	  no_draw = FALSE;
	};
    }
  else if (fl_obj == Aperture_Slider)
    {
      loc_scene->state.visu.fovy =
	(Angle)(fl_get_slider_value(Aperture_Slider)*10.0);
      win_redraw  = TRUE;
      state_saved = FALSE;
    }
  else if (fl_obj == Translation_Slider)
    t_step = fl_get_slider_value(Translation_Slider);
  else if (fl_obj == Rotation_Slider)
    {
      max_angle   = DEG2RAD(fl_get_slider_value(Rotation_Slider));
      ratio_alpha = max_angle/visu_w;
      ratio_beta  = max_angle/visu_h;
    }
}

void edit_pan_cb(FL_OBJECT *fl_obj)
/**************************************************************************
  But 	: Traite les evenements generes par la fenetre d'edition
  Entree: fl_obj    : l'objet qui a emis l'evenement
  Sortie: neant
**************************************************************************/
{
  if (fl_obj == Red_Slider)
    cur_modif.color[0] = fl_get_slider_value(Red_Slider);
  else if (fl_obj == Green_Slider)
    cur_modif.color[1] = fl_get_slider_value(Green_Slider);
  else if (fl_obj == Blue_Slider)
    cur_modif.color[2] = fl_get_slider_value(Blue_Slider);
  else if (fl_obj == Edit_Reflectivity_Input)
    {
      sscanf(fl_get_input(Edit_Reflectivity_Input),
	     "%f", &(cur_modif.ro));
      cur_modif.ro = MIN(1.0, cur_modif.ro);
      cur_modif.ro = MAX(0.0, cur_modif.ro);
    }
  else if (fl_obj == Edit_Emissivity_Input)
    {
      sscanf(fl_get_input(Edit_Emissivity_Input),
	     "%f", &(cur_modif.E));
      cur_modif.E = MAX(0.0, cur_modif.E);
    }
  else if (fl_obj == Ok_to_modify_object_Button)
    enq_modif(&cur_modif);
  else if (fl_obj == Add_Texture_Button)
    update_tex_pan(loc_scene, &cur_modif);

  if ((fl_obj == Red_Slider) ||
      (fl_obj == Green_Slider) ||
      (fl_obj == Blue_Slider))
    {	
      fl_mapcolor(Color_Box->col1,
		  (short)(cur_modif.color[0]*255),
		  (short)(cur_modif.color[1]*255),
		  (short)(cur_modif.color[2]*255));

      fl_redraw_object(Color_Box);
    }
}

void tex_pan_cb(FL_OBJECT *fl_obj)
/**************************************************************************
  But 	: Traite les evenements generes par la fenetre des textures
  Entree: fl_obj    : l'objet qui a emis l'evenement
  Sortie: neant
**************************************************************************/
{
  if (fl_obj == Texture_Name_Browser)
    {
      STR   tmp;

      strcpy(tmp, tex_brow_path);
      strcat(tmp, "/");
      strcat(tmp, fl_get_browser_line(Texture_Name_Browser,
				      fl_get_browser(Texture_Name_Browser)));
      strcat(tmp, ".rgb");
      cur_modif.sel_tex = load_tex(tmp, loc_scene);
      update_tex_pan(loc_scene, &cur_modif);
    }
  else if (fl_obj == Scale_Input)
    {
      sscanf(fl_get_input(Scale_Input), "%f", &(cur_modif.scale));
      cur_modif.scale = MAX(0.0001, 1.0/cur_modif.scale);	
    }
  else if (fl_obj == Tex_Reflectivity_Input)
    {
      sscanf(fl_get_input(Tex_Reflectivity_Input),
	     "%f", &(cur_modif.ro));
      cur_modif.ro = MIN(1.0, cur_modif.ro);
      cur_modif.ro = MAX(0.0, cur_modif.ro);
    }
  else if (fl_obj == Tex_Emissivity_Input)
    {
      sscanf(fl_get_input(Tex_Emissivity_Input),
	     "%f", &(cur_modif.E));
      cur_modif.E = MAX(0.0, cur_modif.E);
    }
  else if (fl_obj == Bind_and_Modify_Button)
    bind_tex(loc_scene, &cur_modif);
  else if (fl_obj == No_Texture_Button)
    {
      cur_modif.select.pure->object.pure.texmap.index = -1;
      cur_modif.cur_tex = -1;
      cur_modif.sel_tex = -1;
      update_edi_pan(loc_scene, &cur_modif);
    }
}

void event_cb(short dev, short val)
/**************************************************************************
  But 	: Traite les evenements generes par la fenetre de visualisation
	  et les evenements n'appartenant pas au toolkit Forms en general
  Entree: dev	: la source de l'evenement
	  val	: la valeur de l'evenement
  Sortie: neant
**************************************************************************/
{
  static long	winxpos, winypos;

  short		xpos, ypos, dum;

#ifdef SPACEBALL
  if (sbEvent(dev, val))
    return;
#endif

  switch(dev) {
    case REDRAW:
      redraw_scene(loc_scene, visu);
      getorigin(&winxpos, &winypos);
      break;
    case TIMER1:
      if ((win_redraw || left_mouse || mid_mouse) && (!no_draw))
	redraw_scene(loc_scene, visu);
      break;
    case MOUSEX:
    case MOUSEY:
      if ((left_mouse || mid_mouse) && (mode == walkthru_mode))
	{
	  xpos = getvaluator(MOUSEX) - winxpos;
	  ypos = getvaluator(MOUSEY) - winypos;
          movement(xpos, ypos, left_mouse, mid_mouse);
	}
      break;
    case LEFTMOUSE:
      if (val)
	{
          left_mouse = TRUE;
	  xpos = getvaluator(MOUSEX) - winxpos;
	  ypos = getvaluator(MOUSEY) - winypos;
	  if (mode == walkthru_mode)
	    movement(xpos, ypos, left_mouse, mid_mouse);
	  else
	    selection(loc_scene, xpos, ypos);
	}
      else
	left_mouse = FALSE;
      if (!(left_button || mid_button))
        {
          M4D_identity(rot_y_m);
          M4D_identity(rot_x_m);
          M4D_identity(trans_m);
          win_redraw = FALSE;
        }
      break;
    case MIDDLEMOUSE:
      if (val)
	{
          mid_mouse = TRUE;
	  xpos = getvaluator(MOUSEX) - winxpos;
	  ypos = getvaluator(MOUSEY) - winypos;
	  if (mode == walkthru_mode)
	    movement(xpos, ypos, left_mouse, mid_mouse);
	}
      else
	mid_mouse = FALSE;
      if (!(left_button || mid_button))
        {
          M4D_identity(rot_y_m);
          M4D_identity(rot_x_m);
          M4D_identity(trans_m);
          win_redraw = FALSE;
        }
      break;
    case RIGHTMOUSE:
      switch(dopup(menu)) {
	case 1:
          mode = walkthru_mode;
          cur_modif.select.pure = NULL;
          cur_modif.select.tri  = NULL;
          cur_modif.select.elem = NULL;
          turn_off_all(loc_scene, &cur_modif);
          win_redraw = TRUE;
          break;
        case 2:
          mode = select_mode;
          break;
	default:
	  break;
      }
      break;
    default:
      break;
  }
}

void misc_init()
/**************************************************************************
  But 	: Initialise quelques variables globales
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  timer_freq = getgdesc(GD_TIMERHZ);
#ifdef SPACEBALL
#ifdef OLDSYS
  sbdataperiod(30, 1500);
#endif
#endif
  M4D_identity(rot_x_m);
  M4D_identity(rot_y_m);
  M4D_identity(trans_m);
  M4D_identity(deltat_m);
  delta_transform = FALSE;
}

void run_display(SCENE_OP scene)
/**************************************************************************
  But 	: Boucle principale. Traite les evenements
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  BOOLEAN   modif_to_make;
  int	    time_quota = QUOTA_INIT;

  loc_scene = scene;
  misc_init();

  fl_init();

  init_display(scene);

  while(TRUE)
    {
      if (fl_check_forms() == NULL)
	{
	  modif_to_make = modif_q_not_empty();
	  if (((scene->state.not_done && (scene->scene != NULL)) ||
	       modif_to_make) &&
	      !hold_computation)
	    {
	      if (modif_to_make)
		{
		  MODIF modif;

		  next_modif(&modif);

	          if (make_modif(scene, &modif))
		    remove_modif();
		}
	      scene->state.not_done = comp_rad(scene, time_quota);
	      update_cpu(time_quota);
	      win_redraw = TRUE;
	      state_saved = FALSE;
	    }
	}
    }
}
