/*************************************************************************

	form*Z by auto*des*sys, Inc.
	Copyright (c) 1989-2004 auto*des*sys, Inc.  All rights reserved.

	File:			rain.c
	Author:			auto*des*sys, Inc.
	Created on:		02/27/04
	Description:	Rain foreground shader plugin.  Shades foreground
					with rain using depth planes.

*************************************************************************/

/* standard headers */

#include <stdlib.h>
#include <math.h>

/* this plugin's header */

#include "rain.h"

/* required for plugin's source file implementing plugin entry function */

#include "fz_plugin_glue_api.h"

/* generic plugin functions */
static fzrt_error_td	rain_check_config(const fzpl_fset_glue_fset* const fset_glue, const fzpl_host_config_td* const hostConfig);
static fzrt_error_td	rain_acquire_fsets(void);
static void				rain_release_fsets(void);
static fzrt_error_td	rain_register_plugins(const fzpl_fset_glue_fset* const fset_glue, const fzpl_host_config_td* const hostConfig);
static fzrt_error_td	rain_finit(const fzpl_fset_glue_fset* const fset_glue);

/* function called by formZ to fill a shader's function set */
/* with pointers to rain's own callback functions. */
static fzrt_error_td	rain_fill_fset(const fzpl_fset_def_ptr fset_def, fzpl_fset_td* const fset);

/* required plugin entry point function */
FZPL_PLUGIN_FUNC(fzrt_error_td) fz_plugin_entry(const fzpl_fset_glue_fset* const fset_glue, fzpl_command_td cmd, const fzpl_host_config_td* const hostConfig);

/* shader callback functions */
static fzrt_error_td	rain_uuid(fzrt_UUID_td uuid);
static fzrt_error_td 	rain_name(char* name, fzrt_int max_len);
static fzrt_error_td 	rain_io_version(fzpl_vers_td* version);
static fzrt_error_td	rain_set_parameters(void);
static fzrt_error_td 	rain_pre_render(void** shdr_data);
static fzrt_error_td 	rain_post_render(void* shdr_data);
static fz_rgb_float_td	rain_shade_pixel(void* shdr_data);



/************************************************************************
							Plugin Functions
*************************************************************************/

/************************************************************************
	rain_check_config
		A first pass check to see if the plugin can be loaded.  It does
		the following:
			Acquires formZ function sets.
			Checks licensing.
			Informs the application if this plugin is dependent on
				function sets from other plugins.
			Opens localization resource file and loads strings.
*************************************************************************/
static fzrt_error_td rain_check_config(const fzpl_fset_glue_fset* const fset_glue, const fzpl_host_config_td* const hostConfig)
/************************************************************************/
{
	fzrt_error_td	err = FZRT_NOERR;
	/* resource file variables */
	fzrt_floc_ptr	floc;
	fzrt_fzr_ref_td	resource_ref;
	char			buffer[STRING_SIZE];
	
	/* assign our copy of the plugin fset pointer to that which formZ provides */
	_fset_glue = fset_glue;

	/* Make sure required application defined function sets are available */
	/* Release the function sets at unload. */

	err = rain_acquire_fsets();

	/* IF NEEDED: Check License */

	/* IF NEEDED: Tell the plugin manager what plugins this plugin is dependent on */

	/* get localized strings */

	if (err == FZRT_NOERR)
	{
		/* initialize file locator structure */
		err = _file_funcs.fzrt_file_floc_init(&floc);
		if (err == FZRT_NOERR)
		{
			/* get the location of this plugin */
			err = _fset_glue->fzpl_plugin_file_get_floc(floc);

			if (err == FZRT_NOERR)
			{
				/* open the resource file eg. rain.ENU.fzr */
				err = _base_funcs.fzrt_fzr_open(floc, "rain", &resource_ref);

				if (err == FZRT_NOERR)
				{
					/* get the localized resource strings for this plugin */
					_base_funcs.fzrt_fzr_get_string(resource_ref, 1, 1, buffer);
					strcpy(_name, buffer);
					_base_funcs.fzrt_fzr_get_string(resource_ref, 1, 2, buffer);
					strcpy(_short_name, buffer);
					_base_funcs.fzrt_fzr_get_string(resource_ref, 1, 3, buffer);
					strcpy(_visibility, buffer);
					_base_funcs.fzrt_fzr_get_string(resource_ref, 1, 4, buffer);
					strcpy(_direction, buffer);
					_base_funcs.fzrt_fzr_get_string(resource_ref, 1, 5, buffer);
					strcpy(_density, buffer);
					_base_funcs.fzrt_fzr_get_string(resource_ref, 1, 6, buffer);
					strcpy(_max_dist, buffer);

					/* close the resource file */
					err = _base_funcs.fzrt_fzr_close(resource_ref);
				}
				/* throw away floc */
				_file_funcs.fzrt_file_floc_finit(&floc);
			}
		}
	}

	if (err != FZRT_NOERR)
	{
		rain_release_fsets();
	}

	return err;
}


