Friday, October 22, 2010

Vectors, every now and then...

The 3d engine heavily uses the TVector type, aside from many operators, it also has a functions like:

  • DotProduct
    • Helps finding the angle between two vectors
  • Distance
    • The distance between two products
  • AngleBetween
    • Returns a number between 0 and 359, which corresponds to the angle
  • Rotate
    • Rotates a given vector through a matrix
  • Normal
    • Calculates a vector perpendicular to the plane (given by three vectors)
  • Length
    • Calculates the length of a vector
  • Normalize
    • Normalizes a vector (the vector is transformed to a magnitude of 1)

unit BB.Math.Vector;

interface

uses
  BB.Types, BB.Math.Matrix;

const
  V_RIGHT = 0;
  V_UP = 1;
  V_PN = 2;

type
  PVector = ^TVector;
  TVector = record
  private
    FX,
    FY,
    FZ,
    FW: TFloat;

    function GetIndex(aIndex: integer): TFloat;
    procedure SetIndex(aIndex: integer; const aValue: TFloat);
    procedure SetX(aValue: TFloat); inline;
    procedure SetY(aValue: TFloat); inline;
    procedure SetZ(aValue: TFloat); inline;
    procedure SetW(aValue: TFloat); inline;
  public
    constructor Create(aX, aY, aZ: TFloat); overload;
    constructor Create(const aVector: TVector); overload;
    class operator Add(const aU, aV: TVector): TVector; inline;
    class operator Subtract(const aU, aV: TVector): TVector; inline;
    class operator Divide(const aVector: TVector; aFactor: TFloat): TVector; inline;
    class operator Multiply(const aVector: TVector; aFactor: TFloat): TVector; overload; inline;
    class operator Multiply(const aU, aV: TVector): TVector; overload; inline;
    class operator Negative(const aVector: TVector): TVector; inline;
    class operator Equal(const aU, aV: TVector): boolean; inline;
    class operator NotEqual(const aU, aV: TVector): boolean; inline;
    class operator GreaterThan(const aU, aV: TVector): boolean; inline;
    class operator GreaterThanOrEqual(const aU, b: TVector): boolean; inline;
    class operator LessThan(const a, b: TVector): boolean; inline;
    class operator LessThanOrEqual(const a, b: TVector): boolean; inline;

    class function Compare(const u, v: TVector): integer; static;
    class function DotProduct(const u, v: TVector): TFloat; static;
    class function Distance(const u, v: TVector): TFloat; static;
    class function AngleBetween(const u, v: TVector): TFloat; static;
    class function Angle(const u, v: TVector): TFloat; static;
    class function DotUnit(const u, v: TVector): TFloat; static;

    procedure Rotate(const aVector: TVector; const aMatrix: TMatrix);
    procedure CrossProduct(const u, v: TVector);
    procedure Normal(const v1, v2, v3: TVector);
    function IsEmpty: boolean; inline;
    procedure Clear; inline;
    function Length: TFloat;
    procedure Normalize; inline;
    function Max: TFloat; inline;
    function Min: TFloat; inline;
    procedure Abs;
    procedure Random(aMin, aMax: TFloat);
    class function Null: TVector; static;

    property Points[index: integer]: TFloat read GetIndex write SetIndex; default;
    property Pitch: TFloat read FX write SetX;
    property Yaw: TFloat read FY write SetY;
    property Roll: TFloat read FZ write SetZ;
    property X: TFloat read FX write SetX;
    property Y: TFloat read FY write SetY;
    property Z: TFloat read FZ write SetZ;
    property W: TFloat read FW write SetW;
  end;

implementation

uses
  Math,
  BB.Math;

{ TVector }

procedure TVector.Abs;
begin
  FX := System.Abs(FX);
  FY := System.Abs(FY);
  FZ := System.Abs(FZ);
end;

class operator TVector.Add(const aU, aV: TVector): TVector;
begin
  result.FX := aU.FX + aV.FX;
  result.FY := aU.FY + aV.FY;
  result.FZ := aU.FZ + aV.FZ;
end;

class function TVector.Angle(const u, v: TVector): TFloat;
var
  length: TFloat;

begin
  length := u.Length * v.Length;
  Result := DotProduct(u, v) / length;
end;

class function TVector.AngleBetween(const u, v: TVector): TFloat;
var
  m, dot: TFloat;

begin
  dot := DotProduct(u, v);
  m := u.Length * v.Length;
  Result := ArcCos(dot / m);
  if IsNan(Result) then
    Result := 0;
end;

constructor TVector.Create(aX, aY, aZ: TFloat);
begin
  FX := aX;
  FY := aY;
  FZ := aZ;
  FW := 1;
end;

class function TVector.Compare(const u, v: TVector): integer;
var
  d1, d2: TFloat;

begin
  d1 := u.Length;
  d2 := v.Length;

  if d2 > d1 then
    result := -1
  else
    if d1 > d2 then
      result := 1
    else
      result := 0;
end;

constructor TVector.Create(const aVector: TVector);
begin
  FX := aVector.FX;
  FY := aVector.FY;
  FZ := aVector.FZ;
  FW := aVector.FW;
end;

procedure TVector.CrossProduct(const u, v: TVector);
begin
  FX := (u.FY * v.FZ) - (u.FZ * v.FY);
  FY := (u.FZ * v.FX) - (u.FX * v.FZ);
  FZ := (u.FX * v.FY) - (u.FY * v.FX);
