MODULE srVoxel;
IMPORT srBase, Math, srMath, srE,  srHex,Out := KernelLog;

TYPE SREAL=srBase.SREAL;
TYPE PT=srBase.PT;
TYPE Ray = srBase.Ray;
TYPE Voxel = srBase.Voxel;

TYPE ColoredVox* = OBJECT(Voxel);
VAR
	r, g, b: SREAL;
PROCEDURE SetColor* (red, green, blue : SREAL);
BEGIN
	r := srBase.clamp(red );
	g := srBase.clamp(green );
	b := srBase.clamp(blue );
END SetColor;
PROCEDURE Shade (VAR ray: Ray);
BEGIN
	ray.r := ray.r + r*ray.ra;
	ray.g := ray.g + g*ray.ga;
	ray.b := ray.b + b*ray.ba;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
END Shade;
END ColoredVox;

TYPE GoorowVox* = OBJECT(Voxel);
VAR
	r, g, b: SREAL;
PROCEDURE Shade (VAR ray: Ray);
BEGIN
	ray.r := ray.r + ray.lxyz.x*ray.ra;
	ray.g := ray.g + ray.lxyz.y*ray.ga;
	ray.b := ray.b + ray.lxyz.z*ray.ba;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a :=0;
END Shade;
END GoorowVox;

TYPE LitVox* = OBJECT(Voxel);
VAR
	r, g, b, nx, ny, nz: SREAL;
PROCEDURE SetColor* (red, green, blue : SREAL);
BEGIN
	r := srBase.clamp(red);
	g := srBase.clamp(green);
	b := srBase.clamp(blue);
END SetColor;
PROCEDURE Shade (VAR ray: Ray);
VAR
	x,y,z,dotrl: SREAL;
BEGIN
	x := 1/2 - ray.lxyz.x; y := 1/2 - ray.lxyz.y; z := 1/2 - ray.lxyz.z;
	srBase.normalize(x,y,z);
	dotrl :=x*srBase.light.x + y*srBase.light.y + z*srBase.light.z;
	IF dotrl > 0 THEN
		ray.r := ray.r +(r*dotrl)*ray.ra ;
		ray.g := ray.g + (g*dotrl)*ray.ga;
		ray.b := ray.b + (b*dotrl)*ray.ba;
	END;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a := 0;
END Shade;
END LitVox;

TYPE ColoredMVox* = OBJECT(Voxel); (*NOT CORRECT YET *)
VAR
	r, g, b, mf, a: SREAL;
PROCEDURE SetColor*(red, green, blue, mfraction: SREAL);
BEGIN
	mf := srBase.clamp(mfraction);
	a := mf;
	r := srBase.clamp(red)*a;
	g := srBase.clamp(green)*a;
	b := srBase.clamp(blue)*a;
END SetColor;
PROCEDURE Shade (VAR ray: Ray);
VAR
	dr, dg, db: SREAL;
BEGIN
	dr :=  r*ray.ra;
	dg :=  g*ray.ga;
	db :=  b*ray.ba;
	ray.r := ray.r + dr;
	ray.g := ray.g + dg;
	ray.b := ray.b + db;
	ray.ra := ray.ra - a*(dg+db);
	ray.ga := ray.ga - a*(dr+db);
	ray.ba := ray.ba - a*(dr+dg);
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
	mirror(ray);
END Shade;
END ColoredMVox;

TYPE DiffuseMVox* = OBJECT(Voxel);
VAR
	r, g, b, mf, a: SREAL;
PROCEDURE Shade (VAR ray: Ray);
VAR
	nx, ny, nz: INTEGER;
	dot: SREAL;
	inside: BOOLEAN;
BEGIN
	CASE ray.face OF
		0: inside := TRUE
		|1: nx := -1
		|2: ny := -1
		|3: nz := -1
		|4: nx := 1
		|5: ny := 1
		|6: nz := 1
	ELSE
	END;
	IF inside THEN dot := 0 ELSE dot := ABS(nx*ray.dxyz.x + ny*ray.dxyz.y+ nz*ray.dxyz.z) END;
	ray.ra := dot*ray.ra- 0.3;
	ray.ga := dot*ray.ga- 0.3;
	ray.ba := dot*ray.ba- 0.3;
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
	mirror(ray);
END Shade;
END DiffuseMVox;

TYPE DiffuseSphMVox* = OBJECT(ColoredVox);
VAR
	 mf, a: SREAL;
PROCEDURE Shade(VAR ray: Ray);
VAR
	nx, ny, nz: SREAL;
	dot: SREAL;
	inside: BOOLEAN;
