/*
 * $Id$
 */

/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * Other licensing options are available on request; please
 * contact the author for more information.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2007 Sarah Kelley
 * All rights reserved.
 *
 */
 
 
/*
 *  sarah0.c: My first procedural texture plugin for Blender!
 *            (Or: A Hello World Plugin Gone Horribly Wrong!)
 *
 *  By Sarah Kelley, sarah@sakelley.org.
 *  Originally Written February 2007.
 *
 *  Release History:
 *
 *    Feb. 14, 2007: prerelease for limited testing
 *
 *    Feb. 15, 2007: broken version, oops.
 *
 *    Feb. 21, 2007: prerelease for limited testing, C89/C90 compliant,
 *                   anti-aliasing still dubious, bump mapping dubious.
 *
 *    Feb. 25, 2007: pulled common definitions into sarahplugins.h.
 *                   pulled common utility functions into sarahutil.h
 *                   cleaning and polishing for first public release.
 */


/*
 *  To maintain the best compatability with all platforms Blender will
 *  run on, we try to stick to straight C90 without any other features.
 *  Compiling with gcc, use -pedantic to see warnings about features
 *  that aren't a part of C90.
 */

/* 
 *  Standard math functions 
 */
#include "math.h"

/* 
 *  Standard IO functions (needed by some parts of sarahutil.h)
 */
#include "stdio.h"

/* 
 * Blender plugin API 
 */
#include "plugin.h"


/*
	Plugin.h gives us the following functions:

	derived from the famous Perlin noise
	float hnoise(float noisesize, float x, float y, float z);
	
	the original Perlin noise
	float hnoisep(float noisesize, float x, float y, float z);

    soft turbulence
	float turbulence(float noisesize, float x, float y, float z, int depth);
	
	hard turbulence
	float turbulence1(float noisesize, float x, float y, float z, int depth);
*/


/*
 *  Define the name and authorship of this texture plugin
 */
#define TEXTURE_NAME "Sarah0"
#define AUTHOR "By Sarah K."
#define AUTHOR2 "Written by Sarah Kelley <sarah@sakelley.org>, Feb 2007" 


#define EXTENDED_DISTORTION_SCALER

/*
 *  Texture name; displayed for the texture's title 
 *  in the Texture Buttons window. 
 */
char name[24]= TEXTURE_NAME;

/* 
 *  Subtypes for optional variations on the basic algorithm of your texture.
 *  Always be sure to define at least one!
 */
#define NR_TYPES 5
#define SUBTYPE_SQUARES  0
#define SUBTYPE_CIRCLES  1
#define SUBTYPE_BOXES    2
#define SUBTYPE_BLURBLES 3
#define SUBTYPE_CIRBLES  4
char stnames[NR_TYPES][16]= {"Squares","Circles", "Boxes","Blurbles","Cirbles"};

/* 
 *  Structure for UI buttons,
 *  Format is: Button code,name,default,min,max,popup text
 *
 *  Button code is control|type:
 *
 *  Supported types:
 *
 *    CHA   - character data? haven't figured this one out yet.
 *    INT   - integer data
 *    FLO   - floating point data
 *
 *  Supported controls:
 *
 *    LABEL  - text label, needs int/float dummy storage
 *    TOG    - toggle button (on/off)  int/float storage
 *    NUM    - number input box with left and right arrows, int/float storage
 *    NUMSLI - number input box with slider, int/float storage
 *    COL - color picker, needs *three* floats for cast data, but...
 *
 *   There's a bug/feature in the color picker handling... it needs
 *   three floats, but only increments the storage by sizeof(float)
 *   afterward, so two color pickers side by side will partially overlap
 *   their storage! One solution is to use a couple dummy labels to
 *   force the storage counter forward by two more floats. Another solution
 *   is to only have one color picker and let it be the last UI element.
 */
