Showing posts with label Vectors. Show all posts
Showing posts with label Vectors. Show all posts

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