BEGIN
	nx := 1/2 - ray.lxyz.x; ny := 1/2-ray.lxyz.y; nz := 1/2-ray.lxyz.z;
	srBase.normalize(nx,ny, nz);
	IF inside THEN dot := 0 ELSE dot := ABS(nx*ray.dxyz.x + ny*ray.dxyz.y+ nz*ray.dxyz.z) END;
	ray.a := dot*ray.a/2;
	ray.ra := dot*ray.ra/2;
	ray.ga := dot*ray.ga/2;
	ray.ba := dot*ray.ba/2;
	mirror(ray);
END Shade;
END DiffuseSphMVox;

TYPE DiffuseSphVox* = OBJECT(ColoredVox);
VAR
	 mf, a: SREAL;
PROCEDURE Shade(VAR ray: Ray);
VAR
	dot: SREAL;
	p: srBase.PT;
BEGIN
	p.x:= 1/2 - ray.lxyz.x; p.y:= 1/2 - ray.lxyz.y; p.z:= 1/2 - ray.lxyz.z;
	srBase.normalizePT(p);
	dot := ABS(p.x*ray.dxyz.x + p.y*ray.dxyz.y+ p.z*ray.dxyz.z);
	ray.r := ray.r + r * ray.ra*dot;
	ray.g := ray.g + g * ray.ga*dot;
	ray.b := ray.b + b * ray.ba*dot;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a := 0;
END Shade;
END DiffuseSphVox;

TYPE AlphaVox* = OBJECT(Voxel);
VAR
	r, g, b, ra, ga, ba: SREAL;
PROCEDURE SetColor* (red, green, blue, alpha  : SREAL);
BEGIN
	r := srBase.clamp(red * alpha);
	g := srBase.clamp(green * alpha);
	b := srBase.clamp(blue * alpha);
END SetColor;
PROCEDURE Shade (VAR ray: Ray);
VAR
	dr, dg, db: SREAL;
BEGIN
	dr :=  r*ray.ra;
	dg :=  g*ray.ga;
	db :=  b*ray.ba;
	ray.r := ray.r + dr;
	ray.g := ray.g + dg;
	ray.b := ray.b + db;
	ray.ra := ray.ra - (dg+db)/2;
	ray.ga := ray.ga - (dr+db)/2;
	ray.ba := ray.ba - (dr+dg)/2;
	ray.a := ray.a -(dr+dg+db)/3;
	ray.length := ray.length + ray.scale;
END Shade;
END AlphaVox;

