/*
 * extended.c
 *
 * Copyright (C) 1989, 1991, Craig E. Kolb
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 * $Id$
 *
 * $Log$
 */
#include "light.h"
#include "libcommon/sampling.h"
#include "extended.h"

static LightMethods *iExtendedMethods = NULL;

Extended *
ExtendedCreate(r, pos)
Float r;
Vector *pos;
{
	Extended *e;

	e = (Extended *)share_malloc(sizeof(Extended));
	e->pos = *pos;
	e->radius = r;
	
	return e;
}

LightMethods *
ExtendedMethods()
{
	if (iExtendedMethods == (LightMethods *)NULL) {
		iExtendedMethods = LightMethodsCreate();
		iExtendedMethods->intens = ExtendedIntens;
		iExtendedMethods->dir = ExtendedDirection;
	}
	return iExtendedMethods;
}

/*
 * Compute intensity ('color') of extended light source 'lp' from 'pos'.
 */
static int
ExtendedIntens(lp, lcolor, cache, ray, dist, noshadow, color)
Extended *lp;
Color *lcolor, *color;
ShadowCache *cache;
Ray *ray;
Float dist;
int noshadow;
{
	int uSample, vSample, islit;
	Float jit, vbase, ubase, vpos, upos, lightdist;
	Color newcol;
	Ray newray;
	Vector Uaxis, Vaxis, ldir;

	if (noshadow) {
		*color = *lcolor;
		return TRUE;
	}

	newray = *ray;
	/*
	 * Determinte two orthoganal vectors that lay in the plane
	 * whose normal is defined by the vector from the center
	 * of the light source to the point of intersection and
	 * passes through the center of the light source.
 	 */
	VecSub(lp->pos, ray->pos, &ldir);
	VecCoordSys(&ldir, &Uaxis, &Vaxis);

	jit = 2. * lp->radius * Sampling.spacing;

	/*
	 * If sample # is >= 0, then take that numbered sample
	 * of the disc.
	 */
	if (ray->sample >= 0) {
		/*
		 * Sample a single point, determined by SampleNumber,
		 * on the extended source.
		 */
		vpos = -lp->radius + (ray->sample % Sampling.sidesamples)*jit;
		upos = -lp->radius + (ray->sample / Sampling.sidesamples)*jit;
		vpos += nrand() * jit;
		upos += nrand() * jit;
		VecComb(upos, Uaxis, vpos, Vaxis, &newray.dir);
		VecAdd(ldir, newray.dir, &newray.dir);
		lightdist = VecNormalize(&newray.dir);

		return !Shadowed(color, lcolor, cache, &newray,
			lightdist, noshadow);
	}

	/*
	 * Else have to sample the entire disc using
	 * Sampling.totsamples samples.
	 */
	color->r = color->g = color->b = 0.;
	islit = FALSE;

	/*
	 * Sample Samples.totsamples points arranged in a square lying on the
	 * plane calculated above.  The size of the square is equal to
	 * the diameter of the light source.  We sample the square at equal
	 * intervals in the U and V direction, with 'jitter'.
	 */
	ubase = -lp->radius;
	for (uSample = 0; uSample < Sampling.sidesamples;
	     uSample++, ubase += jit) {
		vbase = -lp->radius;
		for (vSample = 0; vSample < Sampling.sidesamples;
		     vSample++, vbase += jit) {
			vpos = vbase + nrand() * jit;
			upos = ubase + nrand() * jit;
			VecComb(upos, Uaxis, vpos, Vaxis, &newray.dir);
			VecAdd(ldir, newray.dir, &newray.dir);
			lightdist = VecNormalize(&newray.dir);

			if (!Shadowed(&newcol, lcolor, cache, &newray,
				lightdist, noshadow)) {
				islit = TRUE;
				color->r += newcol.r;
				color->g += newcol.g;
				color->b += newcol.b;
			}
		}
	}

	ColorScale(Sampling.weight, *color, color);
	return islit;
}

void
ExtendedDirection(lp, pos, dir, dist)
Extended *lp;
Vector *pos, *dir;
Float *dist;
{
	/*
	 * Calculate dir from position to center of
	 * light source.
	 */
	VecSub(lp->pos, *pos, dir);
	*dist = VecNormalize(dir);
}
