/*
 * jittered.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 "jittered.h"

static LightMethods *iJitteredMethods = NULL;

Jittered *
JitteredCreate(pos, e1, e2)
Vector *pos, *e1, *e2;
{
	Jittered *j;

	j = (Jittered *)share_malloc(sizeof(Jittered));

	j->pos = *pos;
	j->e1 = *e1;
	j->e2 = *e2;

	return j;
}

LightMethods *
JitteredMethods()
{
	if (iJitteredMethods == (LightMethods *)NULL) {
		iJitteredMethods = LightMethodsCreate();
		iJitteredMethods->intens = JitteredIntens;
		iJitteredMethods->dir = JitteredDirection;
	}
	return iJitteredMethods;
}

int
JitteredIntens(jit, lcolor, cache, ray, dist, noshadow, color)
Jittered *jit;
Color *lcolor, *color;
ShadowCache *cache;
Ray *ray;
Float dist;
int noshadow;
{
	return !Shadowed(color, lcolor, cache, ray, dist, noshadow);
}

void
JitteredDirection(lp, pos, dir, dist)
Jittered *lp;
Vector *pos, *dir;
Float *dist;
{
	/*
	 * Choose a location with the area define by corner, e1
	 * and e2 at which this sample will be taken.
	 */
	VecAddScaled(lp->pos, nrand(), lp->e1, &lp->curpos);
	VecAddScaled(lp->curpos, nrand(), lp->e2, &lp->curpos);
	VecSub(lp->curpos, *pos, dir);
	*dist = VecNormalize(dir);
}

int
AreaLightCreate(color, corner, u, usamp, v, vsamp)
Color *color;
Vector *corner, *u, *v;
int usamp, vsamp;
{
	Vector vpos, curpos;
	int i, j, numlight;
	Float ulen, vlen;
	Color intens;
	Light *ltmp;

	if (usamp < 1 || vsamp < 1) {
		RLerror(RL_ABORT, "Invalid area light specification.\n");
		return FALSE;
	}

	numlight = usamp * vsamp;	/* Total number of jittered sources */

	/*
	 * Sum of all intensities is equal to specified intensity.
	 */
	intens.r = color->r / (Float)numlight;
	intens.g = color->g / (Float)numlight;
	intens.b = color->b / (Float)numlight;

	VecSub(*u, *corner, u);
	VecSub(*v, *corner, v);
	/*
	 * Make sure that u and v are not degenerate.
	 */
	ulen = VecNormalize(u);
	vlen = VecNormalize(v);
	if (ulen < EPSILON || vlen < EPSILON) {
		RLerror(RL_ABORT, "Degenerate area light source.\n");
		return FALSE;
	}
	/*
	 * Scale u and v such that they define the area covered by a
	 * single sample.
	 */
	VecScale(ulen / (Float)usamp, *u, u); 
	VecScale(vlen / (Float)vsamp, *v, v);
	/*
	 * For each sample...
	 */
	vpos = *corner;
	for (i = 0; i < vsamp; i++) {
		curpos = vpos;
		for (j = 0; j < usamp; j++) {
			/*
			 * current pos is the "corner" of a new light
			 * source.  A jittered position based on
			 * the "corner" and the two edge vectors
			 * is used as the position for the
			 * light source in lighting calculations.
			 */
			ltmp = LightJitteredCreate(&intens, &curpos, u, v);
			LightAddToDefined(ltmp);
			VecAdd(curpos, *u, &curpos);
		}
		VecAdd(vpos, *v, &vpos);
	}
	return TRUE;
}
