unit ObjTrace;

                INTERFACE
Uses UVector,Matrices,GTypes,Objects;
CONST
  MaxDeep   = 2500;
  VisRange  = 100;
  TRAHeader : string [11] = 'Rod''sTracer';
  CurVer    : longint     = $00010001;

{------------- [TRACER] ---------------}
CONST
  MAXOBJS = 3000;
  MAXTEXTURES = 10;
TYPE
  PObjectArray  = ^TObjectArray;
  TObjectArray  = array [1..MAXOBJS] of PObject;
  PTextureArray = ^TTextureArray;
  TTextureArray = array [1..MAXTEXTURES] of PTexture;

  PViewPlane = ^TViewPlane;
  TViewPlane = record
    LLpos : tVec;
    Up    : tVec;
    Right : tVec;
    CamPos : TVec;
  end;

  PTracer = ^TTracer;
  TTracer = object
    ObjectCount         : Word;
    Objects             : PObjectArray;
    LightCount          : Word;
    Lights              : PObjectArray;
    TextureCount       : Word;
    Textures            : PTextureArray;
    Ambient             : RGB;
    View                : TViewPlane;
    Constructor Init;
    Procedure SetPlane(aLLPos,aUp,aRight : tVec);
    Procedure SetCamera(aOrg,aTarg:tVec;aRoll,aFov,aXtoY:float);
    Procedure LoadCamera(var F:FILE);
    Procedure Load(Inp:string);
    Procedure Save(Out:string;aView:Boolean);
    Procedure AddObject(aNew : PColObj);virtual;
    Procedure AddLight(aNew : PLight);virtual;
    Procedure AddTexture(aNew : PTexture);virtual;
    Function  GetObject(Index:word):PColObj;virtual;
    Function  GetLight(Index:word):PLight;virtual;
    Procedure Trace;
    Function FindISect(aRay:TRay;var oT:float):PObject;
    Destructor Done;virtual;
  end;