VarStruct varstr[]= {
	{  LABEL|INT, AUTHOR,  0,    0,   0,     AUTHOR2 },
	{   NUM |FLO, "Scale", 1.0,  0.0, 100.0, "Scales texture size"},
	{ NUMSLI|FLO, "Lo",    0.01, 0.0, 1.0,   "Lower bound of something"},
	{ NUMSLI|FLO, "Hi",    0.99, 0.0, 1.0,   "Upper bound of something"},
	{ NUMSLI|FLO, "Pa",    0.01, 0.0, 1.0,   "Parameter A, use depends on subtype and variant"},
	{ NUMSLI|FLO, "Pb",    0.99, 0.0, 1.0,   "Parameter B, use depends on subtype and variant"},
	{ NUMSLI|FLO, "Ds",    0.0,  0.0, 1.0,   "Distortion Scaler (for Nb,Np,Ts,Th)"},
	{ NUMSLI|FLO, "Nb",    0.0,  0.0, 2.0,   "Blender noise (0 = off)"},
	{ NUMSLI|FLO, "Np",    0.0,  0.0, 2.0,   "Perlin Noise (0 = off)"},
	{ NUMSLI|FLO, "Ts",    0.0,  0.0, 5.0,   "Soft turbulence (0 = off)"},
	{ NUMSLI|FLO, "Th",    0.0,  0.0, 5.0,   "Hard turbulence (0 = off)"},
	{ NUM   |INT, "Td",    1,    1,   10,    "Depth of turbulence"},
	{ NUM   |INT, "Var",   1,    1,   4,     "Algorithm variant"},
#ifdef EXTENDED_DISTORTION_SCALER
	{ NUMSLI|FLO, "NbS",    0.5,  0.0, 1.0,   
		"Extended Blender noise scaler (only used Ds=0.0; 0.5 = none)"},
	{ NUMSLI|FLO, "NpS",    0.5,  0.0, 1.0,   
		"Extended Perlin Noise scaler (only used Ds=0.0; 0.5 = none)"},
	{ NUMSLI|FLO, "TsS",    0.5,  0.0, 1.0,   
		"Extended Soft turbulence scaler (only used Ds=0.0; 0.5 = none)"},
	{ NUMSLI|FLO, "ThS",    0.5,  0.0, 1.0,   
		"Extended Hard turbulence scaler (only used Ds=0.0; 0.5 = none)"},
#endif
	{ NUM   |FLO, "Nabla",  0.025, 0.0, 0.1, 
		"Defines size of derivative offset used for calculating normal"},
	

};

/*
 *  Structure used to access the plugin data provided by the buttons
 */
typedef struct Cast {
	int dummyForLabel;
	float scale;
	float low;
	float high;
	float param_a;
	float param_b;
	float distort_scale;
	float noise_blender;
	float noise_perlin;
	float turbulence_soft;
	float turbulence_hard;
	int turbulence_depth;
	int algorithm_variant;
#ifdef EXTENDED_DISTORTION_SCALER
	float blender_noise_scaler;
	float perlin_noise_scaler;
	float soft_turbulence_scaler;
	float hard_turbulence_scaler;
#endif
	float nabla;
	
} Cast;

/**
 *  Extra stuff that needs to be passed
 *  to the intensity calculation functions.
 */
typedef struct Extra {
	float prnd; /* position dependent random number, 0..1 */
	float trnd; /* position dependent random number, but constant over unit interval */
} Extra;

/* My common definitions and structs */
#include "sarahplugins.h"

/* My utility functions. CONTAINS CODE. */
#include "sarahutil.h"

/*
 *  Structure used to pass data to/from the plugin.
 *  result[0] 	Intensity value        0.0 to 1.0
 *  result[1] 	Red color value        0.0 to 1.0
 *  result[2] 	Green color value      0.0 to 1.0
 *  result[3] 	Blue color value       0.0 to 1.0
 *  result[4] 	Alpha color value      0.0 to 1.0
 *  result[5] 	X normal displacement
 *  result[6] 	Y normal displacement
 *  result[7] 	Z normal displacement 
 */
float final_result_global[8];

/*
 *  Current frame. May be +/- 0.5 depending on field setting.
 */
float cfra;

/**
 *  Prototype for the entry point for our texture function
 */
int plugin_tex_doit(int, Cast*, float*, float*, float*);

/*
 *  Specifies which version of the plugin API we are using
 */
int plugin_tex_getversion(void) {
	return B_PLUGIN_VERSION;
}

/* 
 *  Called when a button in the UI changes 
 */
void plugin_but_changed(int but) { }

/* 
 *  Function called to initialize the plugin. Note: may be called multiple
 *  times if the plugin has multipe instances.
 */
void plugin_init(void) { }

/*
 *  Allows blender to get info about our plugin. Typically does not need
 *  to 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= final_result_global;
	info->cfra= &cfra;
	info->varstr= varstr;
	info->init= plugin_init;
	info->tex_doit= (TexDoit) plugin_tex_doit;
	info->callback= plugin_but_changed;
	
	/* Blender 2.43 will support instance init as well */
	/*info->instance_init=(void *)my_instance_init;   */
}



