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


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

char name[24]= "T_Bricks";

/* Subtype names must be less than 15 characters */
#define NR_TYPES	2
char stnames[NR_TYPES][16]= {"3D", "2D" };


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

VarStruct varstr[]= {
{	NUM|FLO,	"Turbulance",		1.0, 	0.0, 20.0, ""},
{	NUM|FLO,	"Noise Size",		0.25, 	0.001, 10.0, ""},
{	NUM|INT,	"Noise Depth",		0.0, 	0.0, 6.0, ""},
{	NUM|FLO,	"Detail Size",		0.5, 	0.01, 0.99, ""},
{ 	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,	"Two-way Filter",   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 Oversampling mode"},
{	NUM|FLO,	"Map Max",		1.0, 	0.0, 1.0, ""},
{	NUM|FLO,	"Map Min",		0.0, 	0.0, 1.0, ""},
{	NUM|FLO,	"Space width",		0.1, 	0.0, 0.95, ""},
{	NUM|FLO,	"Bevel width",		0.1, 	0.0, 0.95, ""},
{	NUM|FLO,	"Smoothness",		1.0, 	0.0, 1.0, ""},
{	NUM|FLO,	"Z Size",		1.0, 	0.1, 1.0, ""},
{	LABEL|INT,   	" ",             	0.,     0.,   0.,   ""},
{	NUM|FLO,	"Random Color",		0.0, 	0.0, 1.0, ""},
{	NUM|INT,	"Bevel Type",		1.0, 	1.0, 2.0, 
	"1:Curved 2:Straight"},
{	NUM|INT,	"Corner Type",		2.0, 	1.0, 3.0, 
	"1:Sharp 2:Medium 3:Soft"},
};

/* 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 space_width;
	float bevel_width;
	float smoothness;
	float z_scale;
	int dummy3;
	float random_color;
	int bevel_method;
	int corner_method;
} 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 *color)
{
	float vec[3];
	float res;
	float max;
	float min;
	float p[3],d[3];
	int a,ny,nz;
	int sector[3],high[3];
	float par_res[3];
	float sw[3],bw[3];

	if (stype == 0)
	{
		z = z / cast->z_scale;
		sw[0] = (cast->space_width * cast->z_scale) -0.5;
		bw[0] = cast->bevel_width * cast->z_scale;
		sw[1] = sw[0];
		bw[1] = bw[0];
		sw[2] = (cast->space_width) -0.5;
		bw[2] = bw[0] / cast->z_scale;
	}
	else
	{
		y = y / cast->z_scale;
		z = 0.0;
		sw[0] = (cast->space_width * cast->z_scale) -0.5;
		bw[0] = cast->bevel_width * cast->z_scale;
		sw[1] = (cast->space_width) -0.5;
		bw[1] = bw[0] / cast->z_scale;
		sw[2] = -0.5;
		bw[2] = 0.0;
	}
	x += 12345.0;
	y += 12345.0;
	z += 12345.0;

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

	p[0] = x + vec[0] * cast->turbulance_weight;
	p[1] = y + vec[1] * cast->turbulance_weight;
	p[2] = (z + vec[2] * cast->turbulance_weight);


	for (a=0; a<3; a++)
	{
		sector[a] = floorf(p[a]);
		d[a] = p[a] - floorf(p[a]);
		d[a] -= 0.5;
	}

	for (a=0; a<3; a++)
		high[a] = sector[a] & 1;

	ny = sector[1]  & 2;
	nz = sector[2]  & 2;


	if (stype == 0)
	{
		if (nz)
		{
			high[0] = !high[0];
			high[1] = !high[1];
		}
		else
		{
		}
	}
	else
	{
		if (ny)
			high[0] = !high[0];
	}
	for (a=0; a<3; a++)
		if (high[a])
			d[a] = -d[a];

	if ((d[0] < sw[0]) || (d[1] < sw[1]) || (d[2] < sw[2]))
		res = 0.0;
	else if ((d[0] > (sw[0] + bw[0])) && (d[1] > (sw[1] + bw[1])) &&(d[2] > (sw[2] + bw[2])))
		res = 1.0;
	else
	{
		for (a=0;a<3;a++)
			if (d[a] < (sw[a] + bw[a]))
			{
				if (cast->bevel_method == 1)
					par_res[a] =  +  sin(0.5 * 3.14 * 
					   ((1.0 - cast->smoothness) +
					   (((d[a] - sw[a]) * cast->smoothness)
					   / (bw[a]))));
				else if (cast->bevel_method == 2)
					par_res[a] = ((1.0 - cast->smoothness) +
				 	   (((d[a] - sw[a]) * cast->smoothness)
					   / (bw[a])));
			}
			
		res = 1.0;
		if (cast->corner_method == 1)
		{
			for (a=0;a<3;a++)
				if (d[a] < (sw[a] + bw[a]))
					if (res > par_res[a]) 
						res = par_res[a];
		}
		else if (cast->corner_method == 2)
		{
			for (a=0;a<3;a++)
				if (d[a] < (sw[a] + bw[a]))
					res *= (par_res[a]);
		}
		else if (cast->corner_method == 3)
		{
			for (a=0;a<3;a++)
				if (d[a] < (sw[a] + bw[a]))
					res -= (1.0 - par_res[a]);
		}
	}
	for (a=0; a<3; a++)
		if (high[a])
			sector[a] -= 1;
	max = cast->map_max;
	min = cast->map_min;
	if (max <= min)
	{
		max = (max + min) * 0.05;
		min = max - 0.01;
	}
	if (res < min)
	{
		res = 0.0;
	}
	else if (res > max)
		res = 1.0;
	else
		res = (res - min) / (max - min);

	*color = res * (1.0 - cast->random_color * 
		(float)(hash[(hash[(hash[(cast->seed + sector[0]) & 255] + 
		sector[1]) & 255] + sector[2]) & 255]) / 256.0);

	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;
	float color,t,tcolor,osa_color;
	int i,optimum_osa_level;
	float sample_factor;
	float osa_size,good_osa_size;
	float bump_factor;
	float fx,fy;
	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) {
		osa_size = vec_len(dxt) + vec_len(dyt);
		good_osa_size = MIN2((cast->noise_size * 0.5) / 
				(cast->turbulance_weight+0.0001), 0.25);
		sample_factor = 1.0;
		if (osa_size > good_osa_size)
		{
			sample_factor = (osa_size / good_osa_size);
		}
		bump_factor /= sample_factor;

		osa_color = 0.0f;
/*
 * optimum_osa_level = MIN2(cast->osa, (int)((sample_factor)*(sample_factor))); */
		optimum_osa_level = cast->osa;
		for (i=0; i<optimum_osa_level; i++)
		{
			if (i == 0)
			{
				fx = 0.0;
				fy = 0.0;
			}
			else
			{
				fx = fixed_rand(2 *i,texvec[0],texvec[1],
						texvec[2]) - 0.5f;
				fy = fixed_rand(2 *i +1,texvec[0],texvec[1],
						texvec[2]) - 0.5f;
			}
			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),&tcolor);
			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 += tcolor;
		}
		color = osa_color / (float)(optimum_osa_level);
	}
	else
	{
		x = texvec[0];
		y = texvec[1];
		z = texvec[2];
		first_result = intensity(stype,cast,x, y, z,&color);
	}
	if (cast->do_bump)
	{
		result[5] = intensity(stype,cast,x + s, y    , z    ,&t) - 
			first_result;
		result[6] = intensity(stype,cast,x    , y + s, z    ,&t) - 
			first_result;
		result[7] = intensity(stype,cast,x    , y    , z + s,&t) - 
			first_result;
		if (cast->double_bump)
		{
			result[5] -= intensity(stype,cast,x - s, y    , z,
					&t) - first_result;
			result[6] -= intensity(stype,cast,x    , y - s, z,
					&t) - first_result;
			result[7] -= intensity(stype,cast,x    , y    , z - s,
					&t) - 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;

		result[0] = color;
		return 2;
	}
	result[0] = color;
	return 0;
}
