/******************************************************************************
 * Copyright 1986-2002 by mental images GmbH & Co.KG, Fasanenstr. 81, D-10623
 * Berlin, Germany. All rights reserved.
 ******************************************************************************
 * Created:	7.4.99
 * Module:	physics
 * Purpose:	lens depth of field shader
 *
 * Exports:
 *      physical_lens_dof_version
 *      physical_lens_dof
 *
 * History:
 *	10.06.99: use mi_eval
 *
 * Description:
 *****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <shader.h>

static miColor black = {0.0, 0.0, 0.0, 0.0};

char *mi_physics_shader_version(void)
	{return("=@@= physics.so:\tversion 1.1" + 5);}



/*
 * New depth of field shader using low discrepancy sampling for the
 * simulation of depth of field. It does supersampling of the lens before
 * the  ordinary oversampling. A fixed number of four samples is used. This
 * shader is now also in softimage.so, as soft_dof, where it replaces the
 * original Softimage depth of field shaders.
 */

struct dof {
	miScalar	plane;		/* distance to focused plane */
	miScalar	radius;		/* size of the lens */
};

#define NO_SAMPLES 4

DLLEXPORT int physical_lens_dof_version(void) {return(1);}

DLLEXPORT miBoolean physical_lens_dof(
	register miColor	*result,
	register miState	*state,
	register struct dof	*paras)
{
	miScalar	plane = *mi_eval_scalar(&paras->plane);
	miScalar	rads  = *mi_eval_scalar(&paras->radius);
	miVector	org, dir;	/* original state->org, state->dir */
	miVector	rayorg, raydir;	/* ray origin and direction */
	miVector	dest;
	double		t, radius, angle;
	miColor		average;
	int		i;
	double		pad[2];
	miUint		n;

	average = black;
	mi_point_to_camera (state, &org, &state->org);
	mi_vector_to_camera(state, &dir, &state->dir);
	t = (plane - org.z) / dir.z;
	dest.x = org.x + t * dir.x;
	dest.y = org.y + t * dir.y;
	dest.z = plane;

	i = 0;
	n = NO_SAMPLES;
	while(mi_sample(pad, &i, state, 2, &n)) {
		radius = rads * sqrt(pad[0]);
		angle = 2.0 * M_PI * pad[1];

		rayorg.x = org.x + radius * cos(angle);
		rayorg.y = org.y + radius * sin(angle);
		rayorg.z = org.z;
		raydir.x = dest.x - rayorg.x;
		raydir.y = dest.y - rayorg.y;
		raydir.z = dest.z - rayorg.z;
		mi_point_from_camera (state, &rayorg, &rayorg);
		mi_vector_from_camera(state, &raydir, &raydir);

		(void)mi_trace_eye(result, state, &rayorg, &raydir);

		average.r += result->r;
		average.g += result->g;
		average.b += result->b;
		average.a += result->a;
        }
	result->r = average.r / NO_SAMPLES;
	result->g = average.g / NO_SAMPLES;
	result->b = average.b / NO_SAMPLES;
	result->a = average.a / NO_SAMPLES;
	return(miTRUE);
}