/*
 *  Calculate a pseudorandom number for the position in vector v.
 *  return it in extra->prnd (position-dependent random number)
 */
void set_prnd(Cast *c,float *v,Extra *e) {
	e->prnd=hnoise((c->param_a+0.002)/2.0,
		v[0]*c->scale,v[1]*c->scale,v[2]*c->scale)/(2.0*c->param_b);
	e->trnd=hnoise((c->param_a+0.02)/2.0,
		floorf(v[0]*c->scale),floorf(v[1]*c->scale),floorf(v[2]*c->scale))/(2.0*c->param_b);
}


/* Experimental extended distortion scaling.
 * If the slider is in the range 0..0.5, we multiply by twice that,
 * which means we're multiplying by a value between 0 and 1, which
 * has the effect of scaling downward (making noise smaller).
 *
 * If the slider is beyond 0.5, we scale the remaining half of
 * the range to be a value of 1 to 1000 (making noise bigger).
 *
 * The intended effect is that we can both shrink and expand with
 * just a single slider.
 */
float extended_distortion_scaler(float value_to_scale,float scale_value) {
	return extended_scaler(value_to_scale,scale_value);
}

/*
 *  Given a vector stored as an array of three floats,
 *  distort the coordinates as specified by the settings 
 *  in the UI panel. The resulting distorted vector is
 *  returned in another caller-provided vector array.
 */
void distort_vector(Cast *c,float *orig,float *distort) {
	float x,y,z,distort_scale,sum,count,distort_value;
	
	x=orig[0]; y=orig[1]; z=orig[2];
	distort_scale=c->distort_scale;
	if (distort_scale<=0.0) distort_scale=0.001;
	distort_scale=distort_scale*5;
	sum=0.0;
	count=0.0;
	
	/* If Ds is set to 0, use extended scaling. */
	if (c->distort_scale==0.0) {

#ifdef EXTENDED_DISTORTION_SCALER	
		if (c->turbulence_soft>0.0) {
			sum+=extended_distortion_scaler(turbulence(c->turbulence_soft,x,y,z,
				c->turbulence_depth)-0.5,c->soft_turbulence_scaler);
			count=count+1.0;
		}
		if (c->turbulence_hard>0.0) {
			sum+=extended_distortion_scaler(turbulence1(c->turbulence_hard,x,y,z,
				c->turbulence_depth)-0.5,c->hard_turbulence_scaler);
			count=count+1.0;
		}
		if (c->noise_blender>0.0) {
			sum+=extended_distortion_scaler(hnoise(c->noise_blender,x,y,z)-0.5,
				c->blender_noise_scaler);
			count=count+1.0;
		}	
		if (c->noise_perlin>0.0) {
			sum+=extended_distortion_scaler(hnoisep(c->noise_perlin,x,y,z)-0.5,
				c->perlin_noise_scaler);
			count=count+1.0;
#endif

		}

	/* Ds nonzero, so use single scale factor for all. */
	} else {
	
		if (c->turbulence_soft>0.0) {
			sum+=(turbulence(c->turbulence_soft,x,y,z,
				c->turbulence_depth)-0.5)/distort_scale;
			count=count+1.0;
		}
		if (c->turbulence_hard>0.0) {
			sum+=(turbulence1(c->turbulence_hard,x,y,z,
				c->turbulence_depth)-0.5)/distort_scale;
			count=count+1.0;
		}
		if (c->noise_blender>0.0) {
			sum+=(hnoise(c->noise_blender,x,y,z)-0.5)/distort_scale;
			count=count+1.0;
		}	
		if (c->noise_perlin>0.0) {
			sum+=(hnoisep(c->noise_perlin,x,y,z)-0.5)/distort_scale;
			count=count+1.0;
		}
	}
	
	distort_value=0.0;
	if (count>0.0) {
		distort_value=sum/count;
	}
	distort[0]=x+distort_value;
	distort[1]=y+distort_value;
	distort[2]=z+distort_value;
}


/*
 *  Calculate the intensity for an alternating pattern of squares.
 *  This is a bit of a misnomer; it's actually the classic checkerboard
 *  pattern in 3D space.
 */
