(*
    DEMO03 - Simple star animation, morphs between a cube and a sphere
    (c) 1994 by Alessandro Scotti
*)
uses Crt, Modex, Threed;

const
  MAXVTX = 1000;        (* Number of points *)
  EDGE = 70;            (* Length of cube edge *)
  RADIUS = 90;          (* Radius of sphere *)
  WAITCOUNT = 192;      (* Frames to wait for non-morphing shapes *)
  MS = 32;              (* Number of steps for morphing *)
  Trans : TPoint = ( X:0; Y:0; Z:0 );
  InitMorph1: array[ 0..3 ] of integer = ( 0, MS, 0, 0 );
  InitMorph2: array[ 0..3 ] of integer = ( 0, 0, 0, MS );
  InitDelta1: array[ 0..3 ] of integer = ( 0, -1, 0, +1 );
  InitDelta2: array[ 0..3 ] of integer = ( 0, +1, 0, -1 );
type
  T2DPoint = record
    X, Y: integer;
  end;
  T3DPointArray = array[ 0..MAXVTX-1 ] of TPoint;
  P3DPointArray = ^T3DPointArray;
var
  CubeVtx, SphereVtx, Vtx, XVtx: P3DPointArray;
  VVtx  : array[ 0..MAXVTX-1 ] of T2DPoint;
  Page  : word;
  Status, Count, Delta1, Delta2, Morph1, Morph2: integer;

procedure Swap( var A, B: longint );
var
  L: longint;
begin
  L := A; A := B; B := L;
end;

function Toggle( A: longint ): longint;
begin
  Toggle := A;
  if( Random(2) = 0 ) then Toggle := -A;
end;

procedure Init;
label Retry;
var
  I: integer;
begin
  New( CubeVtx );
  New( SphereVtx );
  New( Vtx );
  New( XVtx );
  (* Build vertexes (yes, I know this piece of code is terrible) *)
  Randomize;
  for I:=0 to MAXVTX-1 do begin
    with CubeVtx^[I] do begin
      (* Build cube *)
      X := (longint(Random(2*EDGE))-EDGE)*$10000;
      Y := (longint(Random(2*EDGE))-EDGE)*$10000;
      Z := Toggle( EDGE*$10000 );
      case Random(3) of
        0: Swap( X, Z );
        1: Swap( Y, Z );
      end;
    end;
    with SphereVtx^[I] do begin
      (* Build sphere *)
Retry:
      X := (longint(Random(2*RADIUS))-RADIUS);
      Y := (longint(Random(2*RADIUS))-RADIUS);
      if( X*X+Y*Y > RADIUS*RADIUS ) then goto Retry;
      Z := Toggle( Round( Sqrt( Abs( RADIUS*RADIUS-X*X-Y*Y ) ) ) );
      case Random(3) of
        0: Swap( X, Z );
        1: Swap( Y, Z );
      end;
      X := X * $10000; Y := Y * $10000; Z := Z * $10000;
    end;
  end;
  (* Initialize morphing *)
  Move( CubeVtx^, Vtx^, SizeOf(Vtx^) );
  Status := 0;
  Count := WAITCOUNT;
end;

procedure Morph;
var
  I: integer;
begin
  (* Fully unoptimized, slowest loop I could think of! *)
  for I:=0 to MAXVTX-1 do begin
    Vtx^[I].X := ((CubeVtx^[I].X * Morph1)+(SphereVtx^[I].X * Morph2)) div MS;
    Vtx^[I].Y := ((CubeVtx^[I].Y * Morph1)+(SphereVtx^[I].Y * Morph2)) div MS;
    Vtx^[I].Z := ((CubeVtx^[I].Z * Morph1)+(SphereVtx^[I].Z * Morph2)) div MS;
  end;
end;

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

  AX := 0;
  AY := 0;
  AZ := 0;
  (* Init 3D transforms, perspective is intentionally exaggerated *)
  tdSetTranslation( Trans );
  tdSetPerspective( 400*$10000, $10000, $10000 );
  C := #0;
  repeat
    tdSetRotation( AX, AY, AZ );                (* Set new angles *)
    tdTransform( Vtx^, XVtx^, MAXVTX );         (* 3D transform points *)
    tdTransformToImage( XVtx^, VVtx, MAXVTX, 160, 120+Page );
    Inc( AX, 1 );                               (* Bump angles *)
    Inc( AY, 1 );
    Inc( AZ, 2 );
    mxSetClipRegion( 0, Page, 320, 240 );       (* Set clip to new page *)
    mxSetClip( TRUE );
    mxFillBox( 0, Page, 320, 240, 0, OP_MOVE ); (* Clear screen *)
    (* Draw points *)
    for I:=0 to MAXVTX-1 do
      mxPutPixel( VVtx[I].X, VVtx[I].Y, 128 + XVtx^[I].Z shr 18 );
    mxStartLine( Page );                        (* Flip pages *)
    Page := 240-Page;
    (* Morph *)
    if( Odd(Status) ) then begin
      Morph;
      Inc( Morph1, Delta1 );
      Inc( Morph2, Delta2 );
      if( Morph1 < 0 )or( Morph2 < 0 ) then Inc( Status );
      if( Status = 4 ) then Status := 0;
    end
    else begin
      Dec( Count );
      if( Count < 0 ) then begin
        Inc( Status );
        Count :=  WAITCOUNT;
        Morph1 := InitMorph1[Status];
        Morph2 := InitMorph2[Status];
        Delta1 := InitDelta1[Status];
        Delta2 := InitDelta2[Status];
      end;
    end;
  until( KeyPressed );

  mxSetMode( MX_TEXT );
  mxTerm;
end.