{--------------------  MODIFICATIONS ----------------}
   Procedure SetRotate(Pivot,Axis:tVec;Ang:float;var ob : PObject);
   Procedure SetTranslate(v:tVec;var ob : PObject);
   Procedure SetScale(Pivot, V : tVec;var ob : PObject);

                IMPLEMENTATION
{TTracer}
    Constructor TTracer.Init;
    begin
      ObjectCount := 0;
      New(Objects);
      FillChar(Objects^,MAXOBJS*sizeof(Pointer),0);
      LightCount := 0;
      New(Lights);
      FillChar(Lights^,MAXOBJS*sizeof(Pointer),0);
      TextureCount := 0;
      New(Textures);
      FillChar(Textures^,MAXTEXTURES*sizeof(Pointer),0);
    end;
    Procedure TTracer.AddObject(aNew : PColObj);
    begin
      If ObjectCount = MaxObjs then
       begin
        Write('Object array overflow! :(');
        exit;
       end;
      Inc(ObjectCount);
      Objects^[ObjectCount] := aNew;
    end;
    Procedure TTracer.AddTexture(aNew : PTexture);
    begin
      If TextureCount = MaxTextures then
       begin
        Write('Texture array overflow! :(');
        exit;
       end;
      Inc(TextureCount);
      Textures^[TextureCount] := aNew;
    end;
    Procedure TTracer.AddLight(aNew : PLight);
    begin
      If LightCount = MaxObjs then
       begin
        Write('Object array overflow! :(');
        exit;
       end;
      Inc(LightCount);
      Lights^[LightCount] := aNew;
    end;
    Function  TTracer.GetObject(Index:word):PColObj;
    begin
     if Index > ObjectCount then GetObject := nil
     else GetObject := PColObj(Objects^[Index]);
    end;
    Function  TTracer.GetLight(Index:word):PLight;
    begin
      if Index > LightCount then getLight := nil
      else GetLight := PLight(Lights^[Index]);
    end;
    Procedure TTracer.Trace;
    begin
    end;
    Procedure   TTracer.SetPlane(aLLPos,aUp,aRight : tVec);
    begin
      with View do begin LLPos := aLLPos; Up:=aUp;Right:=aRight;end;
    end;

    Procedure   TTracer.SetCamera;
    var
      Dist,Ds,Ds2             : tVec;
      D,Len                   : float;
      aSx,aCx,aSy,aCy,aSz,aCz : float;
      Trans                   : TTRANSFORM;

    begin
      VLinear(Dist,aTarg,aOrg,1,-1);
      View.CamPos := aOrg;
      D := sqrt(VDot(Dist,Dist));
      View.LLpos := aTarg;
      if (Dist.X = 0) and (Dist.Z=0) then VSet(View.Right,0,0,1)
      else VSet(View.Right,Dist.Z,0,-Dist.X);

      Compute_Axis_Rotation_Transform(trans,Dist,M_PI_180*aRoll);
      MTransPoint(View.Right,View.Right,Trans);


      VCross(View.Up,Dist,View.Right);
      VNorm(View.Right,D*TAN(PI*aFov/360));
      VNorm(View.Up,D*TAN(PI*aFov/360)/aXtoY);

      VLinear(View.LLpos,View.LLpos,View.up,1,-1);
      VLinear(View.LLpos,View.LLpos,View.Right,1,-1);
      VLinear(View.up,View.up,View.up,1,1);
      VLinear(View.Right,View.Right,View.Right,1,1);

    end;
    Procedure   TTracer.Load(Inp:string);
    var
      Header     : string;
      ID         : ObjID;
      m_PSph     : PSphere;
      m_PPl      : PSurfPlane;
      m_Tri      : PTriangle;
      m_PLight   : POmni;
      m_Quad     : PQuad;
      m_Texture  : PTexture;
      m_PInterS  : PInterS;
      m_Cone     : PCone;
      tName      : string[13];
      Rad,Len    : float;
      NeedAdd    : boolean;
      F          : File;
      Cur        : LongInt;
      Ls         : longint;
      Pivot,Axis : tVec;
      Ang        : float;
    begin
      Assign(F,Inp);
      {$I-}
      Reset(F,1);
      if IOResult <> 0 then
       begin
         WriteLn('Error opening ',Inp);
         Halt(250);
       end;
      BlockRead(F,Header,12);
      if Header <> TRAHeader then
      begin
        WriteLn('Not Our format');
        Halt(240);
      end;
      BlockRead(F,Cur,4);
      if Cur <> curver then
      begin
        WriteLn('Not current version');
        Halt(230);
      end;
      while Not EOF(F) do
       begin
         BlockRead(F,ID,sizeof(ObjID));
         case ID of
          F_ROTATE:
           begin
            BlockRead(F,Ls,sizeof(LongInt));
            BlockRead(F,Pivot,vecsize);
            BlockRead(F,Axis,vecsize);
            BlockRead(F,Ang,sizeof(float));
            Ls := ObjectCount+1 - Ls;
            if Ls>=0 then for Cur := ObjectCount downto Ls do
              SetRotate(Pivot,Axis,Ang,Objects^[Cur]);
           end;
          F_TRANSLATE:
           begin
            BlockRead(F,Ls,sizeof(LongInt));
            BlockRead(F,Pivot,vecsize);
            Ls := ObjectCount+1 - Ls;
            if Ls>=0 then for Cur := ObjectCount downto Ls do
              SetTranslate(Pivot,Objects^[Cur]);
           end;
          F_SCALE:
           begin
            BlockRead(F,Ls,sizeof(LongInt));
            BlockRead(F,Pivot,vecsize);
            BlockRead(F,Axis,vecsize);
            Ls := ObjectCount+1 - Ls;
            if Ls>=0 then for Cur := ObjectCount downto Ls do
              SetScale(Pivot,Axis,Objects^[Cur]);
           end;
          F_TEXTURE:
           begin
             BlockRead(F,Ls,sizeof(LongInt));
             BlockRead(F,TName,13);
             BlockRead(F,rad,sizeof(float));
             BlockRead(F,Len,sizeof(float));
             for cur := 1 to TextureCount do
              if Textures^[cur]^.Name = TName then break;
             if  Textures^[cur]^.Name = TName then m_Texture :=Textures^[cur]
             else
              begin
                New(m_Texture,Init(1,1));
                m_Texture^.LoadTGA(TName);
                AddTexture(m_Texture);
              end;
             Ls := ObjectCount+1 - Ls;
             if Ls>=0 then for Cur := ObjectCount downto Ls do
              PSurfObj(Objects^[Cur])^.SetTexture(m_Texture,rad,len);
           end;
          F_INTER_SPHERE:
           begin
            New(m_PInterS,Load(F));
            AddObject(m_PInterS);
           end;
          F_CONE:
           begin
            New(m_Cone,Load(F));
            AddObject(m_Cone);
           end;
          F_OBJ_SPHERE:
           begin
            New(m_PSph,Load(F));
            AddObject(m_PSph);
           end;
          F_OBJ_SURF_PLANE:
           begin
            New(m_PPl,Load(F));
            AddObject(m_PPl);
           end;
          F_TRI_PLANE:
           begin
            New(m_PPl,TriLoad(F));
            AddObject(m_PPl);
           end;
          F_TRI:
           begin
            New(m_Tri,Load(F));
            AddObject(m_Tri);
           end;
          F_QUAD:
           begin
            New(m_Quad,Load(F));
            AddObject(m_Quad);
           end;
          F_LIGHT_OMNI:
           begin
            New(m_PLight,Load(F));
            AddLight(m_PLight);
           end;
          F_VIEWPORT:
           begin
             BlockRead(F,View.CamPos,vecsize);
             BlockRead(F,View.LLPos,vecsize);
             BlockRead(F,View.Up,vecsize);
             BlockRead(F,View.Right,vecsize);
           end;
          F_AMBIENT: LoadRGB(F,Ambient);
          F_CAMERA : LoadCamera(F);
         end;
       end;
       Close(F);
    end;
    Procedure   TTracer.LoadCamera;
    var
     org,targ : tVec;
     roll, fov, xtoy : float;
    begin
      BlockRead(F,org,vecsize);
      BlockRead(F,targ,vecsize);
      BlockRead(F,roll,sizeof(float));
      BlockRead(F,fov,sizeof(float));
      BlockRead(F,xtoy,sizeof(float));
      SetCamera(org,targ,roll,fov, xtoy);
    end;
    Procedure   TTracer.Save;
    var
     F:FILE;
     i:word;
    begin
      Assign(F,Out);
      Rewrite(F,1);
      BlockWrite(F,TRAHeader,12);
      BlockWrite(F,CurVer,sizeof(LongInt));
      if aView then
        begin
          I:=Ord(F_VIEWPORT);BlockWrite(F,I,1);
          BlockWrite(F,View.CamPos,vecsize);
          BlockWrite(F,View.LLPos,vecsize);
          BlockWrite(F,View.Up,vecsize);
          BlockWrite(F,View.Right,vecsize);
        end;
      I:=Ord(F_AMBIENT);BlockWrite(F,I,1);SaveRGB(F,Ambient);
      for i := 1 to ObjectCount do if Objects^[i] <> nil then
         if Pos('_FILE_',Objects^[i]^.OType) <> 0 then
           PFileObj(Objects^[i])^.Save(F);
      for i := 1 to LightCount do if Lights^[i] <> nil then
       begin
         if Pos('_FILE_',Lights^[i]^.OType) <> 0 then
           PLight(Lights^[i])^.Save(F);
       end;
      Close(F);
    end;
    Function    TTracer.FindISect(aRay:TRay;var oT:float):PObject;
    var
       Cur : float;
       i : integer;
    begin
      FindISect:=nil;
      oT:=INF;
      for i := 1 to ObjectCount do
       begin
         Cur := Objects^[i]^.ISect(@aRay);
         if (Cur < oT) and (Cur > EPS) and (Cur < MaxDeep) then
          begin
            FindISect := Objects^[i];
            oT := Cur;
          end;
       end;
    end;{TGATrace.FindISect}
    Destructor TTracer.Done;
    var
     i : word;
    begin
      for i := 1 to ObjectCount do Dispose(Objects^[i],Done);
      for i := 1 to LightCount do Dispose(Lights^[i],Done);
      for i := 1 to TextureCount do Dispose(Textures^[i],Done);
      Dispose(Objects);
      Dispose(Lights);
      Dispose(Textures);
    end;
{----------------------------------------------------}
Procedure SetRotate(Pivot,Axis:tVec;Ang:float;var ob : PObject);
var
  p_inv : tVec;
  TR,tr2 : TTRANSFORM;