float calculate_squares(Cast *c,float tx,float ty,float tz,Extra *extra) {
	float low,high,lowend,highend,rr,gg,bb;
	int ir,ig,ib,ix;
	
	low=c->low;
	high=c->high;
	if (low>high) { 
		low=c->high;
		high=c->low;
	}
	lowend=0.5*low;
	highend=0.5*high;
	
	tx=tx*c->scale;
	ty=ty*c->scale;
	tz=tz*c->scale;
	rr=fabs(tx-floorf(tx));
	gg=fabs(ty-floorf(ty));
	bb=fabs(tz-floorf(tz));
	ir,ig,ib,ix;
	
	/* This variant makes the cubes on the Z axis line up in a certain way. */
	if (c->algorithm_variant==1) {
		ir=((rr>=lowend)&&(rr<=highend));
		ig=((gg>=lowend)&&(gg<=highend));
		ib=!((bb>=lowend)&&(bb<=highend));
		ix=(ir+ig+ib)&1;
		if ((ir+ig+ib)&1) {
			return c->param_a;
		} else {
			return c->param_b;
		}
	}
	
	/* This variant makes the cubes on the Z axis line up in a different way! */
	if (c->algorithm_variant==2) {
		ir=((rr>=lowend)&&(rr<=highend));
		ig=((gg>=lowend)&&(gg<=highend));
		ib=((bb>=lowend)&&(bb<=highend));
		ix=(ir+ig+ib)&1;
		if ((ir+ig+ib)&1) {
			return c->param_a;
		} else {
			return c->param_b;
		}
	}
	
	/* This is variant 1, but with the odd cubes taking a random value. */ 
	if (c->algorithm_variant==3) {
		ir=((rr>=lowend)&&(rr<=highend));
		ig=((gg>=lowend)&&(gg<=highend));
		ib=!((bb>=lowend)&&(bb<=highend));
		ix=(ir+ig+ib)&1;
		if ((ir+ig+ib)&1) {
			return c->param_a;
		} else {
			return extra->trnd;
		}
	}
	
	/* This is variant 1, but with the even cubes taking a random value.
	   Honestly, we should probably find a better use for variant 4,
	   since it's virtually identical to variant 3. */
	if (c->algorithm_variant==4) {
		ir=((rr>=lowend)&&(rr<=highend));
		ig=((gg>=lowend)&&(gg<=highend));
		ib=!((bb>=lowend)&&(bb<=highend));
		ix=(ir+ig+ib)&1;
		if ((ir+ig+ib)&1) {
			return extra->trnd;
		} else {
			return c->param_b;
		}
	}
	return 0;
}


/**
 *  Texture generator for the "squares" subtype.
 *  Uses calculate_squares() to compute pixel intensity.
 */
int plugin_tex_squares(Cast *c, float *texvec, float *dxt, float *dyt,Result *result) {
	float distortvec[3];
	int nor;
	Extra extra;

	distort_vector(c,texvec,distortvec);
	set_prnd(c,distortvec,&extra);
	result->intensity = 
		calculate_intensity(&calculate_squares,c,distortvec,dxt,dyt,&extra);
	nor=calculate_bumpmap(&calculate_squares,c,distortvec,dxt,dyt,&extra,
			c->nabla,c->scale,result);
	return TEX_INT|nor;
}



/*
 *  Calculate circles in a 3D grid. The basic value is derived from
 *  the distance between the center of the unit box and the current
 *  pixel, using a radius function. The circles can have an inner
 *  and an outer ring, depending on the settings. 
 */
float calculate_circles(Cast *c,float tx,float ty,float tz,Extra *extra) {
	float low,high,xx,yy,zz,radius,max_radius;
	
	low=c->low;
	high=c->high;
	if (low>high) { 
		low=c->high;
		high=c->low;
	}
	tx=tx*c->scale;
	ty=ty*c->scale;
	tz=tz*c->scale;
	xx=fabs(tx-floorf(tx))-0.5; /* -0.5 ... 0.5 */
	yy=fabs(ty-floorf(ty))-0.5; /* -0.5 ... 0.5 */
	zz=fabs(tz-floorf(tz))-0.5; /* -0.5 ... 0.5 */
	
	radius=fsqrt((xx*xx)+(yy*yy)+(zz*zz)); /* 0... 0.86602540378 */
	
	max_radius=fsqrt((0.5*0.5)+(0.5*0.5)+(0.5*0.5)); /* 0.86602540378 */
	
	/* Scale the radius to 0...1. I'm not sure this works very well,
	 * or it may be that this needs to be scaled differently, as
	 * I can't seem to make circles that are perfectly white on
	 * the outside and perfectly black at the center.
	 */
	radius=radius*(1.0/max_radius);  /* 0.0 ... 1.0 */

	/* Make everything between low and high be gradient shaded. */
	if (c->algorithm_variant==1) {
		if (radius<low) return 0.0;   /* clip low */
		if (radius>high) return 1.0;  /* clip high */
		
		return ((high-low)*radius);
	}
	
	/* Make the innermost part of the circle randomly shaded,
	 * but otherwise same as variant 1.
	 */
	if (c->algorithm_variant==2) {
		if (radius<low) return extra->trnd;   /* clip low */
		if (radius>high) return 1.0;  /* clip high */
		
		return ((high-low)*radius);
	}
	
	/*
	 * Make the outermost part of the circle randomly shaded,
	 * but otherwise same as variant 1.
	 */
	if (c->algorithm_variant==3) {
		if (radius<low) return 0.0;   /* clip low */
		if (radius>high) return extra->trnd;  /* clip high */
		
		return ((high-low)*radius);
	}
	
	/*
	 * Make everything between inside the inner ring or outside
	 * the outer ring be randomly shaded, but otherwise
	 * same as variant 1.
	 */
	if (c->algorithm_variant==4) {
		if (radius<low) return extra->trnd;   /* clip low */
		if (radius>high) return extra->trnd;  /* clip high */
		
		return ((high-low)*radius);
	}
	return 0;
	
}


