/*
 * Copyright (c) 2001, Armagan YAVUZ
 * based on sample file by NAN
 */

#include <math.h>
#include "t_texture.h"
#include <stdio.h>

char name[24]= "T_Marble_Terrain";

/* Subtype names must be less than 15 characters */

#define NR_TYPES	3
char stnames[NR_TYPES][16]= {"Bands", "Rings", "Horizontal"};

/* Structure for buttons,
 *  butcode      name           default  min  max  0
 */

VarStruct varstr[]= {
{	NUM|FLO,	"Turbulance",		1.0, 	0.0, 100.0, ""},
{	NUM|FLO,	"Noise Size",		0.25, 	0.0001, 10.0, ""},
{	NUM|INT,	"Noise Depth",		1.0, 	0.0, 6.0, ""},
{	NUM|FLO,	"Detail Size",		0.5, 	0.0001, 1.0, ""},
{ 	TOG|INT,	"Hard Noise",   0.0,	 0.0, 1.0,  ""},
{	NUM|INT,	"Seed",		0.0, 	0.0, 255.0, ""},
{ 	TOG|INT,	"BumpMap",   1.0,	 0.0, 1.0,  
	"Generate bump map"},
{	NUM|FLO,	"Filter",		0.15, 	0.0001, 1.0, 
	"Lower means better accuracy, higher means less artifacts"},
{ 	TOG|INT,	"Double Sample",   0.0,	 0.0, 1.0,  
	"Use slower (and higher quality) bumpmap method"},
{	NUM|INT,	"OSA",		1.0, 	1.0, 16.0, 
	"Number of samples used in OSA mode"},
{	NUM|FLO,	"Map Max",		1.0, 	0.0, 1.0, ""},
{	NUM|FLO,	"Map Min",		0.0, 	0.0, 1.0, ""},
{	NUM|FLO,	"Width",		1.0, 	0.001, 1.0, ""},
{	LABEL|INT,   "Contraints",             0.,    0.,   0.,   ""},
{	NUM|FLO,	"Altitude Max",		100.0, 	-100.0, 100.0, ""},
{	NUM|FLO,	"Altitude Min",		-100.0, 	-100.0, 100.0, 
	""},
{	NUM|FLO,	"Slope Max",		90.0, 	0.0, 180.0, ""},
{	NUM|FLO,	"Slope Min",		0.0, 	  0.0, 180.0, ""},
{	NUM|FLO,	"Bias", 0.0, -1.0, 1.0, ""},
{	LABEL|INT,   "Sharp",             0.,    0.,   0.,   ""},
{	NUM|FLO,	"",		1.0, 	0.0, 1.0, ""},
{	NUM|FLO,	"",		1.0, 	0.0, 1.0, ""},
{	NUM|FLO,	"",		1.0, 	0.0, 1.0, ""},
{	NUM|FLO,	"",		1.0, 	0.0, 1.0, ""}
};

/* The cast struct is for input in the main doit function
   Varstr and Cast must have the same variables in the same order,
   INCLUDING dummy variables for label fields. */

typedef struct Cast {
	float turbulance_weight;
	float noise_size;
	int depth;
	float falloff;
	int hard_noise;
	int seed;
	int do_bump;
	float filter;
	int double_bump;
	int osa;
	float map_max;
	float map_min;
	float width;
	int limit_slope;
	float maxz;
	float minz;
	float max_slope;
	float min_slope;
	float mid;
	int sharpness_label;
	float maxz_sharp;
	float minz_sharp;
	float max_slope_sharp;
	float min_slope_sharp;
} Cast;

/* result:
   Intensity, R, G, B, Alpha, nor.x, nor.y, nor.z
 */

float result[8];

/* cfra: the current frame */

float cfra;

int plugin_tex_doit(int, Cast*, float*, float*, float*);

/* ******************** Fixed functions ***************** */

int plugin_tex_getversion(void)
{
	return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but)
{
}

void plugin_init(void)
{

}

/* this function should not be changed: */

void plugin_getinfo(PluginInfo *info)
{
	info->name= name;
	info->stypes= NR_TYPES;
	info->nvars= sizeof(varstr)/sizeof(VarStruct);

	info->snames= stnames[0];
	info->result= result;
	info->cfra= &cfra;
	info->varstr= varstr;

	info->init= plugin_init;
	info->tex_doit=  (TexDoit) plugin_tex_doit;
	info->callback= plugin_but_changed;
}

