home *** CD-ROM | disk | FTP | other *** search
-
- {
- Perspective Texture Mapping Demo. 11 Aug 96.
- Copyright (c) 1996 Keith Harrison.
- Delphi 2.0 32 bit version for Windows 95.
- For private use only.
- }
-
- unit Texture;
-
- interface
-
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
- ExtCtrls, ComCtrls, StdCtrls, Buttons;
-
- type
- ScanPoints = (NoPoints, OnePoint, TwoPoints); //Note: Convex polygons only!
-
- ScanType = record //One of these for each Y coord on screen
- points: ScanPoints; //Either none, one, or two points
- x1, x2: Integer; //X coord of two points max (Y coord is from position in array)
- u1, v1, z1: Single; //Texture coordinates
- u2, v2, z2: Single; //Texture coordinates
- end;
-
- VertType = record
- x, y, z: Integer; //Coordinates of vertex in worldspace
- sx, sy: Integer; //Screen coordinate after projection
- u, v: Single; //Coordinate of texture at this vertex
- end;
-
- TForm1 = class(TForm)
- GroupBox1: TGroupBox;
- RadioButton1: TRadioButton;
- RadioButton2: TRadioButton;
- GroupBox2: TGroupBox;
- Image1: TImage;
- Label1: TLabel;
- TrackBar1: TTrackBar;
- lblRepeat: TLabel;
- GroupBox3: TGroupBox;
- Label2: TLabel;
- Label3: TLabel;
- Label4: TLabel;
- Edit1: TEdit;
- Edit2: TEdit;
- Edit3: TEdit;
- Edit4: TEdit;
- Edit5: TEdit;
- Edit6: TEdit;
- Edit7: TEdit;
- Edit8: TEdit;
- Label5: TLabel;
- Image2: TImage;
- Edit9: TEdit;
- Edit10: TEdit;
- Edit11: TEdit;
- Edit12: TEdit;
- Label6: TLabel;
- Label7: TLabel;
- Label8: TLabel;
- SpeedButton1: TSpeedButton;
- procedure TrackBar1Change(Sender: TObject);
- procedure FormCreate(Sender: TObject);
- procedure FormPaint(Sender: TObject);
- procedure SpeedButton1Click(Sender: TObject);
- procedure FormDestroy(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- end;
-
- Const
- TEX_X = 127; //Texture mask (width of texture bitmap - 1)
- TEX_Y = 127; //Texture mask (height of texture bitmap - 1)
- HSCALE = 10; //Scale used in perspective projection.
- VSCALE = 10; //(as above)
- FormCaption = 'Perspective Texture Mapping Test: ';
-
- var
- Form1: TForm1;
- Bitmap: TBitmap; //We will draw onto this then blit to the screen.
-
- SCREEN_X, SCREEN_Y, MID_X, MID_Y: Integer; //Screen info
- tex: array[0..TEX_X, 0..TEX_Y] of TColor; //Holds the texture pixels
- scan: array[0..1023] of ScanType; //One for each potential screen Y coord
- v: array[0..3] of VertType; //Hold the vertices
-
- implementation
-
- {$R *.DFM}
-
- procedure TForm1.TrackBar1Change(Sender: TObject);
- begin
- lblRepeat.Caption := IntToStr(TrackBar1.Position);
- end;
-
- procedure TForm1.FormCreate(Sender: TObject);
- var
- i, j: Integer;
- begin
- SCREEN_X := Image2.Width; //The bitmap is made to fit its container
- SCREEN_Y := Image2.Height;
- MID_X := SCREEN_X div 2;
- MID_Y := SCREEN_Y div 2;
-
- //Make our drawing surface - CreateDIBSection would be preferable... 8)
- Bitmap := TBitmap.Create;
- Bitmap.Width := SCREEN_X;
- Bitmap.Height := SCREEN_Y;
-
- for i:=0 to TEX_X do
- for j:= 0 to TEX_Y do
- tex[i,j] := Image1.Canvas.Pixels[i,j]; //This could be done much faster.
-
- //This program does not bother with palettes, hence this warning
- if ((1 shl GetDeviceCaps(Canvas.Handle, BITSPIXEL)) < 65536) then
- MessageDlg('This program runs best in 16bit colour or higher',
- mtInformation, [mbOk], 0);
- end;
-
- procedure InitScans;
- var
- i: Integer;
- begin
- for i:=0 To SCREEN_Y do
- scan[i].points := NoPoints; //Empty all scanlines in the edge list
- end;
-
- procedure AddEdge(p1, p2: Integer);
- var
- temp, i, v1, v2: Integer;
- dy: Single;
- xp, zp, up, vp: Single; //Prime vars
- dxp, dzp, dup, dvp: Single; // Delta-Prime vars
- zp1, up1, vp1: Single; //Primes of 1st point
- zp2, up2, vp2: Single; //Primes of 2nd point
- begin
- v1 := p1; //These are pointers - could be altered to Vertex pointers
- v2 := p2; //""
-
- //Horizontal lines are ignored
- if v[v1].sy = v[v2].sy then exit;
-
- if v[v2].sy < v[v1].sy then begin
- //Swap pointers
- temp := v1;
- v1 := v2;
- v2 := temp;
- end;
-
- zp1 := 1.0 / v[v1].z;
- up1 := v[v1].u * zp1;
- vp1 := v[v1].v * zp1;
-
- zp2 := 1.0 / v[v2].z;
- up2 := v[v2].u * zp2;
- vp2 := v[v2].v * zp2;
-
- xp := v[v1].sx;
- up := up1;
- vp := vp1;
- zp := zp1;
-
- dy := 1.0 / (v[v2].sy - v[v1].sy);
- dxp := (v[v2].sx - v[v1].sx) * dy;
- dup := (up2 - up1) * dy;
- dvp := (vp2 - vp1) * dy;
- dzp := (zp2 - zp1) * dy;
-
- for i:= v[v1].sy to v[v2].sy - 1 do begin
- if (i >= 0) and (i < SCREEN_Y) then
- case scan[i].points of
- NoPoints: begin
- scan[i].points := OnePoint;
- scan[i].x1 := Trunc(xp);
- scan[i].u1 := up;
- scan[i].v1 := vp;
- scan[i].z1 := zp;
- scan[i].x2 := Trunc(xp); //Copying to both points eliminates the
- scan[i].u2 := up; //need to swap at case:OnePoints
- scan[i].v2 := vp;
- scan[i].z2 := zp;
- end;
- OnePoint: begin
- scan[i].points := TwoPoints;
- if scan[i].x1 > xp then begin
- scan[i].x1 := Trunc(xp);
- scan[i].u1 := up;
- scan[i].v1 := vp;
- scan[i].z1 := zp;
- end
- Else begin
- scan[i].x2 := Trunc(xp);
- scan[i].u2 := up;
- scan[i].v2 := vp;
- scan[i].z2 := zp;
- end;
- end;
- end;
- xp := xp + dxp;
- up := up + dup;
- vp := vp + dvp;
- zp := zp + dzp;
- end;
- end;
-
- procedure RenderScansAffine;
- var
- i, j, length, tx, ty: Integer;
- dx, up, vp, up0, vp0, up1, vp1, dup, dvp: Single;
- {dzp: Single; Not used in this proc.}
- begin
- for i:= 0 To Bitmap.Height - 1 do
- if ((scan[i].points = TwoPoints) and (scan[i].x1 <> scan[i].x2)) then begin
- up0 := scan[i].u1 / scan[i].z1;
- vp0 := scan[i].v1 / scan[i].z1;
- up1 := scan[i].u2 / scan[i].z2;
- vp1 := scan[i].v2 / scan[i].z2;
- up := up0;
- vp := vp0;
- length := scan[i].x2 - scan[i].x1;
- dx := 1.0 / length;
- dup := (up1 - up0) * dx;
- dvp := (vp1 - vp0) * dx;
- {dzp := (scan[i].z2 - scan[i].z1) * dx; Not used in this proc.}
- for j := scan[i].x1 to scan[i].x2 do begin
- tx := Trunc(up * TEX_X) and TEX_X; //Convert U-prime to texture coord
- ty := Trunc(vp * TEX_Y) and TEX_Y; //Convert V-prime to texture coord
- Bitmap.Canvas.Pixels[j,i] := tex[tx, ty]; //THIS IS SLOW!!!
- up := up + dup;
- vp := vp + dvp;
- end;
- end;
- end;
-
- procedure RenderScansPerspective;
- var
- i, j: Integer;
- x1, x2, tx, ty: Integer;
- tu, tv, tz: Single;
- dtu, dtv, dtz: Single;
- begin
- {
- Note that this procedure gives perfect results with no distortion.
- 'Quake' uses sub-division for extra speed (sampling U and V at
- intervals of 16 bits and linearly interpolating). The distortion is
- hardly noticable.
- }
- for i:= 0 To Bitmap.Height - 1 do
- if ((scan[i].points = TwoPoints) and (scan[i].x1 <> scan[i].x2)) then begin
- x1 := scan[i].x1;
- x2 := scan[i].x2;
- tu := scan[i].u1;
- tv := scan[i].v1;
- tz := scan[i].z1;
- dtu := (scan[i].u2 - tu) / (x2 - x1);
- dtv := (scan[i].v2 - tv) / (x2 - x1);
- dtz := (scan[i].z2 - tz) / (x2 - x1);
- for j:= x1 To x2 do begin
- tx := Trunc(tu / tz * TEX_X) and TEX_X;
- ty := Trunc(tv / tz * TEX_Y) and TEX_Y;
- Bitmap.Canvas.Pixels[j,i] := tex[tx, ty]; //THIS IS SLOW!!!
- tu := tu + dtu;
- tv := tv + dtv;
- tz := tz + dtz;
- end;
- end;
- end;
-
- procedure RenderScans;
- begin
- if Form1.RadioButton1.Checked = True then begin
- Form1.Caption := FormCaption + 'AFFINE (LINEAR)';
- RenderScansAffine;
- end
- else begin
- Form1.Caption := FormCaption + 'PERSPECTIVE (PERFECT)';
- RenderScansPerspective;
- end;
- end;
-
- procedure DrawTexturedPolygon;
- var
- i, z: Integer;
- uv: Single;
- begin
- Screen.Cursor := crHourglass;
-
- try
- begin
- //Clear bitmap
- with Bitmap.Canvas do begin
- Brush.Color := clBlack;
- Brush.Style := bsSolid;
- Fillrect(Rect(0, 0, Bitmap.Width, Bitmap.Height));
- end;
-
- //Read vertex coords
- with Form1 do begin
- v[0].x := StrToInt(Edit1.Text);
- v[0].y := StrToInt(Edit2.Text);
- v[0].z := StrToInt(Edit3.Text);
- v[1].x := StrToInt(Edit4.Text);
- v[1].y := StrToInt(Edit5.Text);
- v[1].z := StrToInt(Edit6.Text);
- v[2].x := StrToInt(Edit7.Text);
- v[2].y := StrToInt(Edit8.Text);
- v[2].z := StrToInt(Edit9.Text);
- v[3].x := StrToInt(Edit10.Text);
- v[3].y := StrToInt(Edit11.Text);
- v[3].z := StrToInt(Edit12.Text);
- end;
-
- //Texture coord's (floats)
- uv := Form1.TrackBar1.Position;
- v[0].u := 0.0;
- v[0].v := 0.0;
- v[1].u := uv;
- v[1].v := 0.0;
- v[2].u := uv;
- v[2].v := uv;
- v[3].u := 0.0;
- v[3].v := uv;
-
- //Perspective projection
- for i := 0 To 3 do begin
- z := v[i].z;
- if z = 0 Then z := 1; // Catch divide by zero.
- v[i].sx := (v[i].x * HSCALE div z) + MID_X;
- v[i].sy := (-v[i].y * VSCALE div z) + MID_Y;
- end;
-
- InitScans; //Set up the scanline edge list
-
- AddEdge(0,1); //Add an edge to the edge list
- AddEdge(1,2);
- AddEdge(2,3);
- AddEdge(3,0);
-
- RenderScans; //Draw any spans in the edge list
-
- Form1.Image2.Canvas.Draw(0, 0, Bitmap); //Blit to screen
- end;
- finally
- Screen.Cursor := crDefault;
- end;
- end;
-
-
- procedure TForm1.FormPaint(Sender: TObject);
- begin
- DrawTexturedPolygon;
- end;
-
- procedure TForm1.SpeedButton1Click(Sender: TObject);
- begin
- DrawTexturedPolygon;
- end;
-
- procedure TForm1.FormDestroy(Sender: TObject);
- begin
- Bitmap.Free;
- end;
-
- end.
-
-
-