/**
 *  Texture generator for the "circles" subtype.
 *  Uses calculate_circles() to compute pixel intensity.
 */
int plugin_tex_circles(Cast *c, float *texvec, float *dxt, float *dyt,Result *result) {
	float distortvec[3];
	int nor;
	Extra extra;

	distort_vector(c,texvec,distortvec);
	set_prnd(c,distortvec,&extra);
	result->intensity = 
		calculate_intensity(&calculate_circles,c,distortvec,dxt,dyt,&extra);
	nor=calculate_bumpmap(&calculate_circles,c,distortvec,dxt,dyt,&extra,
		c->nabla,c->scale,result);
	return TEX_INT|nor;
}


/**
 *  Calculate boxes on a 3D grid. Unlike squares/checkerboard,
 *  the squares can be hollow, and like circles, they can have
 *  multiple "rings" depending on settings.
 */
float calculate_boxes(Cast *c,float tx,float ty,float tz,Extra *extra) {
	float low,high,xx,yy,zz,fx,fy,fz;
	float radius,max_radius;
	
	low=c->low;
	high=c->high;
	if (low>high) { 
		low=c->high;
		high=c->low;
	}
	low=low/2.0;   /* 0... 0.5 */
	high=high/2.0; /* 0.0 ... 0.5 */
	tx=tx*c->scale;
	ty=ty*c->scale;
	tz=tz*c->scale;
	xx=fabs(tx-floorf(tx))-0.5; /* -0.5 ... 0.5 */
	yy=fabs(ty-floorf(ty))-0.5; /* -0.5 ... 0.5 */
	zz=fabs(tz-floorf(tz))-0.5; /* -0.5 ... 0.5 */
	fx=fabs(xx);  /* 0.0 ... 0.5 */
	fy=fabs(yy);  /* 0.0 ... 0.5 */
	fz=fabs(zz);  /* 0.0 ... 0.5 */
	
	/* Make boxes with a white outside, a grey middle, and a black inside.
	 * This only returns those three possible intensities.
	 */
	if (c->algorithm_variant==1) {
		if ((fx<low)&&(fy<low)) return 0.0;
		if ((fx<low)&&(fz<low)) return 0.0;
		if ((fy<low)&&(fz<low)) return 0.0;
		
		if ((fx>high)&&(fy>high)) return 1.0;
		if ((fx>high)&&(fz>high)) return 1.0;
		if ((fy>high)&&(fz>high)) return 1.0;
		
		return 0.5;
	}
	
	
	/* NOTE: Variant 1 is fine, but the rest aren't very good...
	 * more work needs to be done here.
	 */
	

	/* Same as variant 1, but the boxes are in pure 3D space,
	 * rather than multiple 2D space. Try rendering both variants on
	 * a sphere to see the difference.
	 */
	if (c->algorithm_variant==2) {
		if ((fx<low)&&(fy<low)&&(fz<low)) return 0.0;
		if ((fx>high)&&(fy>high)&&(fz>high)) return 1.0;
		return 0.5;
	}
	
	
	/*
	 *  This is very similar to circles... in fact, close to identical.
	 *  It's probably not a very good variant.
	 */
	if (c->algorithm_variant==3) {
		radius=fsqrt((xx*xx)+(yy*yy)+(zz*zz)); /* 0... 0.86602540378 */
		max_radius=fsqrt((0.5*0.5)+(0.5*0.5)+(0.5*0.5)); /* 0.86602540378 */
		
		radius=radius*(1.0/max_radius);  /* 0.0 ... 1.0 */
		return ((high-low)*radius);
	}
	
	/*
	 *  Another not-very-good variant.
	 */
	if (c->algorithm_variant==4) {
		if (  (fabs(xx)>c->param_a)  &&  (fabs(yy)>c->param_a) && (fabs(zz)>c->param_a) ) {
			return c->high;
		} else {
			return c->low;
		}
	}
	


}

