unit camera;

{$I init.inc}

{*Unit Camera*************************************************************

 The Camera procedures, incl screen, trace and shade

 Created            : 11/02/93
 Last Change        : 13/05/93
 Revisions          : none

 *************************************************************************}

interface

uses globals, raytree, lights, crt;

var
  v_from,
  v_at,
  v_up : Vec;
  v_aspect,
  v_angle : Flt;
  BackgroundColor : Vec;

procedure Render;
procedure Trace(level : byte; weight : Flt; var Ray : TRay; var Color : Col);
procedure Shade(level : byte; weight : Flt; P,N,I : Vec; Hit : PHitlist; var Color : Col);


var
  Frustrumheight,
  Frustrumwidth    : Flt;
  Jitter : Boolean;

implementation

{$I vectors.inc }

procedure Render;

var
 upvec,
 leftvec,
 tempvec,
 viewvec       : vec;
 Ray           : TRay;
 color         : col;


procedure Statistics(y : word);

begin
  clrscr;
  writeln('Rendering: ',infilename);
  writeln;
  writeln('Traced line: ',y);
  writeln;
  writeln('Total number of rays           ',stats.nrays:10:0);
  writeln('      number of eye rays       ',stats.neyerays:10:0);
  writeln('      number of reflected rays ',stats.nreflectedrays:10:0);
  writeln;
  writeln('Total number of shadow tests   ',stats.nshadowrays:10:0);
  writeln;
end;

{ *** No Antialiasing, one ray through the center of each pixel *** }
Procedure Scan_A_None;

var
  x, y : Flt;
  xcnt, ycnt : word;
  dir : vec;

begin

  for ycnt := 0 to (Image^.yres-1) do
  begin
    for xcnt := 0 to (Image^.xres-1) do
    begin

    if (jitter) then
      begin
        x := xcnt + random(1);
        y := ycnt + random(1);
      end
    else
      begin
        x := xcnt + 0.5;
        y := ycnt + 0.5;
      end;

    {$IFDEF Debug}
       writeln(con, 'Pixel ',xcnt,' ',ycnt,' x y ',x:5:5,' ',y:5:5);
       writeln(con);
    {$ENDIF}

    { *** aim at (x,y) *** }

    VecComb((-frustrumheight*((2.0*y/Image^.yres) - 1.0)),
            upvec,
            (frustrumwidth*((2.0*x/Image^.xres) - 1.0)),
            leftvec,
            dir);
    VecAdd(dir, viewvec, ray.D);
    VecUnit(ray.D);

    { *** Fire! *** }

    stats.neyerays := stats.neyerays + 1;

    Trace(0, 1.0, ray, color);

    if color[0]>1.0 then color[0] := 1.0;
    if color[1]>1.0 then color[1] := 1.0;
    if color[2]>1.0 then color[2] := 1.0;

    { *** Store Color in buffer *** Note That rgb is stored bgr *** }
    Image^.buffer[xcnt][0] := Round(255.0 * color[2]);
    Image^.buffer[xcnt][1] := Round(255.0 * color[1]);
    Image^.buffer[xcnt][2] := Round(255.0 * color[0]);

  end;

  Image^.Writeline;

  if tickflag then statistics(ycnt);

  end;

end; { *** Scan_A_None *** }

Procedure Scan_A_Adaptive;
begin
end;

procedure Scan_A_Quick;
begin
end;

begin
  {*Camera vectors calculations*****************************************}

  VecCopy(v_up, upvec);
  VecUnit(upvec);

  VecSub(v_at, v_from, viewvec);
  VecUnit(viewvec);

  VecCross(upvec, viewvec, leftvec);
  VecUnit(leftvec);
  VecScale(-1, leftvec);

  VecCross(leftvec, viewvec, upvec);
  VecUnit(upvec);

  VecCopy(v_from, Ray.P);

  frustrumwidth := sin(v_angle)/cos(v_angle);
  frustrumheight := sin(v_angle/v_aspect)/cos(v_angle/v_aspect);

  {*Generate the image!********************************************}

  case antialias of
     A_NONE: Scan_A_None;
     A_ADAPTIVE: Scan_A_Adaptive;
  end;

  {*Display Final Stats********************************************}

  FinalStatistics; {unit globals}
  
