unit Main_u;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, Math;
type
{ Project types }
Vector3D = record
x, y, z: double;
end;
Triangle = array[0..2] of Vector3D;
Matrix4x4 = array[0..3, 0..3] of double;
Mesh = array of Triangle;
{ TfrmMain }
TfrmMain = class(TForm)
pbDisplay: TPaintBox;
tmRefresh: TTimer;
procedure FormCreate(Sender: TObject);
procedure pbDisplayPaint(Sender: TObject);
procedure tmRefreshTimer(Sender: TObject);
private
procedure SetTriangle(n: integer; v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z: real);
procedure MultiplyMatrixVector(inVector: Vector3D; var outVector: Vector3D; inMatrix: Matrix4x4);
procedure DrawTriangles(var bmp: TBitmap);
function MultiplyMatrixVector(inVector: Vector3D; inMatrix: Matrix4x4): Vector3D;
public
end;
var
frmMain: TfrmMain;
meshCube: Mesh; // Create a cube mesh
implementation
{$R *.lfm}
{ TfrmMain }
procedure TfrmMain.FormCreate(Sender: TObject);
begin
setlength(meshCube, 12);
// SOUTH
SetTriangle(0, 1, 0, 0, 0, 1, 0, 1, 1, 0);
SetTriangle(1, 0, 0, 0, 1, 1, 0, 1, 0, 0);
// EAST
SetTriangle(2, 1, 0, 0, 1, 1, 0, 1, 1, 1);
SetTriangle(3, 1, 0, 0, 1, 1, 1, 1, 0, 1);
// NORTH
SetTriangle(4, 1, 0, 1, 1, 1, 1, 0, 1, 1);
SetTriangle(5, 1, 0, 1, 0, 1, 1, 0, 0, 1);
// WEST
SetTriangle(6, 0, 0, 1, 0, 1, 1, 0, 1, 0);
SetTriangle(7, 0, 0, 1, 0, 1, 0, 0, 0, 0);
// TOP
SetTriangle(8, 0, 1, 0, 0, 1, 1, 1, 1, 1);
SetTriangle(9, 0, 1, 0, 1, 1, 1, 1, 1, 0);
// BOTTOM
SetTriangle(10, 1, 0, 1, 0, 0, 1, 0, 0, 0);
SetTriangle(11, 1, 0, 1, 0, 0, 0, 1, 0, 0);
end;
procedure TfrmMain.pbDisplayPaint(Sender: TObject);
var
bmp: TBitmap;
begin
// Bitmap manipulation
bmp := TBitmap.Create;
bmp.Width := pbDisplay.Width;
bmp.Height := pbDisplay.Height;
bmp.Canvas.Brush.Color := clBlack;
bmp.Canvas.Pen.Color := clWhite;
bmp.Canvas.Pen.Width := 1;
// Drawing routine
DrawTriangles(bmp);
// Draw test line
bmp.Canvas.Line(20,20, 300, 300);
// Bitmap manipulation
pbDisplay.Canvas.Draw(0, 0, bmp);
bmp.Free;
end;
procedure TfrmMain.tmRefreshTimer(Sender: TObject);
begin
pbDisplay.Refresh;
end;
procedure TfrmMain.SetTriangle(n: integer; v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z: real);
var
vec: Vector3D;
tri: Triangle;
begin
vec.x := v1x;
vec.y := v1y;
vec.Z := v1z;
tri[0] := vec;
vec.x := v2x;
vec.y := v2y;
vec.Z := v2z;
tri[1] := vec;
vec.x := v3x;
vec.y := v3y;
vec.Z := v3z;
tri[2] := vec;
meshCube[n] := tri; // Pascal will copy the variable to the matrix
end;
procedure TfrmMain.MultiplyMatrixVector(inVector: Vector3D; var outVector: Vector3D; inMatrix: Matrix4x4);
var
w: double;
begin
outVector.x := inVector.x * inMatrix[0, 0] + inVector.y * inMatrix[1, 0] + inVector.z * inMatrix[2, 0] + inMatrix[3, 0];
outVector.y := inVector.x * inMatrix[0, 1] + inVector.y * inMatrix[1, 1] + inVector.z * inMatrix[2, 1] + inMatrix[3, 1];
outVector.z := inVector.x * inMatrix[0, 2] + inVector.y * inMatrix[1, 2] + inVector.z * inMatrix[2, 2] + inMatrix[3, 2];
w := inVector.x * inMatrix[0, 3] + inVector.y * inMatrix[1, 3] + inVector.z * inMatrix[2, 3] + inMatrix[3, 3];
if w <> 0 then
begin
outVector.x := outVector.x / w;
outVector.y := outVector.y / w;
outVector.z := outVector.z / w;
end;
end;
function TfrmMain.MultiplyMatrixVector(inVector: Vector3D; inMatrix: Matrix4x4): Vector3D;
var
x, y, z, w: double;
r: Vector3D;
begin
x := inVector.x * inMatrix[0, 0] + inVector.y * inMatrix[1, 0] + inVector.z * inMatrix[2, 0] + inMatrix[3, 0];
y := inVector.x * inMatrix[0, 1] + inVector.y * inMatrix[1, 1] + inVector.z * inMatrix[2, 1] + inMatrix[3, 1];
z := inVector.x * inMatrix[0, 2] + inVector.y * inMatrix[1, 2] + inVector.z * inMatrix[2, 2] + inMatrix[3, 2];
w := inVector.x * inMatrix[0, 3] + inVector.y * inMatrix[1, 3] + inVector.z * inMatrix[2, 3] + inMatrix[3, 3];
if w <> 0 then
begin
r.x := x / w;
r.y := y / w;
r.z := z / w;
end;
Result := r;
end;
procedure TfrmMain.DrawTriangles(var bmp: TBitmap);
var
fNear: real; // Near plane
fFar: real; // Far plane
fFov: real; // Field of view
fAspectRatio: real; // Aspect ratio
fFovRad: real; // Tangent calculation
matProj: Matrix4x4; // Projection matrix
triProjected: Triangle; // Projected triangle
tri: Triangle; // A triangle iterator
begin
// Projection matrix
fNear := 0.1;
fFar := 1000.0;
fFov := 90.0;
fAspectRatio := pbDisplay.Height / pbDisplay.Width;
fFovRad := 1 / tan(fFov * 0.5 / 180 * pi);
matProj[0, 0] := fAspectRatio * fFovRad;
matProj[1, 1] := fFovRad;
matProj[2, 2] := fFar / (fFar - fNear);
matProj[3, 2] := (-fFar * fNear) / (fFar - fNear);
matProj[2, 3] := 1.0;
matProj[3, 3] := 0.0;
// Calculate projection
for tri in meshCube do
begin
triProjected[0] := MultiplyMatrixVector(tri[0], matProj);
triProjected[1] := MultiplyMatrixVector(tri[1], matProj);
triProjected[2] := MultiplyMatrixVector(tri[2], matProj);
//MultiplyMatrixVector(tri[0], triProjected[0], matProj);
//MultiplyMatrixVector(tri[1], triProjected[1], matProj);
//MultiplyMatrixVector(tri[2], triProjected[2], matProj);
end;
bmp.Canvas.Line(trunc(triProjected[0].x), trunc(triProjected[0].y), trunc(triProjected[1].x), trunc(triProjected[1].y));
bmp.Canvas.Line(trunc(triProjected[1].x), trunc(triProjected[1].y), trunc(triProjected[2].x), trunc(triProjected[2].y));
bmp.Canvas.Line(trunc(triProjected[2].x), trunc(triProjected[2].y), trunc(triProjected[0].x), trunc(triProjected[0].y));
end;
end.