10
		
			
		
	 		
	Tags:   
	
			delphi
			
	
		
	
	
	
	
	
		
		
			Skrevet af 
Bruger #1474
			 @ 11.08.2008 
		 
		
	 	
	TGA KlasseVores lille program vil blive en simpel konsol. Konsol programmer har ingen grafisk brugerflade og kan derfor kun vise tekst. Det gør heldigvis ikke noget, for vi vil gemme de renderede pixels i et billedformat og til sidst gemme billedet til harddisken. Billedet kan derefter vises i et billedbehandligsprogram. Det er ganske almindelig at bruge konsol programmer når man skriver renderings software, da det giver den mulighed at man kan forstørre billedet for at studere nærmere detaljer. Vi vil derfor skrive en klasse der kan både gemme og indlæse et kendt billedformat. Indlæsningen vil blive brugt til senere brug, når vi vil lægge tekstur (texture) oven på vores 3D objekter. Der findes mange forskellige billedformater som sagtens kan anvendes til dette formål. Jeg har valgt at bruge TGA formatet, fordi dette er en ukomprimeret format der kan behandles på alle platforme.
TGA klassen minder meget om mange andre klasser af samme art. Den kan som sagt indlæse en TGA fil fra harddisken og skrive til en TGA fil. Der er dog et par små funktioner der er tilføjet. Alle pixels i et digitalt billede bliver normalt gemt i bytes. Det vil sige, at farve skalaen for et billede vil ligge fraogmed 0 tilogmed 255. Når vi beregner farver i forbindelse med en rendering, vil vi gerne bruge en større og mere nøjagtig skala. Vi vil også gerne havde muligheden for at gå ud over den almindelige farve skala. Det færdige resultat vil dog tilsidst blive gemt i bytes. Da komponenterne X, Y, Z og W i vores vektor klasse er af float typen: double, hvorfor så ikke bruge samme type for vores farver! Vi vil derfor gerne kunne tilskrive og aflæse pixels i TGA klassen, som om de var af float typen: double. Vi kunne sagtens gøre dette udenfor klassen, men for at undgå at havde samme kode skrevet mange forskellige steder, skriver vi det ind i vores klasse.
Der er en anden funktion i TGA klassen der er forskellig fra en almindelig billede klasse. Vi har muligheden for at aflæse en pixel ved hjælp af UVW eller tekstur (texture) koordinater. Normalt vil man aflæse en pixel ved at angive indekset for den ønskede pixel eller ved at angine X og Y koordinaterne. Vi vil få brug for denne funktion når vi vil skal til at rendere tekstur (texture) på vores geometri. Funktionen hedder GetPixel og er en overload til de mere almindelige funktioner af samme navn. TGA Klassen vil komme til at se således ud:
type
  //Den officelle TGA fil header!
  TFILEHEADER = packed record
    IdentSize : Byte;
    ColorMapType : Byte;
    ImageType : Byte;
    ColorMapStart : Word;
    ColorMapLength : Word;
    ColorMapBits : Byte;
    XStart : Word;
    YStart : Word;
    Width : Word;
    Height : Word;
    Bits : Byte;
    Descriptor : Byte;
  end;
  //Vores special designet TGA klasse!
  TTGAFILE = class
  public
    Header : TFILEHEADER;
    Pixels : array of Byte;
    constructor Create; overload;
    constructor Create( const Width, Height : Word; const AlphaChannel : Boolean ); overload;
    destructor Free;
    function GetWidth : Word;
    function GetHeight : Word;
    function AlphaChannel : Boolean;
    function SetPixel( const X, Y : Word; const Red, Green, Blue, Alpha : Byte ) : Boolean; overload;
    function SetPixel( const X, Y : Word; const Red, Green, Blue, Alpha : Double ) : Boolean; overload;
    function SetPixel( const X, Y : Word; Color : TRGBA ) : Boolean; overload;
    function GetPixel( const X, Y : Word ) : TRGBA; overload;
    function GetPixel( const I : LongWord ) : TRGBA; overload;
    function GetPixel( Coord : TUVW; const BilinearFilter : Boolean ) : TRGBA; overload;
    function Load( const Filename : string ) : Boolean;
    function Save( const Filename : string ) : Boolean;
  end;
