//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// lights.cc
//
// Vincent LE PRINCE <vincentleprince@users.sourceforge.net>
// Copyright (C) 2000-2005 Vincent LE PRINCE
// This file is part of the TRUEVISION Package

//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//
//   This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */ 
//*******************************************************************************************
#include "include/lights.h"
#include "include/viewmanager.h"
#include "include/objectlist.h"
#include "include/tvio.h"
#include <math.h>
#include "include/preferences.h"

//********************************************************************************************
// POINTLIGHT
//********************************************************************************************

//**************************************
// Constructeur
//**************************************
PointLight::PointLight( app_objs *appref ) : Object3D( appref )
{
type = TV_OBJ3D_POINTLIGHT;
category = TV_OBJ3D_LIGHTS;
set_name( "Point light" );
OBJLIST_DEF
light_id = objlist->light_request();

// Base
location = new ObjParam_point( N_("Location"), "LOC", NULL, app_ref, true );
location->set( 0.6, 0.6, 0.6 );
color = new ObjParam_color( N_("Color"), "COL", NULL, app_ref, true );
color->set( 255, 255, 255, 255 );
media_attenuation = new ObjParam_bool( N_("Media attenuation"), "MED_ATT", NULL, app_ref, false, false );
media_interaction = new ObjParam_bool( N_("Media interaction"), "MED_INT", NULL, app_ref, false, false );
parallel = new ObjParam_bool( N_("Parallel"), "PARA", NULL, app_ref, false, false );
look_like = new ObjParam_objref( N_("Looks like"), "LLIKE", NULL, app_ref, false );
projected_through = new ObjParam_objref( N_("Projected through"), "PROJT", NULL, app_ref, false );
	
// Fade
fade = new ObjParam_bool_activator( N_("Use fading"), "FADE_USED", NULL, app_ref, false, false );
fade_power = new ObjParam_float( N_("Fade power"), "FADE_POW", NULL, app_ref, false, 0 );
	fade_power->set_range( 10, 0, 0.1, 6 );
fade_distance = new ObjParam_float( N_("Fade distance"), "FADE_DIST", NULL, appref, false, 2 );
	fade_distance->set_range( 10, 0, 0.1, 6 );

// Photons
refraction = new ObjParam_bool( N_("Refraction"), "PREFRAC", NULL, app_ref, false, false );
reflection = new ObjParam_bool( N_("Reflection"), "PREFLEC", NULL, app_ref, false, false );
area = new ObjParam_bool( N_("Area light"), "PAREA", NULL, app_ref, false, false );
}

PointLight::PointLight( PointLight & ref ) : Object3D( ref )
{
OBJLIST_DEF
light_id = objlist->light_request();

location = new ObjParam_point( *ref.location );
color = new ObjParam_color( *ref.color );
media_attenuation = new ObjParam_bool( *ref.media_attenuation );
media_interaction = new ObjParam_bool( *ref.media_interaction );
fade = new ObjParam_bool_activator( *ref.fade );
fade_power = new ObjParam_float( *ref.fade_power );
fade_distance = new ObjParam_float( *ref.fade_distance );
parallel = new ObjParam_bool( *ref.parallel );
refraction = new ObjParam_bool( *ref.refraction );
reflection = new ObjParam_bool( *ref.reflection );
area = new ObjParam_bool( *ref.area );
look_like = new ObjParam_objref( *ref.look_like );
projected_through = new ObjParam_objref( *ref.projected_through );
}

PointLight::~PointLight()
{
OBJLIST_DEF
objlist->light_release( light_id );
delete location;
delete color;
delete media_attenuation;
delete media_interaction;
delete fade;
delete fade_power;
delete fade_distance;
delete parallel;
delete refraction;
delete reflection;
delete area;
delete look_like;
delete projected_through;
}

//**************************************
// Display
// Dessin de la lampe
//**************************************
void PointLight::display( glview *view, bool set_col )
{
OBJLIST_DEF
if ( hidden->value() ) 
	{
	objlist->light_disable( light_id );
	//cout << "\nLight is hidden !"; cout.flush();
	return;
	}

// Rappel de liste deja calculee
if ( location->changed() || color->changed() ) list.invalidate();
Object3D::display( view );	
if ( set_col ) set_color();

if ( look_like->get_current() != NULL ) 
	{
	glPushMatrix();
	
	gfloat x, y, z;
	location->get( x, y, z );
	glTranslatef( x, y, z );
	look_like->get_current()->display( view );
	glPopMatrix();
	}

if ( projected_through->get_current() != NULL ) 
	{
	glPushMatrix();
	
	gfloat x, y, z;
	location->get( x, y, z );
	glTranslatef( x, y, z );
	projected_through->get_current()->display( view );
	glPopMatrix();
	}

if ( ! list.exec() )
	{
	// mise à zero des parametres
	location->unchange();
	color->unchange();

	// creation de la liste si necessaire
	//cout << "  & Creating light !"; cout.flush();
	list.begin();
	//glPushMatrix();
	//glLoadIdentity();
	objlist->light_disable( light_id );
	
	GLfloat coldef[4];
	color->gl_array( coldef );	
	//cout << "\n\tcolor = ";
	//for ( int i = 0 ;i < 4 ; i++ ) cout << coldef[i] << " - ";
	//cout.flush();
	float a, b, c;
	float loc[3];
	location->get( a, b, c );
	loc[0] = a;
	loc[1] = b;
	loc[2] = c;
	//cout << "\n\tposition = " << a << " - " << b << " - " << c;
	//cout << "\n\tligh id = " << light_id;
	//cout.flush();
	objlist->light_set_properties( light_id, loc, coldef );
	objlist->light_enable( light_id );

	// Position et direction
	gfloat x, y, z;
	const float lof = 0.025;

	location->get( x, y, z );

	glBegin( GL_LINES );
		glVertex3f( x-lof, y, z );
		glVertex3f( x+lof, y, z );
		glVertex3f( x, y-lof, z );
		glVertex3f( x, y+lof, z );
		glVertex3f( x, y, z-lof );
		glVertex3f( x, y, z+lof );

		glVertex3f( x-lof, y+lof, z );
		glVertex3f( x+lof, y-lof, z );	
		glVertex3f( x+lof, y+lof, z );
		glVertex3f( x-lof, y-lof, z );

		glVertex3f( x-lof, y, z-lof );
		glVertex3f( x+lof, y, z+lof );	
		glVertex3f( x+lof, y, z-lof );
		glVertex3f( x-lof, y, z+lof );

		glVertex3f( x, y+lof, z+lof );
		glVertex3f( x, y-lof, z-lof );	
		glVertex3f( x, y+lof, z-lof );
		glVertex3f( x, y-lof, z+lof );		
	glEnd();
	//glPopMatrix();
	list.end();
	}
}


//***********************************************
// Edit
//***********************************************
void PointLight::edit_widget( GtkWidget *wid )
{
PREF_DEF
bool tt = pref->tooltips->value();
// Options communes
Object3D::edit_widget( wid );

// Options de geometrie
new_table( 	edit_cont, _("General settings"), 7 );
	location->get_widget( table, tt, 1 );
	color->get_widget( table, tt, 2 );
	media_interaction->get_widget( table, tt, 3 );
	media_attenuation->get_widget( table, tt ,4 );
	parallel->get_widget( table, tt ,5 );
	look_like->get_widget( table, tt, 6 );
	projected_through->get_widget( table, tt, 7 );
	

// Fade
new_frame( edit_cont, _("Light fading") );
GtkWidget *box = gtk_vbox_new( FALSE, 0 );
fade->get_widget( frame, tt, box );
gtk_box_pack_start( GTK_BOX(frame), box, TRUE, TRUE, 3 );
new_table_no_frame( box, 2 );
	fade_power->get_widget( table, tt, 1 );
	fade_distance->get_widget( table, tt, 2 );

// Photons
new_frame( edit_cont, _("Photons") );
refraction->get_widget( frame, tt );
reflection->get_widget( frame, tt );
area->get_widget( frame, tt );

gtk_widget_show_all( wid );
}

//***********************************************
// Mouse drag
//***********************************************
void PointLight::mouse_drag( struct drag_info *drag )
{
VMAN_DEF OBJLIST_DEF
switch ( vmanager->get_pointer_mode() )
	{
	case TV_PMODE_SELECT:
	case TV_PMODE_TRANSLATE:
		location->mouse_drag( drag );
		break;

	case TV_PMODE_CUSTOM:
		((ObjParam_point*)(objlist->get_current_param()))->mouse_drag( drag );
		break;

	default:
		break;
	}
}



//***********************************************
// Pref_changed
//***********************************************
void PointLight::pref_changed()
{
Object3D::pref_changed();
location->pref_changed();
color->pref_changed();
media_attenuation->pref_changed();
media_interaction->pref_changed();
fade->pref_changed();
fade_power->pref_changed();
fade_distance->pref_changed();
parallel->pref_changed();
refraction->pref_changed();
reflection->pref_changed();
area->pref_changed();
look_like->pref_changed();
projected_through->pref_changed();
}

//***********************************************
// Destroy editor
//***********************************************
void PointLight::destroy_editor()
{
Object3D::destroy_editor();
location->clear_widget();
color->clear_widget();
media_attenuation->clear_widget();
media_interaction->clear_widget();
fade->clear_widget();
fade_power->clear_widget();
fade_distance->clear_widget();
parallel->clear_widget();
refraction->clear_widget();
reflection->clear_widget();
area->clear_widget();
look_like->clear_widget();
projected_through->clear_widget();
}

//***********************************************
// Output to povray
//***********************************************
void PointLight::output_to_povray_pass1( ofstream & file )
{
if ( look_like->get_current() != NULL ) 
	look_like->get_current()->output_to_povray_pass1( file );
if ( projected_through->get_current() != NULL ) 
	projected_through->get_current()->output_to_povray_pass1( file );
	
file << "\n\n// Point light : " << name->value();
file << "\n#declare "; get_underscore_name( file ); file << " ="; 

file << "\n\nlight_source {\n\t";
location->output_to_povray(file);
file << "\n\tcolor ";
color->output_to_povray(file);

if ( media_attenuation->value() ) file << "\n\tmedia_attenuation ";
if ( media_interaction->value() ) file << "\n\tmedia_interaction ";
if ( no_shadow->value() ) file << "\n\tshadowless";
if ( parallel->value() ) file << "\n\tparallel";

if ( fade->value() )
	{
	file << "\n\tfade_distance ";
	fade_distance->output_to_povray(file);
	file << "\n\tfade_power ";
	fade_power->output_to_povray(file);
	}
	
if ( look_like->get_current() != NULL ) 
	{
	file << "\n\tlooks_like { object { ";
	look_like->get_current()->get_underscore_name( file );
	file << " } }";
	}
	
if ( projected_through->get_current() != NULL ) 
	{
	file << "\n\tprojected_through { object { ";
	projected_through->get_current()->get_underscore_name( file );
	file << " } }";
	}

file << "\n\tphotons {";
file << "\n\t\trefraction " << ( refraction->value() ? "on" : "off" );
file << "\n\t\treflection " << ( reflection->value() ? "on" : "off" );
if ( area->value() ) file << "\n\t\tarea_light";
file << "\n\t}";
file << "\n}";
}

void PointLight::save( ofstream & file )
{
file << "\nPOINTLIGHT{\n";
save_basics( file );
location->save( file );
color->save( file );
media_attenuation->save( file );
media_interaction->save( file );
fade->save( file );
fade_distance->save( file );
fade_power->save( file );
parallel->save( file );
refraction->save( file );
reflection->save( file );
area->save( file );
look_like->save( file );
projected_through->save( file );
file << "\n}";
}

bool PointLight::load( ifstream & file, char *ltag )
{
if ( strcmp( ltag, "POINTLIGHT" ) ) return false;
set_load_progress( file );

char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	
	if ( load_basics( file, tag ) ) continue;
 	if ( location->load( file, tag ) ) continue;
 	if ( color->load( file, tag ) ) continue;
	if ( media_interaction->load( file, tag ) ) continue;
 	if ( media_attenuation->load( file, tag ) ) continue;
 	if ( fade->load( file, tag ) ) continue;
 	if ( fade_distance->load( file, tag ) ) continue;
 	if ( fade_power->load( file, tag ) ) continue;
 	if ( parallel->load( file, tag ) ) continue;
 	if ( refraction->load( file, tag ) ) continue;
 	if ( reflection->load( file, tag ) ) continue;
 	if ( area->load( file, tag ) ) continue;
 	if ( look_like->load( file, tag ) ) continue;
 	if ( projected_through->load( file, tag ) ) continue;
		
	tvio_skip_section(file );
	} while ( tag != NULL );

return true;
}





//********************************************************************************************
// AREALIGHT
//********************************************************************************************

//**************************************
// Constructeur
//**************************************
AreaLight::AreaLight( app_objs *appref ) : PointLight( appref )
{
type = TV_OBJ3D_AREALIGHT;
category = TV_OBJ3D_LIGHTS;
set_name( "Area light" );
OBJLIST_DEF
light_id = objlist->light_request();

// Base
size = new ObjParam_point( N_("Size"), "SIZE", NULL, app_ref, true );
size->set( 0.1, 0, 0.1 );
rotation = new ObjParam_rotation( N_("Rotation"), "ROT", NULL, app_ref, true );
rotation->set( 0, 0, 0 );
sizex = new ObjParam_int( N_("X density"), "XDENS", NULL, app_ref, true, 2 );
sizex->set_range( 100, 1, 1 );
sizez = new ObjParam_int( N_("Z density"), "ZDENS", NULL, app_ref, true, 2 );
sizez->set_range( 100, 1, 1 );
	
jitter = new ObjParam_bool( N_("Jitter"), "JITTER", NULL, app_ref, false, false );
adaptive = new ObjParam_int( N_("Adaptive"), "ADAPT", NULL, app_ref, false, 0 );
adaptive->set_range( 10, 0, 1 );
	
circular = new ObjParam_bool( N_("Circular"), "CIRC", NULL, app_ref, false );
orient = new ObjParam_bool( N_("Orient"), "ORIENT", NULL, app_ref, false );
parallel = new ObjParam_bool( N_("Parallel"), "PARA", NULL, app_ref, false );
}

AreaLight::AreaLight( AreaLight & ref ) : PointLight( ref )
{
size = new ObjParam_point( *ref.size );
rotation = new ObjParam_rotation( *ref.rotation );
sizex = new ObjParam_int( *ref.sizex );
sizez = new ObjParam_int( *ref.sizez );
jitter = new ObjParam_bool( *ref.jitter );
adaptive = new ObjParam_int( *ref.adaptive );
circular = new ObjParam_bool( *ref.circular );
orient = new ObjParam_bool( *ref.orient );
parallel = new ObjParam_bool( *ref.parallel );
}

AreaLight::~AreaLight()
{
delete size;
delete rotation;
delete sizex;
delete sizez;
delete jitter;
delete adaptive;
delete circular;
delete orient;
delete parallel;
}

//**************************************
// Display
// Dessin de la lampe
//**************************************
void AreaLight::display( glview *view, bool set_col )
{
OBJLIST_DEF
if ( hidden->value() )
	{
	objlist->light_disable( light_id );
	return;
	}

// Rappel de liste deja calculee
if ( location->changed() || color->changed() || size->changed() || rotation->changed() || sizez->changed() || sizex->changed() ) list.invalidate();

Object3D::display( view );	
set_color();
if ( ! list.exec() )
	{
	// mise à zero des parametres
	location->unchange();
	color->unchange();
	rotation->unchange();
	size->unchange();
	sizez->unchange();
	sizex->unchange();

	// creation de la liste si necessaire
	list.begin();
	glPushMatrix();
	objlist->light_enable( light_id );
	GLfloat coldef[4];
	color->gl_array( coldef );
	objlist->light_set_properties( light_id, location->get(), coldef );

	gfloat x, y, z;
	location->get( x, y, z );
	glTranslatef( x, y, z );

		
	if ( projected_through->get_current() != NULL ) 
		{
		glPushMatrix();
		
		gfloat x, y, z;
		location->get( x, y, z );
		glTranslatef( x, y, z );
		projected_through->get_current()->display( view );
		glPopMatrix();
		}

	if ( look_like->get_current() != NULL ) 
		{
		glPushMatrix();
		
		gfloat x, y, z;
		location->get( x, y, z );
		glTranslatef( x, y, z );
		look_like->get_current()->display( view );
		glPopMatrix();
		}
	
	rotation->gl_rotate();
	size->get( x, y, z );

	glBegin( GL_LINE_LOOP );
		glVertex3f( 0, 0, 0 );
		glVertex3f( x, 0, 0 );
		glVertex3f( x, 0, z );
		glVertex3f( 0, 0, z );
	glEnd();
	
	glPointSize( 4 );	
	float xoffset = x / ( sizex->value() - 1 );
	float zoffset = z / ( sizez->value() - 1 );
	x += 0.0001;
	z += 0.0001;
	if ( xoffset == 0 ) xoffset = 0.0001;
	if ( zoffset == 0 ) zoffset = 0.0001;
	glBegin( GL_POINTS );
		for ( float i = 0 ; i <=  x ; i += xoffset )
			for ( float j = 0 ; j <= z ; j += zoffset )
				glVertex3f( i, 0, j );
	glEnd();

	glPopMatrix();	
	list.end();
	}
}


//***********************************************
// Edit
//***********************************************
void AreaLight::edit_widget( GtkWidget *wid )
{
PREF_DEF
bool tt = pref->tooltips->value();
// Options communes
Object3D::edit_widget( wid );

// Options de geometrie
new_table( edit_cont, _("General settings"),  15 );
	location->get_widget( table, tt, 1 );
	size->get_widget( table, tt ,2 );
	rotation->get_widget( table, tt, 3 );
	sizex->get_widget( table, tt, 4 );
	sizez->get_widget( table, tt, 5 );
	circular->get_widget( table, tt, 6 );
	orient->get_widget( table, tt, 7 );
	color->get_widget( table, tt, 8 );
	look_like->get_widget( table, tt, 9 );
	projected_through->get_widget( table, tt, 10 );
	media_interaction->get_widget( table, tt, 11 );
	media_attenuation->get_widget( table, tt, 12 );
	parallel->get_widget( table, tt, 13 );
	adaptive->get_widget( table, tt, 14 );
	jitter->get_widget( table, tt, 15 );

// Fade
new_frame( edit_cont, _("Light fading") );
GtkWidget *box = gtk_vbox_new( FALSE, 0 );
fade->get_widget( frame, tt, box );
gtk_box_pack_start( GTK_BOX(frame), box, TRUE, TRUE, 3 );
new_table_no_frame( box, 2 );
	fade_power->get_widget( table, tt, 1 );
	fade_distance->get_widget( table, tt, 2 );

// Photons
new_frame( edit_cont, _("Photons") );
refraction->get_widget( frame, tt );
reflection->get_widget( frame, tt );
area->get_widget( frame, tt );

gtk_widget_show_all( wid );
}

//***********************************************
// Mouse drag
//***********************************************
void AreaLight::mouse_drag( struct drag_info *drag )
{
VMAN_DEF OBJLIST_DEF
switch ( vmanager->get_pointer_mode() )
	{
	case TV_PMODE_ROTATE:
		rotation->mouse_drag( drag );
		break;
		
	case TV_PMODE_SCALE:
		size->mouse_drag( drag );
		break;
	
	case TV_PMODE_SELECT:
	case TV_PMODE_TRANSLATE:
		location->mouse_drag( drag );
		break;

	case TV_PMODE_CUSTOM:
		((ObjParam_point*)(objlist->get_current_param()))->mouse_drag( drag );
		break;

	default:
		break;
	}
}



//***********************************************
// Pref_changed
//***********************************************
void AreaLight::pref_changed()
{
PointLight::pref_changed();
size->pref_changed();
rotation->pref_changed();
sizex->pref_changed();
sizez->pref_changed();
jitter->pref_changed();
adaptive->pref_changed();
circular->pref_changed();
orient->pref_changed();
parallel->pref_changed();
}

//***********************************************
// Destroy editor
//***********************************************
void AreaLight::destroy_editor()
{
PointLight::destroy_editor();
size->clear_widget();
rotation->clear_widget();
sizex->clear_widget();
sizez->clear_widget();
jitter->clear_widget();
adaptive->clear_widget();
circular->clear_widget();
orient->clear_widget();
parallel->clear_widget();
}

//***********************************************
// Output to povray
//***********************************************
void AreaLight::output_to_povray_pass1( ofstream & file )
{
if ( circular->value() == TRUE )
	{
	if ( sizex->value() == 1 ) sizex->set( 2 );
	if ( sizez->value() == 1 ) sizez->set( 2 );
	}
		
	
if ( look_like->get_current() != NULL ) 
	look_like->get_current()->output_to_povray_pass1( file );
if ( projected_through->get_current() != NULL ) 
	projected_through->get_current()->output_to_povray_pass1( file );
	
file << "\n\n// Area light : " << name->value();
file << "\n#declare "; get_underscore_name( file ); file << " ="; 
	
file << "\n\nlight_source { <0, 0, 0>\n\t";
file << "\n\tcolor ";
color->output_to_povray(file);

file <<"\n\tarea_light <0.1,0,0>, <0,0,0.1>, ";
file << sizex->value() << ", " << sizez->value();
float x, y, z;
size->get( x, y, z );
file << "\n\tscale <" << x << ",1," << -z << "> \n\t";
rotation->output_to_povray( file );
location->get( x, y, z );
file << "\n\ttranslate <" << x << "," << y << "," << -z << "> \n\t";

if ( media_attenuation->value() ) file << "\n\tmedia_attenuation ";
if ( media_interaction->value() ) file << "\n\tmedia_interaction ";
if ( no_shadow->value() ) file << "\n\tshadowless";
if ( jitter->value() ) file << "\n\tjitter";
if ( circular->value() ) file << "\n\tcircular";
if ( orient->value() ) file << "\n\torient";
if ( parallel->value() ) file << "\n\tparallel";	
if ( adaptive->value() > 0 ) file << "\n\tadaptive " << adaptive->value();

if ( fade->value() )
	{
	file << "\n\tfade_distance ";
	fade_distance->output_to_povray(file);
	file << "\n\tfade_power ";
	fade_power->output_to_povray(file);
	}
if ( look_like->get_current() != NULL ) 
	{
	file << "\n\tlooks_like { object { ";
	look_like->get_current()->get_underscore_name( file );
	file << " } }";
	}
if ( projected_through->get_current() != NULL ) 
	{
	file << "\n\tprojected_through { object { ";
	projected_through->get_current()->get_underscore_name( file );
	file << " } }";
	}

file << "\n\tphotons {";
file << "\n\t\trefraction " << ( refraction->value() ? "on" : "off" );
file << "\n\t\treflection " << ( reflection->value() ? "on" : "off" );
if ( area->value() ) file << "\n\t\tarea_light";
file << "\n\t}";

file << "\n}";
}



void AreaLight::save( ofstream & file )
{
file << "\nAREALIGHT{\n";
save_basics( file );
location->save( file );
color->save( file );
media_interaction->save( file );
media_attenuation->save( file );
fade->save( file );
fade_distance->save( file );
fade_power->save( file );
size->save( file );
rotation->save( file );
sizex->save( file );
sizez->save( file );
jitter->save( file );
adaptive->save( file );
circular->save( file );
orient->save( file );
parallel->save( file );
refraction->save( file );
reflection->save( file );
area->save( file );
look_like->save( file );
projected_through->save( file );
file << "\n}";
}

bool AreaLight::load( ifstream & file, char *ltag )
{
if ( strcmp( ltag, "AREALIGHT" ) ) return false;
set_load_progress( file );

char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	
	if ( load_basics( file, tag ) ) continue;
 	if ( location->load( file, tag ) ) continue;
 	if ( color->load( file, tag ) ) continue;
 	if ( media_interaction->load( file, tag ) ) continue;
 	if ( media_attenuation->load( file, tag ) ) continue;
 	if ( fade->load( file, tag ) ) continue;
 	if ( fade_distance->load( file, tag ) ) continue;
 	if ( fade_power->load( file, tag ) ) continue;
 	if ( size->load( file, tag ) ) continue;
 	if ( rotation->load( file, tag ) ) continue;
 	if ( sizex->load( file, tag ) ) continue;
 	if ( sizez->load( file, tag ) ) continue;
 	if ( jitter->load( file, tag ) ) continue;
 	if ( adaptive->load( file, tag ) ) continue;
 	if ( circular->load( file, tag ) ) continue;
 	if ( orient->load( file, tag ) ) continue;
	if ( parallel->load( file, tag ) ) continue;
	if ( refraction->load( file, tag ) ) continue;
	if ( reflection->load( file, tag ) ) continue;
	if ( area->load( file, tag ) ) continue;
	if ( look_like->load( file, tag ) ) continue;
	if ( projected_through->load( file, tag ) ) continue;

	tvio_skip_section(file );
	} while ( tag != NULL );

return true;
}



//***********************************************************************************************
// SPOTLIGHT
//***********************************************************************************************

//**************************************
// Constructeur
//**************************************
SpotLight::SpotLight( app_objs *appref ) : Object3D( appref )
{
type = TV_OBJ3D_SPOTLIGHT;
category = TV_OBJ3D_LIGHTS;
set_name( "Spot light" );
OBJLIST_DEF
light_id = objlist->light_request();

// Base
location = new ObjParam_point( N_("Location"), "LOC", NULL, app_ref, true );
location->set( 0.6, 0.6, 0.6 );
pointat = new ObjParam_point( N_("Point at"), "AT", NULL, app_ref, true );
pointat->set( 0, 0, 0 );
radius = new ObjParam_float( N_("Radius"), "RAD", NULL, app_ref, 30, true );
radius->set( 70 );
radius->set_range( 360, 0, 1, 3 );
falloff = new ObjParam_float( N_("Falloff"), "FOFF", NULL, app_ref, 45, true );
falloff->set( 70 );
falloff->set_range( 360, 0, 1, 3 );
tightness = new ObjParam_int( N_("Tightness"), "TNESS", NULL, app_ref, 0, true );
tightness->set_range( 100, 1, 1 );

frustum = new ObjParam_bool( N_("Show frutum"), "FRUS", NULL, app_ref, true, true );
color = new ObjParam_color( N_("Color"), "COL", NULL, app_ref, true );
color->set( 255, 255, 255, 255 );
media_attenuation = new ObjParam_bool( N_("Media attenuation"), "MED_ATT", NULL, app_ref, false, false );
media_interaction = new ObjParam_bool( N_("Media interaction"), "MED_INT", NULL, app_ref, false, false );
parallel = new ObjParam_bool( N_("Parallel"), "PARA", NULL, app_ref, false, false );
look_like = new ObjParam_objref( N_("Looks like"), "LLIKE", NULL, app_ref, false );
projected_through = new ObjParam_objref( N_("Projected through"), "PROJT", NULL, app_ref, false );

// Fade
fade = new ObjParam_bool_activator( N_("Use fading"), "FADE_USED", NULL, app_ref, false, false );
fade_power = new ObjParam_float( N_("Fade power"), "FADE_POW", NULL, app_ref, false, 0 );
	fade_power->set_range( 10, 0, 0.1 );
fade_distance = new ObjParam_float( N_("Fade distance"), "FADE_DIST", NULL, appref, false, 2 );
	fade_distance->set_range( 10, 0, 0.1 );

// Photons
refraction = new ObjParam_bool( N_("Refraction"), "PREFRAC", NULL, app_ref, false, false );
reflection = new ObjParam_bool( N_("Reflection"), "PREFLEC", NULL, app_ref, false, false );
area = new ObjParam_bool( N_("Area light"), "PAREA", NULL, app_ref, false, false );
}

SpotLight::SpotLight( SpotLight & ref ) : Object3D( ref )
{
OBJLIST_DEF
light_id = objlist->light_request();

location = new ObjParam_point( *ref.location );
pointat = new ObjParam_point( *ref.pointat );
radius = new ObjParam_float( *ref.radius );
falloff = new ObjParam_float( *ref.falloff );
tightness = new ObjParam_int( *ref.tightness );
frustum = new ObjParam_bool( *ref.frustum );
color = new ObjParam_color( *ref.color );
media_attenuation = new ObjParam_bool( *ref.media_attenuation );
media_interaction = new ObjParam_bool( *ref.media_interaction );
fade = new ObjParam_bool_activator( *ref.fade );
fade_power = new ObjParam_float( *ref.fade_power );
fade_distance = new ObjParam_float( *ref.fade_distance );
parallel = new ObjParam_bool( *ref.parallel );
refraction = new ObjParam_bool( *ref.refraction );
reflection = new ObjParam_bool( *ref.reflection );
area = new ObjParam_bool( *ref.area );
look_like = new ObjParam_objref( *ref.look_like );
projected_through = new ObjParam_objref( *ref.projected_through );
}

SpotLight::~SpotLight()
{
OBJLIST_DEF
objlist->light_release( light_id );
delete location;
delete pointat;
delete radius;
delete falloff;
delete tightness;
delete frustum;
delete color;
delete media_attenuation;
delete media_interaction;
delete fade;
delete fade_power;
delete fade_distance;
delete parallel;
delete refraction;
delete reflection;
delete area;
delete look_like;
delete projected_through;
}

//**************************************
// Display
// Dessin de la lampe
//**************************************
void SpotLight::display( glview *view, bool set_col )
{
OBJLIST_DEF
if ( hidden->value() ) 
	{
	objlist->light_disable( light_id );
	return;
	}

// Rappel de liste deja calculee
if ( location->changed() || color->changed() || pointat->changed() || radius->changed() || frustum->changed() ) list.invalidate();
GLint polymode;
glGetIntegerv( GL_POLYGON_MODE, &polymode );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

Object3D::display( view );	
set_color();
if ( ! list.exec() )
	{
	// mise à zero des parametres
	location->unchange();
	pointat->unchange();
	frustum->unchange();
	radius->unchange();
	color->unchange();

	// creation de la liste si necessaire
	list.begin();

if ( look_like->get_current() != NULL ) 
	{
	glPushMatrix();
	
	gfloat x, y, z;
	location->get( x, y, z );
	glTranslatef( x, y, z );
	look_like->get_current()->display( view );
	glPopMatrix();
	}

if ( projected_through->get_current() != NULL ) 
	{
	glPushMatrix();
	
	gfloat x, y, z;
	location->get( x, y, z );
	glTranslatef( x, y, z );
	projected_through->get_current()->display( view );
	glPopMatrix();
	}
	
	glPushMatrix();
	objlist->light_enable( light_id );
	GLfloat coldef[4];
	color->gl_array( coldef );
	objlist->light_set_properties( light_id, location->get(), pointat->get(), radius->value(), coldef );	

	// Position et direction
	const float lof = 0.025;
	float x, y, z, a, b, c;
	location->get( x, y, z );
	pointat->get( a, b, c );

	glTranslatef( x, y, z );
	

	
	glRotatef( atan2( a-x, c-z )*180/3.1415, 0, 1, 0 );	
	glRotatef( -atan2( b-y, sqrt( (c-z)*(c-z) + (a-x)*(a-x) ) )*180/3.1415, 1, 0, 0 );
	
	glBegin( GL_LINES );
		glVertex3f( -lof, 0, 0 );
		glVertex3f( +lof, 0, 0 );
		glVertex3f( 0, -lof, 0 );
		glVertex3f( 0, +lof, 0 );
		glVertex3f( 0, 0, -lof );
		glVertex3f( 0, 0, +lof );

		glVertex3f( -lof, +lof, 0 );
		glVertex3f( +lof, -lof, 0 );	
		glVertex3f( +lof, +lof, 0 );
		glVertex3f( -lof, -lof, 0 );

		glVertex3f( -lof, 0, -lof );
		glVertex3f( +lof, 0, +lof );	
		glVertex3f( +lof, 0, -lof );
		glVertex3f( -lof, 0, +lof );

		glVertex3f( 0, +lof, +lof );
		glVertex3f( 0, -lof, -lof );	
		glVertex3f( 0, +lof, -lof );
		glVertex3f( 0, -lof, +lof );		
	glEnd();

	if ( frustum->value() )
		{
		float dist = sqrt( (x-a)*(x-a) + (y-b)*(y-b) + (z-c)*(z-c) );
		GLUquadricObj *quadobj = gluNewQuadric();
		if ( !quadobj ) { app_warning( N_("Out of memory" ) ); return; }	
    	gluCylinder(quadobj, 0, sin(radius->value() *3.1415 / 360 )*dist*2, dist, 12, 12 );
		}

	glPopMatrix();
	list.end();
	}
glPolygonMode( static_cast<GLenum>(GL_FRONT_AND_BACK), static_cast<GLenum>(polymode) );
}


//***********************************************
// Edit
//***********************************************
void SpotLight::edit_widget( GtkWidget *wid )
{
PREF_DEF
bool tt = pref->tooltips->value();
edit_widget_base( wid );
hidden->get_widget( edit_cont, tt );
hidden->connect_signal( GTK_SIGNAL_FUNC(sign_hidden_changed), this );
render->get_widget( edit_cont, tt );
frustum->get_widget( edit_cont, tt );

// Options de geometrie
new_table( edit_cont, _("General settings"), 11 );
	location->get_widget( table, tt, 1 );
	pointat->get_widget( table, tt, 2 );
	radius->get_widget( table, tt, 3 );
	falloff->get_widget( table, tt, 4 );
	tightness->get_widget( table, tt, 5 );
	color->get_widget( table, tt, 6 );
	media_interaction->get_widget( table, tt, 7 );
	media_attenuation->get_widget( table, tt, 8 );
	parallel->get_widget( table, tt, 9 );
	look_like->get_widget( table, tt, 10 );
	projected_through->get_widget( table, tt, 11 );
	
// Fade
new_frame( edit_cont, _("Light fading") );
GtkWidget *box = gtk_vbox_new( FALSE, 0 );
fade->get_widget( frame, tt, box );
gtk_box_pack_start( GTK_BOX(frame), box, TRUE, TRUE, 3 );
new_table_no_frame( box, 2 );
	fade_power->get_widget( table, tt, 1 );
	fade_distance->get_widget( table, tt, 2 );

// Photons
new_frame( edit_cont, _("Photons") );
refraction->get_widget( frame, tt );
reflection->get_widget( frame, tt );
area->get_widget( frame, tt );

gtk_widget_show_all( wid );
}

//***********************************************
// Mouse drag
//***********************************************
void SpotLight::mouse_drag( struct drag_info *drag )
{
VMAN_DEF OBJLIST_DEF
switch ( vmanager->get_pointer_mode() )
	{
	case TV_PMODE_SELECT:
	case TV_PMODE_TRANSLATE:
		location->mouse_drag( drag );
		break;

	case TV_PMODE_CUSTOM:
		((ObjParam_point*)(objlist->get_current_param()))->mouse_drag( drag );
		break;

	default:
		break;
	}
}



//***********************************************
// Pref_changed
//***********************************************
void SpotLight::pref_changed()
{
Object3D::pref_changed();
location->pref_changed();
pointat->pref_changed();
radius->pref_changed();
falloff->pref_changed();
tightness->pref_changed();
frustum->pref_changed();
color->pref_changed();
media_attenuation->pref_changed();
media_interaction->pref_changed();
fade->pref_changed();
fade_power->pref_changed();
fade_distance->pref_changed();
parallel->pref_changed();
refraction->pref_changed();
reflection->pref_changed();
area->pref_changed();
look_like->pref_changed();
projected_through->pref_changed();
}

//***********************************************
// Destroy editor
//***********************************************
void SpotLight::destroy_editor()
{
Object3D::destroy_editor();
location->clear_widget();
pointat->clear_widget();
radius->clear_widget();
falloff->clear_widget();
tightness->clear_widget();
frustum->clear_widget();
color->clear_widget();
media_attenuation->clear_widget();
media_interaction->clear_widget();
fade->clear_widget();
fade_power->clear_widget();
fade_distance->clear_widget();
parallel->clear_widget();
refraction->clear_widget();
reflection->clear_widget();
area->clear_widget();
look_like->clear_widget();
projected_through->clear_widget();
}

//***********************************************
// Output to povray
//***********************************************
void SpotLight::output_to_povray_pass1( ofstream & file )
{
if ( look_like->get_current() != NULL ) 
	look_like->get_current()->output_to_povray_pass1( file );
if ( projected_through->get_current() != NULL ) 
	projected_through->get_current()->output_to_povray_pass1( file );
	
file << "\n\n// Spotlight : " << name->value();
file << "\n#declare "; get_underscore_name( file ); file << " ="; 
	
file << "\n\nlight_source {\n\t";
location->output_to_povray(file);
file << "\n\tcolor ";
color->output_to_povray(file);
file << "\n\tspotlight";
file << "\n\tradius "; 
radius->output_to_povray(file);
file << "\n\tfalloff ";
falloff->output_to_povray(file);
file << "\n\tpoint_at ";
pointat->output_to_povray(file);
file << "\n\ttightness ";
tightness->output_to_povray(file);

if ( media_attenuation->value() ) file << "\n\tmedia_attenuation ";
if ( media_interaction->value() ) file << "\n\tmedia_interaction ";
if ( parallel->value() ) file << "\n\tparallel ";

if ( fade->value() )
	{
	file << "\n\tfade_distance ";
	fade_distance->output_to_povray(file);
	file << "\n\tfade_power ";
	fade_power->output_to_povray(file);
	}
	
if ( look_like->get_current() != NULL ) 
	{
	file << "\n\tlooks_like { object { ";
	look_like->get_current()->get_underscore_name( file );
	file << " } }";
	}
if ( projected_through->get_current() != NULL ) 
	{
	file << "\n\tprojected_through { object { ";
	projected_through->get_current()->get_underscore_name( file );
	file << " } }";
	}
	
file << "\n\tphotons {";
file << "\n\t\trefraction " << ( refraction->value() ? "on" : "off" );
file << "\n\t\treflection " << ( reflection->value() ? "on" : "off" );
if ( area->value() ) file << "\n\t\tarea_light";
file << "\n\t}";

file << "\n}";
}

void SpotLight::save( ofstream & file )
{
file << "\nSPOTLIGHT{\n";
save_basics( file );
location->save( file );
color->save( file );
radius->save( file );
falloff->save( file );
pointat->save( file );
tightness->save( file );
media_interaction->save( file );
media_attenuation->save( file );
fade->save( file );
fade_distance->save( file );
fade_power->save( file );
parallel->save( file );
refraction->save( file );
reflection->save( file );
area->save( file );
look_like->save( file );
projected_through->save( file );
file << "\n}";
}

bool SpotLight::load( ifstream & file, char *ltag )
{
if ( strcmp( ltag, "SPOTLIGHT" ) ) return false;
set_load_progress( file );

char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	
	if ( load_basics( file, tag ) ) continue;
 	if ( location->load( file, tag ) ) continue;
 	if ( color->load( file, tag ) ) continue;
 	if ( media_interaction->load( file, tag ) ) continue;
 	if ( media_attenuation->load( file, tag ) ) continue;
 	if ( fade->load( file, tag ) ) continue;
 	if ( fade_distance->load( file, tag ) ) continue;
 	if ( fade_power->load( file, tag ) ) continue;
 	if ( radius->load( file, tag ) ) continue;
 	if ( falloff->load( file, tag ) ) continue;
 	if ( pointat->load( file, tag ) ) continue;
 	if ( tightness->load( file, tag ) ) continue;
	if ( parallel->load( file, tag ) ) continue;
	if ( area->load( file, tag ) ) continue;
	if ( refraction->load( file, tag ) ) continue;
	if ( reflection->load( file, tag ) ) continue;
	if ( look_like->load( file, tag ) ) continue;
	if ( projected_through->load( file, tag ) ) continue;
	
	tvio_skip_section(file );
	} while ( tag != NULL );

return true;
}


//*****************************************************
// CylindricalLight
//*****************************************************
CylindricalLight::CylindricalLight( app_objs *app_ref ) : SpotLight( app_ref ) 
{
type = TV_OBJ3D_CYLLIGHT;
category = TV_OBJ3D_LIGHTS;
set_name( "Cylindrical light" );
}

//**************************************
// Display
// Dessin de la lampe
//**************************************
void CylindricalLight::display( glview *view, bool set_col )
{
OBJLIST_DEF
if ( hidden->value() ) 
	{
	objlist->light_disable( light_id );
	return;
	}

// Rappel de liste deja calculee
if ( location->changed() || color->changed() || pointat->changed() || radius->changed() || frustum->changed() ) list.invalidate();
GLint polymode;
glGetIntegerv( GL_POLYGON_MODE, &polymode );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

Object3D::display( view );	
set_color();
if ( ! list.exec() )
	{
	// mise à zero des parametres
	location->unchange();
	pointat->unchange();
	frustum->unchange();
	radius->unchange();
	color->unchange();

	// creation de la liste si necessaire
	list.begin();

if ( look_like->get_current() != NULL ) 
	{
	glPushMatrix();
	
	gfloat x, y, z;
	location->get( x, y, z );
	glTranslatef( x, y, z );
	look_like->get_current()->display( view );
	glPopMatrix();
	}

if ( projected_through->get_current() != NULL ) 
	{
	glPushMatrix();
	
	gfloat x, y, z;
	location->get( x, y, z );
	glTranslatef( x, y, z );
	projected_through->get_current()->display( view );
	glPopMatrix();
	}	
	
	glPushMatrix();
	objlist->light_enable( light_id );
	GLfloat coldef[4];
	color->gl_array( coldef );
	objlist->light_set_properties( light_id, location->get(), pointat->get(), radius->value(), coldef );	

	// Position et direction
	const float lof = 0.025;
	float x, y, z, a, b, c;
	location->get( x, y, z );
	pointat->get( a, b, c );

	glTranslatef( x, y, z );
	
	glRotatef( atan2( a-x, c-z )*180/3.1415, 0, 1, 0 );	
	glRotatef( -atan2( b-y, sqrt( (c-z)*(c-z) + (a-x)*(a-x) ) )*180/3.1415, 1, 0, 0 );
	
	glBegin( GL_LINES );
		glVertex3f( -lof, 0, 0 );
		glVertex3f( +lof, 0, 0 );
		glVertex3f( 0, -lof, 0 );
		glVertex3f( 0, +lof, 0 );
		glVertex3f( 0, 0, -lof );
		glVertex3f( 0, 0, +lof );

		glVertex3f( -lof, +lof, 0 );
		glVertex3f( +lof, -lof, 0 );	
		glVertex3f( +lof, +lof, 0 );
		glVertex3f( -lof, -lof, 0 );

		glVertex3f( -lof, 0, -lof );
		glVertex3f( +lof, 0, +lof );	
		glVertex3f( +lof, 0, -lof );
		glVertex3f( -lof, 0, +lof );

		glVertex3f( 0, +lof, +lof );
		glVertex3f( 0, -lof, -lof );	
		glVertex3f( 0, +lof, -lof );
		glVertex3f( 0, -lof, +lof );		
	glEnd();

	if ( frustum->value() )
		{
		float dist = sqrt( (x-a)*(x-a) + (y-b)*(y-b) + (z-c)*(z-c) );
		float rad = sin(radius->value() *3.1415 / 360 )*dist*2;
		GLUquadricObj *quadobj = gluNewQuadric();
		if ( !quadobj ) { app_warning( N_("Out of memory" ) ); return; }	
    	gluCylinder(quadobj, rad, rad, dist, 12, 12 );
		}

	glPopMatrix();
	list.end();
	}
glPolygonMode(  static_cast<GLenum>(GL_FRONT_AND_BACK), static_cast<GLenum>(polymode) );
}

//***********************************************
// Output to povray
//***********************************************
void CylindricalLight::output_to_povray_pass1( ofstream & file )
{
if ( look_like->get_current() != NULL ) 
	look_like->get_current()->output_to_povray_pass1( file );
if ( projected_through->get_current() != NULL ) 
	projected_through->get_current()->output_to_povray_pass1( file );

file << "\n\n// Cylindrical light : " << name->value();
file << "\n#declare "; get_underscore_name( file ); file << " ="; 

file << "\n\nlight_source {\n\t";
location->output_to_povray(file);
file << "\n\tcolor ";
color->output_to_povray(file);
file << "\n\tcylinder";
file << "\n\tradius "; 
radius->output_to_povray(file);
file << "\n\tfalloff ";
falloff->output_to_povray(file);
file << "\n\tpoint_at ";
pointat->output_to_povray(file);
file << "\n\ttightness ";
tightness->output_to_povray(file);

if ( media_attenuation->value() ) file << "\n\tmedia_attenuation ";
if ( media_interaction->value() ) file << "\n\tmedia_interaction ";
if ( parallel->value() ) file << "\n\tparallel ";

if ( fade->value() )
	{
	file << "\n\tfade_distance ";
	fade_distance->output_to_povray(file);
	file << "\n\tfade_power ";
	fade_power->output_to_povray(file);
	}
	
if ( look_like->get_current() != NULL ) 
	{
	file << "\n\tlooks_like { object { ";
	look_like->get_current()->get_underscore_name( file );
	file << " } }";
	}
	
if ( projected_through->get_current() != NULL ) 
	{
	file << "\n\tprojected_through { object { ";
	projected_through->get_current()->get_underscore_name( file );
	file << " } }";
	}
	
file << "\n\tphotons {";
file << "\n\t\trefraction " << ( refraction->value() ? "on" : "off" );
file << "\n\t\treflection " << ( reflection->value() ? "on" : "off" );
if ( area->value() ) file << "\n\t\tarea_light";
file << "\n\t}";

file << "\n}";
}

void CylindricalLight::save( ofstream & file )
{
file << "\nCYLINDERLIGHT{\n";
save_basics( file );
location->save( file );
color->save( file );
radius->save( file );
falloff->save( file );
pointat->save( file );
tightness->save( file );
media_interaction->save( file );
media_attenuation->save( file );
fade->save( file );
fade_distance->save( file );
fade_power->save( file );
parallel->save( file );
refraction->save( file );
reflection->save( file );
area->save( file );
look_like->save( file );
projected_through->save( file );
file << "\n}";
}


bool CylindricalLight::load( ifstream & file, char *ltag )
{
if ( strcmp( ltag, "CYLINDERLIGHT" ) ) return false;
set_load_progress( file );

char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	
	if ( load_basics( file, tag ) ) continue;
 	if ( location->load( file, tag ) ) continue;
 	if ( color->load( file, tag ) ) continue;
 	if ( media_interaction->load( file, tag ) ) continue;
 	if ( media_attenuation->load( file, tag ) ) continue;
 	if ( fade->load( file, tag ) ) continue;
 	if ( fade_distance->load( file, tag ) ) continue;
 	if ( fade_power->load( file, tag ) ) continue;
 	if ( radius->load( file, tag ) ) continue;
 	if ( falloff->load( file, tag ) ) continue;
 	if ( pointat->load( file, tag ) ) continue;
 	if ( tightness->load( file, tag ) ) continue;
 	if ( parallel->load( file, tag ) ) continue;
 	if ( refraction->load( file, tag ) ) continue;
 	if ( reflection->load( file, tag ) ) continue;
 	if ( area->load( file, tag ) ) continue;
	if ( look_like->load( file, tag ) ) continue;
	if ( projected_through->load( file, tag ) ) continue;
	
	tvio_skip_section(file );
	} while ( tag != NULL );

return true;
}
