/////////////////////////////////////////////////////////////
// CINEMA SDK : SHADER																		 //
/////////////////////////////////////////////////////////////
// VERSION    : CINEMA 4D																	 //
/////////////////////////////////////////////////////////////
// (c) 1989-2002 MAXON Computer GmbH, all rights reserved	 //
/////////////////////////////////////////////////////////////

// original source code of Illuminance1..Illuminance3
// The API versions are faster (no calling overhead and
// various speedups) but if you want to introduce new 
// illumination models this source is the base for further
// experiments

#include "c4d.h"

inline void LowClip(Vector *v)
{
	if (v->x<0.0) v->x=0.0;
	if (v->y<0.0) v->y=0.0;
	if (v->z<0.0) v->z=0.0;
}

void ShIlluminance1(VolumeData *sd, Vector *diffuse, Vector *specular, Real exponent)
{
	RayLight		*lp;
	LONG				i;
	Vector			col,lv;
	Real				cosa,cosb;
	Bool				nodif,nospec,calc_specular = exponent>0.0;

	*specular = 0.0;

	if (sd->calc_illum)
		*diffuse = 0.0;
	else
	{
		*diffuse = 1.0;
		return;
	}

	for (i=sd->GetLightCount()-1; i>=0; i--)
	{
		lp = sd->GetLight(i);
		if (!sd->Illuminate(lp,&col,&lv,sd->p,sd->bumpn,sd->calc_shadow,sd->lhit)) continue;
		if (lp->ambient) { *diffuse += col; continue; }

		nodif=nospec=FALSE;
		if (lp->lr.object) CalcRestrictionInc(&lp->lr,sd->op,nodif,nospec);

		if (lp->type==RT_LT_TUBE || lp->type==RT_LT_AREA)
		{
			// tube and area light calculations are very difficult to calculate
			// if you want to combine area lights with your own lighting models
			// you probably have to sample multiple times
			// the tube light goes from lp->m.off-lp->outerradius*lp->m.v3 to lp->m.off+lp->outerradius*lp->m.v3
			// the area light goes from lp->m.off-lp->outerradius*(lp->m.v1+lp->m.v2*lp->aspectratio) to lp->m.off+lp->outerradius*(lp->m.v1+lp->m.v2*lp->aspectratio)
	
			Real dif,spc;
			sd->LightCalcSpecial(lp,exponent,nodif,nospec,SV(sd->ray->v),sd->cosc,sd->p,sd->bumpn,dif,spc);

			*diffuse  += dif*col;
			*specular += spc*col;
		}
		else 
		{
			cosa=lv*sd->bumpn;
			if (!(lp->nodiffuse || nodif) && sd->cosc*cosa>=0.0) 
			{
				if (lp->contrast!=0.0)
					*diffuse += Pow(Abs(cosa),Exp(-lp->contrast))*col;
				else
					*diffuse += Abs(cosa)*col;
			}

			if (calc_specular && !(lp->nospecular || nospec))
			{
				cosb=SV(sd->ray->v) * (lv - sd->bumpn*(2.0 * cosa));
				if (cosb<=0.0) continue;

				*specular += Pow(cosb,exponent)*col;
			}
		}
	}

	LowClip(diffuse); // there can be subtractive light sources
	LowClip(specular);
}

void ShIlluminance2(VolumeData *sd, Vector *diffuse, Vector *specular, Real exponent, Vector *p, Vector *n, Vector *bumpn, Vector *rayv, LONG calc_shadow)
{
	RayLight	*lp;
	LONG			i;
	Vector		col,lv;
	Real			cosa,cosb,cosc;
	Bool			calc_specular = exponent>0.0;

	*specular = 0.0;
	*diffuse  = 0.0;

	cosc = -(*rayv) * (*n);

	for (i=sd->GetLightCount()-1; i>=0; i--)
	{
		lp = sd->GetLight(i);
		if (!sd->Illuminate(lp,&col,&lv,*p,*bumpn,calc_shadow,0)) continue;

		cosa=lv*(*bumpn);
		if (cosc*cosa>=0.0) 
			*diffuse  += Abs(cosa)*col;

		if (!calc_specular) continue;

		cosb=(*rayv) * (lv - (*bumpn)*(2.0 * cosa));
		if (cosb<=0.0) continue;

		*specular += Pow(cosb,exponent)*col;
	}

	LowClip(diffuse); // there can be subtractive light sources
	LowClip(specular);
}

Vector ShIlluminance3(VolumeData *sd, Vector *p, LONG calc_shadow)
{
	RayLight	*lp;
	LONG			i;
	Vector		col,lv,sum=0.0;

	for (i=sd->GetLightCount()-1; i>=0; i--)
	{
		lp = sd->GetLight(i);
		if (!sd->Illuminate(lp,&col,&lv,*p,lv,calc_shadow,0)) continue;
		sum += col;
	}

	LowClip(&sum); // there can be subtractive light sources

	return sum;
}