/************************************************************************
	rain_acquire_fsets
		Acquires formZ provided service function sets.
*************************************************************************/
static fzrt_error_td rain_acquire_fsets(void)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;


	err = _fset_glue->fzpl_fset_acquire( FZ_SHDR_FSET_TYPE,
								   FZ_SHDR_FSET_VERSION,
								   FZRT_UUID_NULL,
						 		   FZPL_TYPE_STRING(fz_shdr_fset),
						 		   sizeof(_shdr_funcs),
						 		   (fzpl_fset_td*)&_shdr_funcs );
		

	if (err == FZRT_NOERR)
	{
		err = _fset_glue->fzpl_fset_acquire( FZ_TYPE_FSET_TYPE,
									   FZ_TYPE_FSET_VERSION,
									   FZRT_UUID_NULL,
									   FZPL_TYPE_STRING(fz_type_fset),
									   sizeof(_type_funcs),
									   (fzpl_fset_td*)&_type_funcs );
	}
	
	if (err == FZRT_NOERR)
	{
		err = _fset_glue->fzpl_fset_acquire( FZ_FZRT_MEM_FSET_TYPE,
									   FZ_FZRT_MEM_FSET_VERSION,
									   FZRT_UUID_NULL,
									   FZPL_TYPE_STRING(fz_fzrt_mem_fset),
									   sizeof(_mem_funcs),
									   (fzpl_fset_td*)&_mem_funcs );
	}

	if (err == FZRT_NOERR)
	{
		err = _fset_glue->fzpl_fset_acquire( FZ_FZRT_BASE_FSET_TYPE,
									   FZ_FZRT_BASE_FSET_VERSION,
									   FZRT_UUID_NULL,
									   FZPL_TYPE_STRING(fz_fzrt_base_fset),
									   sizeof(_base_funcs),
									   (fzpl_fset_td*)&_base_funcs );
	}
	

	if (err == FZRT_NOERR)
	{
		err = _fset_glue->fzpl_fset_acquire( FZ_FZRT_FILE_FSET_TYPE,
									   FZ_FZRT_FILE_FSET_VERSION,
									   FZRT_UUID_NULL,
									   FZPL_TYPE_STRING(fz_fzrt_file_fset),
									   sizeof(_file_funcs),
									   (fzpl_fset_td*)&_file_funcs );
	}

	if (err == FZRT_NOERR)
	{
		err = _fset_glue->fzpl_fset_acquire( FZ_MATH_FSET_TYPE,
									   FZ_MATH_FSET_VERSION,
									   FZRT_UUID_NULL,
									   FZPL_TYPE_STRING(fz_math_fset),
									   sizeof(_math_funcs),
									   (fzpl_fset_td*)&_math_funcs );
	}


	return err;
}

/************************************************************************
	rain_release_fsets
		Releases all function sets used from formZ.
*************************************************************************/
static void rain_release_fsets(void)
/************************************************************************/
{
	_fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_shdr_funcs );
	_fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_type_funcs );
	_fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_mem_funcs );
	_fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_base_funcs );
	_fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_file_funcs );
	_fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_math_funcs );
}

 

