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.