/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__TRACER_CORE__
#define	__TRACER_CORE__


		#include "raytracer/raytracer_scene.h"
		#include "raytracer/raytracer_spread.h"
		#include "core/shader_tree.h"
		#include "core/material.h"
		#include "picture/pict.h"


namespace GS {
	struct Color;

	namespace Core {
		struct ShaderBlock;
		struct ShaderBlockValue;
	}
	namespace S3D { class Scene; }
	namespace Raytrace {

//
struct	Bounce
{
		uint	indirect,
				reflection,
				refraction;
};

/// Ray grid (ray density estimation).
struct	RayGrid
{
		Vector4		p[4], d[4];

		RayGrid(const Vector4 &_p, const Vector4 &_d)
		{	p[0] = _p; d[0] = _d;	}
};

//
struct	Statistics
{
		uint		ray_count,
					tri_test;

		void		Reset()
		{
			ray_count = 0;
			tri_test = 0;
		}

		Statistics()
		{	Reset();	}
};

//
struct	Configuration
{
		uint		trace_shadow_transparency_max_recursion,
					trace_reflection_max_recursion,
					trace_refraction_max_recursion,
					indirect_gi_bounce,
					gi_sample,
					aa_sample;

		bool		trace_shadow,
					trace_gi,
					trace_aa,
					trace_transparency,
					trace_reflection,
					trace_refraction,
					gi_use_ambient,
					aa_jitter,
					ao_activate,
					fresnel_activate;

		float		ao_length, 
					ao_angle;

		bool		interlaced,									///< Interlaced render.
					interlace_even,								///< Start with even ordered scanline.
					interlaced_trace_half_frame;				///< When rendering interlaced, compute half-frames instead of full ones (at a very small cost in quality).

		float		aa_threshold;

		uint		job_affinity;

		Configuration()
		{
			trace_shadow_transparency_max_recursion = 32;
			trace_reflection_max_recursion = 2;
			trace_refraction_max_recursion = 6;
			indirect_gi_bounce = 1;

			trace_shadow = true;
			trace_reflection = true;
			trace_refraction = true;
			trace_gi = false;
			trace_aa = true;
			trace_transparency = true;

			ao_length = 1.0f;
			ao_angle  = 90.0f;
			ao_activate = false;

			fresnel_activate = false;

			gi_use_ambient = false;
			gi_sample = 8;
			aa_sample = 4;
			aa_threshold = 0.0075f;
			aa_jitter = false;

			interlaced = false;
			interlace_even = true;
			interlaced_trace_half_frame = true;

			job_affinity = uint(~0);
		}
};

class	Raytracer;

//
struct	Progress
{
		const Raytracer	*instance;

		String			description;
		uint			start_clock;	///< System clock when rendering started.

		float			progress;
		int				w, h;
		Color			*buffer;

		bool			aborted;		///< Rendering aborted.
		bool			done;			///< Rendering done.
};

/// Progress hook.
struct	ProgressHook
{	virtual	bool		RaytracerProgress(const Progress &) = 0;	};

/*!
	@short	Raytracer.

	This class can be used as a generic raytracer to render raytraced pictures
	of a scene or as a baking tool for the nGeometry class.
	Supports, forward raytracing, Monte-Carlo raytracing, photon mapping along
	more common material properties, diffuse, specular, reflection, refraction,
	etc...

	@author Emmanuel Julien (ejulien@nworks.fr)
*/
class	Raytracer
{
public:

/// Geometry attributes.
enum	GeometryAttribute
{
	GeometryNormal			=	0,
	GeometryVertexColor
};

protected:

		bool			abort;

		Core::ResourceFactory *gf;

		const S3D::Scene *scene;

		SceneBIH		scene_tree,					///< Scene tree.
						scene_shadow_tree;			///< Shadow scene tree.

		Array <Light>	lgt;						///< Light array.

		Statistics		statistics;
		Configuration	configuration;
		Spread			monte_carlo[32];			///< Monte-Carlo vector spread.

		Vector2		viewport;						///< Output resolution of the current rendering.

		float			render_clock;				///< Reference clock frozen at frame start.

		/// Compute the Fresnel factor
		float			Fresnel(const Vector4 &v, const Vector4 &np, float eta);

		/// Compute trace result radiance.
		void			ComputeRadiance(Trace &trace, Color &o, Bounce &bounce);
		/// Raytrace.
		void			Raytrace(const RayGrid &ray, Color &o, Bounce &bounce, Trace *previous_trace = 0);

		/// Evaluate shader block on the CPU.
		bool			EvaluateShaderBlock(const Trace &trace, const Core::ShaderBlock *block, Core::ShaderBlockValue &out);

		/// Sample material opacity.
		float			SampleMaterialOpacity(const Trace &trace);
		/// Sample material sink.
virtual Vector4			SampleMaterialSink(const Trace &trace, Core::ShaderTree::ShaderSinkType sink);
		/// Sample material attribute.
		Vector4			SampleMaterialAttribute(const Trace &trace, Core::MaterialChannel channel);
		/// Sample geometry attribute.
		Vector4			SampleGeometryAttribute(const Trace &trace, GeometryAttribute attr);

		/// Shadow feel.
		float			ShadowFeel(const Vector4 &s, const Vector4 &d, float l, int r);

/*!
	@name	Interlace support.
	@{
*/
		bool			interlace_even;				///< Internal interlace parity.
		Picture			interlace_half_frame;		///< The previous interlace frame.
/// @}

public:

		ProgressHook	*hook;

		/// Get system configuration.
		Configuration	&GetConfiguration()
		{	return configuration;	}
		/// Set system configuration.
		void			SetConfiguration(const Configuration &config);

		/// Return system statistics object.
		const Statistics &GetStatistics() const { return statistics; }

		/// Abort current rendering.
		void			Abort();
		/// Last render abort flag.
		bool			Aborted() const { return abort; }

		/*!
			@short	Start a rendering sequence.

			@note	This call is only necessary when rendering an interlaced
					sequence.
		*/
		void			StartInterlacedSequence();

		/// Trace a primary ray across the scene tree, returns resulting HDR color.
		void			PrimaryRay(const RayGrid &ray, Color &o);
		/*!
			@short	Raytrace scene to a raw raytracing structure.

			Use this function if you need the intersected scene object and its
			geometric properties rather than a fully computed evaluation.
		*/
		void			RawPrimaryRay(const Vector4 &s, const Vector4 &d, Trace &t, float l = -1.f) { scene_tree.RaytraceScene(t, s, d, l); }
		/*!
			@short	Perform a full scene raytracing.

			@note	The raytracer internally only works with real colors.
					The frame is converted by the end of the render to rgb24.
			@note	The provided picture object will be reallocated to the
					render output dimensions and all previous content will be
					lost.
		*/
		bool			Render(Picture &, uint width = 640, uint height = 480);

		/// Return the internal scene BIH tree.
		SceneBIH		&GetBIH() { return scene_tree; }
		/// Return the internal scene shadow BIH tree.
		SceneBIH		&GetShadowBIH() { return scene_shadow_tree; }

		/*!
			@short	Setup a scene for use by the raytracer.

			@note		The raytracer does not mirror any data from the scene
						so it will rely on the object remaining valid as long
						as it is registered as the current raytracer scene.
			@warning	Before deleting the referred scene object do not forget
						to call SetScene(NULL) in order to release all
						references.
		*/
		bool			SetScene(const S3D::Scene *);

		void			Free();

		Raytracer(Core::ResourceFactory * = 0);
virtual	~Raytracer() {}
};

	} // Raytrace
} // GS


#endif	// __TRACER_CORE__