/************************************************************************
	rain_register_plugins
		Registers plugins and their associated function sets.
*************************************************************************/
static fzrt_error_td rain_register_plugins(const fzpl_fset_glue_fset* const fset_glue, const fzpl_host_config_td* const hostConfig)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;

	/* register a shader plugin */
	err = fset_glue->fzpl_plugin_register( RAIN_PLUGIN_UUID,
									 _name,
									 RAIN_PLUGIN_VERSION,
									 RAIN_PLUGIN_VENDOR,
									 RAIN_PLUGIN_URL,
									 FZ_SHDR_EXTS_TYPE,
									 FZ_SHDR_EXTS_VERSION,
									 NULL /*error string function*/,
									 0,
									 NULL,
									 &_rain_plugin_runtime_id);
	if (err == FZRT_NOERR)
	{
		/* register a foreground shader fset for the plugin with matching runtime id. */
		err = fset_glue->fzpl_plugin_add_fset( _rain_plugin_runtime_id,
										 FZ_SHDR_CBAK_FGND_FSET_TYPE,
										 FZ_SHDR_CBAK_FGND_FSET_VERSION,
										 FZ_SHDR_CBAK_FGND_FSET_NAME,
										 FZPL_TYPE_STRING(fz_shdr_cbak_fgnd_fset),
										 sizeof(fz_shdr_cbak_fgnd_fset),
										 rain_fill_fset,
										 FALSE);
	}

	if (err != FZRT_NOERR)
	{
		rain_finit(fset_glue);
	}

	return err;
}

/************************************************************************
	rain_finit
		Unregisters plugins.  Releases function sets.
*************************************************************************/
static fzrt_error_td rain_finit(const fzpl_fset_glue_fset* const fset_glue)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;

	err = fset_glue->fzpl_plugin_unregister(_rain_plugin_runtime_id);

	rain_release_fsets();

	return err;
}


/************************************************************************
							Plugin Fill Function
*************************************************************************/

/************************************************************************
	rain_fill_fset
		Fills the fset structure with the callback functions for a
		foreground shader plugin.
*************************************************************************/
static fzrt_error_td rain_fill_fset(const fzpl_fset_def_ptr fset_def, fzpl_fset_td* const fset)
/************************************************************************/
{
	fzrt_error_td			  err = FZRT_NOERR;
	fz_shdr_cbak_fgnd_fset* install_struct;

	if (fset_def && fset)
	{
		err = _fset_glue->fzpl_fset_def_check( fset_def,
										 FZ_SHDR_CBAK_FGND_FSET_VERSION,
										 FZPL_TYPE_STRING(fz_shdr_cbak_fgnd_fset),
										 sizeof ( fz_shdr_cbak_fgnd_fset ),
										 FZPL_VERSION_OP_NEWER );
		if (err == FZRT_NOERR)
		{
			install_struct = (fz_shdr_cbak_fgnd_fset*)fset;

			install_struct->fz_shdr_cbak_fgnd_uuid				= rain_uuid;
			install_struct->fz_shdr_cbak_fgnd_name				= rain_name;
			install_struct->fz_shdr_cbak_fgnd_version			= rain_io_version;
			install_struct->fz_shdr_cbak_fgnd_set_parameters	= rain_set_parameters;
			install_struct->fz_shdr_cbak_fgnd_pre_render		= rain_pre_render;
			install_struct->fz_shdr_cbak_fgnd_post_render		= rain_post_render;
			install_struct->fz_shdr_cbak_fgnd_pixel				= rain_shade_pixel;
		}
	}
	else
		err = _base_funcs.fzrt_error_set(FZRT_BAD_PARAM_ERROR, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_FZRT, 0);

	return err;
}

/************************************************************************
						Required Plugin Functions
*************************************************************************/

/************************************************************************
	fz_plugin_entry
		The main entry point for a formZ plugin.
*************************************************************************/
FZPL_PLUGIN_FUNC(fzrt_error_td) fz_plugin_entry(const fzpl_fset_glue_fset* const fset_glue, fzpl_command_td cmd, const fzpl_host_config_td* const hostConfig)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;

	switch (cmd)
	{
		case FZPL_PLUGIN_CHECK:
			err = rain_check_config(fset_glue, hostConfig);
		break;
		
		case FZPL_PLUGIN_INITIALIZE:
			err = rain_register_plugins(fset_glue, hostConfig);
		break;
		
		case FZPL_PLUGIN_EXIT:
			err = rain_finit(fset_glue);
		break;
	}

	return (err);
}









/************************************************************************
						Rain Shader Data Structures
*************************************************************************/

/************************************************************************
	rain_data_td
	
		Description
			Options/Parameters that a user can change to alter how
			the rain shader behaves.
			
		Members
			rain1		- farthest away depth plane where rain drops
						  can be streaked.
			rain2		- second farthest away depth plane where rain drops
						  can be streaked.
			rain3		- closest depth plane where rain drops can be
						  streaked.
			visibility  - how visible rain drops are.
			direction	- direction vector of the rain on the screen plane.
			density		- amount of rain drops per layer of depth.
			max_dist	- maximum distance to use in calcuations.
			max_dist_inv - inverse of the maximum distance.
			height		- height of the screen.
			width		- width of the screen.
			num_pixels	- number of pixels in the screen.
*************************************************************************/
typedef struct
{	char*		rain1;
	char*		rain2;
	char*		rain3;
	double		visibility;
	fz_xy_td	direction;
	double		density;
	double		max_dist;
	double		max_dist_inv;
	fzrt_int		height;
	fzrt_int		width;
	fzrt_int		num_pixels;
} rain_data_td;