/**
 *  Texture generator for the "boxes" subtype.
 *  Uses calculate_boxes() to compute pixel intensity.
 */
int plugin_tex_boxes(Cast *c, float *texvec, float *dxt, float *dyt,Result *result) {
	float distortvec[3];
	int nor;
	Extra extra;

	distort_vector(c,texvec,distortvec);
	set_prnd(c,distortvec,&extra);
	result->intensity = 
		calculate_intensity(&calculate_boxes,c,distortvec,dxt,dyt,&extra);
	nor=calculate_bumpmap(&calculate_boxes,c,distortvec,dxt,dyt,&extra,
		c->nabla,c->scale,result);
	return TEX_INT|nor;
}


/**
 *  Calculate... um, blurbles. I didn't know what to call them so I
 *  made up a word. And I'm not really sure how to describe them, as
 *  what I intended to create and what I wound up with turned out
 *  to be two entirely different things!
 *
 *  Blurbles are kind of like groups of splotches on a 3D grid.
 *  They're actually kinda cool.
 */
float calculate_blurbles(Cast *c,float tx,float ty,float tz,Extra *extra) {
	float low,high,blurble,xx,yy,zz,fx,fy,fz;
	
	low=c->low;
	high=c->high;
	if (low>high) { 
		low=c->high;
		high=c->low;
	}
	low=low/2.0;   /* 0... 0.5 */
	high=high/2.0; /* 0.0 ... 0.5 */
	blurble=extra->prnd;
	tx=tx*c->scale;
	ty=ty*c->scale;
	tz=tz*c->scale;
	xx=fabs(tx-floorf(tx))-0.5; /* -0.5 ... 0.5 */
	yy=fabs(ty-floorf(ty))-0.5; /* -0.5 ... 0.5 */
	zz=fabs(tz-floorf(tz))-0.5; /* -0.5 ... 0.5 */
	
	fx=fabs(xx);  /* 0.0 ... 0.5 */
	fy=fabs(yy);  /* 0.0 ... 0.5 */
	fz=fabs(zz);  /* 0.0 ... 0.5 */

	/*  Make a blurble with three splotch intensities. */
	if (c->algorithm_variant==1) {
	
		if ((fx-blurble<low)&&(fy-blurble<low)) return 0.0;
		if ((fx-blurble<low)&&(fz-blurble<low)) return 0.0;
		if ((fy-blurble<low)&&(fz-blurble<low)) return 0.0;
		
		if ((fx+blurble>high)&&(fy+blurble>high)) return 1.0;
		if ((fx+blurble>high)&&(fz+blurble>high)) return 1.0;
		if ((fy+blurble>high)&&(fz+blurble>high)) return 1.0;
		
		return 0.5;
	}
	
	/* Make a blurble like variant 1 but with the black splotches
	 * taking on a random intensity.
	 */
	if (c->algorithm_variant==2) {
		if ((fx-blurble<low)&&(fy-blurble<low)) return extra->trnd;
		if ((fx-blurble<low)&&(fz-blurble<low)) return extra->trnd;
		if ((fy-blurble<low)&&(fz-blurble<low)) return extra->trnd;
		
		if ((fx+blurble>high)&&(fy+blurble>high)) return 1.0;
		if ((fx+blurble>high)&&(fz+blurble>high)) return 1.0;
		if ((fy+blurble>high)&&(fz+blurble>high)) return 1.0;
		
		return 0.5;
	}
	
	/* Make a blurble like variant 1 but with the white splotches
	 * taking on a random intensity.
	 */
	if (c->algorithm_variant==3) {
		if ((fx-blurble<low)&&(fy-blurble<low)) return 0.0;
		if ((fx-blurble<low)&&(fz-blurble<low)) return 0.0;
		if ((fy-blurble<low)&&(fz-blurble<low)) return 0.0;
		
		if ((fx+blurble>high)&&(fy+blurble>high)) return extra->trnd;
		if ((fx+blurble>high)&&(fz+blurble>high)) return extra->trnd;
		if ((fy+blurble>high)&&(fz+blurble>high)) return extra->trnd;
		
		return 0.5;
	}
	
	/* Make a blurble like variant 1 but with the grey splotches
	 * taking on a random intensity.
	 */
	if (c->algorithm_variant==4) {
		if ((fx-blurble<low)&&(fy-blurble<low)) return 0.0;
		if ((fx-blurble<low)&&(fz-blurble<low)) return 0.0;
		if ((fy-blurble<low)&&(fz-blurble<low)) return 0.0;
		
		if ((fx+blurble>high)&&(fy+blurble>high)) return 1.0;
		if ((fx+blurble>high)&&(fz+blurble>high)) return 1.0;
		if ((fy+blurble>high)&&(fz+blurble>high)) return 1.0;
		
		return extra->trnd;
	}
	
	return 0;
}