implementation
constructor TTGAFILE.Create;
begin
  Header.Width := 0;
  Header.Height := 0;
  Header.Bits := 0;
  SetLength( Pixels, 0 );
end;
constructor TTGAFILE.Create( const Width, Height : Word; const AlphaChannel : Boolean );
var
  I : Integer;
begin
  Header.Width := Width;
  Header.Height := Height;
  if ( AlphaChannel ) then
  begin
    Header.Bits := 32;
    SetLength( Pixels, Width * Height * 4 );
    for I := 0 to Width * Height * 4 do
    Pixels[ I ] := 0;
  end
  else
    begin
      Header.Bits := 24;
      SetLength( Pixels, Width * Height * 3 );
      for I := 0 to Width * Height * 3 do
      Pixels[ I ] := 0;
    end;
end;
destructor TTGAFILE.Free;
begin
  SetLength( Pixels, 0 );
end;
function TTGAFILE.GetWidth : Word;
begin
  Result := Header.Width;
end;
function TTGAFILE.GetHeight : Word;
begin
  Result := Header.Height;
end;
function TTGAFILE.AlphaChannel : Boolean;
begin
  if ( Header.Bits = 32 ) then
  Result := True
  else
    Result := False;
end;
function TTGAFILE.GetPixel( const X, Y : Word ) : TRGBA;
var
  I : Integer;
begin
  Result := nil;
  if ( X < Header.Width ) and ( Y < Header.Height ) then
  begin
    case ( Header.Bits ) of
      32: begin
            I := ( Y * Header.Width + X ) * 4;
            Result := TRGBA.Create( Pixels[ I + 2 ] / 255,
                                    Pixels[ I + 1 ] / 255,
                                    Pixels[ I + 0 ] / 255,
                                    Pixels[ I + 3 ] / 255 );
          end;
      24: begin
            I := ( Y * Header.Width + X ) * 3;
            Result := TRGBA.Create( Pixels[ I + 2 ] / 255,
                                    Pixels[ I + 1 ] / 255,
                                    Pixels[ I + 0 ] / 255, 1 );
          end;
    end;
  end;
end;
function TTGAFILE.GetPixel( const I : LongWord ) : TRGBA;
begin
  Result := nil;
  if ( I < Header.Width * Header.Height ) then
  begin
    case ( Header.Bits ) of
      32: begin
            Result := TRGBA.Create( Pixels[ I * 4 + 2 ] / 255,
                                    Pixels[ I * 4 + 1 ] / 255,
                                    Pixels[ I * 4 + 0 ] / 255,
                                    Pixels[ I * 4 + 3 ] / 255 );
          end;
      24: begin
            Result := TRGBA.Create( Pixels[ I * 3 + 2 ] / 255,
                                    Pixels[ I * 3 + 1 ] / 255,
                                    Pixels[ I * 3 + 0 ] / 255,
                                    1 );
          end;
    end;
  end;
end;
function TTGAFILE.GetPixel( Coord : TUVW; const BilinearFilter : Boolean ) : TRGBA;
var
  X, Y, I : Integer;
  Color, C : TRGBA;
  U, V : Double;