end;

procedure Trace;

var
  Hit   : PHitlist;
  P,N   : Vec;
  heap  : pointer;

begin

  if (level >= maxlevel) then
  begin
     {$IFDEF Debug}
       writeln(con, 'level >= maxlevel');
       writeln(con, 'level = ',level);
       writeln(con);
     {$ENDIF}

     color[0] := 0.0;
     color[1] := 0.0;
     color[2] := 0.0;
     exit;
  end;

  stats.nrays := stats.nrays + 1;

  {$IFDEF Debug}
    Writeln(con, 'Intersecting Ray');
    PrintVec('P ',Ray.P);
    PrintVec('D ',Ray.D);
    Writeln(con);
  {$ENDIF}

  mark(heap);

  new(hit,init);

  if Tree.Intersect(ray, hit) <> 0 then
  begin
    RayPoint(Ray,Hit^.next^.t,P);
    hit^.next^.Prim^.normal(P,N);     { calculate normal }

    if (VecDot(ray.D,N) >= 0.0) then   { We may have to ...         }
       VecNegate(N);                   {       ... flip the normal  }

    Shade(level, weight, P, N, ray.D, hit, color);
  end
  else
  begin
    {$IFDEF Debug}
       Writeln(con, 'No Intersections found -> Background Color');
       Writeln(con);
    {$ENDIF}
    VecCopy(Backgroundcolor, color);
  end;

  release(heap);

end;


procedure Shade;

var
  L       : vec;
  ray     : Tray;
  tempclr,
  clr     : col;
  t,
  diff    : Flt;
  k_spec  : Vec;
  max_k_spec : Flt;

function max(a,b : Flt) : Flt;
begin
  if a > b then max := a else max := b;
end;

procedure SpecularDirection(I,N : Vec; var R : vec);

begin

  VecAddS(-2.0*VecDot(I,N),N,I,R);

  {$IFDEF Debug}
    Writeln(con, 'Specular Direction');
    PrintVec('I (incident ray) ',I);
    PrintVec('N (normal)       ',N);
    PrintVec('R (specular dir) ',R);
    Writeln(con);
  {$ENDIF}

end;

begin { Shade }

  {$IFDEF Debug}
     Writeln(con, 'Shading');
     PrintVec('Point        ',P);
     PrintVec('Incident ray ',I);
     PrintVec('Normal       ',N);
     writeln(con);
  {$ENDIF}

  clr[0]:=0;
  clr[1]:=0;
  clr[2]:=0;

  { *** Diffuse and specular reflection of light sources *** }

  LightTree.Shade(P,N,I,Hit,Clr);

  { *** Specular reflection from other bodies *** }

  If (level+1 < maxlevel) then

  begin

    VecCopy(P, ray.P);
    VecCopy(hit^.next^.prim^.surf^.kspec, K_Spec);

    max_k_spec := max(max(k_spec[0], k_spec[1]),k_spec[2]);
    if (max_k_spec * weight > minweight) then
    begin
      stats.nreflectedrays := stats.nreflectedrays + 1;
      SpecularDirection(I,N,ray.D);
      VecUnit(ray.D);
      Trace(level+1,max_k_spec * weight,ray,tempclr);
      clr[0] := clr[0] + (tempclr[0] * k_spec[0]);
      clr[1] := clr[1] + (tempclr[1] * k_spec[1]);
      clr[2] := clr[2] + (tempclr[2] * k_spec[2]);
    end;
  end;

  { *** Ambient lighting *** }

  VecAdd(clr,hit^.next^.prim^.surf^.kamb,color);

end; { Shade }

end.
