Program FillTriangleProgram;

{ simple program for texture-mapping }
{ programmed by Christian Fleischer }
{ If you use this programm or inportant }
{ parts of it in your own program }
{ please give me credits somewhere and email me! }
{ email: daydream@cs.tu-berlin.de }

Uses
  Crt;

const
  Rund=256;
  Zoom=100;
  ClipZ=10;
  LenX:LongInt=100;
  LenY:LongInt=80;

type
  Pun2DInt=Record x,y:LongInt; end;
  Pun3DInt=Record x,y,z:LongInt; end;
  Triangle3D=Array[0..2] of Pun3DInt;
  Polygon2D=Array[0..3] of Pun2DInt;

Var
  Scr:Array[-99..100,-159..160] of Byte absolute $A000:0;
  Tri3D:Triangle3D;
  BMP:Array[0..127,0..127] of Byte;

Procedure Project(P1,P2:Pun3DInt; Var Poly:Polygon2D; Var LastPt:integer);
  begin   { projiziert einen Punkt auf die Bildschirm-Ebene }
    if P1.z>=ClipZ then
      begin  { Startpunkt der Linie liegt vor der ClipZ-Ebene }
        Inc(LastPt);
        Poly[LastPt].x:=(P1.x*Zoom) div P1.z;
        Poly[LastPt].y:=(P1.y*Zoom) div P1.z;
      end;
    if ((P2.z<ClipZ) and (P1.z>=ClipZ)) or ((P1.z<ClipZ) and (P2.z>=ClipZ)) then
      begin  { einer der beiden Punkte liegt hinter der ClipZ-Ebene }
        Inc(LastPt);
        Poly[LastPt].x:=(P2.x+(ClipZ-P2.z)*(P1.x-P2.x) div (P1.z-P2.z))*Zoom div ClipZ;
        Poly[LastPt].y:=(P2.y+(ClipZ-P2.z)*(P1.y-P2.y) div (P1.z-P2.z))*Zoom div ClipZ;
      end;
  end;

Procedure Swap(Var a,b:LongInt);
Var
  c:LongInt;
  begin
    c:=a;
    a:=b;
    b:=c;
  end;

Procedure FillTriangle(Tri3D:Triangle3D);
Var   { texturiert ein Dreieck }
  LoPun,HiPun,PtX,PtY:LongInt;
  Poly2D:Polygon2D;
  P,A,B,Norm,PMulA,PMulB:Pun3DInt;
  LinksX,LinksDiffY,LinksDiffX,LinksAddX,
  RechtsX,RechtsDiffY,RechtsDiffX,RechtsAddX,
  StartY,EndeY,
  Orientierung:LongInt;
  Alpha,Beta,
  LeftPt,RightPt,NextLeftPt,NextRightPt,
  Poly2DLastPt,dir:integer;   { Orientierung der Ebene (direction des Punktedurchlaufs) }

{ zwei Hilfsfunktionen von FillTriangle: }

Function GetBeta(Rx,Ry,Rz:LongInt):integer;
{ Bitmap-Index Beta berechnen }
  begin
    GetBeta:=((Rx*PMulA.x+Ry*PMulA.y+Rz*PMulA.z)*127)
          div (Rx*Norm.x+Ry*Norm.y+Rz*Norm.z);
  end;

Function GetAlpha(Rx,Ry,Rz:LongInt):integer;
{ Bitmap-Index Alpha berechnen }
  begin
    GetAlpha:=((Rx*PMulB.x+Ry*PMulB.y+Rz*PMulB.z)*127)
           div (Rx*Norm.x+Ry*Norm.y+Rz*Norm.z);
  end;


  begin
    P:=Tri3D[1];
    A:=Tri3D[0];
    B:=Tri3D[2];

    Poly2DLastPt:=-1;
    Project(Tri3D[0],Tri3D[1],Poly2D,Poly2DLastPt);
    Project(Tri3D[1],Tri3D[2],Poly2D,Poly2DLastPt);
    Project(Tri3D[2],Tri3D[0],Poly2D,Poly2DLastPt);
    if Poly2DLastPt<2 then
      Exit;   { weniger als drei Punkte ergeben keine Flche... }

    { allgemeine Vorberechnungen }
    { Norm ist ein Vektor (im Raum) senkrecht zum Dreick }
    Norm.x:=(A.y-P.y)*(B.z-P.z)-(A.z-P.z)*(B.y-P.y);
    Norm.y:=(A.z-P.z)*(B.x-P.x)-(A.x-P.x)*(B.z-P.z);
    Norm.z:=(A.x-P.x)*(B.y-P.y)-(A.y-P.y)*(B.x-P.x);
    PMulA.x:=Tri3D[1].y*Tri3D[0].z-Tri3D[1].z*Tri3D[0].y;
    PMulA.y:=Tri3D[1].z*Tri3D[0].x-Tri3D[1].x*Tri3D[0].z;
    PMulA.z:=Tri3D[1].x*Tri3D[0].y-Tri3D[1].y*Tri3D[0].x;
    PMulB.x:=-Tri3D[1].y*Tri3D[2].z+Tri3D[1].z*Tri3D[2].y;
    PMulB.y:=-Tri3D[1].z*Tri3D[2].x+Tri3D[1].x*Tri3D[2].z;
    PMulB.z:=-Tri3D[1].x*Tri3D[2].y+Tri3D[1].y*Tri3D[2].x;
    Orientierung:=Norm.x*P.x+Norm.y*P.y+Norm.z*P.z;
    { wie herum mssen die Punkte des Poly2D durchlaufen werden? }
    dir:=1;
    if Orientierung=0 then
      Exit    { Ebene nur als Strich zu sehen }
    else
      if Orientierung<0 then
        dir:=-1;
    { herausfinden, wo welcher Eckpunkt sich befindet }
    LoPun:=0;
    if Poly2D[1].y<Poly2D[LoPun].y then
      LoPun:=1;
    if Poly2D[2].y<Poly2D[LoPun].y then
      LoPun:=2;
    if Poly2DLastPt=3 then
      if Poly2D[3].y<Poly2D[LoPun].y then
        LoPun:=3;

    HiPun:=0;
    if Poly2D[1].y>Poly2D[HiPun].y then
      HiPun:=1;
    if Poly2D[2].y>Poly2D[HiPun].y then
      HiPun:=2;
    if Poly2DLastPt=3 then
      if Poly2D[3].y>Poly2D[HiPun].y then
        HiPun:=3;

    { Parameter fr Fll-Algorithmus berechnen }
    {  }
    { Kantenlinien-Parameter }
      { fr links }
    LeftPt:=LoPun;
    NextLeftPt:=(LeftPt+dir+Poly2DLastPt+1) mod (Poly2DLastPt+1);

    LinksDiffY:=Poly2D[LeftPt].y-Poly2D[NextLeftPt].y-1;  { -1 wegen Randpunkten: gehren beide dazu! }
    LinksDiffX:=Poly2D[LeftPt].x-Poly2D[NextLeftPt].x-1;
    LinksAddX:=(LinksDiffX*Rund) div LinksDiffY;
    LinksX:=Poly2D[LeftPt].x*Rund;

      { fr rechts }
    RightPt:=LoPun;
    NextRightPt:=(RightPt-dir+Poly2DLastPt+1) mod (Poly2DLastPt+1);

    RechtsDiffY:=Poly2D[RightPt].y-Poly2D[NextRightPt].y-1;
    RechtsDiffX:=Poly2D[RightPt].x-Poly2D[NextRightPt].x-1;
    RechtsAddX:=(RechtsDiffX*Rund) div RechtsDiffY;
    RechtsX:=Poly2D[RightPt].x*Rund;
    StartY:=Poly2D[LoPun].y;
    EndeY:=Poly2D[HiPun].y;

    For PtY:=StartY to EndeY do
      begin
        { linke und rechte Kantenlinie verfolgen }
        Inc(LinksX,LinksAddX);
        Inc(RechtsX,RechtsAddX);
        For PtX:=LinksX div Rund to RechtsX div Rund do
          begin
            Alpha:=GetAlpha(PtX,PtY,Zoom);
            Beta:=GetBeta(PtX,PtY,Zoom);
            if (Alpha>=0) and (Alpha<=127) and (Beta>=0) and (Beta<=127) then
              Scr[PtY,PtX]:=BMP[Alpha,Beta];
          end;
        if PtY=Poly2D[NextLeftPt].y then
          begin  { neuen Eckpunkt verwenden! }
            LeftPt:=NextLeftPt;
            NextLeftPt:=(LeftPt+dir+Poly2DLastPt+1) mod (Poly2DLastPt+1);
            LinksDiffY:=Poly2D[LeftPt].y-Poly2D[NextLeftPt].y-1;
            LinksDiffX:=Poly2D[LeftPt].x-Poly2D[NextLeftPt].x-1;
            LinksAddX:=(LinksDiffX*Rund) div LinksDiffY;
            LinksX:=Poly2D[LeftPt].x*Rund;
          end;
        if PtY=Poly2D[NextRightPt].y then
          begin  { neuen Eckpunkt verwenden! }
            RightPt:=NextRightPt;
            NextRightPt:=(RightPt-dir+Poly2DLastPt+1) mod (Poly2DLastPt+1);
            RechtsDiffY:=Poly2D[RightPt].y-Poly2D[NextRightPt].y-1;
            RechtsDiffX:=Poly2D[RightPt].x-Poly2D[NextRightPt].x-1;
            RechtsAddX:=(RechtsDiffX*Rund) div RechtsDiffY;
            RechtsX:=Poly2D[RightPt].x*Rund;
          end;
      end;
  end;

Var
  i,j:integer;
  Key:Char;

begin
  directVideo:=false;
  asm
    mov ax,$13
    int $10
  end;
  FillChar(Scr,SizeOf(Scr),0);
  For i:=0 to 127 do
    For j:=0 to 127 do
      BMP[j,i]:=i+20;
  For i:=0 to 62 do
    begin
      BMP[i,i]:=10;
      BMP[i+1,i]:=10;
    end;
  For i:=0 to 360 do
    BMP[Round(cos(i/360*2*Pi)*31)+32,Round(sin(i/360*2*Pi)*31)+32]:=4;
  For i:=0 to 127 do
    For j:=0 to 127 do
      Scr[j-90,i-150]:=BMP[j,i];

  Tri3D[0].x:=0; Tri3D[0].y:=-90; Tri3D[0].z:=150;
  Tri3D[1].x:=-110; Tri3D[1].y:=50;  Tri3D[1].z:=220;
  Tri3D[2].x:=30; Tri3D[2].y:=80; Tri3D[2].z:=100;

  FillTriangle(Tri3D);
  Key:=UpCase(ReadKey);
end.