begin
  U := Coord.U;
  V := Coord.V;
  //Sikre os at tekstur koordinaterne vil ligge mellm 0 og 1
  if ( U > 1.0 ) or ( U < -1.0 ) then
  U := Frac( U );
  if ( V > 1.0 ) or ( V < -1.0 ) then
  V := Frac( V );
  //Sikre os at tekstur koordinaterne altid vil være positive
  if ( U < 0.0 ) then
  U := U + 1;
  if ( V < 0.0 ) then
  V := V + 1;
  //Find 2D koordinater udfra tekstur koordinaterne UVW
  X := Trunc( ( Header.Height - 1 ) * U );
  Y := Trunc( ( Header.Width - 1 ) * V );
  //Find den endelig pixel i teksturen
  Color := GetPixel( X, Y );
  I := 1;
  //Bilineær filter tager alle omkring liggende pixels og finder gennemsnittet!
  if ( BilinearFilter ) then
  begin
    C := GetPixel( X + 1, Y );
    if ( C <> nil ) then
    begin
      Color.SetValues( Color.R + C.R, Color.G + C.G, Color.B + C.B, Color.A + C.A );
      I := I + 1;
      C.Free;
    end;
    C := GetPixel( X - 1, Y );
    if ( C <> nil ) then
    begin
      Color.SetValues( Color.R + C.R, Color.G + C.G, Color.B + C.B, Color.A + C.A );
      I := I + 1;
      C.Free;
    end;
    C := GetPixel( X, Y + 1 );
    if ( C <> nil ) then
    begin
      Color.SetValues( Color.R + C.R, Color.G + C.G, Color.B + C.B, Color.A + C.A );
      I := I + 1;
      C.Free;
    end;
    C := GetPixel( X, Y - 1 );
    if ( C <> nil ) then
    begin
      Color.SetValues( Color.R + C.R, Color.G + C.G, Color.B + C.B, Color.A + C.A );
      I := I + 1;
      C.Free;
    end;
    C := GetPixel( X + 1, Y - 1 );
    if ( C <> nil ) then
    begin
      Color.SetValues( Color.R + C.R, Color.G + C.G, Color.B + C.B, Color.A + C.A );
      I := I + 1;
      C.Free;
    end;
    C := GetPixel( X + 1, Y + 1 );
    if ( C <> nil ) then
    begin
      Color.SetValues( Color.R + C.R, Color.G + C.G, Color.B + C.B, Color.A + C.A );
      I := I + 1;
      C.Free;
    end;
    C := GetPixel( X - 1, Y - 1 );
    if ( C <> nil ) then
    begin
      Color.SetValues( Color.R + C.R, Color.G + C.G, Color.B + C.B, Color.A + C.A );
      I := I + 1;
      C.Free;
    end;
    C := GetPixel( X - 1, Y + 1 );
    if ( C <> nil ) then
    begin
      Color.SetValues( Color.R + C.R, Color.G + C.G, Color.B + C.B, Color.A + C.A );
      I := I + 1;
      C.Free;
    end;
    //Find gennemsnittet af de samlede farver
    Result := TRGBA.Create( Color.R / I, Color.G / I, Color.B / I, Color.A / I );
  end
  else
    Result := GetPixel( X, Y );
  Color.Free;
end;
function TTGAFILE.SetPixel( const X, Y : Word; const Red, Green, Blue, Alpha : Byte ) : Boolean;
var
  I : Integer;
begin
  Result := False;
  if ( X < Header.Width ) and ( Y < Header.Height ) then
  begin
    case ( Header.Bits ) of
      32: begin
            I := ( Y * Header.Width + X ) * 4;
            Pixels[ I + 0 ] := Blue;
            Pixels[ I + 1 ] := Green;
            Pixels[ I + 2 ] := Red;
            Pixels[ I + 3 ] := Alpha;
            Result := True;
          end;
      24: begin
            I := ( Y * Header.Width + X ) * 3;
            Pixels[ I + 0 ] := Blue;
            Pixels[ I + 1 ] := Green;
            Pixels[ I + 2 ] := Red;
            Result := True;
          end;
    end;
  end;
end;
function TTGAFILE.SetPixel( const X, Y : Word; const Red, Green, Blue, Alpha : Double ) : Boolean;
var
  I : Integer;
begin
  Result := False;
  if ( X < Header.Width ) and ( Y < Header.Height ) then
  begin
    case ( Header.Bits ) of
      32: begin
            I := ( Y * Header.Width + X ) * 4;
            Pixels[ I + 0 ] := Round( Blue * 255 );
            Pixels[ I + 1 ] := Round( Green * 255 );
            Pixels[ I + 2 ] := Round( Red * 255 );
            Pixels[ I + 3 ] := Round( Alpha * 255 );
            Result := True;
          end;
      24: begin
            I := ( Y * Header.Width + X ) * 3;
            Pixels[ I + 0 ] := Round( Blue * 255 );
            Pixels[ I + 1 ] := Round( Green * 255 );
            Pixels[ I + 2 ] := Round( Red * 255 );
            Result := True;
          end;
    end;
  end;