/**
 *  Texture generator for the "blurbles" subtype.
 *  Uses calculate_blurbles() to compute pixel intensity.
 */
int plugin_tex_blurbles(Cast *c, float *texvec, float *dxt, float *dyt,Result *result) {
	float distortvec[3];
	int nor;
	Extra extra;
	
	distort_vector(c,texvec,distortvec);

	set_prnd(c,distortvec,&extra);
	
	result->intensity = 
		calculate_intensity(&calculate_blurbles,c,distortvec,dxt,dyt,&extra);
	nor=calculate_bumpmap(&calculate_blurbles,c,distortvec,dxt,dyt,&extra,
		c->nabla,c->scale,result);
	return TEX_INT|nor;


}


/**
 *  Calculate intensity for cirbles, which are cubes and other
 *  interesting shapes on a 3D grid.
 */
float calculate_cirbles(Cast *c,float tx,float ty,float tz,Extra *extra) {
	float rnd,low,high,xx,yy,zz,radius,max_2d_radius,r1,r2,r3;
	
	
	/* Variant 1 is so simple that we deal with it at the beginning.
	 * we just return a random color that is unique on the unit cube.
	 */
	rnd=extra->trnd;
	if (c->algorithm_variant==1) return rnd;


	low=c->low;
	high=c->high;
	if (low>high) { 
		low=c->high;
		high=c->low;
	}
	low=low/2.0;   /* 0... 0.5 */
	high=high/2.0; /* 0.0 ... 0.5 */

	
	tx=tx*c->scale;
	ty=ty*c->scale;
	tz=tz*c->scale;
	
	xx=fabs(tx-floorf(tx))-0.5; /* -0.5 ... 0.5 */
	yy=fabs(ty-floorf(ty))-0.5; /* -0.5 ... 0.5 */
	zz=fabs(tz-floorf(tz))-0.5; /* -0.5 ... 0.5 */

	/* Variant 2 still needs a lot of work.
	 * It generates cubes, circles, and plus signs, but it's
	 * actually reasonably tricky to adjust the sliders until
	 * some actual texture appears. More work on the scaling
	 * (or something) is needed.
	 */
	if (c->algorithm_variant==2) {
		
		max_2d_radius=fsqrt((0.5*0.5)+(0.5*0.5)); 
		r1=fsqrt((xx*xx)+(yy*yy))*(1.0/max_2d_radius); /* 0 ... 1 */
		r2=fsqrt((xx*xx)+(zz*zz))*(1.0/max_2d_radius); /* 0 ... 1 */
		r3=fsqrt((yy*yy)+(zz*zz))*(1.0/max_2d_radius); /* 0 ... 1 */
		
		if ((r1-rnd<low)&&(r2-rnd<low)) return 0.0;
		if ((r2-rnd<low)&&(r3-rnd<low)) return 0.0;
		if ((r1-rnd<low)&&(r3-rnd<low)) return 0.0;
		
		if ((r1+rnd>high)&&(r2+rnd>high)) return 1.0;
		if ((r2+rnd>high)&&(r3+rnd>high)) return 1.0;
		if ((r3+rnd>high)&&(r1+rnd>high)) return 1.0;
		
		return rnd;
	}
	
	/* Variant 3 generates mostly spheres on the 3D grid.
	 * It's fairly tolerant about where you set the parameters.
	 */
	if (c->algorithm_variant==3) {
	
		radius=fsqrt((xx*xx)+(yy*yy)+(zz*zz))*1.15470053838;
		radius=radius/2.0;
		
		if ( radius>rnd ) {
			return c->high;
		} else {
			return c->low;
		}
	}
	
	/* Like variant 1, this needs work.
	 */
	if (c->algorithm_variant==4) {
		
		max_2d_radius=fsqrt((0.5*0.5)+(0.5*0.5)); 
		r1=fsqrt((xx*xx)+(yy*yy))*(1.0/max_2d_radius); /* 0 ... 1 */
		r2=fsqrt((xx*xx)+(zz*zz))*(1.0/max_2d_radius); /* 0 ... 1 */
		r3=fsqrt((yy*yy)+(zz*zz))*(1.0/max_2d_radius); /* 0 ... 1 */
		
		if ((r1-rnd<low)&&(r2-rnd<low)) return 0.0;
		if ((r2-rnd<low)&&(r3-rnd<low)) return 0.0;
		if ((r1-rnd<low)&&(r3-rnd<low)) return 0.0;
		
		return rnd;
		
	}

	return 0;
}


