(*
    DEMO04 - Multiple textures and triple buffering (3 pages)
    (c) 1994 by Alessandro Scotti
*)
uses Crt, Modex, Threed;

const
  MAXVTX = 256;
  MAXCUB = 2;
  MAXTXT = 2;
  Trans : TPoint = ( X:0; Y:0; Z:0 );
  TxtSunDial: array[ 0..7 ] of word = (
    $7F80,$0080, $0080,$0080, $0080,$7E80, $7F80,$7E80 );
  TxtSapphire : array[ 0..7 ] of word = (
    $0080,$0080, $0080,$1F80, $1F80,$1F80, $1F80,$0080 );
  TxtMarble: array[ 0..7 ] of word = (
    $0080,$8080, $0080,$FD80, $7F80,$FD80, $7F80,$8080 );
type
  T2DPoint = record
    X, Y: integer;
  end;
  TTexture = record
    Desc   : array[ 0..3 ] of record X, Y: word end;
    Width  : word;
    Data   : pointer;
  end;
  TQuad = record
    Vtx    : array[ 0..3 ] of word;
    Texture: word;
  end;
  TCube    = record
    Face   : array[ 0..5 ] of TQuad;
    Base   : integer;
  end;
var
  Vtx, XVtx: array[ 0..MAXVTX ] of TPoint;
  VVtx     : array[ 0..MAXVTX ] of T2DPoint;
  Cube     : array[ 0..MAXCUB ] of TCube;
  ZList    : array[ 0..MAXCUB ] of integer;
  VtxCnt   : word;
  Txts     : array[ 0..MAXTXT ] of TTexture;
  Page     : word;
  Palette  : array[ byte ] of record R, G, B: byte; end;
  TxtDat1, TxtDat2: pointer;

(* Add a new entry to the vertex array *)
procedure AddVtx( PX, PY, PZ: longint );
begin
  with Vtx[VtxCnt] do begin X:=PX*$10000; Y:=PY*$10000; Z:=PZ*$10000; end;
  Inc( VtxCnt );
end;

procedure MakeCube( var C: TCube; X1,Y1,Z1, X2,Y2,Z2, TX,TY,TZ, Texture: integer );
const
  FaceIdx: array[ 0..23 ] of integer = (
    0,1,2,3, 0,4,5,1, 1,5,6,2, 2,6,7,3, 3,7,4,0, 6,5,4,7 );
var
  I, VC: integer;
begin
  VC := VtxCnt;
  C.Base := VC;
  AddVtx( X1+TX, Y1+TY, Z1+TZ );
  AddVtx( X2+TX, Y1+TY, Z1+TZ );
  AddVtx( X2+TX, Y2+TY, Z1+TZ );
  AddVtx( X1+TX, Y2+TY, Z1+TZ );
  AddVtx( X1+TX, Y1+TY, Z2+TZ );
  AddVtx( X2+TX, Y1+TY, Z2+TZ );
  AddVtx( X2+TX, Y2+TY, Z2+TZ );
  AddVtx( X1+TX, Y2+TY, Z2+TZ );
  for I:=0 to 23 do C.Face[I shr 2].Vtx[I and 3] := VC+FaceIdx[I];
  for I:=0 to 5 do C.Face[I].Texture := Texture;
end;

procedure MakeTexture( Idx: integer; var VtxData );
var
  P: ^word;
  I: integer;
begin
  P := @VtxData;
  with Txts[Idx] do begin
    for I:=0 to 3 do begin
      Desc[I].X := P^; Inc( P );
      Desc[I].Y := P^; Inc( P );
    end;
    Width := 129;
    Data := TxtDat1;
  end;
end;

procedure Init;
var
  I: integer;
  V: integer;
  F: file;
  P: array[ 1..768 ] of byte;
