Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 25

Thread: A Simple OpenGL Framework - Your input required...

  1. #11

    A Simple OpenGL Framework - Your input required...

    So is this better??

    [pascal]
    unit vector;

    interface

    type
    TScalar = single;

    TVector = class( TObject )
    public
    x : TScalar;
    y : TScalar;
    z : TScalar; // x,y,z coordinates

    constructor Create( aX : TScalar = 0; aY : TScalar = 0; aZ : TScalar = 0 ); overload;
    constructor Create( const vec : TVector ); overload;


    // vector assignment
    function Assign( const vec : TVector ) : TVector; overload;
    function Assign( aX : TScalar = 0; aY : TScalar = 0; aZ : TScalar = 0 ) : TVector; overload;

    // vector equality
    function IsEqual( const vec : TVector ) : Boolean;

    // vector inequality
    function IsNotEqual( const vec : TVector ) : Boolean;

    // vector add
    procedure Add( const vec : TVector; var aAddedVector : TVector );

    // vector Increment
    function Inc( const vec : TVector ) : TVector;

    // vector subtraction
    procedure Subtract( const vec : TVector; var aSubtractedVector : TVector );

    // vector Decrement
    function Dec( const vec : TVector ) : TVector;

    // vector negation
    procedure Negative( var aNegativeVector : TVector );

    // vector Positivisation
    procedure Positive( var aPositiveVector : TVector );

    // Scale
    function Scale( s : TScalar ) : TVector;

    // Multiply
    function Multiply( vec : TVector ) : TVector;

    // Divide
    function Divide( vec : TVector ) : TVector;

    // cross product
    procedure CrossProduct( const vec : TVector; var aCrossProductedVector : TVector );

    // dot product
    function DotProduct( const vec : TVector ) : TScalar;


    // Interpolate
    function Interpolate( const vec : TVector; Amount : TScalar ) : TVector;

    // length of vector
    function Length : TScalar;

    // square of the vector length
    function LengthSquare : TScalar;

    // Set the length of vector
    function SetLength( LengthLimit : TScalar ) : TVector;

    // return the unit vector
    function UnitVector : TVector;

    // normalize this vector
    procedure Normalize;

    // return angle between two vectors
    function Angle( const vec : TVector ) : TScalar;

    // reflect this vector off surface with normal vector
    procedure Reflection( const normal : TVector; var aReflectionVector : TVector );
    {
    const CVector vec(*this | 1); // normalize this vector
    result := (vec - normal * 2.0 * (vec % normal)) * !*this;
    }
    end;

    // Taken from Martin Beaudet's clVecteurs
    function VectArcTan2( Y, X : Extended ) : Extended;
    function VectArcCos( X : Single ) : Single;
    function VectArcSin( X : Single ) : Single;
    function RadianToDegrees( Radian : Single ) : Single;
    function DegreesToRadian( Degrees : Single ) : Single;

    implementation

    function TVector.Assign( const vec : TVector ) : TVector;
    begin
    x := vec.x;
    y := vec.y;
    z := vec.z;

    result := self;
    end;

    function TVector.Assign(aX, aY, aZ: TScalar): TVector;
    begin
    x := aX;
    y := aY;
    z := aZ;

    result := self;
    end;

    constructor TVector.Create( aX : TScalar = 0; aY : TScalar = 0; aZ : TScalar = 0 );
    begin
    inherited Create;
    x := aX;
    y := aY;
    z := aZ;
    end;

    constructor TVector.Create( const vec : TVector );
    begin
    inherited Create;
    x := vec.x;
    y := vec.y;
    z := vec.z;
    end;

    function TVector.IsEqual( const vec : TVector ) : Boolean;
    begin
    result := ( ( x = vec.x ) and ( y = vec.y ) and ( z = vec.z ) );
    end;

    function TVector.IsNotEqual( const vec : TVector ) : Boolean;
    begin
    result := not IsEqual( vec );
    end;

    procedure TVector.Add( const vec : TVector; var aAddedVector : TVector );
    begin
    if aAddedVector <> nil then
    aAddedVector.Assign( x + vec.x, y + vec.y, z + vec.z )
    end;

    procedure TVector.Subtract( const vec : TVector; var aSubtractedVector : TVector );
    begin
    if aSubtractedVector <> nil then
    aSubtractedVector.Assign( x - vec.x, y - vec.y, z - vec.z );
    end;

    procedure TVector.Negative( var aNegativeVector : TVector );
    begin
    if aNegativeVector <> nil then
    aNegativeVector.Assign( -x, -y, -z );
    end;

    function TVector.Scale( s : TScalar ) : TVector;
    begin
    x := x * s;
    y := y * s;
    z := z * s;

    result := self;
    end;

    function TVector.Multiply( vec : TVector ) : TVector;
    begin
    x := x * vec.x;
    y := y * vec.y;
    z := z * vec.z;

    result := self;
    end;

    procedure TVector.CrossProduct( const vec : TVector; var aCrossProductedVector : TVector );
    begin
    if aCrossProductedVector <> nil then
    aCrossProductedVector.Assign( y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x );
    end;

    function TVector.DotProduct( const vec : TVector ) : TScalar;
    begin
    result := x * vec.x + y * vec.y + z * vec.z;
    end;

    function TVector.Interpolate( const vec : TVector; Amount : TScalar ) : TVector;
    begin
    X := X + ( vec.X - X ) * Amount;
    Y := Y + ( vec.Y - Y ) * Amount;
    Z := Z + ( vec.Z - Z ) * Amount;

    result := self;
    end;

    function TVector.Length : TScalar;
    begin
    result := sqrt( ( x * x + y * y + z * z ) );
    end;

    function TVector.LengthSquare: TScalar;
    begin
    result := ( x * x + y * y + z * z );
    end;

    function TVector.UnitVector : TVector;
    var
    Len : Single;
    begin
    // Store the Length
    Len := Length;

    x := x / Len;
    y := y / Len;
    z := z / Len;

    result := self;
    end;

    procedure TVector.Normalize;
    var
    ScaleValue, Len : Single;
    begin
    Len := Length;
    if Len = 0.0 then
    Exit;

    ScaleValue := 1.0 / Len;

    Scale( ScaleValue );
    end;

    function TVector.Angle( const vec : TVector ) : TScalar;
    begin
    result := VectArcCos( self.DotProduct( vec ) );
    end;

    procedure TVector.Reflection( const normal : TVector; var aReflectionVector : TVector );
    begin
    if aReflectionVector <> nil then
    begin
    aReflectionVector.Assign( self );
    aReflectionVector.Subtract( normal.Scale( 2.0 * ( aReflectionVector.DotProduct( normal ) ) ), aReflectionVector );
    aReflectionVector.Scale( Length );
    aReflectionVector.Normalize; // normalize this vector
    end;
    end;

    function TVector.Divide( vec : TVector ) : TVector;
    begin
    x := x / vec.x;
    y := y / vec.y;
    z := z / vec.z;

    result := self;
    end;

    procedure TVector.Positive( var aPositiveVector : TVector );
    begin
    if aPositiveVector <> nil then
    aPositiveVector.Assign( +x, +y, +z );
    end;

    function TVector.SetLength( LengthLimit : TScalar ) : TVector;
    begin
    result := Scale( LengthLimit / Length );
    end;

    function TVector.Dec( const vec : TVector ) : TVector;
    begin
    x := x - vec.x;
    y := y - vec.y;
    z := z - vec.z;

    result := self;
    end;

    function TVector.Inc( const vec : TVector ) : TVector;
    begin
    x := x + vec.x;
    y := y + vec.y;
    z := z + vec.z;

    result := self;
    end;

    function VectArcTan2( Y, X : Extended ) : Extended;
    asm
    FLD Y
    FLD X
    FPATAN
    FWAIT
    end;

    function VectArcCos( X : Single ) : Single;
    begin
    Result := VectArcTan2( Sqrt( 1 - X * X ), X );
    end;

    function VectArcSin( X : Single ) : Single;
    begin
    result := VectArcTan2( X, Sqrt( 1 - X * X ) );
    end;

    function RadianToDegrees( Radian : Single ) : Single;
    begin
    result := Radian * ( 180 / PI );
    end;

    function DegreesToRadian( Degrees : Single ) : Single;
    begin
    result := Degrees * ( PI / 180 );
    end;

    end.
    [/pascal]

    Next on the list is a basic camera class.
    <br /><br />There are a lot of people who are dead while they are still alive. I want to be alive until the day I die.<br />-= Paulo Coelho =-

  2. #12

    A Simple OpenGL Framework - Your input required...

    Yes, that looks cleaner.

    UnitVector and Normalize do the same thing. No need to have two methods doing the same thing. Collapse it into one method.
    Code:
    function TVector.Normalize&#58; TVector;
    var
      Len&#58; TScalar;
    begin
      Result &#58;= Self;
      Len &#58;= Length;
      if Len > 0.0 then
        Scale&#40;1.0 / Len&#41;;
    end;
    Some other things to mention there. You have TScalar declared, so use it consistently. You have used Single in some places throughout the code and TScalar everywhere else.

    A quick optimization: When if..then statements are used, try to have the case that is most often true in the 'then' part. This improves branch prediction performance in the CPU.

    I've almost finished my Vector unit. It would be interesting to compare performance between the two.

  3. #13

    A Simple OpenGL Framework - Your input required...

    As TVector is a class then you don't need to use var for returning/changing instance. For example:
    [pascal]procedure TVector.Add( const vec : TVector; var aAddedVector : TVector );
    begin
    if aAddedVector <> nil then
    aAddedVector.Assign( x + vec.x, y + vec.y, z + vec.z )
    end;[/pascal]

    will be better implemented as (will have one less memory dereference):

    [pascal]procedure TVector.Add( const vec : TVector; aAddedVector : TVector );
    begin
    if aAddedVector <> nil then
    aAddedVector.Assign( x + vec.x, y + vec.y, z + vec.z )
    end;[/pascal]

    PS. You probably know what "const vec : TVector" in ObjectPascal doesn't prevent from modifying vec internal data.
    There are only 10 types of people in this world; those who understand binary and those who don't.

  4. #14

    A Simple OpenGL Framework - Your input required...

    True. I saw that, but didn't think much about it.

    This article is an interesting read.
    http://dennishomepage.gugs-cats.dk/C...edInDelphi.doc (~1MB)

    It is chock full of optimization tips. Small simple, some complex, and right down to getting a few CPU cycles shaved off a small routine.

  5. #15

    A Simple OpenGL Framework - Your input required...

    TVector.SetLength should be better implemented as:
    [pascal]function TVector.SetLength( LengthLimit : TScalar ) : TVector;
    begin
    result := Scale( (LengthLimit*LengthLimit) / LengthSquare );
    end;[/pascal]

    Hmmm, something really wrong with:
    [pascal]procedure TVector.Reflection( const normal : TVector; var aReflectionVector : TVector );
    begin
    ...
    aReflectionVector.Scale( Length );
    aReflectionVector.Normalize; // normalize this vector
    ...
    end;[/pascal]
    There are only 10 types of people in this world; those who understand binary and those who don't.

  6. #16

    A Simple OpenGL Framework - Your input required...

    Quote Originally Posted by Clootie
    TVector.SetLength should be better implemented as:
    [pascal]function TVector.SetLength( LengthLimit : TScalar ) : TVector;
    begin
    result := Scale( (LengthLimit*LengthLimit) / LengthSquare );
    end;[/pascal]
    Unfortunately, no. You don't get the same result. For example, let's say the vector is 3 units long and we want to make it 4 units.

    Scale := Limit / Length := 4 / 3 := 1.3
    Scale := (Limit * Limit) / LengthSquare := (4 * 4) / (3 * 3) := 16 / 9 := 1.7

    Different scale values. The first one is correct.

    Hmmm, something really wrong with:
    [pascal]procedure TVector.Reflection( const normal : TVector; var aReflectionVector : TVector );
    begin
    ...
    aReflectionVector.Scale( Length );
    aReflectionVector.Normalize; // normalize this vector
    ...
    end;[/pascal]
    Yes. Scaling then normalizing means the scale is a wasted operation.

  7. #17

    A Simple OpenGL Framework - Your input required...

    For comparison, here is my Vector unit. It is not yet tested for accuracy. It compiles under both Delphi and FPC. Unfortunately, under FPC if you want operator overloads, you cannot use Delphi compatibility mode. Without Delphi compatibility mode, you do not have the implied Result variable in functions. Hence the old-school method of assigning to the function name. I used to do that all the time when I was first learning Pascal at uni (Mac Pascal on a Macintosh 128K), but since I've been using Delphi I've become so used to having the Result variable. You will note that there was one function (VectorCreate) where I had no choice but to use the Result variable in Delphi because it failed compilation using the function name.

    [pascal]unit Vector;

    interface

    uses
    Scalar, Matrix;

    type
    PVector = ^TVector;
    TVector = record
    case Integer of
    0:
    (
    x: TScalar;
    y: TScalar;
    z: TScalar;
    );
    1:
    (
    v: array [0..3] of TScalar;
    );
    end;

    {$IFDEF FPC}
    operator + (const v1: TVector; const v2: TVector) Result: TVector;
    operator - (const v1: TVector; const v2: TVector) Result: TVector;
    operator * (const v1: TVector; const v2: TVector) Result: TVector;
    operator * (const v1: TVector; s: TScalar) Result: TVector;
    operator * (const v1: TVector; const m: TMatrix) Result: TVector;
    operator / (const v1: TVector; s: TScalar) Result: TVector;
    operator = (const v1: TVector; const v2: TVector) Result: Boolean;
    {$ENDIF FPC}

    function VectorAdd(const v1: TVector; const v2: TVector): TVector;
    function VectorSubtract(const v1: TVector; const v2: TVector): TVector;
    function VectorMultiply(const v1: TVector; const v2: TVector): TVector; overload;
    function VectorMultiply(const v1: TVector; s: TScalar): TVector; overload;
    function VectorDivide(const v1: TVector; s: TScalar): TVector;
    function VectorEqual(const v1: TVector; const v2: TVector): Boolean;
    function VectorDot(const v1: TVector; const v2: TVector): TScalar;
    function VectorCross(const v1: TVector; const v2: TVector): TVector;
    function VectorLength(const v1: TVector): TScalar;
    function VectorLengthSq(const v1: TVector): TScalar;
    function VectorInterpolate(const v1: TVector; const v2: TVector; t: TScalar): TVector;
    procedure VectorNormalize(var v1: TVector);
    function VectorNormal(const v1: TVector): TVector;
    function VectorNormalTri(const v1: TVector; const v2: TVector; const v3: TVector): TVector;
    function VectorAngle(const v1: TVector; const v2: TVector): TScalar;

    function VectorCreate(const v1: TVector): TVector; overload;
    function VectorCreate(x, y, z: TScalar): TVector; overload;
    function VectorCreate(a: PScalar): TVector; overload;
    procedure VectorToArray(const v1: TVector; a: PScalar);

    const
    NullVector: TVector = (x: 0.0; y: 0.0; z: 0.0);
    FwdVector: TVector = (x: 0.0; y: 0.0; z: 1.0);
    LeftVector: TVector = (x: 1.0; y: 0.0; z: 0.0);
    UpVector: TVector = (x: 0.0; y: 1.0; z: 0.0);

    implementation

    {$IFDEF FPC}
    operator + (const v1: TVector; const v2: TVector) Result: TVector;
    begin
    Result.x := v1.x + v2.x;
    Result.y := v1.y + v2.y;
    Result.z := v1.z + v2.z;
    end;

    operator - (const v1: TVector; const v2: TVector) Result: TVector;
    begin
    Result.x := v1.x - v2.x;
    Result.y := v1.y - v2.y;
    Result.z := v1.z - v2.z;
    end;

    operator * (const v1: TVector; const v2: TVector) Result: TVector;
    begin
    Result.x := v1.x * v2.x;
    Result.y := v1.y * v2.y;
    Result.z := v1.z * v2.z;
    end;

    operator * (const v1: TVector; s: TScalar) Result: TVector;
    begin
    Result.x := v1.x * s;
    Result.y := v1.y * s;
    Result.z := v1.z * s;
    end;

    operator * (const v1: TVector; const m: TMatrix) Result: TVector;
    begin
    Result.x := v1.x * m.m[0, 0] + v1.y * m.m[1, 0] + v1.z * m.m[2, 0] + m.m[3, 0];
    Result.y := v1.x * m.m[0, 1] + v1.y * m.m[1, 1] + v1.z * m.m[2, 1] + m.m[3, 1];
    Result.z := v1.x * m.m[0, 2] + v1.y * m.m[1, 2] + v1.z * m.m[2, 2] + m.m[3, 2];
    end;

    operator / (const v1: TVector; s: TScalar) Result: TVector;
    var
    r: TScalar;
    begin
    r := 1.0 / s;
    Result.x := v1.x * r;
    Result.y := v1.y * r;
    Result.z := v1.z * r;
    end;

    operator = (const v1: TVector; const v2: TVector) Result: Boolean;
    begin
    Result := (v1.x = v2.x) and (v1.y = v2.y) and (v1.z = v2.z);
    end;
    {$ENDIF FPC}

    function VectorAdd(const v1: TVector; const v2: TVector): TVector;
    begin
    VectorAdd.x := v1.x + v2.x;
    VectorAdd.y := v1.y + v2.y;
    VectorAdd.z := v1.z + v2.z;
    end;

    function VectorSubtract(const v1: TVector; const v2: TVector): TVector;
    begin
    VectorSubtract.x := v1.x - v2.x;
    VectorSubtract.y := v1.y - v2.y;
    VectorSubtract.z := v1.z - v2.z;
    end;

    function VectorMultiply(const v1: TVector; const v2: TVector): TVector;
    begin
    VectorMultiply.x := v1.x * v2.x;
    VectorMultiply.y := v1.y * v2.y;
    VectorMultiply.z := v1.z * v2.z;
    end;

    function VectorMultiply(const v1: TVector; s: TScalar): TVector;
    begin
    VectorMultiply.x := v1.x * s;
    VectorMultiply.y := v1.y * s;
    VectorMultiply.z := v1.z * s;
    end;

    function VectorDivide(const v1: TVector; s: TScalar): TVector;
    var
    r: TScalar;
    begin
    r := 1.0 / s;
    VectorDivide.x := v1.x * r;
    VectorDivide.y := v1.y * r;
    VectorDivide.z := v1.z * r;
    end;

    function VectorEqual(const v1: TVector; const v2: TVector): Boolean;
    begin
    VectorEqual := (v1.x = v2.x) and (v1.y = v2.y) and (v1.z = v2.z);
    end;

    function VectorDot(const v1: TVector; const v2: TVector): TScalar;
    begin
    VectorDot := v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
    end;

    function VectorCross(const v1: TVector; const v2: TVector): TVector;
    begin
    VectorCross.x := v1.y * v2.z - v1.z * v2.y;
    VectorCross.y := v1.z * v2.x - v1.x * v2.z;
    VectorCross.z := v1.x * v2.y - v1.y * v2.x;
    end;

    function VectorLength(const v1: TVector): TScalar;
    begin
    VectorLength := Sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
    end;

    function VectorLengthSq(const v1: TVector): TScalar;
    begin
    VectorLengthSq := v1.x * v1.x + v1.y * v1.y + v1.z * v1.z;
    end;

    function VectorInterpolate(const v1: TVector; const v2: TVector; t: TScalar): TVector;
    begin
    VectorInterpolate.x := v1.x + (v2.x - v1.x) * t;
    VectorInterpolate.y := v1.y + (v2.y - v1.y) * t;
    VectorInterpolate.z := v1.z + (v2.z - v1.z) * t;
    end;

    procedure VectorNormalize(var v1: TVector);
    var
    s: TScalar;
    begin
    s := VectorLength(v1);
    if s > 0.0 then
    begin
    v1.x := v1.x * s;
    v1.y := v1.y * s;
    v1.z := v1.z * s;
    end;
    end;

    function VectorNormal(const v1: TVector): TVector;
    var
    s: TScalar;
    begin
    s := VectorLength(v1);
    if s > 0.0 then
    begin
    VectorNormal.x := v1.x * s;
    VectorNormal.y := v1.y * s;
    VectorNormal.z := v1.z * s;
    end
    else
    begin
    VectorNormal := NullVector;
    end;
    end;

    function VectorNormalTri(const v1: TVector; const v2: TVector; const v3: TVector): TVector;
    begin
    VectorNormalTri := VectorNormal(VectorCross(VectorSubtract(v2, v1), VectorSubtract(v3, v1)));
    end;

    function VectorAngle(const v1: TVector; const v2: TVector): TScalar;
    begin
    VectorAngle := VectorDot(VectorNormal(v1), VectorNormal(v2));
    end;

    function VectorProject(const v1: TVector; const v2: TVector): TVector;
    var
    t: TVector;
    begin
    t := VectorNormal(v2);
    VectorProject := VectorMultiply(t, VectorDot(v1, t));
    end;

    function VectorReflect(const v1: TVector; const n: TVector): TVector;
    begin
    if VectorAngle(v1, n) < 0.0 then
    VectorReflect := VectorAdd(v1, VectorMultiply(VectorProject(v1, n), -2.0))
    else
    VectorReflect := v1;
    end;

    function VectorCreate(const v1: TVector): TVector;
    begin
    VectorCreate := v1;
    end;

    function VectorCreate(x, y, z: TScalar): TVector;
    begin
    VectorCreate.x := x;
    VectorCreate.y := y;
    VectorCreate.z := z;
    end;

    function VectorCreate(a: PScalar): TVector;
    begin
    {$IFDEF FPC}
    Move(a^, VectorCreate, SizeOf(TVector));
    {$ELSE}
    Move(a^, Result, SizeOf(TVector));
    {$ENDIF FPC}
    end;

    procedure VectorToArray(const v1: TVector; a: PScalar);
    begin
    Move(v1, a^, SizeOf(TVector));
    end;

    end.[/pascal]

    I tried some timings using these functions. Note that they may be rough, but the more iterations reduces the effect of test setup overhead.

    Code:
    VectorCreate &#40;10 iterations&#41; = 7892 cycles &#40;789 cycles per iteration&#41;
    VectorCreate &#40;100 iterations&#41; = 4040 cycles &#40;40 cycles per iteration&#41;
    VectorCreate &#40;1000 iterations&#41; = 37200 cycles &#40;37 cycles per iteration&#41;
    VectorCreate &#40;10000 iterations&#41; = 370192 cycles &#40;37 cycles per iteration&#41;
    VectorLength &#40;10 iterations&#41; = 1100 cycles &#40;110 cycles per iteration&#41;
    VectorLength &#40;100 iterations&#41; = 4940 cycles &#40;49 cycles per iteration&#41;
    VectorLength &#40;1000 iterations&#41; = 45240 cycles &#40;45 cycles per iteration&#41;
    VectorLength &#40;10000 iterations&#41; = 448244 cycles &#40;44 cycles per iteration&#41;
    VectorNormalTri &#40;10 iterations&#41; = 3228 cycles &#40;322 cycles per iteration&#41;
    VectorNormalTri &#40;100 iterations&#41; = 27268 cycles &#40;272 cycles per iteration&#41;
    VectorNormalTri &#40;1000 iterations&#41; = 269816 cycles &#40;269 cycles per iteration&#41;
    VectorNormalTri &#40;10000 iterations&#41; = 2695328 cycles &#40;269 cycles per iteration&#41;
    I do not know how much effect Delphi's compiler optimizations may have had on these. I tried turning optimizations off, but that does not disable all optimizations.

  8. #18

    A Simple OpenGL Framework - Your input required...

    I consciously kept the var keyword in there so that it is clear that the Object will be changed. Maybe I should explicitly define it as an out parameter, but I am unsure if FreePascal supports that keyword.

    Thanks to Sly and co for all their input on the Vector class.

    Following is the proposed camera class....

    [pascal]
    unit camera;

    interface

    uses
    vector;

    type
    TCamera = class
    private
    CalcVector : TVector; // A sort of cached dummry vector used in Update methods.
    protected
    // these are used for moving and changing camera orientation
    // through the MoveTo/LookTo methods
    initPosition, finalPosition : TVector;
    initLookAt, finalLookAt : TVector;

    LookAtVelocity : TVector; // velocity for looking at objects
    LookAtAcceleration : TVector; // acceleration for looking at objects

    procedure UpdateLookAt;
    procedure UpdateMoveTo;
    public
    CameraPosition : TVector; // position of camera
    CameraVelocity : TVector; // velocity of camera
    CameraAcceleration : TVector; // acceleration of camera
    CameraLookAt : TVector; // lookat vector

    // up, forward, right vectors
    CameraUp : TVector;
    CameraForward : TVector;
    CameraRight : TVector;

    // yaw and pitch angles
    CameraYaw : TScalar;
    CameraPitch : TScalar;

    ScreenWidth, ScreenHeight : integer;
    ScreenCenterX, ScreenCenterY : integer;

    constructor Create; overload;
    constructor Create( aLook : TVector ); overload;
    constructor Create( aPosition : TVector; aLook : TVector ); overload;
    destructor Destroy; override;

    procedure LookAt( aX : TScalar; aY : TScalar; aZ : TScalar ); overload;
    procedure LookAt( aLook : TVector ); overload;
    procedure MoveTo( aX : TScalar; aY : TScalar; aZ : TScalar ); overload;
    procedure MoveTo( aPosition : TVector ); overload;

    // right rotation along y-axis (yaw)
    procedure RotateYaw( aRadians : TScalar );
    procedure RotatePitch( aRadians : TScalar );
    procedure RotateRoll( aRadians : TScalar );
    end;

    implementation

    { TCamera }

    constructor TCamera.Create;
    begin
    inherited;
    CameraPosition := TVector.Create( 0.0, 0.0, 0.0 );
    CameraLookAt := TVector.Create( 0.0, 0.0, 1.0 );

    CameraForward := CameraLookAt;
    CameraUp := TVector.Create( 0.0, 1.0, 0.0 );
    CameraRight := TVector.Create( 1.0, 0.0, 0.0 );

    CameraVelocity := TVector.Create( 0.0, 0.0, 0.0 );
    CameraAcceleration := TVector.Create( 0.0, 0.0, 0.0 );

    CameraYaw := 0.0;
    CameraPitch := 0.0;

    CalcVector := TVector.Create( 0.0, 0.0, 0.0 );
    end;

    constructor TCamera.Create( aPosition, aLook : TVector );
    begin
    inherited Create;
    CameraPosition := aPosition;
    CameraLookAt := aLook.UnitVector;

    CameraForward := CameraLookAt;
    CameraUp := TVector.Create( 0.0, 1.0, 0.0 );
    CameraRight := TVector.Create( 1.0, 0.0, 0.0 );

    CameraVelocity := TVector.Create( 0.0, 0.0, 0.0 );
    CameraAcceleration := TVector.Create( 0.0, 0.0, 0.0 );

    CameraYaw := 0.0;
    CameraPitch := 0.0;

    CalcVector := TVector.Create( 0.0, 0.0, 0.0 );
    end;

    constructor TCamera.Create( aLook : TVector );
    begin
    inherited Create;
    CameraPosition := TVector.Create( 0.0, 0.0, 0.0 );
    CameraLookAt := aLook.UnitVector;

    CameraForward := CameralookAt;
    CameraUp := TVector.Create( 0.0, 1.0, 0.0 );
    CameraForward.CrossProduct( CameraUp, CameraRight );

    CameraVelocity := TVector.Create( 0.0, 0.0, 0.0 );
    CameraAcceleration := TVector.Create( 0.0, 0.0, 0.0 );

    CameraYaw := 0.0;
    CameraPitch := 0.0;

    CalcVector := TVector.Create( 0.0, 0.0, 0.0 );
    end;

    destructor TCamera.Destroy;
    begin
    if CameraPosition <> nil then
    CameraPosition.Free;
    if CameraLookAt <> nil then
    CameraLookAt.Free;

    if CameraForward <> nil then
    CameraForward.Free;
    if CameraUp <> nil then
    CameraUp.Free;
    if CameraRight <> nil then
    CameraRight.Free;

    if CameraVelocity <> nil then
    CameraVelocity.Free;
    if CameraAcceleration <> nil then
    CameraAcceleration.Free;

    if CalcVector <> nil then
    CalcVector.Free;
    inherited;
    end;

    procedure TCamera.LookAt( aX, aY, aZ : TScalar );
    begin
    CameraLookAt.x := aX;
    CameraLookAt.y := aY;
    CameraLookAt.y := aZ;
    end;

    procedure TCamera.LookAt( aLook : TVector );
    begin
    CameraLookAt.Assign( aLook );
    end;

    procedure TCamera.MoveTo( aX, aY, aZ : TScalar );
    begin
    CameraPosition.x := aX;
    CameraPosition.y := aY;
    CameraPosition.z := aZ;
    end;

    procedure TCamera.MoveTo( aPosition : TVector );
    begin
    CameraPosition.Assign( aPosition );
    end;

    procedure TCamera.RotatePitch( aRadians : TScalar );
    var
    sine, cosine : TScalar;
    begin
    sine := sin( aRadians );
    cosine := cos( aRadians );

    CameraUp.y := cosine * CameraUp.Length;
    CameraUp.z := sine * CameraUp.Length;

    CameraForward.y := -sine * CameraForward.Length;
    CameraForward.z := cosine * CameraForward.Length;
    {* x y z p
    | 1 0 0 0 |
    M = | 0 cos(A) -sin(A) 0 |
    | 0 sin(A) cos(A) 0 |
    | 0 0 0 1 |
    *}
    end;

    procedure TCamera.RotateRoll( aRadians : TScalar );
    var
    sine, cosine : TScalar;
    begin
    sine := sin( aRadians );
    cosine := cos( aRadians );

    CameraRight.x := cosine * CameraRight.Length;
    CameraRight.y := sine * CameraRight.Length;

    CameraUp.x := -sine * CameraForward.Length;
    CameraUp.y := cosine * CameraForward.Length;
    {*
    | cos(A) -sin(A) 0 0 |
    M = | sin(A) cos(A) 0 0 |
    | 0 0 1 0 |
    | 0 0 0 1 |
    *}
    end;

    procedure TCamera.RotateYaw( aRadians : TScalar );
    var
    sine, cosine : TScalar;
    begin
    sine := sin( aRadians );
    cosine := cos( aRadians );

    CameraRight.x := cosine * CameraRight.Length;
    CameraRight.z := sine * CameraRight.Length;

    CameraForward.x := -sine * CameraForward.Length;
    CameraForward.z := cosine * CameraForward.Length;

    {* x y z p
    | cos(A) 0 -sin(A) 0 |
    M = | 0 1 0 0 |
    | sin(A) 0 cos(A) 0 |
    | 0 0 0 1 |
    *}
    end;

    procedure TCamera.UpdateLookAt;
    begin
    CalcVector.Assign( finalLookAt.x - CameralookAt.x,
    finalLookAt.y - CameralookAt.y,
    finalLookAt.z - CameralookAt.z );

    LookAtVelocity.Assign( CalcVector.Scale( 0.5 ) );
    end;

    procedure TCamera.UpdateMoveTo;
    begin
    CalcVector.Assign( finalPosition.x - Cameraposition.x,
    finalPosition.y - Cameraposition.y,
    finalPosition.z - Cameraposition.z );

    CameraVelocity.Assign( CalcVector.Scale( 0.5 ) );
    end;

    end.
    [/pascal]

    The Sine and Cosine operations should probably be a look-up table to speed things up, if you are so inclined .
    <br /><br />There are a lot of people who are dead while they are still alive. I want to be alive until the day I die.<br />-= Paulo Coelho =-

  9. #19

    A Simple OpenGL Framework - Your input required...

    Quote Originally Posted by savage
    I consciously kept the var keyword in there so that it is clear that the Object will be changed. Maybe I should explicitly define it as an out parameter, but I am unsure if FreePascal supports that keyword.
    out is the same as var except the value going in is ignored. const, var and out are only mostly useful when dealing with record types. For objects they have no real benefit.

    [pascal]
    // these are used for moving and changing camera orientation
    // through the MoveTo/LookTo methods
    initPosition, finalPosition : TVector;
    initLookAt, finalLookAt : TVector;[/pascal]
    These vectors are never used.

    [pascal] LookAtVelocity : TVector; // velocity for looking at objects
    LookAtAcceleration : TVector; // acceleration for looking at objects[/pascal]
    LookAtVelocity is referenced, but never created. LookAtAcceleration is never used.

    [pascal] procedure UpdateLookAt;
    procedure UpdateMoveTo;[/pascal]
    How are these methods called if they are in the protected section?

    [pascal] procedure LookAt( aX : TScalar; aY : TScalar; aZ : TScalar ); overload;
    procedure LookAt( aLook : TVector ); overload;
    procedure MoveTo( aX : TScalar; aY : TScalar; aZ : TScalar ); overload;
    procedure MoveTo( aPosition : TVector ); overload;[/pascal]
    I would provide a LookAt method that functions like most other LookAt functions where you provide the eye, target and up vectors.

    [pascal] // right rotation along y-axis (yaw)
    procedure RotateYaw( aRadians : TScalar );
    procedure RotatePitch( aRadians : TScalar );
    procedure RotateRoll( aRadians : TScalar );
    end;[/pascal]
    How about a Rotate(aAngle: TVector) where each element of the vector is the angle in one axis? That is quite common. Instead of (x, y, z), it is (pitch, yaw, roll).

    [pascal]destructor TCamera.Destroy;
    begin
    if CameraPosition <> nil then
    CameraPosition.Free;
    if CameraLookAt <> nil then
    CameraLookAt.Free;

    if CameraForward <> nil then
    CameraForward.Free;
    if CameraUp <> nil then
    CameraUp.Free;
    if CameraRight <> nil then
    CameraRight.Free;

    if CameraVelocity <> nil then
    CameraVelocity.Free;
    if CameraAcceleration <> nil then
    CameraAcceleration.Free;

    if CalcVector <> nil then
    CalcVector.Free;
    inherited;
    end;[/pascal]
    You can call Free on a nil object with no ill effect. No need to check for nil before calling Free.

    The Sine and Cosine operations should probably be a look-up table to speed things up, if you are so inclined .
    The difference is not that great these days with these operations implemented in the FPU. If you do want it to be faster, call SinCos instead of calling Sin and Cos separately.

  10. #20

    A Simple OpenGL Framework - Your input required...

    From my experience on modern computers sin/cos tables are only beneficial when they can be used constantly, like in texture generation, but when you want good precision it might not fit in the cash. When you?¢_Tre unlikely to get a cash hit fpu functions are faster.

Page 2 of 3 FirstFirst 123 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •