/****************************************************************************
 *                  glow.cpp
 *
 *  This module contains functions for the glow feature.
 *
 * from Persistence of Vision(tm) Ray Tracer version 3.6.
 * Copyright 1991-2003 Persistence of Vision Team
 * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
 *---------------------------------------------------------------------------
 * NOTICE: This source code file is provided so that users may experiment
 * with enhancements to POV-Ray and to port the software to platforms other
 * than those supported by the POV-Ray developers. There are strict rules
 * regarding how you are permitted to use this file. These rules are contained
 * in the distribution and derivative versions licenses which should have been
 * provided with this file.
 *
 * These licences may be found online, linked from the end-user license
 * agreement that is located at http://www.povray.org/povlegal.html
 *---------------------------------------------------------------------------
 * This program is based on the popular DKB raytracer version 2.12.
 * DKBTrace was originally written by David K. Buck.
 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 *---------------------------------------------------------------------------
 *
 *===========================================================================
 * This file is part of MegaPOV, a modified and unofficial version of POV-Ray
 * For more information on MegaPOV visit our website:
 * http://megapov.inetart.net/
 *===========================================================================
 *
 * $RCSfile: glow.cpp,v $
 * $Revision: 1.9 $
 * $Author: chris $
 *
 *****************************************************************************/

#include "frame.h"

#ifdef GLOW_PATCH
#include "povray.h"
#include "vector.h"
#include "matrices.h"
#include "colour.h"
#include "express.h"
#include "pigment.h"
#include "warps.h"
#include "glow.h"

#include "parse.h"
#include "parstxtr.h"
#include "tokenize.h" 
#include "texture.h"

#include <algorithm>
BEGIN_POV_NAMESPACE

/*****************************************************************************
* Local preprocessor defines
******************************************************************************/

/*****************************************************************************
* Local typedefs
******************************************************************************/

/*****************************************************************************
* Static functions
******************************************************************************/

static void Compute_Glow(GLOW * Glow, INTERSECTION * Isect, RAY * Ray, COLOUR Colour);

/*****************************************************************************
* Local variables
******************************************************************************/

/*****************************************************************************
*
* FUNCTION   Create_Glow()
*
*   
*
* INPUT
*
* OUTPUT
*
* RETURNS   pointer to a GLOW struct
*
* AUTHOR
*
*   Chris Huff
*
* DESCRIPTION
*
*   Creates and initializes a glow.
*
* CHANGES
*
*   2000.9.02: Creation.
*
******************************************************************************/
GLOW * Create_Glow()
{
	GLOW * New = (GLOW *)POV_MALLOC(sizeof(GLOW), "glow");
	Make_Vector(New->Center, 0, 0, 0);
	Make_Colour(New->Colour, 1, 1, 1);
	New->Glow_Type = 1;
	New->Size = 1;
	New->Cutoff_Radius = 0;
	New->fade_pow = 1;
	New->Warps = NULL;
	New->next = NULL;

	return New;
}

/*****************************************************************************
*
* FUNCTION   Add_Glow()
*
*   
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Chris Huff
*
* DESCRIPTION
*
*   Adds a glow to the list in Frame.
*
* CHANGES
*
*   2000.9.02: Creation.
*
******************************************************************************/
void Add_Glow(GLOW * Glow)
{
	Glow->next = Frame.Glows;
	Frame.Glows = Glow;

    Frame.NumOfGlows++;
}

/*****************************************************************************
*
* FUNCTION   Destroy_Glow()
*
*   
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Chris Huff
*
* DESCRIPTION
*
*   Destroys a glow.
*
* CHANGES
*
*   2000.9.02: Creation.
*
******************************************************************************/
void Destroy_Glow(GLOW * Glow)
{
	if(Glow->Warps != NULL)
	{
		Destroy_TPat_Fields(Glow->Warps);
		POV_FREE(Glow->Warps);
	}
	POV_FREE(Glow);
}