end;

class function TVector.Distance(const u, v: TVector): TFloat;
var
  c: TVector;

begin
  c := u - v;
  result := c.Length;
end;

class operator TVector.Divide(const aVector: TVector; aFactor: TFloat): TVector;
var
  inv: TFloat;
  
begin
  inv := 1 / aFactor;
  Result.FX := aVector.FX * inv;
  Result.FY := aVector.FY * inv;
  Result.FZ := aVector.FZ * inv;
end;

class function TVector.DotProduct(const u, v: TVector): TFloat;
begin
  result := (u.FX * v.FX) + (u.FY * v.FY) + (u.FZ * v.FZ);
end;

class function TVector.DotUnit(const u, v: TVector): TFloat;
var
  length: TFloat;

begin
  length := u.Length * v.Length;
  result := TVector.DotProduct(u, v) * (1 / length);
end;

function TVector.IsEmpty: boolean;
begin
  result := (FX = 0) and (FY = 0) and (FZ = 0);
end;

class operator TVector.Equal(const aU, aV: TVector): boolean;
begin
  result := (aU.FX = aV.FX) and (aU.FY = aV.FY) and (aU.FZ = aV.FZ);
end;

function TVector.GetIndex(aIndex: integer): TFloat;
begin
  case aIndex of
    0: result := FX;
    1: result := FY;
    2: result := FZ;
    3: result := FW;
  else
    result := 0;
  end;
end;

class operator TVector.GreaterThan(const aU, aV: TVector): boolean;
begin
  result := aU.Length > aV.Length;
end;

class operator TVector.GreaterThanOrEqual(const aU, b: TVector): boolean;
begin
  result := aU.Length >= b.Length;
end;

function TVector.Length: TFloat;
begin
  Result := Sqrt(Sqr(FX) + Sqr(FY) + Sqr(FZ));
  if Result = 0 then
    Result := 0.00001;
end;

class operator TVector.LessThan(const a, b: TVector): boolean;
begin
  result := a.Length < b.Length;
end;

class operator TVector.LessThanOrEqual(const a, b: TVector): boolean;
begin
  result := a.Length <= b.Length;
end;

function TVector.Max: TFloat;
begin
  Result := FX;
  if FY > Result then
    Result := FY;
  if FZ > Result then
    Result := FZ;
end;

function TVector.Min: TFloat;
begin
  Result := FX;
  if FY < Result then
    Result := FY;
  if FZ < Result then
    Result := FZ;
end;

class operator TVector.Multiply(const aU, aV: TVector): TVector;
begin
  result.FX := aU.FX * aV.FX;
  result.FY := aU.FY * aV.FY;
  result.FZ := aU.FZ * aV.FZ;
end;

class operator TVector.Multiply(const aVector: TVector; aFactor: TFloat): TVector;
begin
  result.FX := aVector.FX * aFactor;
  result.FY := aVector.FY * aFactor;
  result.FZ := aVector.FZ * aFactor;
end;

class operator TVector.Negative(const aVector: TVector): TVector;
begin
  result.FX := -result.FX;
  result.FY := -result.FY;
  result.FZ := -result.FZ;
end;

procedure TVector.Normal(const v1, v2, v3: TVector);
begin
  CrossProduct(v2 - v1, v3 - v1);
end;

procedure TVector.Normalize;
var
  l: TFloat;

begin
  l := 1 / Length;
  FX := FX * l;
  FY := FY * l;
  FZ := FZ * l;
//  W := W * l;
end;

class operator TVector.NotEqual(const aU, aV: TVector): boolean;
begin
  result := (aU.FX <> aV.FX) or (aU.FY <> aV.FY) or (aU.FZ <> aV.FZ);
end;

class function TVector.Null: TVector;
begin
  result.Clear;
end;

procedure TVector.Random(aMin, aMax: TFloat);
begin
  FX := Rnd(aMin, aMax);
  FY := Rnd(aMin, aMax);
  FZ := Rnd(aMin, aMax);
end;

procedure TVector.Rotate(const aVector: TVector; const aMatrix: TMatrix);
begin
  FX := DotProduct(aVector, TVector(aMatrix._4x4[0]));
  FY := DotProduct(aVector, TVector(aMatrix._4x4[1]));
  FZ := DotProduct(aVector, TVector(aMatrix._4x4[2]));
end;

procedure TVector.SetIndex(aIndex: integer; const aValue: TFloat);
begin
  case aIndex of
    0: FX := aValue;
    1: FY := aValue;
    2: FZ := aValue;
    3: FW := aValue;
  end;
end;

procedure TVector.SetW(aValue: TFloat);
begin
  FW := aValue;
end;

procedure TVector.SetX(aValue: TFloat);
begin
  FX := aValue;
end;

procedure TVector.SetY(aValue: TFloat);
begin
  FY := aValue;
end;

procedure TVector.SetZ(aValue: TFloat);
begin
  FZ := aValue;
end;

class operator TVector.Subtract(const aU, aV: TVector): TVector;
begin
  result.FX := aU.FX - aV.FX;
  result.FY := aU.FY - aV.FY;
  result.FZ := aU.FZ - aV.FZ;
end;

procedure TVector.Clear;
begin
  FX := 0;
  FY := 0;
  FZ := 0;
  FW := 1;
end;

end.
All cameras, rotations, lights, etc uses this unit

No comments:

Post a Comment