/******************************************************************************
 * Copyright 1986-2002 by mental images GmbH & Co.KG, Fasanenstr. 81, D-10623
 * Berlin, Germany. All rights reserved.
 ******************************************************************************
 * Created    10.12.2001
 * Module     baseshader
 * Purpose    base shaders for Phenomenon writers
 *
 * Exports:
 *   mib_illum_hair()
 *
 * History:
 *   10.12.2001: created
 *
 * Description:
 *****************************************************************************/

#include "shader.h"

#define MIB_ILLUM_HAIR_VERSION 1

struct mib_illum_hair {
	miColor		ambience;	/* ambient colour multiplier */
	miColor		ambient;	/* ambient colour */
	miColor		diffuse;	/* diffuse colour */
	miColor		specular;	/* specular colour */
	miScalar	exponent;	/* shinyness */
	int		mode;		/* light mode: 0..2 */
	int		i_light;	/* index of first light */
	int		n_light;	/* number of lights */
	miTag		light[1];	/* list of lights */
};

DLLEXPORT int mib_illum_hair_version()
{
	return MIB_ILLUM_HAIR_VERSION;
}

DLLEXPORT miBoolean mib_illum_hair(
	miColor                 *result,
	miState                 *state,
	struct mib_illum_hair   *paras)
{
	/* shader parameters */
	miColor		*ambient;
	miColor		*diffuse;
	miColor		*specular;
	miScalar	 exponent;
	miInteger	 mode;
	miInteger	 i_light;
	miInteger	 n_light;
	miTag		*light;

	miInteger	 samples;		/* for light sampling       */
	miInteger	 i;			/* for light loop           */
	miColor		 lightcol;		/* light colour             */
	miScalar	 dot_nl;		/* for diffuse colour       */
	miScalar	 dot_th;		/* for specular colour      */
	miVector	 l;			/* light direction          */
	miVector	 h;			/* halfway vector           */
	miScalar	 spec;			/* specular factor          */
	miVector	 t = state->derivs[0];	/* tangent to hair          */
	miVector	 v = state->dir;	/* eye ray                  */
	miVector	 cross, hair_n, shading_n;  /* shading normal       */
	miColor		 sum;			/* light contribution       */
	miScalar	 blend;			/* shading normal blend     */
	void		*pri = state->pri;	/* for nulling/restoring    */
	miScalar	 rootdarken;		/* darker roots             */

	/* check if we are a shadow shader */
	if (state->type == miRAY_SHADOW) {
		diffuse	= mi_eval_color(&paras->diffuse);
		result->r *= diffuse->r;
		result->g *= diffuse->g;
		result->b *= diffuse->b;
		return miTRUE;
	}

	/* tangent is not normalized yet */
	mi_vector_normalize(&t);

	/* get parameters */
	*result    = *mi_eval_color(&paras->ambience);
	ambient    =  mi_eval_color(&paras->ambient);
	result->r *=  ambient->r;
	result->g *=  ambient->g;
	result->b *=  ambient->b;
	diffuse    =  mi_eval_color(&paras->diffuse);
	specular   =  mi_eval_color(&paras->specular);
	exponent   = *mi_eval_scalar(&paras->exponent);
	mode       = *mi_eval_integer(&paras->mode);
	i_light    = *mi_eval_integer(&paras->i_light);
	n_light    = *mi_eval_integer(&paras->n_light);
	light      =  mi_eval_tag(paras->light) + i_light;

	rootdarken = pow(state->bary[0], 1.0f/5.0f);

	/* correct light list, if requested */
	if (mode == 1)
		mi_inclusive_lightlist(&n_light, &light, state);
	else if (mode == 2)
		mi_exclusive_lightlist(&n_light, &light, state);

	/* calculate current opacity (0.5 at root, 1.0 at tip) */
	result->a = 0.5f + state->bary[0] * 0.5f;
	result->a = 1.0f;

	/* prepare some values */
	mi_vector_neg(&v);

	/* get shading normal */
	mi_vector_prod(&cross, &state->normal_geom, &t);
	mi_vector_prod(&hair_n, &t, &cross);
	blend = mi_vector_dot(&state->normal_geom, &t);
	shading_n.x = (1.0f-blend)*hair_n.x + blend*state->normal_geom.x;
	shading_n.y = (1.0f-blend)*hair_n.y + blend*state->normal_geom.y;
	shading_n.z = (1.0f-blend)*hair_n.z + blend*state->normal_geom.z;
	mi_vector_normalize(&shading_n);

	/* remove state->pri for now, for sampling lights */
	state->pri = NULL;

	/* loop over lights */
	for (i=0; i<n_light; i++) {
		/* initially colour and samples */
		sum.r = sum.g = sum.b = 0.0f;
		samples = 0;

		/* potentially multiply sample each light */
		while (mi_sample_light(&lightcol, &l, &dot_nl,
				       state, light[i], &samples)) {
			/* calculate dot_nl from our shading normal */
			dot_nl = mi_vector_dot(&shading_n, &l);

			/* diffuse term */
			sum.r += rootdarken * dot_nl * diffuse->r * lightcol.r;
			sum.g += rootdarken * dot_nl * diffuse->g * lightcol.g;
			sum.b += rootdarken * dot_nl * diffuse->b * lightcol.b;

			/* find the halfway vector h */
			mi_vector_add(&h, &v, &l);
			mi_vector_normalize(&h);

			/* specular coefficient from auk paper */
			dot_th = mi_vector_dot(&t, &h);
			spec = 1.0 - dot_th*dot_th;
			spec = pow(spec, exponent / 2.0);

			/* specular colour */
			if (spec > 0.0) {
				sum.r += spec * specular->r * lightcol.r;
				sum.g += spec * specular->g * lightcol.g;
				sum.b += spec * specular->b * lightcol.b;
			}
		}

		/* add it in */
		if (samples) {
			result->r += sum.r / samples;
			result->g += sum.g / samples;
			result->b += sum.b / samples;
		}
	}

	/* restore state->pri */
	state->pri = pri;

	/* if we are translucent, trace more rays */
	if (result->a < 0.9999) {
		miScalar alphafactor;
		miColor col = {0.0, 0.0, 0.0, 0.0};
		mi_trace_transparent(&col, state);
		alphafactor = 1.0f - result->a;
		result->r   = alphafactor * col.r + result->a * result->r;
		result->g   = alphafactor * col.g + result->a * result->g;
		result->b   = alphafactor * col.b + result->a * result->b;
		result->a  += alphafactor * col.a;
	}

	return miTRUE;
}