end;
function TTGAFILE.SetPixel( const X, Y : Word; Color : TRGBA ) : Boolean;
var
  I : Integer;
begin
  Result := False;
  if ( X < Header.Width ) and ( Y < Header.Height ) then
  begin
    case ( Header.Bits ) of
      32: begin
            I := ( Y * Header.Width + X ) * 4;
            Pixels[ I + 0 ] := Round( Color.B * 255 );
            Pixels[ I + 1 ] := Round( Color.G * 255 );
            Pixels[ I + 2 ] := Round( Color.R * 255 );
            Pixels[ I + 3 ] := Round( Color.A * 255 );
            Result := True;
          end;
      24: begin
            I := ( Y * Header.Width + X ) * 3;
            Pixels[ I + 0 ] := Round( Color.B * 255 );
            Pixels[ I + 1 ] := Round( Color.G * 255 );
            Pixels[ I + 2 ] := Round( Color.R * 255 );
            Result := True;
          end;
    end;
  end;
end;
function TTGAFILE.Load( const Filename : string ) : Boolean;
var
  Handle : File;
  I : Integer;
begin
  Result := False;
  AssignFile( Handle, Filename );
  Reset( Handle, 1 );
  BlockRead( Handle, Header, SizeOf( TFILEHEADER ), I );
  if ( I <> SizeOf( TFILEHeader ) ) then
  begin
    CloseFile( Handle );
    Exit;
  end;
  //Denne klasse understøtter kun 24bits og 32bits format!
  case ( Header.Bits ) of
    32: begin
          SetLength( Pixels, Header.Width * Header.Height * 4 );
          BlockRead( Handle, Pixels[ 0 ], Header.Width * Header.Height * 4, I );
          if ( I <> Header.Width * Header.Height * 4 ) then
          begin
            CloseFile( Handle );
            Exit;
          end;
        end;
    24: begin
          SetLength( Pixels, Header.Width * Header.Height * 3 );
          BlockRead( Handle, Pixels[ 0 ], Header.Width * Header.Height * 3, I );
          if ( I <> Header.Width * Header.Height * 3 ) then
          begin
            CloseFile( Handle );
            Exit;
          end;
        end;
  end;
  CloseFile( Handle );
  Result := True;
end;
function TTGAFILE.Save( const Filename : string ) : Boolean;
var
  Handle : File;
  I : Integer;
begin
  Result := False;
  AssignFile( Handle, Filename );
  Rewrite( Handle, 1 );
  Header.IdentSize := 0;
  Header.ColorMapType := 0;
  Header.ImageType := 2;
  Header.ColorMapStart := 0;
  Header.ColorMapLength := 0;
  Header.ColorMapBits := 0;
  Header.XStart := 0;
  Header.YStart := 0;
  Header.Descriptor := 0;
  BlockWrite( Handle, Header, SizeOf( TFILEHEADER ), I );
  if ( I <> SizeOf( TFILEHeader ) ) then
  begin
    CloseFile( Handle );
    Exit;
  end;
  //Denne klasse understøtter kun 24bits og 32bits format!
  case ( Header.Bits ) of
    32: begin
          BlockWrite( Handle, Pixels[ 0 ], Header.Width * Header.Height * 4, I );
          if ( I <> Header.Width * Header.Height * 4 ) then
          begin
            CloseFile( Handle );
            Exit;
          end;
        end;
    24: begin
          BlockWrite( Handle, Pixels[ 0 ], Header.Width * Header.Height * 3, I );
          if ( I <> Header.Width * Header.Height * 3 ) then
          begin
            CloseFile( Handle );
            Exit;
          end;
        end;
  end;
  CloseFile( Handle );
  Result := True;
end;
Som du sikkert har bemærket bruger TGA klassen en anden klasse ved navn: TRGBA. Denne klasse er en simpel klasse med komponenterne: R, G, B og A. De er alle af float typen: double. Denne klasse vil repræsentere vores farver. Der er en lille funktion i klassen der hedder: Clamp. Den har to parameterer: Min og Max. Med denne funktion kan farve komponenterne afskæres til et minimum og maksimum værdi. I de fleste tilfælde vil vi bruge en skala mellem 0 og 1. Her er klassen:
type
  TRGBA = class
    R, G, B, A : Double;
    constructor Create; overload;
    constructor Create( const R, G, B, A : Double ); overload;
    procedure SetValues( const R, G, B, A : Double ); overload;
    procedure SetValues( const R, G, B : Double ); overload;
    procedure Clamp( const Min, Max : Double );
  end;