/************************************************************************
						Rain Shader Callbacks
*************************************************************************/

/************************************************************************
	rain_uuid
		Returns the UUID of this shader's function set.
*************************************************************************/
static fzrt_error_td rain_uuid(fzrt_UUID_td uuid)
/************************************************************************/
{
	_base_funcs.fzrt_UUID_copy(RAIN_PLUGIN_FSET_UUID, uuid);
	return(FZRT_NOERR);
}
/************************************************************************
	rain_name
		Tells formZ what the name of this plugin is which shows up
		in the drop-down menu among other places.
*************************************************************************/
static fzrt_error_td rain_name(char* name, fzrt_int max_len)
/************************************************************************/
{
	strncpy(name, _short_name, max_len);
	return(FZRT_NOERR);
}

/************************************************************************
	rain_io_version
		Tells formZ the version of this plugin's data io.
		This is NOT the same thing as the plugin's version.
		The io of the data may not necessarily change between plugin versions.
*************************************************************************/
static fzrt_error_td rain_io_version(fzpl_vers_td* version)
/************************************************************************/
{
	*version = RAIN_PLUGIN_DATA_IO_VERSION;
	return(FZRT_NOERR);
}

/************************************************************************
	rain_set_parameters
		Tells formZ what parameters to cause to show up in the "options"
		dialog of the shader, (not necessarily in same order).
*************************************************************************/
static fzrt_error_td rain_set_parameters(void)
/************************************************************************/
{
	/* setup slider for rain drop visibility (0 = most translucent, 100% = most visible). */
	_shdr_funcs.fz_shdr_set_sld_flt_parm(_visibility, 0.5, 1, 1, 0);
	/* setup field for angle of the direction vector in which the rain is directed. */
	_shdr_funcs.fz_shdr_set_flt_parm(_direction, 0, -90, 90, FZ_FUIM_FORMAT_FLOAT_ANGLE, 1, 1, 1);
	/* setup slider for the percentage of coverage of the rain in the scene. */
	_shdr_funcs.fz_shdr_set_sld_flt_parm(_density, 0.5, 1, 1, 2);
	/* field for maximum distance */
	_shdr_funcs.fz_shdr_set_flt_parm(_max_dist, 5000, 1, 5000, FZ_FUIM_FORMAT_FLOAT_DISTANCE, 1, 2, 3);

	return(FZRT_NOERR);
}