begin
  if ob = nil then exit;
  if Pos('_SURF_',ob^.OType) = 0 then exit;

  VLinear(p_inv,pivot,pivot,0,-1);
  Compute_Translation_Transform(TR,p_inv);

  Compute_Axis_Rotation_Transform(TR2,axis,ang*M_PI_180);
  Compose_Transforms(tr,tr2);

  Compute_Translation_Transform(TR2,pivot);
  Compose_Transforms(tr,tr2);

  PSurfObj(ob)^.Transform(tr);
end;{LoadRotate}
{----------------------------------------------------}
Procedure SetTranslate(v:tVec;var ob : PObject);
var
  TR   : TTRANSFORM;
begin
  if ob = nil then exit;
  if Pos('_SURF_',ob^.OType) = 0 then exit;
  Compute_Translation_Transform(TR,v);
  PSurfObj(ob)^.Transform(tr);
end;{LoadTranslate}
{----------------------------------------------------}
Procedure SetScale(Pivot, V : tVec;var ob : PObject);
var
  p_inv : tVec;
  TR,tr2  : TTRANSFORM;

begin
  if ob = nil then exit;
  if Pos('_SURF_',ob^.OType) = 0 then exit;

  VLinear(p_inv,pivot,pivot,0,-1);

  Compute_Translation_Transform(TR,p_inv);
  Compute_Scaling_Transform(TR2,v);
  Compose_Transforms(tr,tr2);

  Compute_Translation_Transform(TR2,pivot);
  Compose_Transforms(tr,tr2);

  PSurfObj(ob)^.Transform(tr);
end;{LoadScale}
{----------------------------------------------------}

END.