implementation
constructor TRGBA.Create;
begin
  R := 1;
  G := 1;
  B := 1;
  A := 1;
end;
constructor TRGBA.Create( const R, G, B, A : Double );
begin
  Self.R := R;
  Self.G := G;
  Self.B := B;
  Self.A := A;
end;
procedure TRGBA.SetValues( const R, G, B, A : Double );
begin
  Self.R := R;
  Self.G := G;
  Self.B := B;
  Self.A := A;
end;
procedure TRGBA.SetValues( const R, G, B : Double );
begin
  Self.R := R;
  Self.G := G;
  Self.B := B;
end;
procedure TRGBA.Clamp( const Min, Max : Double );
begin
  if ( R > Max ) then
  R := Max
  else
    if ( R < Min ) then
    R := Min;
  if ( G > Max ) then
  G := Max
  else
    if ( G < Min ) then
    G := Min;
  if ( B > Max ) then
  B := Max
  else
    if ( B < Min ) then
    B := Min;
  if ( A > Max ) then
  A := Max
  else
    if ( A < Min ) then
    A := Min;
end;
Geometri KlasseDenne klasse vil holde alt data for vores geometri eller 3D objekter. Som nævnt tidligere er 3D objekter som regel bygget op af trekanter. Det vil vores 3D objekter derfor også være. Men der er forskellige måder at definere trekanter på. Det kommer helt an på, hvordan man ønsker at komprimere dataerne. Denne artkel vil ikke bruge komprimeringsmodeller. For at gøre denne artikel så simpel som muligt vil vi kun anvende ukomprimeret data. Klassen indeholder en array af vertex'er, hvoraf tre vertex'er vil definere en trekant. 
En vertex indeholder følgende data:
1) Geometrisk koordinater
2) En farve
3) Tekstur (texture) koordinater
4) En normal
Den geometriske koordinat bliver repræsenteret af en vektor.
Farven er repræsenteret af en TRGBA klasse.
Tekstur (texture) koordinaterne vil blive repræsenteret af en ny klasse som indeholder komponenterne U,V og W. Tidligere nævnte jeg at vi ikke vil komme til at bruge W komponenten men for en ordens skyld bør vi nok tilføje den. Denne klasse har ikke nogle nævneværdige funktioner og vil derfor komme til at se således ud:
type
  TUVW = class
    U, V, W : Double;
    constructor Create; overload;
    constructor Create( const U, V, W : Double ); overload;
    procedure SetValues( const U, V, W : Double ); overload;
    procedure SetValues( const U, V : Double ); overload;
  end;
implementation
constructor TUVW.Create;
begin
  U := 0;
  V := 0;
  W := 0;
end;
constructor TUVW.Create( const U, V, W : Double );
begin
  Self.U := U;
  Self.V := V;
  Self.W := W;
end;
procedure TUVW.SetValues( const U, V, W : Double );
begin
  Self.U := U;
  Self.V := V;
  Self.W := W;
end;
procedure TUVW.SetValues( const U, V : Double );
begin
  Self.U := U;
  Self.V := V;
end;
Nomalen for vores vertex vil vi komme til at bruge når vi skal beregne lysfaldet for vores trekanter. Den bruger kun tre komponenter X, Yog Z. Af nemheds skyld vil vi derfor definer den som en almindelig vektor trods vi aldrig kommer til at bruge W komponenten. Det vil være nemmest at lave en lille vertex klasse der indeholder disse klasser:
type
  TVERTEX = class
    Vertex : TVECTOR;
    Normal : TVECTOR;
    Color  : TRGBA;
    Coord  : TUVW;
    constructor Create;
  end;
implementation
constructor TVERTEX.Create;
begin
  Vertex := TVECTOR.Create( 0, 0, 0, 1 );
  Normal := TVECTOR.Create( 0, 0, 0, 1 );
  Color  := TRGBA.Create( 1, 1, 1, 1 );
  Coord  := TUVW.Create( 0, 0, 0 );