/*****************************************************************************
*
* FUNCTION   Destroy_Glow_List()
*
*   
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Chris Huff
*
* DESCRIPTION
*
*   Destroys a list of glows.
*
* CHANGES
*
*   2000.9.02: Creation.
*
******************************************************************************/
void Destroy_Glow_List(GLOW * Glow_List)
{
	GLOW * Glow = Glow_List;
	while(Glow != NULL)
	{
		GLOW * Next_Glow = Glow->next;
		Destroy_Glow(Glow);
		Glow = Next_Glow;
	}

}

/*****************************************************************************
*
* FUNCTION   Do_Glow()
*
*   
*
* INPUT
*
* OUTPUT
*
* RETURNS 
*
* AUTHOR
*
*   Chris Huff
*
* DESCRIPTION
*
*   Calculates effect of a single glow.
*
* CHANGES
*
*   2000.9.02: Creation.
*
******************************************************************************/
static void Compute_Glow(GLOW * Glow, INTERSECTION * Isect, RAY * Ray, COLOUR Colour)
{
	VECTOR Pt;/*Point on plane perpendicular to ray and passing through glow*/
	VECTOR lightDir;/*direction of glow source*/
	COLOUR scattered_light;
	DBL Depth = Isect->Depth;/*Distance to intersection*/
	DBL scattering = 0;
	DBL cosA;/*cosine of the angle between the ray and the direction of the glow source*/
	DBL Dist;
    
	/* cosA Computing */
	VSub(lightDir, Ray->Initial, Glow->Center);
	VDot(cosA, lightDir, Ray->Direction);

	/* d0 Computing */
	VScale(Pt, Ray->Direction, -cosA);
	VAddEq(Pt, Ray->Initial);
	if(Glow->Warps != NULL)
		Warp_EPoint(Pt, Pt, Glow->Warps);
		
	VSubEq(Pt, Glow->Center);
	VLength(Dist, Pt);
	if(Glow->Cutoff_Radius == 0 || Dist < Glow->Cutoff_Radius)
	{
		/* scattered energy integration along ray */
		switch(Glow->Glow_Type)
		{
			case 0:/* A model, I(d) = 1/d^2 */
				Dist /= Glow->Size;
				scattering = (atan((Depth+cosA)/Dist) - atan(cosA/Dist))/Dist;
			break;

			case 1:/* B model, I(d) = 1/(d^2+1) */
			{
				DBL d0;
				VDot(d0, Pt, Pt);
				DBL denom = sqrt(d0 + 1)/Glow->Size;
				if(Depth >= Max_Distance) /* Optimization */
					scattering = (M_PI_2 - atan(cosA/denom))/denom;
				else
					scattering = (atan((Depth+cosA)/denom) - atan(cosA/denom))/denom;
			}
		    break;

			case 2:/*exp() falloff*/
				if(cosA < 0)
					scattering = exp(-Dist/Glow->Size);
			break;

			case 3:/*Cosine falloff*/
			{
				DBL d = Dist/Glow->Size;
				if(d < 1 && cosA < 0)
					scattering = sin(max(0.0, 1-d)*M_PI_2);
			}
			break;
		}
		if(Glow->fade_pow != 1)
		{
			if(Glow->fade_pow == 2)
				scattering *= scattering;
			else
				scattering = pow(scattering, Glow->fade_pow);
		}
	    Scale_Colour(scattered_light, Glow->Colour, scattering);
        Add_Colour(Colour, Colour, scattered_light);
	}
}

/*****************************************************************************
*
* FUNCTION   Do_Glow()
*
*   
*
* INPUT
*
* OUTPUT
*
* RETURNS 
*
* AUTHOR
*
*   Chris Huff
*
* DESCRIPTION
*
*   Calculates glow effect.
*
* CHANGES
*
*   2000.9.02: Creation.
*
******************************************************************************/
void Do_Glow(INTERSECTION * Isect, RAY * Ray, COLOUR Colour)
{
	GLOW * Glow = Frame.Glows;
	while(Glow != NULL)
	{
		Compute_Glow(Glow, Isect, Ray, Colour);
		Glow = Glow->next;
	}
}



void Transform_Glow(GLOW * Glow, TRANSFORM * Trans)
{
	MTransPoint(Glow->Center, Glow->Center, Trans);
}
END_POV_NAMESPACE
#endif