float intensity(int stype, Cast *cast, float x,float y, float z, float *normal)
{
	float vec[3];
	float res;
	float max;
	float min;
	float maxz,minz;
	float factor;
	float dz,zf;
	float z_interval_up;
	float z_interval_down;
	float slope;
	float w,t,tt;
	factor = 1.0;

		maxz = cast->maxz;
		minz = cast->minz;
		if (minz >= maxz)
		{
			maxz = (minz + maxz) * 0.5;
			minz = maxz - 0.1;
		}
	

		z_interval_up = 10.0  * (1.001 - cast->maxz_sharp);
		dz = z - maxz;
		zf = (dz / z_interval_up);
		zf = 0.5 + zf * 0.5;
		zf = CLAMP(zf,0.0,1.0);
		factor -= zf;

		z_interval_down = 10.0 * (1.0 - cast->minz_sharp);
		dz = z - minz;
		zf = -(dz / z_interval_down);
		zf = 0.5 + zf * 0.5;
		zf = CLAMP(zf,0.0,1.0);
		factor -= zf;

		if (factor <= cast->map_min)
			return 0.0;
	if (normal)
	{
		maxz = cast->max_slope;
		minz = cast->min_slope;
		slope = acos(normal[2]); 
		slope *= 180.0 / 3.1415926;
		if (minz >= maxz)
		{	
			maxz = (minz + maxz) * 0.5;
			minz = maxz - 0.1;
		}
		z_interval_up = 45.0 * (1.001 - cast->max_slope_sharp);
		dz = slope - (maxz);
		zf = (dz / z_interval_up);
		zf = 0.5 + zf * 0.5;
		zf = CLAMP(zf,0.0,1.0);
		factor -= zf;

		z_interval_down = 45.0 * (1.001 - cast->min_slope_sharp);
		dz = slope - (minz - 0.1f);
		zf = -(dz / z_interval_down);
		zf = 0.5 + zf * 0.5;
		zf = CLAMP(zf,0.0,1.0);
		factor -= zf;

		if (factor <= cast->map_min)
			return 0.0;
	}

	point_turbulance(cast->noise_size, cast->falloff, cast->hard_noise, 
		x, y, z, vec, cast->depth,cast->seed);

	w = cast->width;
	x += vec[0] * cast->turbulance_weight;
	y += vec[1] * cast->turbulance_weight;
	z += vec[2] * cast->turbulance_weight;

	if (stype == 0)
	{
		t = (x + y + z);
	}
	else if (stype == 1)
	{
		t = sqrt(x*x + y*y + z*z);
	}
	else
	{
		t = z;
	}

	t = (0.5 * (t + 1.0));
	t = t - floorf(t);
	t = (t)* 2.0 - 1.0;

	if (fabs(t) > w)
	{
		res = 0.0;
	}
	else
	{
		t = t / w;
		if (t < cast->mid)
		{
			tt = (cast->mid - t) / (cast->mid + 1.0);
		}
		else
		{
			tt = (t - cast->mid) / (1.0 - cast->mid);
		}

		res = cos(tt * 3.1415926 ) * 0.5 + 0.5;
	}


	max = cast->map_max;
	min = cast->map_min + (1.0 - factor);
	if (max <= min)
	{
		max = min+0.01;
	}
	if (res < min)
		res = 0.0;
	else if (res > max)
		res = 1.0;
	else
		res = (res - min) / (max - min);
	res = CLAMP(res,0.0,1.0);
	return res;
}

#define between(x,a,b) (((a) <= (x))&&((x) < (b)))
#define FRAND (((((float)(((unsigned int)rand()) & 8191)) / 4096.0) - 1.0) * 0.5)

int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, 
	float *dyt)
{
	float s;
	int i;
	float normal[3],xvec[3],yvec[3];
	float *normal_vec;
	float bump_factor;
	float osa_size,good_osa_size;
	float fx,fy;
	float osa_color;
	float x,y,z,tresult,first_result;

	bump_factor = 0.05;

	s = cast->noise_size;
  	s = s * cast->filter;
	if (cast->depth > 0)
		s = s * cast->falloff;
	for (i=1; i<cast->depth; i++)
	  s = s * 0.6;

	if (dxt)
	{
		vec_copy(xvec,dxt);
		vec_copy(yvec,dyt);
		crossp(normal,xvec,yvec);
		vec_normalize(normal);
		normal_vec = normal;
	}
	else
		normal_vec = 0;
	if (dxt) 
	{
		osa_color = 0.0f;
		for (i=0; i<cast->osa; i++)
		{
			fx = fixed_rand(2 *i,texvec[0],texvec[1],texvec[2]) - 
				0.5;
			fy = fixed_rand(2 *i +1,texvec[0],texvec[1],texvec[2]) -
				0.5;
			tresult = intensity(stype,cast,texvec[0] + 
				(dxt[0] * fx + dyt[0] * fy) , texvec[1] + 
				(dxt[1] * fx + dyt[1] * fy) , texvec[2] + 
				(dxt[2] * fx + dyt[2] * fy),normal_vec);
			if (i==0)
			{
				first_result = tresult;
				x = texvec[0] + (dxt[0] * fx + dyt[0] * fy);
				y = texvec[1] + (dxt[1] * fx + dyt[1] * fy);
				z = texvec[2] + (dxt[2] * fx + dyt[2] * fy);
			}
			osa_color += tresult;
		}
		result[0] = osa_color / (float)(cast->osa);
	}
	else
	{
		x = texvec[0];
		y = texvec[1];
		z = texvec[2];
		first_result = intensity(stype,cast,texvec[0], texvec[1], 
			texvec[2],normal_vec);
		result[0] = first_result;
	}
	if (cast->do_bump)
	{
		if (dxt)
		{
			osa_size = vec_len(dxt) + vec_len(dyt);
			good_osa_size = (cast->noise_size * 0.5);
			if (osa_size > good_osa_size)
			{
				bump_factor /= (osa_size / good_osa_size);
			}
		}
		result[5] = intensity(stype,cast,x + s, y    , z    ,normal_vec)
			- first_result ;
		result[6] = intensity(stype,cast,x    , y + s, z    ,normal_vec)
			- first_result ;
		result[7] = intensity(stype,cast,x    , y    , z + s,normal_vec)
			- first_result ;
		if (cast->double_bump)
		{
			result[5] -= intensity(stype,cast,x - s, y    , z    ,
				normal_vec) - first_result ;
			result[6] -= intensity(stype,cast,x    , y - s, z    ,
				normal_vec) - first_result ;
			result[7] -= intensity(stype,cast,x    , y    , z - s,
				normal_vec) - first_result ;
			result[5] *= 0.5;
			result[6] *= 0.5;
			result[7] *= 0.5;	
		}

		result[5] *= bump_factor / s;
		result[6] *= bump_factor / s;
		result[7] *= bump_factor / s;
		return 2;
	}
	return 0;
}