begin
  (* Initialize objects *)
  VtxCnt := 0;
  MakeCube( Cube[0], -64,-64,8, 64,64,-8, 0,0,0, 1 );   (* Sundial *)
  Cube[0].Face[0].Texture := 0;
  V := VtxCnt;
  MakeCube( Cube[1], -16,-16,16, 16,16,-16, 0,0,0, 2 ); (* Sapphire *)
  tdSetTranslation( Trans );
  tdSetRotation( 32, 32, 00 );
  tdRotate( Vtx[V], XVtx[V], 8 );       (* Got to rotate this cube *)
  for I:=V to V+7 do begin
    Vtx[I].X := XVtx[I].X;
    Vtx[I].Y := XVtx[I].Y;
    Vtx[I].Z := XVtx[I].Z + 100*$10000;
  end;
  MakeCube( Cube[2], -64,-4,48, 64,4,-48, 0,68,56, 1 ); (* Marble *)
  (* Load texture and palette *)
  Assign( F, 'DEMO04.DAT' );
  Reset( F, 1 );
  BlockRead( F, P, SizeOf(P) );
  mxSetPalette( @P, 0, 256 );
  GetMem( TxtDat1, 63*1024 );
  BlockRead( F, TxtDat1^, 129*286 );
  Close( F );
  TxtDat2 := Ptr( Seg(TxtDat1^), Ofs(TxtDat1^)+129*254 );
  (* Init textures *)
  MakeTexture( 0, TxtSundial );
  MakeTexture( 1, TxtMarble );
  MakeTexture( 2, TxtSapphire );
  Txts[2].Data := TxtDat2;
end;

(* Sort procedure, not worth optimizing with only a few objects *)
procedure SortObjects;
var
  I, J, K: integer;
  ZMax: array[ 0..MAXCUB ] of longint;
  ZI: integer;
  L: longint;
begin
  for I:=0 to MAXCUB do begin
    L := XVtx[Cube[I].Base].Z;
    for J:=1 to 7 do
      if( L > XVtx[Cube[I].Base+J].Z ) then L := XVtx[Cube[I].Base+J].Z;
    ZMax[I] := L;
    ZList[I] := I;
  end;
  for I:=0 to MAXCUB-1 do begin
    ZI := I;
    for J:=I+1 to MAXCUB do
      if( ZMax[ZList[J]] > ZMax[ZList[ZI]] ) then ZI := J;
    if( ZI <> I ) then begin
      K := ZList[I];
      ZList[I] := ZList[ZI];
      ZList[ZI] := K;
    end;
  end;
end;

var
  AX, AY, AZ: byte;
  I, J, K: word;
begin
  mxInit;
  mxSetMode( MX_320x240 );
  Init;
  Page := 240;          (* Start with hidden page *)

  (* Init 3D transforms, perspective is intentionally exaggerated *)
  AX := 0; AY := 0; AZ := 0;
  tdSetTranslation( Trans );
  tdSetPerspective( 600*$10000, $10000, $10000 );
  (* Main loop, all magic here! *)
  while( not KeyPressed ) do begin
    tdSetRotation( AX, AY, AZ );                (* Set new angles *)
    tdTransform( Vtx, XVtx, VtxCnt );           (* 3D transform points *)
    tdTransformToImage( XVtx, VVtx, VtxCnt, 160, 120+Page );
    Inc( AX, 1 );                               (* Bump angles *)
    Inc( AY, 2 );
    Inc( AZ, 1 );
    mxSetClipRegion( 0, Page, 320, 240 );       (* Set clip to new page *)
    mxSetClip( TRUE );
    mxFillBox( 0, Page, 320, 240, 0, OP_MOVE ); (* Clear screen *)
    (* Draw objects *)
    SortObjects;
    for I:=0 to MAXCUB do with Cube[ZList[I]] do begin
      for J:=0 to 5 do begin
        K := Face[J].Texture;
        mxTexturePoly( 4, Face[J].Vtx, VVtx, Txts[K].Desc, Txts[K].Data^, Txts[K].Width );
      end;
    end;
    (* Flip page: at 320x240 the Start Address Register Low is always zero *)
    case Page of
      0  : begin PortW[$3D4] := $000C; Page := 240; end;
      240: begin PortW[$3D4] := $4B0C; Page := 480; end;
      480: begin PortW[$3D4] := $960C; Page := 0; end;
    end;
    mxWaitRetrace; (* If the frame rate seems low, try to remove this line *)
  end;

  mxSetMode( MX_TEXT );
  mxTerm;
end.
