/*
 * Copyright (c) 2000, Not a Number b.v.
 * This code is based at Ken Perlin's famous works...
 *
 */

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

char name[24]= "r_Weave";

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

#define NR_TYPES	0
/*char stnames[NR_TYPES][16]= {"Bands", "Rings" }; */

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

VarStruct varstr[]= {
{	NUM|INT,	"Noise Depth",		2.0, 	0.0, 6.0, ""},
{	NUM|FLO,	"Noise Size",		1.0, 	0.0, 5.0, ""},
{ TOG|INT,	"Hard Noise",   0.0,	 0.0, 1.0,  ""},
{ TOG|INT,	"Analytic BumpMp",   1.0,	 0.0, 1.0,  
	"Generate Analytic Bump Map"},
{ TOG|INT,	"Wrap X",	        0.0,	 0.0, 1.0,  
	"Wrap Horizontally"},
{ TOG|INT,	"Wrap Y",	        0.0,	 0.0, 1.0,  "Wrap Vertically"},
{	NUM|FLO,	"Turbulance",		1.0, 	0.0, 20.0, ""},
{	NUM|FLO,	"Width",		0.45, 	0.0, 0.5, ""},
{	NUM|INT,	"Pattern",		212.0, 	0.0, 511.0, 
	"Weave pattern"},
{ NUM|INT,  "OSA",        1.0,    1.0,  10.0,   "Oversampling amount"},
{	NUM|INT,	"Tiles",		1.0, 	1.0, 20.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 {
	int depth;
	float noise_size;
/*	float dummy; */
	int hard_noise;
	int do_bump;
	int wrap_x;
	int wrap_y;
	float turbulance_weight;
	float width;
	int pattern;
	int OSA;
	int size;
} 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= NULL; /*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 raw_intensity(int stype, Cast *cast, float x,float y, float z, 
	float *raw_bump)
{
	float res, dx, dy, wi_x, wi_y;
        int ix, iy, mx, my, p, w, nb1x, nb2x, nb1y, nb2y;
	int curp, wave_x, wave_y;

        res = 0.0;
	raw_bump[0] = 0.0;
	raw_bump[1] = 0.0;
	raw_bump[2] = 0.0;
	ix = floorf(x);
	dx = x - floorf(x);
	dx = dx - 0.5;
	iy = floorf(y);
	dy = y - floorf(y);
	dy = dy - 0.5;
	mx = (ix + 9000) % 3;
	my = (iy + 9000) % 3;
	p = (1 << (mx+3*my));
	w = 0;
	nb1x = 1 << (((mx + 2) % 3) + 3 * my);
	nb2x = 1 << (((mx + 1) % 3) + 3 * my);
	nb1y = 1 << (mx + 3 * ((my + 2) % 3));
	nb2y = 1 << (mx + 3 * ((my + 1) % 3));
	wave_x = wave_y = 1;
	if (p & cast->pattern) 
		curp=1;
	else
		curp=2;
	if (nb1y & cast->pattern) 
		nb1y=1;
	else
		nb1y=2;
	if (nb2y & cast->pattern) 
		nb2y=1;
	else
		nb2y=2;

	if (nb1x & cast->pattern) 
		nb1x=1;
	else
		nb1x=2;
	if (nb2x & cast->pattern) 
		nb2x=1;
	else
		nb2x=2;

	if ((dy < 0.0) && (nb1y == curp))
	{
		if (curp == 1)
		{
			wave_y = 2;
			wi_x = 0.8 * cast->width;
		}
		else
		{
			wave_y = 0;
			wi_x = 1.0 * cast->width;
		}
	}
	else if ((dy > 0.0) && (nb2y == curp))
	{
		if (curp == 1)
		{
			wave_y = 2;
			wi_x = 0.8 * cast->width;
		}
		else
		{
			wave_y = 0;
			wi_x = 1.0 * cast->width;
		}
	}
	else
		wi_x= cast->width * (0.9 - cos(dy * 6.2832) * 0.1);

	if ((dx < 0.0) && (nb1x == curp))
	{
		if (curp == 2)
		{
			wave_x = 2;
			wi_y = 0.8 * cast->width;
		}
		else
		{
			wave_x = 0;
			wi_y = 1.0 * cast->width;
		}
	}
	else if ((dx > 0.0) && (nb2x == curp))
	{
		if (curp == 2)
		{
			wave_x = 2;
			wi_y = 0.8 * cast->width;
		}
		else
		{
			wave_x = 0;
			wi_y = 1.0 * cast->width;
		}
	}
	else
		wi_y= cast->width * (0.9 - cos(dx * 6.2832) * 0.1);


	if ((fabs(dx) < wi_x) && (fabs(dy) < wi_y))
	{
		if (p & cast->pattern) 
			w=1;
		else
			w=2;
	}
	else if ((fabs(dx) < wi_x))
	{
		w = 1;
	}
	else if ((fabs(dy) < wi_y))
	{
		w = 2;
	}
	if (w == 1)
	{
		if (stype == 0)
		{
			if (wave_y == 1)
			{
				res = (cos(3.1415926 * 0.8 * (1.0 - fabs(dy)) *
					dx / wi_x)+ cos(dy * 6.28) * 0.3 + 
					0.0) / 1.3; /* * 0.5; */
				raw_bump[0] = -sin(3.1415926 * 0.8 * (1.0 - 
					fabs(dy)) * dx / wi_x);
				raw_bump[1] = -sin(dy * 6.28) * 0.3;
			}
			else if (wave_y == 2)
			{
				res = (cos(3.1415926 * 0.8 * (1.0) * dx / wi_x)
					+ (1.0) * 0.3 + 0.0) / 1.3;/* * 0.5; */
				raw_bump[0] = -sin(3.1415926 * 0.8 * (1.0) * dx
					 / wi_x);
				raw_bump[1] = 0;
			}
			else if (wave_y == 0)
			{
				res = (cos(3.1415926 * 0.8 * (0.5) * dx / wi_x)
					+ (-1.0) * 0.3 + 0.0) / 1.3;/* * 0.5; */
				raw_bump[0] = -sin(3.1415926 * 0.8 * (0.5) * dx
					/ wi_x);
				raw_bump[1] = 0;
			}
		}
	}
	else if (w == 2)
	{
		if (stype == 0)
		{
			if (wave_x == 1)
			{
				res = (cos(3.1415926 * 0.8 * (1.0 - fabs(dx)) *
					dy / wi_y)+ cos(dx * 6.28) * 0.3 + 0.0)
					/ 1.3;/* * 0.5; */
				raw_bump[1] = -sin(3.1415926 * 0.8 * (1.0 - 
					fabs(dx)) * dy / wi_y);
				raw_bump[0] = -sin(dx * 6.28) * 0.3;
			}
			else if (wave_x == 2)
			{
				res = (cos(3.1415926 * 0.8 * (1.0) * dy / wi_y)
					+ (1.0) * 0.3 + 0.0) /1.3;/* * 0.5; */
				raw_bump[1] = -sin(3.1415926 * 0.8 * (1.0) * 
					dy / wi_y);
				raw_bump[0] = 0;
			}
			else if (wave_x == 0)
			{
				res = (cos(3.1415926 * 0.8 * (0.5) * dy / wi_y)
					+ (-1.0) * 0.3 + 0.0) / 1.3;/* * 0.5; */
				raw_bump[1] = -sin(3.1415926 * 0.8 * (0.5) * 
					dy / wi_y);
				raw_bump[0] = 0;
			}
		}
	}
	return res;
}