end;
Geometri klassen har et par nævneværdige funktioner: InterpolateVertexColors, InterpolateTextureCoordinates og InterpolateNormals. De alle har fire ens parametere. Den første parameter forventer en skæringsvektor og de sidste forventer tre vertex'er. Funktionen: InterpolateVertexColor, vil finde den farve som ligger tættest på skæreringspunktet ved at blande de tre vertex farver afhængigt af vektorens barycentriske koordinater. Resultatet vil blive returneret i en farve klasse. De to andre funktioner gør nøjagtig det samme dog bare med trekantens henholdsvis tekstur (texture) koordinater og normaler. Sidst i klassen er der en stribe funktioner der hver kan lave en geometrisk objekt så som en kasse og en cylinder. Af hensyn til denne artikels længde vil vi ikke komme nærmere indpå, hvordan algoritmerne i disse funktioner virker. Geometri klassen ser således ud:
type
  TGEOMETRY = class
  private
    VertexCount : LongWord;
    VertexList : array of TVERTEX;
  public
    constructor Create;
    destructor Free;
    procedure SetVertexList( const Count : LongWord );
    function GetVertexCount : LongWord;
    function GetVertex( const Index : LongWord ) : TVERTEX;
    function InterpolateVertexColors( Ray : TVECTOR; V1, V2, V3 : TVERTEX ) : TRGBA;
    function InterpolateTextureCoordinates( Ray : TVECTOR; V1, V2, V3 : TVERTEX ) : TUVW;
    function InterpolateNormals( Ray : TVECTOR; V1, V2, V3 : TVERTEX ) : TVECTOR;
    procedure CreatePlane( const Width, Depth : Double; const X : Double = 0; const Y : Double = 0; const Z : Double = 0 );
    procedure CreateBox( const Width, Height, Depth : Double; const X : Double = 0; const Y : Double = 0; const Z : Double = 0 );
    procedure CreateCylinder( const XSegment, YSegment : Word; const Height, Radius : Double; const X : Double = 0; const Y : Double = 0; const Z : Double = 0 );
  end;
implementation
constructor TGEOMETRY.Create;
begin
  VertexCount := 0;
end;
destructor TGEOMETRY.Free;
var
  I : Integer;
begin
  if ( VertexCount > 0 ) then
  for I := 0 to VertexCount - 1 do
  if ( VertexList[ I ] <> nil ) then
  VertexList[ I ].Free;
  SetLength( VertexList, 0 );
end;
procedure TGEOMETRY.SetVertexList( const Count : LongWord );
var
  I : Integer;
begin
  if ( VertexCount > 0 ) then
  for I := 0 to VertexCount - 1 do
  if ( VertexList[ I ] <> nil ) then
  VertexList[ I ].Free;
  SetLength( VertexList, Count );
  VertexCount := Count;
  for I := 0 to VertexCount - 1 do
  VertexList[ I ] := TVERTEX.Create;
end;
function TGEOMETRY.GetVertexCount : LongWord;
begin
  Result := VertexCount;
end;
function TGEOMETRY.GetVertex( const Index : LongWord ) : TVERTEX;
begin
  if ( Index < VertexCount ) then
  Result := VertexList[ Index ]
  else
    Result := nil;
end;
function TGEOMETRY.InterpolateVertexColors( Ray : TVECTOR; V1, V2, V3 : TVERTEX ) : TRGBA;
begin
  //Interpoler de tre vertex farver afhængig af vektor Ray!
  Result := TRGBA.Create( V1.Color.R * ( 1 - Ray.X - Ray.Y ) + V2.Color.R * Ray.X + V3.Color.R * Ray.Y,
                          V1.Color.G * ( 1 - Ray.X - Ray.Y ) + V2.Color.G * Ray.X + V3.Color.G * Ray.Y,
                          V1.Color.B * ( 1 - Ray.X - Ray.Y ) + V2.Color.B * Ray.X + V3.Color.B * Ray.Y, 1 );