/************************************************************************
	rain_pre_render
		Sets up the shader by precomputing as much as possible.
		Precomputes where streaks of rain occur on three depth planes
		Also, gets parameters that don't change throughout the rendering
		and stores them in a data structure.
*************************************************************************/
static fzrt_error_td rain_pre_render(void** shdr_data)
/************************************************************************/
{
	fzrt_error_td	err = FZRT_NOERR;
	/* get params */
	fz_type_td		data;
	rain_data_td*	rain_data;
	/* create rain streaks */
	double			likeliness_of_drop, length_of_drop;
	char			value_of_drop;
	fzrt_int			i, j, k, plane, cx, cy, rx, ry;
	char*			tmp_rain;
	double			angle;
	double			density_factor;
	double			length_factor;
	fzrt_int			dpi;
	fz_xyz_td		eye;
	
	/* initialize */

	*shdr_data = NULL;

	/* keep randomness looking the same across successive renderings */
	_shdr_funcs.fz_shdr_get_view_eye(&eye);
	srand(1001 + (fzrt_int)(eye.x*100 + eye.y*100 + eye.z*100));
	
	/* allocate memory */
	err = _mem_funcs.fz_mem_zone_alloc(NULL,sizeof(rain_data_td),FALSE, (fzrt_ptr*)shdr_data);
	if (err == FZRT_NOERR)
	{
		rain_data = (rain_data_td*)*shdr_data;

		if (_shdr_funcs.fz_shdr_is_in_preview() == TRUE)
		{
			/* smaller window if in preview */
			rain_data->width = 100;
			rain_data->height = 100;
		}
		else /* not in preview */
		{
			/* get screen resolution */
			_shdr_funcs.fz_shdr_get_rndr_resolution(&rain_data->width, &rain_data->height, &dpi);
		}
		
		rain_data->num_pixels = rain_data->width*rain_data->height;

		/* Get parameters in options dialog */
		
		_shdr_funcs.fz_shdr_get_parm(0, &data);
		_type_funcs.fz_type_get_double(&data, &rain_data->visibility);
		rain_data->visibility = rain_data->visibility*0.4 + 0.1;
		_shdr_funcs.fz_shdr_get_parm(1, &data);
		_type_funcs.fz_type_get_double(&data, &angle);
		/* angle already in radians */
		rain_data->direction.x = -sin(angle);
		rain_data->direction.y = -cos(angle);
		_shdr_funcs.fz_shdr_get_parm(2, &data);
		_type_funcs.fz_type_get_double(&data, &rain_data->density);
		rain_data->density *= 0.005;
		_shdr_funcs.fz_shdr_get_parm(3, &data);
		_type_funcs.fz_type_get_double(&data, &rain_data->max_dist);
		rain_data->max_dist_inv = 1.0 / rain_data->max_dist;
		
		
		/* Create the rain drop data in three different depth planes. */
		
		for (plane = 0; plane < 3; plane++)
		{
			/* allocate memory for plane of pixels */
			err = _mem_funcs.fz_mem_zone_alloc(NULL, rain_data->num_pixels*sizeof(char), FALSE, (fzrt_ptr*)&tmp_rain);
			if (err == FZRT_NOERR)
			{
				/* initialize it */
				for (i = 0; i < rain_data->num_pixels; i++)
				{
					tmp_rain[i] = 0;
				}
				
				/* remember it for the shading function later */
				if (plane == 0)
					rain_data->rain1 = tmp_rain;
				else if (plane == 1)
					rain_data->rain2 = tmp_rain;
				else if (plane == 2)
					rain_data->rain3 = tmp_rain;
				
				/* closer planes have less density because they have */
				/* longer streaks, so it evens out. */
				density_factor = rain_data->density*5*(3 - plane);
				length_factor = 3*plane;

				/* iterate through rows */
				for (i = 0; i < rain_data->height; i++)
				{
					/* iterate through columns */
					for (j = 0; j < rain_data->width; j++)
					{
						
						/* create a random drop */
						likeliness_of_drop = _math_funcs.fz_math_random_range(0.0, 1.0); /* 0.0 .. 1.0 */
						value_of_drop = (char)(fzrt_int)(127*_math_funcs.fz_math_random_range(0.0, 1.0)); /* 0 .. 127 */
						length_of_drop = _math_funcs.fz_math_random_range(0.0, 1.0)*4+length_factor; /* 0 .. 4, 3 .. 7, 6 .. 10 */

						/* does a drop occur at this pixel? */
						if (likeliness_of_drop < density_factor)
						{
							/* yes, create rain streak across several pixels */
							
							/* not anti-aliased... */
					
							/* create a "darker" streak		 */
							cx = j;
							cy = i;
					
							for (k = 0; k < length_of_drop; k++)
							{
								rx = (fzrt_int)(cx + k*rain_data->direction.x);
								ry = (fzrt_int)(cy + k*rain_data->direction.y);

								if (ry >= 0 && rx >= 0 &&
									ry < rain_data->height && rx < rain_data->width)
								{
									tmp_rain[ry*rain_data->width+rx] = -value_of_drop/2;
								}
							}
							
							/* create a "lighter" streak right next door */
							cx = j+1;
							
							for (k = 0; k < length_of_drop; k++)
							{
								rx = (fzrt_int)(cx + k*rain_data->direction.x);
								ry = (fzrt_int)(cy + k*rain_data->direction.y);

								if (ry >= 0 && rx >= 0 &&
									ry < rain_data->height && rx < rain_data->width)
								{
									tmp_rain[ry*rain_data->width+rx] = value_of_drop;
								}
							}
							

						}/* else do not create streak */
						
						/* done with this column, go on to next */
					}
					/* done with this row, go on to next */
				}
			}
			
			/* done setting up this array */
		}
		
		/* done setting up all three arrays */
	}

	return(err);
}