float intensity(int stype, Cast *cast, float x,float y, float z ,float *bump)
{
	float vec[3];
	float res;
	float tvec[3];
	float raw_bump[3];

	point_turbulance(cast->noise_size, cast->hard_noise, x, y, z, vec, 
		cast->depth,cast->wrap_x,cast->wrap_y,0);

	res = raw_intensity(stype,cast,x * cast->size * 1.5 + vec[0] * 
		cast->turbulance_weight,y * cast->size * 1.5  + vec[1] * 
		cast->turbulance_weight,z + vec[2] * cast->turbulance_weight, 
		raw_bump);

	tvec[0] = vec[0] * cast->turbulance_weight + raw_bump[0];
	tvec[1] = vec[1] * cast->turbulance_weight + raw_bump[1];
	tvec[2] = vec[2] * cast->turbulance_weight + raw_bump[2];

	bump[0] = tvec[0] / (1 + cast->turbulance_weight);
	bump[1] = tvec[1] / (1 + cast->turbulance_weight) ;
	bump[2] = tvec[2] / (1 + cast->turbulance_weight) ;

	return res;
}

#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 bump[3],bumpa[3], fx, fy, r;
        int iosa, osa;
	if (dxt && dyt)
	{
		osa = cast->OSA;
		r = 0;
		bump[0] = 0;
		bump[1] = 0;
		bump[2] = 0;
		for (iosa = 0; iosa < osa; iosa ++)
		{
			if (iosa == 0)
			{
				fx = 0; fy = 0;
			}
			else
			{
		    fx = FRAND; fy = FRAND;
			}
			r += 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),bumpa);
			bump[0] += bumpa[0];
			bump[1] += bumpa[1];
			bump[2] += bumpa[2];
		}
		result[0] = r / ((float) osa);
		bump[0] = bump[0] / ((float) osa);
		bump[1] = bump[1] / ((float) osa);
		bump[2] = bump[2] / ((float) osa);
	}
	else
	{
		result[0] = intensity(stype,cast,texvec[0], texvec[1], 
			texvec[2],bump);
	}

	if (!(cast->do_bump))
		return 0;
	else
	{
		result[5] = bump[0] ;
		result[6] = bump[1] ;
		result[7] = bump[2] ;
	
		return 2;
	}
}