end;
function TGEOMETRY.InterpolateTextureCoordinates( Ray : TVECTOR; V1, V2, V3 : TVERTEX ) : TUVW;
begin
  //Interpoler de tre tekstur koordinater afhængig af vektor Ray!
  Result := TUVW.Create( V1.Coord.U * ( 1 - Ray.X - Ray.Y ) + V2.Coord.U * Ray.X + V3.Coord.U * Ray.Y,
                         V1.Coord.V * ( 1 - Ray.X - Ray.Y ) + V2.Coord.V * Ray.X + V3.Coord.V * Ray.Y,
                         V1.Coord.W * ( 1 - Ray.X - Ray.Y ) + V2.Coord.W * Ray.X + V3.Coord.W * Ray.Y );
end;
function TGEOMETRY.InterpolateNormals( Ray : TVECTOR; V1, V2, V3 : TVERTEX ) : TVECTOR;
begin
  //Interpoler de tre normaler afhængig af vektor Ray!
  Result := TVECTOR.Create( ( V1.Normal.X * ( 1 - Ray.X - Ray.Y ) + V2.Normal.X * Ray.X + V3.Normal.X * Ray.Y ) / 3,
                            ( V1.Normal.Y * ( 1 - Ray.X - Ray.Y ) + V2.Normal.Y * Ray.X + V3.Normal.Y * Ray.Y ) / 3,
                            ( V1.Normal.Z * ( 1 - Ray.X - Ray.Y ) + V2.Normal.Z * Ray.X + V3.Normal.Z * Ray.Y ) / 3, 1 );
end;
procedure TGEOMETRY.CreatePlane( const Width, Depth : Double; const X : Double; const Y : Double; const Z : Double );
var
  I : Integer;
begin
  //Lav en flade
  SetVertexList( 6 );
  I := 0;
  //Trekant 1
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  //Trekant 2
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
end;
procedure TGEOMETRY.CreateBox( const Width, Height, Depth : Double; const X, Y, Z : Double );
var
  I : Integer;