/**
 *  Texture generator for the "cirbles" subtype.
 *  Cirbles is a made up word similar to blurbles, but suggessting
 *  circles (which is actually what I had originally planned for blurbles!)
 *
 *  Uses calculate_cirbles() to compute pixel intensity.
 */
int plugin_tex_cirbles(Cast *c, float *texvec, float *dxt, float *dyt,Result *result) {
	
	float distortvec[3];
	int nor;
	Extra extra;

	distort_vector(c,texvec,distortvec);

	set_prnd(c,distortvec,&extra);

	result->intensity = 
		calculate_intensity(&calculate_cirbles,c,distortvec,dxt,dyt,&extra);
	nor=calculate_bumpmap(&calculate_cirbles,c,distortvec,dxt,dyt,&extra,
		c->nabla,c->scale,result);
	return TEX_INT|nor;

}


/*
 *  Calculate and return a texture pixel for Blender.
 *
 *  stype: index of which subtype, from the list specified earlier
 *  cast:  plugin data, including UI controls
 *  texvec: pointer to three floats, texture coordinates of the pixel
 *          whose value is to be returned.
 *  dxt,dyt: if non-null, these point to two vectors (two arrays of three floats)
 *           that define the size of the requested texture value in pixel space.
 *           They are only non-NULL when OSA is on, and are used to calculate 
 *           proper anti aliasing.
 *
 *  This function should fill in the values in the result[] array,
 *  and then return one of the following:
 *
 *    0: only filled in intensity                (TEX_INT)
 *    1: filled in a color value, and intensity  (TEX_RGB|TEX_INT)
 *    2: filled in a normal value, and intensity (TEX_NOR|TEX_INT)
 *    3: filled in everything                    (TEX_NOR|TEX_RGB|TEX_INT)
 */
int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, float *dyt) 
{
	Result result;
	int rv=0;
	
#ifdef DEBUG_LOG_INPUTS
	debug_log_inputs(texvec,dxt,dyt);
#endif

	begin_doit();
	
	switch(stype) {
		case SUBTYPE_SQUARES:
			rv=plugin_tex_squares(cast,texvec,dxt,dyt,&result);
			break;
		case SUBTYPE_CIRCLES:
			rv=plugin_tex_circles(cast,texvec,dxt,dyt,&result);
			break;
		case SUBTYPE_BOXES:
			rv=plugin_tex_boxes(cast,texvec,dxt,dyt,&result);
			break;
		case SUBTYPE_BLURBLES:
			rv=plugin_tex_blurbles(cast,texvec,dxt,dyt,&result);
			break;
		case SUBTYPE_CIRBLES:
			rv=plugin_tex_cirbles(cast,texvec,dxt,dyt,&result);
			break;
		default:
			rv=0;
	}
	
	/*
		I started to see garbage noise in my render when I started to add
		bump mapping code; I think it had to do with multiple overlapping
		accesses to the global result[] array passed via plugin_getinfo.
	
		I couldn't see an easy way to correctly fix this; however, doing
		all my calculations into a Result struct and copying the results
		into the result[] array at the end minimizes the critical section
		of code, and thus minimizes the chance of overlap.
		
		I'm not sure if this indicates a general threading issue with
		the plugin API or whether I need to do some kind of semaphore
		within my plugin, or whether I'm misunderstanding the situation.
		More research is needed. :-)
	*/
	set_final_result(final_result_global,&result);
	end_doit();
	return rv;
}
