435 lines
No EOL
11 KiB
ObjectPascal
435 lines
No EOL
11 KiB
ObjectPascal
{ RAYTRACE.PAS }
|
|
{ Ray-tracing unit, generic. }
|
|
|
|
unit RayTrace;
|
|
|
|
interface
|
|
uses Objects;
|
|
{ Base objects on Turbo Vision's TObject to make them useable on
|
|
Turbo Vision streams. }
|
|
|
|
type
|
|
VECTOR = object
|
|
dx, dy, dz: real; { Three dimensional vector }
|
|
end;
|
|
RAY = object
|
|
dx, dy, dz: real; { Direction vector }
|
|
ox, oy, oz: real; { Origin }
|
|
constructor Init (x, y, z, vx, vy, vz: real);
|
|
end;
|
|
|
|
PObj3d = ^Obj3d;
|
|
PUniverse = ^Universe;
|
|
|
|
Obj3d = object(TObject)
|
|
Owner: PUniverse;
|
|
xp,yp,zp: Real;
|
|
constructor Init(x,y,z: Real);
|
|
function Intersect(aray: RAY): Real; virtual;
|
|
procedure SurfNormal(aray: Ray; time: Real; var oray: Ray); virtual;
|
|
procedure ReflectRay(aray: Ray; time: Real; var oray: Ray); virtual;
|
|
function GetColor(aray: Ray; time: Real): LongInt; virtual;
|
|
end;
|
|
PLANE = object(Obj3d)
|
|
nx, ny, nz: real; { Vector normal (perpendicular) to plane }
|
|
constructor Init(x, y, z, vx, vy, vz: real);
|
|
function Intersect(aray: RAY): real; virtual;
|
|
procedure SurfNormal(aray: Ray; time: Real; var oray: Ray); virtual;
|
|
function GetColor(aray: RAY; time: Real): LongInt; virtual;
|
|
end;
|
|
SPHERE = object(Obj3d)
|
|
ra,r2: real; { Radius squared }
|
|
constructor Init(x, y, z, r: real);
|
|
function Intersect(aray: RAY): real; virtual;
|
|
procedure SurfNormal(aray: Ray; time: Real; var oray: Ray); virtual;
|
|
function GetColor(aray: Ray; time: Real): LongInt; virtual;
|
|
end;
|
|
|
|
Cylinder = object(Obj3d)
|
|
{ xp,yp,zp = center of bottom circle
|
|
x2,y2,z2 = center of top circle
|
|
ra = radius }
|
|
x2,y2,z2,ra,r2: Real;
|
|
constructor Init(x,y,z,xb,yb,zb,r: Real);
|
|
function Intersect(aray: RAY): real; virtual;
|
|
procedure SurfNormal(aray: Ray; time: Real; var oray: Ray); virtual;
|
|
function GetColor(aray: Ray; time: Real): LongInt; virtual;
|
|
end;
|
|
|
|
PlanePnt = object(Plane)
|
|
xb,yb,zb,xc,yc,zc: Real;
|
|
constructor Init(x1,y1,z1,x2,y2,z2,x3,y3,z3: Real);
|
|
function GetColor(aray: Ray; time: Real): LongInt; virtual;
|
|
end;
|
|
|
|
Obj3dCollection = object(TCollection)
|
|
destructor Done; virtual;
|
|
end;
|
|
|
|
Universe = object(TObject)
|
|
Width,Height: Integer;
|
|
Items: Obj3dCollection;
|
|
constructor Init(aWidth,aHeight: Integer);
|
|
destructor Done; virtual;
|
|
procedure Insert(o: PObj3d);
|
|
procedure Delete(o: PObj3d);
|
|
function TraceRay(aray: Ray): LongInt;
|
|
function TracePoint(x,y: Integer): LongInt;
|
|
end;
|
|
|
|
|
|
{ LongInt: $00rrggbb }
|
|
procedure SepLong(c: LongInt; var r,g,b: Integer);
|
|
function GetRgb(r,g,b: Integer): LongInt;
|
|
function GetHsv(h,s,v: Integer): LongInt;
|
|
procedure Hsv2Rgb(h,s,v: Integer; var r,g,b: Integer);
|
|
|
|
implementation
|
|
|
|
procedure SepLong(c: LongInt; var r,g,b: Integer);
|
|
begin
|
|
r := (c and $ff0000) shr 16;
|
|
g := (c and $ff00) shr 8;
|
|
b := c and $ff;
|
|
end;
|
|
|
|
procedure Hsv2Rgb(h,s,v: Integer; var r,g,b: Integer);
|
|
var
|
|
rr,gg,bb,f,p1,p2,p3: Integer;
|
|
begin
|
|
while h > 359 do Dec(h,360);
|
|
while h < 0 do Inc(h,360);
|
|
if s < 0 then s := 0;
|
|
if s > 100 then s := 100;
|
|
if v < 0 then v := 0;
|
|
if v > 100 then v := 100;
|
|
|
|
f := (h mod 60) * 5 div 3;
|
|
h := h div 60;
|
|
p1 := v*(100-s) div 625 * 16;
|
|
p2 := v*(100-(s*f div 100)) div 625 * 16;
|
|
p3 := v*(100-(s*(100-f) div 100)) div 625 * 16;
|
|
v := v * 64 div 25;
|
|
case h of
|
|
0: begin r := v; g := p3; b := p1; end;
|
|
1: begin r := p2; g := v; b := p1; end;
|
|
2: begin r := p1; g := v; b := p3; end;
|
|
3: begin r := p1; g := p2; b := v; end;
|
|
4: begin r := p3; g := p1; b := v; end;
|
|
5: begin r := v; g := p1; b := p2; end;
|
|
end;
|
|
end;
|
|
|
|
function GetHsv(h,s,v: Integer): LongInt;
|
|
var r,g,b: Integer;
|
|
begin
|
|
Hsv2Rgb(h,s,v,r,g,b);
|
|
asm
|
|
mov al,byte ptr [b]
|
|
mov byte ptr [@Result],al
|
|
mov al,byte ptr [g]
|
|
mov byte ptr [@Result+1],al
|
|
mov al,byte ptr [r]
|
|
mov byte ptr [@Result+2],al
|
|
mov byte ptr [@Result+3],0
|
|
end;
|
|
end;
|
|
|
|
function GetRgb(r,g,b: Integer): LongInt;
|
|
begin
|
|
if r > 255 then r := 255;
|
|
if r < 0 then r := 0;
|
|
if g > 255 then g := 255;
|
|
if g < 0 then g := 0;
|
|
if b > 255 then b := 255;
|
|
if b < 0 then b := 0;
|
|
asm
|
|
mov al,byte ptr [b]
|
|
mov byte ptr [@Result],al
|
|
mov al,byte ptr [g]
|
|
mov byte ptr [@Result+1],al
|
|
mov al,byte ptr [r]
|
|
mov byte ptr [@Result+2],al
|
|
mov byte ptr [@Result+3],0
|
|
end;
|
|
end;
|
|
|
|
constructor Obj3d.Init(x,y,z: Real);
|
|
begin
|
|
TObject.Init;
|
|
xp := x; yp := y; zp := z;
|
|
end;
|
|
|
|
function Obj3d.Intersect(aray: RAY): Real;
|
|
begin
|
|
{ 0 or neg = no intersect }
|
|
Intersect := 0;
|
|
end;
|
|
|
|
procedure Obj3d.SurfNormal(aray: Ray; time: Real; var oray: Ray);
|
|
begin
|
|
{ No intersect, will never get called here }
|
|
end;
|
|
|
|
procedure Obj3d.ReflectRay(aray: RAY; time: real; var oray: RAY);
|
|
var
|
|
normal: Ray; { Used for readability }
|
|
ndotn: real; { Used for readability }
|
|
idotn: real; { Used for readability }
|
|
idotn_div_ndotn_x2: real; { Used for optimization }
|
|
begin
|
|
oray.ox := aray.dx * time + aray.ox; { Find the point of }
|
|
oray.oy := aray.dy * time + aray.oy; { intersection between }
|
|
oray.oz := aray.dz * time + aray.oz; { iray and sphere. }
|
|
SurfNormal(aray,time,normal);
|
|
|
|
ndotn := (normal.dx * normal.dx +
|
|
normal.dy * normal.dy +
|
|
normal.dz * normal.dz);
|
|
idotn := (normal.dx * aray.dx +
|
|
normal.dy * aray.dy +
|
|
normal.dz * aray.dz);
|
|
idotn_div_ndotn_x2 := (2.0 * (idotn) / ndotn);
|
|
|
|
oray.dx := aray.dx - idotn_div_ndotn_x2 * normal.dx;
|
|
oray.dy := aray.dy - idotn_div_ndotn_x2 * normal.dy;
|
|
oray.dz := aray.dz - idotn_div_ndotn_x2 * normal.dz;
|
|
end;
|
|
|
|
function Obj3d.GetColor(aray: Ray; time: Real): LongInt;
|
|
begin
|
|
{ See SurfNormal }
|
|
end;
|
|
|
|
|
|
constructor RAY.Init(x, y, z, vx, vy, vz: real);
|
|
begin
|
|
ox := x;
|
|
oy := y;
|
|
oz := z;
|
|
dx := vx;
|
|
dy := vy;
|
|
dz := vz;
|
|
end; { ----- End: RAY::RAY() ----- }
|
|
|
|
constructor SPHERE.Init(x, y, z, r: real);
|
|
begin
|
|
Obj3d.Init(x,y,z);
|
|
ra := r;
|
|
r2 := r * r;
|
|
end; { ----- End: SPHERE::SPHERE() ----- }
|
|
|
|
function SPHERE.Intersect(aray: RAY): real;
|
|
var
|
|
a, b, c, t1, t2, t3, close, farther: real;
|
|
begin
|
|
a := aray.dx * aray.dx + aray.dy * aray.dy + aray.dz * aray.dz;
|
|
close := -1.0;
|
|
farther := -1.0;
|
|
if a <> 0 then
|
|
begin
|
|
b := 2.0 * ((aray.ox - xp) * aray.dx
|
|
+ (aray.oy - yp) * aray.dy
|
|
+ (aray.oz - zp) * aray.dz);
|
|
c := (aray.ox - xp) * (aray.ox - xp)
|
|
+ (aray.oy - yp) * (aray.oy - yp)
|
|
+ (aray.oz - zp) * (aray.oz - zp) - r2;
|
|
t1 := b * b - 4.0 * a * c;
|
|
if t1 > 0 then
|
|
begin
|
|
t2 := sqrt(t1);
|
|
t3 := 2.0 * a;
|
|
close := -(b + t2) / t3;
|
|
farther := -(b - t2) / t3;
|
|
end;
|
|
end;
|
|
if close < farther then Intersect := close else Intersect := farther;
|
|
{ Intersect := (double)((close < farther) ? close : farther);}
|
|
end; { ---- End: SPHERE::Intersect() ----- }
|
|
|
|
procedure Sphere.SurfNormal(aray: Ray; time: Real; var oray: Ray);
|
|
begin
|
|
oray.ox := aray.dx * time + aray.ox; { Find the point of }
|
|
oray.oy := aray.dy * time + aray.oy; { intersection between }
|
|
oray.oz := aray.dz * time + aray.oz; { aray and sphere. }
|
|
oray.dx := oray.ox - xp; { Find the ray normal }
|
|
oray.dy := oray.oy - yp; { to the sphere at the }
|
|
oray.dz := oray.oz - zp; { intersection point. }
|
|
end;
|
|
|
|
function Sphere.GetColor(aray: Ray; time: Real): LongInt;
|
|
var
|
|
r: Ray;
|
|
c: LongInt;
|
|
cc: record b,g,r: Byte end absolute c;
|
|
begin
|
|
ReflectRay(aray,time,r);
|
|
c := Owner^.TraceRay(r);
|
|
cc.r := Integer(cc.r) div 2;
|
|
cc.g := Integer(cc.g) div 2;
|
|
cc.b := Integer(cc.b) div 2 + 128;
|
|
GetColor := c;
|
|
end;
|
|
|
|
constructor PLANE.Init(x, y, z, vx, vy, vz: real);
|
|
begin
|
|
Obj3d.Init(x,y,z);
|
|
nx := vx;
|
|
ny := vy;
|
|
nz := vz;
|
|
end; { ----- End: PLANE::PLANE() ----- }
|
|
|
|
function PlanePattern(x, y: word): LongInt;
|
|
var
|
|
r,g,b: Integer;
|
|
begin
|
|
r := (((x+y) mod 8))*32;
|
|
g := ((x mod 8) xor (y mod 8))*32;
|
|
b := (((x * x + y * y) mod 8))*32;
|
|
PlanePattern := GetRgb(r,g,b);
|
|
end; { ----- End: PlanePattern() ----- }
|
|
|
|
|
|
function PLANE.GetColor(aray: RAY; time: real): LongInt;
|
|
begin
|
|
GetColor := PlanePattern(Round(time * aray.dz + aray.oz),
|
|
Round(time * aray.dx + aray.ox));
|
|
end; { ----- End: PLANE::Pattern() ----- }
|
|
|
|
function PLANE.Intersect(aray: RAY): real;
|
|
var
|
|
p1, p2, p3: real;
|
|
begin
|
|
p1 := xp * nx + yp * ny + zp * nz;
|
|
p2 := aray.ox * nx + aray.oy * ny + aray.oz * nz;
|
|
p3 := aray.dx * nx + aray.dy * ny + aray.dz * nz;
|
|
if p3 = 0 then p3 := 0.001;
|
|
Intersect := (p1-p2)/p3;
|
|
end; { ----- End: PLANE::Intersect() ----- }
|
|
|
|
procedure Plane.SurfNormal(aray: Ray; time: Real; var oray: Ray);
|
|
begin
|
|
oray.dx := nx; oray.dy := ny; oray.dz := nz;
|
|
end;
|
|
|
|
constructor Cylinder.Init(x,y,z,xb,yb,zb,r: Real);
|
|
begin
|
|
Obj3d.Init(x,y,z);
|
|
x2 := xb; y2 := yb; z2 := zb;
|
|
ra := r; r2 := Sqr(r);
|
|
end;
|
|
|
|
function Cylinder.Intersect(aray: RAY): real;
|
|
begin
|
|
Intersect := 0;
|
|
end;
|
|
|
|
procedure Cylinder.SurfNormal(aray: Ray; time: Real; var oray: Ray);
|
|
begin
|
|
end;
|
|
|
|
function Cylinder.GetColor(aray: Ray; time: Real): LongInt;
|
|
begin
|
|
GetColor := $c0c0c0;
|
|
end;
|
|
|
|
constructor PlanePnt.Init(x1,y1,z1,x2,y2,z2,x3,y3,z3: Real);
|
|
var
|
|
a,b,c,d: Real;
|
|
begin
|
|
a := y1*(z2-z3)+y2*(z3-z1)+y3*(z1-z2);
|
|
b := z1*(x2-x3)+z2*(x3-x1)+z3*(x1-x2);
|
|
c := x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2);
|
|
d := -x1*(y2*z3-y3*z2)-x2*(y3*z1-y1*z3)-x3*(y1*z2-y2*z1);
|
|
Plane.Init(x1,y1,z1,a,b,c);
|
|
xb := x2; yb := y2; zb := z2;
|
|
xc := x3; yc := y3; zc := z3;
|
|
end;
|
|
|
|
function PlanePnt.GetColor(aray: Ray; time: Real): LongInt;
|
|
var
|
|
d: Real;
|
|
c: Integer;
|
|
begin
|
|
d := Sqrt(
|
|
Sqr(time * aray.dx + aray.ox - xp) +
|
|
Sqr(time * aray.dy + aray.oy - yp) +
|
|
Sqr(time * aray.dz + aray.oz - zp));
|
|
if d > 255.0 then d := 255.0;
|
|
c := 255-Round(d);
|
|
if c < 96 then c := 96;
|
|
GetColor := GetRgb(c,c,c);
|
|
end;
|
|
|
|
|
|
constructor Universe.Init(aWidth,aHeight: Integer);
|
|
begin
|
|
TObject.Init;
|
|
Width := aWidth; Height := aHeight;
|
|
Items.Init(5,5);
|
|
end;
|
|
|
|
destructor Universe.Done;
|
|
begin
|
|
Items.Done;
|
|
end;
|
|
|
|
procedure Universe.Insert(o: PObj3d);
|
|
begin
|
|
Items.Insert(o);
|
|
o^.Owner := @Self;
|
|
end;
|
|
|
|
procedure Universe.Delete(o: PObj3d);
|
|
begin
|
|
Items.Delete(o);
|
|
o^.Owner := nil;
|
|
end;
|
|
|
|
function Universe.TraceRay(aray: Ray): LongInt;
|
|
type
|
|
arr = array[0..1000] of Real;
|
|
parr = ^arr;
|
|
var
|
|
times: parr;
|
|
i,j: Integer;
|
|
max: Real;
|
|
procedure TraceOne(o: PObj3d); far;
|
|
begin
|
|
times^[i] := o^.Intersect(aray);
|
|
Inc(i);
|
|
end;
|
|
begin
|
|
GetMem(times,Items.Count*SizeOf(Real));
|
|
i := 0;
|
|
Items.ForEach(@TraceOne);
|
|
max := 1.7e38; { darn big }
|
|
j := -1;
|
|
for i := 0 to Items.Count-1 do
|
|
if (times^[i] < max) and (times^[i] > 0.001) then begin
|
|
max := times^[i]; j := i;
|
|
end;
|
|
if j <> -1 then
|
|
TraceRay := PObj3d(Items.At(j))^.GetColor(aray,max)
|
|
else
|
|
TraceRay := 0;
|
|
FreeMem(Times,Items.Count*SizeOf(Real));
|
|
end;
|
|
|
|
function Universe.TracePoint(x,y: Integer): LongInt;
|
|
var
|
|
r: Ray;
|
|
begin
|
|
r.ox := 0; r.oy := 0; r.oz := 0;
|
|
r.dx := (x - (Width / 2)) / Width;
|
|
r.dy := (y - (Height / 2)) / Height * 0.75;
|
|
r.dz := 1;
|
|
TracePoint := TraceRay(r);
|
|
end;
|
|
|
|
destructor Obj3dCollection.Done;
|
|
begin
|
|
TObject.Done;
|
|
end;
|
|
|
|
end. |