(*TYPE ColoredDetailVox*=OBJECT(ColoredVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	l, x, y, z: SREAL;
	ecount: INTEGER;
BEGIN
	ray.r := ray.r + r * ray.a;
	ray.g := ray.g + g * ray.a;
	ray.b := ray.b + b * ray.a;
	ray.a := ray.a - a
END Shade;

END ColoredDetailVox; *)

TYPE TransparaVox*=OBJECT(Voxel);
VAR
	r, g, b, black: SREAL;
PROCEDURE SetColor* (red, green, blue,bl  : SREAL);
BEGIN
	r := red;
	g := green;
	b := blue;
	black:=bl;
	passable := TRUE;
END SetColor;

PROCEDURE Shade (VAR ray: Ray);
VAR
	depth: SREAL;
	exit:PT;
	dr,dg,db,dblack: SREAL;
BEGIN
	exit:=srBase.Exit(ray);
	depth:=srBase.distsquared(ray.lxyz,exit);
	dr := r*depth;
	dg := g*depth;
	db := b*depth;
	dblack:=black*depth;
	ray.r := ray.r + dr;
	ray.g := ray.g + dg;
	ray.b := ray.b + db;
	ray.ra := ray.ra - dr-dblack;
	ray.ga := ray.ga - dg-dblack;
	ray.ba := ray.ba - db-dblack;
	srBase.clamp3(ray.ra,ray.ga,ray.ba);
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
END Shade;
END TransparaVox;

TYPE RainbowVox*=OBJECT(Voxel);
VAR
	r, g, b, black: SREAL;
PROCEDURE SetColor* (red, green, blue,bl  : SREAL);
BEGIN
	r := red;
	g := green;
	b := blue;
	black:=bl;
	passable := TRUE;
END SetColor;

PROCEDURE Shade (VAR ray: Ray);
VAR
	depth: SREAL;
	exit:PT;
	dr,dg,db,dblack: SREAL;
BEGIN
	exit:=srBase.Exit(ray);
	depth:=srBase.distsquared(ray.lxyz,exit);
	dr := ABS(r*depth*ray.dxyz.x);
	dg := ABS(g*depth*ray.dxyz.y);
	db := ABS(b*depth*ray.dxyz.z);
	dblack:=black*depth;
	ray.r := ray.r + dr;
	ray.g := ray.g + dg;
	ray.b := ray.b + db;
	ray.ra := ray.ra - dr-dblack;
	ray.ga := ray.ga - dg-dblack;
	ray.ba := ray.ba - db-dblack;
	srBase.clamp3(ray.ra,ray.ga,ray.ba);
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
END Shade;
END RainbowVox;


TYPE JelloVox*=OBJECT(AlphaVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	x,y,z: SREAL;
	depth: SREAL;
	dr, dg, db: SREAL;
BEGIN
(*	x := ray.lxyz.x-ray.xlx;
	y := ray.lxyz.y-ray.xly;
	z := ray.lxyz.z-ray.xlz;	*)
	depth := Math.sqrt(x*x+y*y+z*z);
	dr :=  r*ray.ra*depth;
	dg :=  g*ray.ga*depth;
	db :=  b*ray.ba*depth;
	ray.ra := ray.ra - dr;
	ray.ga := ray.ga - dg;
	ray.ba := ray.ba - db;
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
	ray.length := ray.length + ray.scale;
END Shade;
END JelloVox;

TYPE AirVox*=OBJECT(ColoredVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	x,y,z: SREAL;
	depth: SREAL;
	dr, dg, db: SREAL;

BEGIN
(*	x := ray.lxyz.x-ray.xlx;
	y := ray.lxyz.y-ray.xly;
	z := ray.lxyz.z-ray.xlz;	*)
	depth := Math.sqrt(x*x+y*y+z*z)*srBase.fog;
	dr :=  r*ray.ra*depth;
	dg :=  g*ray.ga*depth;
	db :=  b*ray.ba*depth;
	ray.ra := ray.ra - dr;
	ray.ga := ray.ga - dg;
	ray.ba := ray.ba - db;
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
	ray.length := ray.length + ray.scale;
END Shade;
END AirVox;

TYPE InkVox*=OBJECT(ColoredVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	ink: SREAL;
BEGIN
	ink := 0.05*ray.a;
	ray.ra := ray.ra - ink;
	ray.ga := ray.ga - ink;
	ray.ba := ray.ba - ink;
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
	ray.length := ray.length + ray.scale;
END Shade;
END InkVox;

TYPE OutlineVox*=OBJECT(ColoredVox);
VAR
	or, og, ob: SREAL;
PROCEDURE SetOutline* (red, green, blue: SREAL);
BEGIN
	or := red ;
	og := green ;
	ob := blue;
END SetOutline;
PROCEDURE Shade (VAR ray: Ray);
VAR
	ecount: INTEGER;
BEGIN
	IF (ray.lxyz.x< 0.01) OR (ray.lxyz.x > 0.99) THEN INC(ecount) END;
	IF (ray.lxyz.y <0.01) OR (ray.lxyz.y > 0.99) THEN INC(ecount) END;
	IF (ray.lxyz.z < 0.01) OR (ray.lxyz.z > 0.99) THEN INC(ecount) END;
	IF ecount > 1 THEN
		ray.r := ray.r + or * ray.ra;
		ray.g := ray.g + og * ray.ga;
		ray.b := ray.b + ob * ray.ba;
		ray.ra := 0;
		ray.ga := 0;
		ray.ba := 0;
		ray.a := 0;
	ELSE
		ray.r := ray.r + r * ray.ra;
		ray.g := ray.g + g * ray.ga;
		ray.b := ray.b + b * ray.ba;
		ray.ra := 0;
		ray.ga := 0;
		ray.ba := 0;
		ray.a := 0;
	END
END Shade;
END OutlineVox;

TYPE GoutlineVox*=OBJECT(ColoredVox)
VAR
	tx, ty, tz: SREAL;		(* thickness of outline *)
	or, og, ob: SREAL; 	(* outline color *)
PROCEDURE Shade (VAR ray: Ray);
VAR
	ecount: INTEGER;
	l, le, xe, ye, ze: SREAL;
BEGIN
	ecount := 0;
	IF (ray.lxyz.x < 1/100)  THEN
		xe := 100*(1/100-ray.lxyz.x)
	ELSIF (ray.lxyz.x > 99/100) THEN
		xe := 00*(1-ray.lxyz.x)
	END;
	IF (ray.lxyz.y < 1/100)  THEN
		ye := 100*(1/100-ray.lxyz.y)
	ELSIF (ray.lxyz.y > 99/100) THEN
		ye := 100*(1-ray.lxyz.y)
	END;
	IF (ray.lxyz.z < 1/100)  THEN
		ze := 100*(1/100-ray.lxyz.z)
	ELSIF (ray.lxyz.z > 99/100) THEN
		ze := 100*(1-ray.lxyz.z)
	END;
	le := (xe+ye+ze)/3;
	l := (ray.lxyz.x+ray.lxyz.y+ray.lxyz.z)/3;
	ray.r := ray.r + r * ray.ra*l;
	ray.g := ray.g + g * ray.ga*l;
	ray.b := ray.b + b * ray.ba*l;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a := 0;
END Shade;
END GoutlineVox;

TYPE GouraudVox* = OBJECT(ColoredVox);

VAR
	brightness: INTEGER;

PROCEDURE & init*;
BEGIN
	brightness := 16;
END init;

PROCEDURE tick;
BEGIN
	IF srBase.rand.Uniform()>1/2 THEN
		brightness := (brightness + 1) MOD 20;
	ELSE
		brightness := (brightness - 1) MOD 20;
	END
END tick;
PROCEDURE Shade (VAR ray: Ray);
VAR
	l: SREAL;
BEGIN
	l := (ray.lxyz.x+ray.lxyz.y+ray.lxyz.z)/3;
	ray.r := ray.r + r * ray.ra*l;
	ray.g := ray.g + g * ray.ga*l;
	ray.b := ray.b + b * ray.ba*l;
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
END Shade;
END GouraudVox;

TYPE VGouraudVox* = OBJECT(GouraudVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	l: SREAL;
BEGIN
	l := (ray.lxyz.x+ray.lxyz.y)/2;
	ray.r := ray.r + r * ray.ra*l;
	ray.g := ray.g + g * ray.ga*l;
	ray.b := ray.b + b * ray.ba*l;
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
END Shade;
END VGouraudVox;

TYPE HGouraudVox* = OBJECT(GouraudVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	l: SREAL;
BEGIN
	l := (ray.lxyz.x+ray.lxyz.z)/2;
	ray.r := ray.r + r * ray.ra*l;
	ray.g := ray.g + g * ray.ga*l;
	ray.b := ray.b + b * ray.ba*l;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a := 0;
END Shade;
END HGouraudVox;

TYPE NouraudVox* = OBJECT(ColoredVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	l: SREAL;
BEGIN
	l := 2*(ABS(1/2-ray.lxyz.x) + ABS(1/2-ray.lxyz.y) + ABS(1/2-ray.lxyz.z))/3;
	ray.r := ray.r + r * ray.ra*l;
	ray.g := ray.g + g * ray.ga*l;
	ray.b := ray.b + b * ray.ba*l;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a := 0;
END Shade;
END NouraudVox;

TYPE DiffuseVox* = OBJECT(ColoredVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	nx, ny, nz: INTEGER;
	dot: SREAL;
	inside: BOOLEAN;
BEGIN
	CASE ray.face OF
		0: inside := TRUE
		|1: nx := -1
		|2: ny := -1
		|3: nz := -1
		|4: nx := 1
		|5: ny := 1
		|6: nz := 1
	ELSE
	END;
	IF inside THEN dot := 1 ELSE dot := ABS(nx*ray.dxyz.x + ny*ray.dxyz.y+ nz*ray.dxyz.z) END;
	IF dot<1/2 THEN dot:=1/2 END;
	ray.r := ray.r + r * ray.ra*dot ;
	ray.g := ray.g + g * ray.ga*dot;
	ray.b := ray.b + b * ray.ba*dot;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a := 0;
END Shade;

END DiffuseVox;

TYPE DiffuseNouraudVox* = OBJECT(ColoredVox);

PROCEDURE Shade (VAR ray: Ray);
VAR
	nx, ny, nz: INTEGER;
	dot: SREAL;
	inside: BOOLEAN;
	l: SREAL;
BEGIN
	l := 2*(ABS(1/2-ray.lxyz.x) + ABS(1/2-ray.lxyz.y) + ABS(1/2-ray.lxyz.z))/3;
	CASE ray.face OF
		0: inside := TRUE
		|1: nx := -1
		|2: ny := -1
		|3: nz := -1
		|4: nx := 1
		|5: ny := 1
		|6: nz := 1
	ELSE
	END;
	IF inside THEN dot := l ELSE dot := l*(1/3+2*ABS(nx*ray.dxyz.x + ny*ray.dxyz.y+ nz*ray.dxyz.z)/2) END;
	ray.r := ray.r + r * ray.ra*dot;
	ray.g := ray.g + g * ray.ga*dot;
	ray.b := ray.b + b * ray.ba*dot;
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a := 0;
END Shade;

END DiffuseNouraudVox;

TYPE GridVox* = OBJECT(Voxel);
VAR
	r, g, b, a, gr, gg, gb, ga, Z: SREAL;

PROCEDURE SetColor* (red, green, blue, alpha: SREAL);
BEGIN
	r := red * alpha;
	g := green * alpha;
	b := blue * alpha;
	a := alpha;
END SetColor;

PROCEDURE SetGridColor* (red, green, blue, alpha: SREAL);
BEGIN
	gr := red * alpha;
	gg := green * alpha;
	gb := blue * alpha;
	ga := alpha;
END SetGridColor;

PROCEDURE SetGrid*(z: SREAL);
BEGIN
	Z := z;
END SetGrid;

PROCEDURE Shade (VAR ray: Ray);
VAR
	lx, ly, x, y: SREAL;
	i, j: LONGINT;
BEGIN
	CASE ray.face OF
		 1: lx := ray.lxyz.y; ly := ray.lxyz.z;
		|2: lx := ray.lxyz.x; ly := ray.lxyz.z;
		|3: lx := ray.lxyz.x; ly := ray.lxyz.y;
		|4: lx := ray.lxyz.y; ly := ray.lxyz.z;
		|5: lx := ray.lxyz.x; ly := ray.lxyz.z;
		|6: lx := ray.lxyz.x; ly := ray.lxyz.y;
	ELSE
	END;
	x := lx*Z; y := ly*Z;
	i := ENTIER(x); j := ENTIER(y);
	x := x - i; y := y - j;
	IF ((x<0.1) OR (y<0.1)) THEN
		ray.r := ray.r + gr;
		ray.g := ray.g + gg;
		ray.b := ray.b + gb;
		ray.ra := ray.ra - (gg+gb);
		ray.ga := ray.ga - (gr+gb);
		ray.ba := ray.ba - (gr+gg);
	ELSE
		ray.r := ray.r + r;
		ray.g := ray.g + g;
		ray.b := ray.b + b;
		ray.ra := ray.ra - (g+b);
		ray.ga := ray.ga - (r+b);
		ray.ba := ray.ba - (r+g);
	END;
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
END Shade;
END GridVox;

TYPE GridChirkleVox* = OBJECT(Voxel);
VAR
	r, g, b, a, Z: SREAL;

PROCEDURE SetColor* (red, green, blue, alpha: SREAL);
BEGIN
	r := red * alpha;
	g := green * alpha;
	b := blue * alpha;
	a := alpha;
	register;
END SetColor;

PROCEDURE Shade (VAR ray: Ray);
VAR
	dx, dy, dz, d2: SREAL;
BEGIN
	dx := (1/2-ray.lxyz.x);
	dy := (1/2-ray.lxyz.y);
	dz := (1/2-ray.lxyz.z);
	d2 := dx*dx+dy+dy+dz+dz;
	IF d2>1 THEN
		ray.r := ray.r + r;
		ray.g := ray.g + g;
		ray.b := ray.b + b;
		ray.ra := ray.ra - (g+b);
		ray.ga := ray.ga - (r+b);
		ray.ba := ray.ba - (r+g);
		ray.a := (ray.ra+ray.ga+ray.ba)/3;
	END
END Shade;

END GridChirkleVox;

TYPE CheckerVox* = OBJECT(Voxel);
VAR
	r, g, b, a, Z: SREAL;
PROCEDURE SetColor* (red, green, blue, alpha: SREAL);
BEGIN
	r := red * alpha;
	g := green * alpha;
	b := blue * alpha;
	a := alpha;
	register;
END SetColor;
PROCEDURE Shade (VAR ray: Ray);
VAR
	d, x,y,z, dr, dg, db: SREAL;
	ijk: srBase.IPT;
BEGIN
	srE.E(ray.lxyz, ijk);
	x := ray.lxyz.x*2- ijk.i*2;
	y := ray.lxyz.y*2- ijk.j*2;
	z := ray.lxyz.z*2- ijk.k*2;
	d := ABS((1/2-x)*(1/2-x)*(1/2-z)*(Z));
	dr :=  (1- ray.lxyz.x*d)*ray.ra;
	dg :=  (1- ray.lxyz.y*d)*ray.ga;
	db :=  (1 - ray.lxyz.z*d)*ray.ba;
	ray.r := ray.r + dr;
	ray.g := ray.g + dg;
	ray.b := ray.b + db;
	ray.ra := ray.ra - (dg+db);
	ray.ga := ray.ga - (dr+db);
	ray.ba := ray.ba - (dr+dg);
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
END Shade;

PROCEDURE tick*;
BEGIN
	Z := 10+(srBase.frame MOD 13);
END tick;

END CheckerVox;

TYPE HexaVox* = OBJECT(Voxel);
VAR
	V: Voxel;
	hhx: SREAL;

PROCEDURE&init*;
BEGIN
	hhx := 6;
END init;

PROCEDURE setVox*(v: Voxel);
BEGIN
	V := v;
END setVox;

PROCEDURE connectmessage*;
BEGIN
	Out.String("HexaVox"); Out.Ln;
END connectmessage;

PROCEDURE talk*(c: CHAR; VAR connection: BOOLEAN);
BEGIN
	CASE c OF
		'+': hhx := hhx + 0.05; Out.String("hhx +."); Out.Ln;
		| 'G': hhx := hhx - 0.05; Out.String("hhx - "); Out.Ln;
	ELSE
		Out.String(".");
	END;
END talk;

PROCEDURE Shade (VAR ray: Ray);
VAR
	lx,ly: SREAL;
	Q, gray: SREAL;
BEGIN
	CASE ray.face OF
		 1: lx := ray.lxyz.y; ly := ray.lxyz.z;
		|2: lx := ray.lxyz.x; ly := ray.lxyz.z;
		|3: lx := ray.lxyz.x; ly := ray.lxyz.y;
		|4: lx := ray.lxyz.y; ly := ray.lxyz.z;
		|5: lx := ray.lxyz.x; ly := ray.lxyz.z;
		|6: lx := ray.lxyz.x; ly := ray.lxyz.y;
	ELSE
	END;
	Q := srHex.hexize2(50*0.866*lx, 50*0.866*ly);
	IF Q < 1/10 THEN
		gray := (1-Q*10);
		ray.r := ray.r - gray*ray.ra;
		ray.g := ray.g - gray*ray.ga;
		ray.b := ray.b - gray*ray.ba;
		ray.ra := ray.ra-gray;
		ray.ga := ray.ga-gray;
		ray.ba := ray.ba-gray;
		ray.a := (ray.ra+ray.ga+ray.ba)/3;
	END;
	IF V # NIL THEN V.Shade(ray) END;
END Shade;
END HexaVox;

TYPE SPHexaVox*=OBJECT(HexaVox);

PROCEDURE ctop(x,y,z: SREAL; VAR th,ph: SREAL);
BEGIN
	srBase.normalize(x,y,z);
	th := 6.28*srMath.sin(x);
	ph :=  6.28*srMath.cos(y);
END ctop;

PROCEDURE Shade (VAR ray: Ray);
VAR
	Q, gray: SREAL;
	th,ph: SREAL;
BEGIN
	ctop(ray.lxyz.x,ray.lxyz.y,ray.lxyz.z,th,ph);
	Q := srHex.hexize2(3*0.866*th, 3*0.866*ph);
	IF Q < 1/10 THEN
		gray := (1-Q*10);
		ray.ra := ray.ra - gray;
		ray.ga := ray.ga - gray;
		ray.ba := ray.ba - gray;
		ray.a := (ray.ra+ray.ga+ray.ba)/3;
	END;
	IF V # NIL THEN V.Shade(ray) END;
END Shade;

END SPHexaVox;

TYPE PolkaVox* = OBJECT(Voxel);
VAR
	brightness: INTEGER;
	r, g, b, rr, gg, bb: SREAL;

PROCEDURE & init*;
BEGIN
	brightness := 16;
END init;

PROCEDURE SetColor* (red, green, blue, r2, g2, b2 : SREAL);
BEGIN
	r := srBase.clamp(red );
	g := srBase.clamp(green );
	b := srBase.clamp(blue );
	rr := srBase.clamp(r2);
	gg := srBase.clamp(g2);
	bb := srBase.clamp(b2);
END SetColor;

PROCEDURE tick;
BEGIN
	IF srBase.rand.Uniform()>1/2 THEN
		brightness := (brightness + 1) MOD 20;
	ELSE
		brightness := (brightness - 1) MOD 20;
	END
END tick;

PROCEDURE Shade (VAR ray: Ray);
VAR
	l, x, y, z: SREAL;
	nx, ny, nz: INTEGER;
		dot: SREAL;
	inside: BOOLEAN;
BEGIN
	CASE ray.face OF
		0: inside := TRUE
		|1: nx := -1
		|2: ny := -1
		|3: nz := -1
		|4: nx := 1
		|5: ny := 1
		|6: nz := 1
	ELSE
	END;
	IF inside THEN dot := 1 ELSE dot := ABS(nx*ray.dxyz.x + ny*ray.dxyz.y+ nz*ray.dxyz.z) END; 	x := 2*ABS(1/2 - ray.lxyz.x);
	y := 2*ABS(1/2 - ray.lxyz.y);
	z := 2*ABS(1/2 - ray.lxyz.z);
	l := (x+y+z)/3;
	dot := dot*brightness;
	ray.r := ray.r + (r * ray.ra*l)*dot + (rr * ray.ra*(1-l))*dot ;
	ray.g := ray.g + g * ray.ga*l *dot+ (gg * ray.ga*(1-l))*dot;
	ray.b := ray.b + b * ray.ba*l*dot + (bb * ray.ba*(1-l)*dot);
	ray.ra := 0;
	ray.ga := 0;
	ray.ba := 0;
	ray.a := 0;
END Shade;
END PolkaVox;

TYPE GeckoVox* = OBJECT(Voxel);
VAR
	r, g, b, a: SREAL;
	ecount: INTEGER;
PROCEDURE SetColor* (red, green, blue, alpha: SREAL);
BEGIN
	r := red * alpha;
	g := green * alpha;
	b := blue * alpha;
	a := alpha
END SetColor;
PROCEDURE Shade (VAR ray: Ray);
VAR
	d, dr, dg, db: SREAL;
BEGIN
	d := ABS((1/2-ray.lxyz.x)*(1/2-ray.lxyz.y)*(1/2-ray.lxyz.z)*70);
	dr :=  r*ray.ra*d;
	dg :=  g*ray.ga*d;
	db :=  b*ray.ba*d;
	ray.r := ray.r + dr;
	ray.g := ray.g + dg;
	ray.b := ray.b + db;
	ray.ra := ray.ra - (dg+db);
	ray.ga := ray.ga - (dr+db);
	ray.ba := ray.ba - (dr+dg);
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
END Shade;
END GeckoVox;

(*TYPE SerpVox* = OBJECT(Voxel);
VAR
	r1, g1, b1, r2, g2, b2: SREAL;

PROCEDURE SetColor1* (r, g, b: SREAL);
BEGIN
	r1 := r;
	g1 := g;
	b := b
END SetColor1;

PROCEDURE SetColor2* (r, g, b: SREAL);
BEGIN
	r2 := r;
	g2 := g;
	b2 := b
END SetColor2;

PROCEDURE Shade (VAR ray: Ray);
VAR
	i, j, k: LONGINT;
	sc, d: INTEGER;
BEGIN
	ray.splitme := TRUE;
	sc := 0;
	d := 3;
	WHILE d > 0 DO
		IF (1 / 3 < ray.lx) & (ray.lx < 2 / 3) THEN INC(sc) END;
		IF (1 / 3 < ray.ly) & (ray.ly < 2 / 3) THEN INC(sc) END;
		IF (1 / 3 < ray.lz) & (ray.lz < 2 / 3) THEN INC(sc) END;
		IF sc < 2 THEN
			sc := 0;
			IF ray.lx >= 2 / 3  THEN
				ray.lx := ray.lx - 2 / 3
			ELSIF ray.lx >= 1 / 3 THEN
				ray.lx := ray.lx - 1 / 3
			END;
			ray.lx := ray.lx * 3;
			IF ray.ly >= 2 / 3  THEN
				ray.ly := ray.ly - 2 / 3
			ELSIF ray.ly >= 1 / 3 THEN
				ray.ly := ray.ly - 1 / 3
			END;
			ray.ly := ray.ly * 3;
			IF ray.lz >= 2 / 3  THEN
				ray.lz := ray.lz - 2 / 3
			ELSIF ray.lz >= 1 / 3 THEN
				ray.lz := ray.lz - 1 / 3
			END;
			ray.lz := ray.lz * 3
		END;
		DEC(d)
	END;
	IF sc > 1 THEN
		ray.r := ray.r + r1 * ray.ra * ray.lx;
		ray.g := ray.g + g1 * ray.ga * ray.ly;
		ray.b := ray.b + b1 * ray.ba * ray.lz;
		ray.ra := 0;
		ray.ga := 0;
		ray.ba := 0;
	ELSE
		ray.r := ray.r + r2 * ray.ra;
		ray.g := ray.g + g2 * ray.ga;
		ray.b := ray.b + b2 * ray.ba;
	END
END Shade;

END SerpVox;
*)

TYPE BiVox* = OBJECT(Voxel);
VAR
	v1, v2: Voxel;

PROCEDURE set*(x,y: Voxel);
BEGIN
	v1 := x;
	v2 := y;
END set;

PROCEDURE probe*(x,y,z: SREAL):Voxel;
VAR
	v: Voxel;
BEGIN
	v := v1.probe(x,y,z);
	v := v2.probe(x,y,z);
	RETURN(SELF);
END probe;

PROCEDURE Shade (VAR ray: Ray);
BEGIN
 v1.Shade(ray);
 v2.Shade(ray);
END Shade;

END BiVox;

(*
TYPE SphBiVox* = OBJECT(BiVox);
VAR
	cx, cy, cz, R2: SREAL;

PROCEDURE&init;
BEGIN
	cx := 1/2; cy := 1/2; cz :=1/2;
	R2 := 0.3;
END init;

PROCEDURE tick*;
BEGIN
	R2 := 1/3 + ((srBase.frame MOD 10)-4)/450;
END tick;

PROCEDURE Shade (VAR ray: Ray);
VAR
	r2: SREAL;
	x,y,z,ax, ay, az, bx, by, bz : SREAL;
	i: INTEGER;
BEGIN
	r2 := (cx-ray.lx)*(cx-ray.lx) + (cy-ray.ly)*(cy-ray.ly) + (cz-ray.lz)*(cz-ray.lz);
	IF r2 < R2 THEN (* ray is within sphere *)
		IF v2 # NIL THEN v2.Shade(ray) END;
		IF ray.a > 1/10 THEN
			ax := ray.lx; ay := ray.ly; az := ray.lz;
			bx := ray.lx + ray.dx; by := ray.ly+ ray.dy; bz := ray.lz+ ray.dz;
			x := (ax+bx)/2; y := (ay+by)/2; z := (az + bz)/2;
			FOR i := 0 TO 12 DO
				r2 := (cx-x)*(cx-x) + (cy-y)*(cy-y) + (cz-z)*(cz-z);;
				IF r2 > R2 THEN
					bx := x; by := y; bz := z
				ELSE
					ax := x; ay := y; az := z
				END;
				x := (ax+bx)/2; y := (ay+by)/2; z := (az + bz)/2;
			END;
			IF ray.a > 1/10 THEN
				ray.lx := x; ray.ly := y; ray.lz := z;
				IF v1 # NIL THEN v1.Shade(ray) END
			END
		END
	ELSE
		IF v1 # NIL THEN v1.Shade(ray) END
	END;
END Shade;

END SphBiVox;
*)
TYPE FuzzyTVox*=OBJECT(AlphaVox);
VAR
	fuzzdivisor, fuzzsubtract: SREAL;
PROCEDURE & init*;
BEGIN
	passable := TRUE;
	fuzzdivisor := 100;
	fuzzsubtract := 0.005
END init;

PROCEDURE setFuzz*(f: SREAL);
BEGIN
	fuzzdivisor := f;
	fuzzsubtract := 1/(2*fuzzdivisor)
END setFuzz;

PROCEDURE Shade*(VAR ray: Ray);
VAR
	dr, dg, db: SREAL;
BEGIN
	ray.xyz.x := ray.xyz.x + srBase.rand.Uniform()/fuzzdivisor-fuzzsubtract;
	ray.xyz.y := ray.xyz.y + srBase.rand.Uniform()/fuzzdivisor-fuzzsubtract;
	ray.xyz.z:= ray.xyz.z + srBase.rand.Uniform()/fuzzdivisor-fuzzsubtract;
	dr :=  r*ray.ra;
	dg :=  g*ray.ga;
	db :=  b*ray.ba;
	ray.r := ray.r + dr;
	ray.g := ray.g + dg;
	ray.b := ray.b + db;
	ray.ra := ray.ra - (dg+db);
	ray.ga := ray.ga - (dr+db);
	ray.ba := ray.ba - (dr+dg);
	ray.a := (ray.ra+ray.ga+ray.ba)/3;
END Shade;

END FuzzyTVox;

PROCEDURE mirror(VAR ray: Ray);
BEGIN
	CASE ray.face OF
		1: 	 ray.dxyz.x:= -ray.dxyz.x;
		|2:	ray.dxyz.y:= -ray.dxyz.y;
		|3:	ray.dxyz.z:= -ray.dxyz.z;
		|4: 	ray.dxyz.x:= -ray.dxyz.x;
		|5:	ray.dxyz.y:= -ray.dxyz.y;
		|6:	ray.dxyz.z:= -ray.dxyz.z;
	ELSE
	END;
	ray.changed := TRUE;
END mirror;

END srVoxel.