begin
  //Lav en kasse
  SetVertexList( 6 * 6 );
  I := 0;
  //Forsiden
  //Trekant 1
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 0, 1 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 0 );
  VertexList[ I ].Normal.SetValues( 0, 0, 1 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 0, 1 );
  I := I + 1;
  //Trekant 2
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 0, 1 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 1 );
  VertexList[ I ].Normal.SetValues( 0, 0, 1 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 0, 1 );
  I := I + 1;
  //Bagsiden
  //Trekant 1
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 0, -1 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 0 );
  VertexList[ I ].Normal.SetValues( 0, 0, -1 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 0, -1 );
  I := I + 1;
  //Trekant 2
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 0, -1 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 1 );
  VertexList[ I ].Normal.SetValues( 0, 0, -1 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z  );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 0, -1 );
  I := I + 1;
  //Top
  //Trekant 1
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  //Trekant 2
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  //Bund
  //Trekant 1
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  //Trekant 2
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 1 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 0, 1, 0 );
  I := I + 1;
  //Venstre side
  //Trekant 1
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( -1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 0 );
  VertexList[ I ].Normal.SetValues( -1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( -1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( -1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 1 );
  VertexList[ I ].Normal.SetValues( -1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( -Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( -1, 0, 0 );
  I := I + 1;
  //Højre side
  //Trekant 1
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 0 );
  VertexList[ I ].Normal.SetValues( 1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 1, 0, 0 );
  I := I + 1;
  //Trekant 2
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, +Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 1, 1 );
  VertexList[ I ].Normal.SetValues( 1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, +Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 1 );
  VertexList[ I ].Normal.SetValues( 1, 0, 0 );
  I := I + 1;
  VertexList[ I ].Vertex.SetValues( +Width * 0.5 + X, -Height * 0.5 + Y, -Depth * 0.5 + Z );
  VertexList[ I ].Coord.SetValues( 0, 0 );
  VertexList[ I ].Normal.SetValues( 1, 0, 0 );
  I := I + 1;
end;
procedure TGEOMETRY.CreateCylinder( const XSegment, YSegment : Word; const Height, Radius : Double; const X : Double; const Y : Double; const Z : Double );
var
  IX, IY, I : Integer;
  XStep, YStep : Double;
  A, B, V : TVERTEX;
begin
  //Lav en cylinder
  SetVertexList( XSegment * YSegment * 6 );
  XStep := 360 / XSegment;
  YStep := Height / YSegment;
  //Lav to midlertidige vektorer
  A := TVERTEX.Create;
  B := TVERTEX.Create;
  I := 0;
  for IY := 0 to YSegment - 1 do
  for IX := 0 to XSegment - 1 do
  begin
    //Beregn nye koordinater
    A.Vertex.SetValues( Sin( DegToRad( XStep * IX ) ) * Radius + X, YStep * IY - Height * 0.5 + Y, Cos( DegToRad( XStep * IX ) ) * Radius + Z );
    A.Coord.SetValues(  IX / XSegment, IY / YSegment );
    A.Normal.SetValues( Sin( DegToRad( XStep * IX ) ), 0, Cos( DegToRad( XStep * IX ) ) );
    B.Vertex.SetValues( Sin( DegToRad( XStep * ( IX + 1 ) ) ) * Radius + X, YStep * ( IY + 1 ) - Height * 0.5 + Y,  Cos( DegToRad( XStep * ( IX + 1 ) ) ) * Radius + Z );
    B.Coord.SetValues( ( IX + 1 ) / XSegment, ( IY + 1 ) / YSegment );
    B.Normal.SetValues( Sin( DegToRad( XStep * ( IX + 1 ) ) ), 0, Cos( DegToRad( XStep * ( IX + 1 ) ) ) );
    //Trekant 1
    VertexList[ I ].Vertex.SetValues( A.Vertex.X, A.Vertex.Y, A.Vertex.Z );
    VertexList[ I ].Coord.SetValues(  A.Coord.U, A.Coord.V );
    VertexList[ I ].Normal.SetValues( A.Normal.X, A.Normal.Y, A.Normal.Z );
    I := I + 1;
    VertexList[ I ].Vertex.SetValues( B.Vertex.X, A.Vertex.Y, B.Vertex.Z );
    VertexList[ I ].Coord.SetValues(  B.Coord.U, A.Coord.V );
    VertexList[ I ].Normal.SetValues( B.Normal.X, A.Normal.Y, B.Normal.Z );
    I := I + 1;
    VertexList[ I ].Vertex.SetValues( B.Vertex.X, B.Vertex.Y, B.Vertex.Z );
    VertexList[ I ].Coord.SetValues(  B.Coord.U, B.Coord.V );
    VertexList[ I ].Normal.SetValues( B.Normal.X, B.Normal.Y, B.Normal.Z );
    I := I + 1;
    //Trekant 2
    VertexList[ I ].Vertex.SetValues( B.Vertex.X, B.Vertex.Y, B.Vertex.Z );
    VertexList[ I ].Coord.SetValues(  B.Coord.U, B.Coord.V );
    VertexList[ I ].Normal.SetValues( B.Normal.X, B.Normal.Y, B.Normal.Z );
    I := I + 1;
    VertexList[ I ].Vertex.SetValues( A.Vertex.X, B.Vertex.Y, A.Vertex.Z );
    VertexList[ I ].Coord.SetValues(  A.Coord.U, B.Coord.V );
    VertexList[ I ].Normal.SetValues( A.Normal.X, B.Normal.Y, A.Normal.Z );
    I := I + 1;
    VertexList[ I ].Vertex.SetValues( A.Vertex.X, A.Vertex.Y, A.Vertex.Z );
    VertexList[ I ].Coord.SetValues(  A.Coord.U, A.Coord.V );
    VertexList[ I ].Normal.SetValues( A.Normal.X, A.Normal.Y, A.Normal.Z );
    I := I + 1;
  end;
  //Frigør midlertidige klasser fra hukommelsen!
  A.Free;
  B.Free;
end;
	
	
	
	
	
	
	Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
	
	Del også gerne artiklen med dine Facebook venner:  
	
	
	
	
Kommentarer (2)
		
		
			Hmm, god artikel, men ringe du har lavet PRÆCIS den samme artikel, bare med C++!
		
	
		
		
			Koden er jo ikke den samme!
		
	
		
	Du skal være 
logget ind for at skrive en kommentar.