unit Lights;

{$I init.inc }

{*Lights******************************************************************

 Light sources... :-)

 Created            : 20/02/93
 Last Change        : 03/05/93
 Revisions          : none

 *************************************************************************}

interface

uses globals, raytree;

type
  PLight = ^TLight;
  TLight = Object
    next : PLight;
    constructor Init;
    function Shadow(Ray : TRay; tmax : Flt) : boolean; Virtual;
    procedure Shade(P,N,I : Vec; Hit : PHitlist; var Clr : Vec); virtual;
    procedure Add(Item : PLight);
    Destructor Done; Virtual;
  end;

  PPointLight = ^TPointLight;
  TPointLight = Object(TLight)
    lightpos,
    Color      : Vec;
    constructor Init(pos, clr : vec);
    function Shadow(Ray : Tray; tmax : Flt) : boolean; Virtual;
    procedure Shade(P,N,I : Vec; Hit : PHitlist; var Clr : Vec); virtual;
    destructor Done; Virtual;
  end;

var
  LightTree : TLight;

implementation

{$I vectors.inc }

constructor TLight.Init;

begin
  next := nil;
end;

procedure TLight.Add;
begin
  item^.next := next;
  next := item;
end;

procedure TLight.Shade;
var
  dummy : PLight;

begin
  dummy := next;
  while dummy <> nil do
    begin
      dummy^.Shade(P,N,I,Hit,Clr);
      dummy := dummy^.next;
    end;
end;

function TLight.Shadow;
begin

end;

destructor TLight.Done;
begin
end;

constructor TPointLight.Init;
begin
  TLight.Init;
  lightpos := pos;
  color := clr;
end;

procedure TPointLight.Shade;

var
  R, L : Vec;
  diff : vec;
  spec : flt;
  tmax : Flt;
  ray : TRay;
  VecDot_N_L : Flt;

function pow(G, E : Flt) : Flt;
begin
  Pow := exp(E*ln(G));  { a^b = e^ln(a^b) = e^(b*ln(a)) }
end;

begin
  VecSub(lightpos,P,L);
  VecDot_N_L := VecDot(N,L);

  {$IFDEF DebugShader}
    writeln(con, 'Calculating Diffuse reflection from PointLight ');
    PrintVec('Lightpos ',lightpos);
    PrintVec('Point    ',P);
    writeln(con);
  {$ENDIF}

  if (VecDot_N_L >= 0.0) then
    begin
      tmax := VecLen(L);
      VecUnit(L);
      VecCopy(P, ray.P);
      VecCopy(L, ray.D);

      stats.nshadowrays := stats.nshadowrays + 1;

      if not Shadow(ray,tmax) then
      begin

       diff[0] := VecDot_N_L * hit^.next^.Prim^.surf^.kdiff[0] * Color[0];
       diff[1] := VecDot_N_L * hit^.next^.Prim^.surf^.kdiff[1] * Color[1];
       diff[2] := VecDot_N_L * hit^.next^.Prim^.surf^.kdiff[2] * Color[2];

       VecAdd(Clr, diff, Clr);

       {$IFDEF DebugShader}
          PrintVec(con, 'Found Diffuse ',diff);
          writeln(con);
       {$ENDIF}

       { *** Specular Highlight *** }

       if (hit^.next^.prim^.surf^.shine > rayeps) then
       begin
         VecAddS(-2.0*VecDot(I,N),N,I,R);
         VecUnit(R);
         spec := VecDot(R,L);
         if spec>rayeps then
         begin
           spec := pow(spec,hit^.next^.prim^.surf^.shine);
           clr[0] := clr[0] + spec;
           clr[1] := clr[1] + spec;
           clr[2] := clr[2] + spec;
         end;
       end;
     end;
  end;
end;

function TPointLight.Shadow;

var
  Hit : PHitlist;

begin
  {$IFDEF DebugShader}
    Writeln(con, 'Shadow Feeler ');
    PrintVec('P ',Ray.P);
    PrintVec('D ',Ray.D);
    Writeln(con, 'tmax ', tmax);
    Writeln(con);
  {$ENDIF}

  If (Tree.Intersect(Ray, hit) = 0) OR (Hit^.next^.t > (tmax - rayeps) )
     then Shadow := false
     else Shadow := true;
end;

destructor TPointLight.Done;
begin
end;

end.