/************************************************************************
	rain_post_render
		Frees memory used for parameters in rendering
*************************************************************************/
static fzrt_error_td rain_post_render(void* shdr_data)
/************************************************************************/
{
	if (shdr_data != NULL)
	{
		_mem_funcs.fz_mem_zone_free(NULL, (fzrt_ptr*)&((rain_data_td*)shdr_data)->rain1);
		_mem_funcs.fz_mem_zone_free(NULL, (fzrt_ptr*)&((rain_data_td*)shdr_data)->rain2);
		_mem_funcs.fz_mem_zone_free(NULL, (fzrt_ptr*)&((rain_data_td*)shdr_data)->rain3);
		_mem_funcs.fz_mem_zone_free(NULL, (fzrt_ptr*)&shdr_data);
	}
	return(FZRT_NOERR);
}

/************************************************************************
	rain_shade_pixel
		Shades a pixel.  The shader shades a scene with streaks of
		rain, using three different depth planes to streak the rain on.
		Still not true volumetric shading, but shades fairly quickly.
		
		returns
			color computed at this pixel.
*************************************************************************/
static fz_rgb_float_td rain_shade_pixel(void* shdr_data)
/************************************************************************/
{
	/* pre-computed data	 */
	rain_data_td*	rain_data;
	/* rain streaks */
	fz_rgb_float_td	col;
	double			dist, dist_f;
	fz_xy_td		xy;
	double			drop_intensity;
	double			drop_scale;
	double			res;
	fzrt_int			drop_index;
	
	/* initialize */
	
	rain_data = (rain_data_td*)shdr_data;

	drop_intensity = 0;

	drop_scale = rain_data->visibility*scale_char;

	/* get distance to intersection if any */
	_shdr_funcs.fz_shdr_get_dist_eye_world_pnt(&dist);

	/* clamp distance (especially needed if hit a background pixel) */
	if (dist > rain_data->max_dist)
		dist = rain_data->max_dist;

	/* normalize */
	dist = dist * rain_data->max_dist_inv;
	/* reverse so that 0 = farthest away, 1 = closest */
	dist = 1 - dist;
	/* fade colors by at most 50% as max distance approaches */
	dist_f = dist*0.5 + 0.5;
	
	/* get the color at this pixel, as it has been computed by  */
	/* all other shaders. */
	_shdr_funcs.fz_shdr_get_col(&col);

	/* gray background if in preview */
	if (_shdr_funcs.fz_shdr_is_in_preview() == TRUE)
	{
		col.red   = 0.5;
		col.green = 0.5;
		col.blue  = 0.5;
	}
	/* get the current pixel coordinates within a unit-sized screen */
	_shdr_funcs.fz_shdr_get_ispace_xy(&xy);

	/* index into array of rain drops based on the current */
	/* pixel location, which must be scaled according to the */
	/* number of pixels in the screen. */
	drop_index = (fzrt_int)(xy.y*rain_data->height)*rain_data->width + (fzrt_int)(xy.x*rain_data->width);

	/* the closest plane of rain drops is first looked at to add */
	/* any rain drop there */
	drop_intensity += drop_scale * rain_data->rain3[drop_index];

	/* check for a rain drop at the second depth plane if applicable */
	if (dist < .75)
	{
		/* smoothly transition so depth plane not so noticeable */
		res = _shdr_funcs.fz_shdr_smooth_step(.6, .75, dist);
		if (res < _math_funcs.fz_math_random_range(0.0, 1.0))
		{
			drop_intensity += drop_scale * rain_data->rain2[drop_index];
		}
	}

	/* check for a rain drop at the third depth plane if applicable */
	if (dist < .4)
	{
		/* smoothly transition so depth plane not so noticeable */
		res = _shdr_funcs.fz_shdr_smooth_step(.3, .4, dist);
		if (res < _math_funcs.fz_math_random_range(0.0, 1.0))
		{
			drop_intensity += drop_scale * rain_data->rain1[drop_index];
		}
	}

	/* add the rain drop to the current pixel's color */
	/* which has a translucent effect.	 */
	col.red   = (float)(drop_intensity + col.red   * dist_f);
	col.green = (float)(drop_intensity + col.green * dist_f);
	col.blue  = (float)(drop_intensity + col.blue  * dist_f);

	/* clamp */
	if (col.red < 0)
		col.red = 0;
	if (col.red > 1)
		col.red = 1;
	if (col.green < 0)
		col.green = 0;
	if (col.green > 1)
		col.green = 1;
	if (col.blue < 0)
		col.blue = 0;
	if (col.blue > 1)
		col.blue = 1;

	return(col);
}
