New modern API for vectors and matrices

Posted on

Chinchilla model in X3D, based on high-poly version in "Big Buck Bunny"

Our unit CastleVectors was completely reworked this week, and it now features a new, comfortable API for vector and matrix types, using “advanced records” (records with methods).

I hope that you like the changes 🙂 Of course you can already test it all by using the engine version from GitHub !

Type names

The main vector and matrix types (using Single-precision) are now called TVector2, TVector3, TVector4 (in short: TVector{2,3,4}) and TMartix{2,3,4}. So we have 2D, 3D and 4D vectors (4D vectors are useful for “homogeneous coordinates” in 3D, and to store colors with alpha). And we have 2×2, 3×3, 4×4 matrices.

These correspond to previous array-based types called TVector{2,3,4}Single and TMatrix{2,3,4}Single. As you can see, we have removed the Single suffix, as these types are used very often throughout the engine and, as such, they are our “default” vector and matrix types.

As before, we also feature vectors with Double precision (TVector{2,3,4}Double), and based on various integer types (Byte, Integer and Cardinal). They are all available in 2D, 3D, 4D variants, and are implented using the new “advanced records approach” and are consistent with Single-precision vectors.


  • As these types are “advanced records”, they include various methods, class methods and constants nicely grouped within the record. This makes the API look nicer, e.g. now you can write MyVector.Length instead of VectorLen(MyVector). They have one field, Data, which is defined like array [0..2] of Single for TVector3. This allows to keep them fast (in terms of speed, and memory layout). See the documentation of the generic 3D vector (this API is used by both TVector3 and TVector3Double).

  • You can access the individual components of the vector as V.X, V[0] or V.Data[0]. And the last two notations are checked, even at compile-time, so this will make a clear warning (that index is 3, but should be between 0..2):

    uses CastleVectors;
      V: TVector3;
      V[3] := 123; // incorrect example, index should be in 0..2 !
  • All the operators, like + and *, are overloaded on vectors and matrices. Just like with old types.

  • These types work in both FPC and Delphi. They work without the FPC macros (as Delphi doesn’t support them). I wish they could use generics, but they cannot (it’s not possible to put constraints on Pascal generics to enable efficient arithmetic operations on fields).

  • Along with it, triangles (TTriangle{2,3,4}) was also remodelled as a record, and the Double-precision equivalent (TTriangle3Double) was removed (not really useful, and implementing it without macros or generics is uneasy). See TTriangle3 documentation.

  • Along with it, the TBox3D was a bit improved, it now has Min and Max properties, that look friendlier than Data[0] and Data[1]. See TBox3D documentation.

  • Along with it, the color types have changed too, as TCastleColor and TCastleColorRGB are still just aliases for (respectively) TVector4 and TVector3. See CastleColors unit documentation.

  • The new vectors vaguely resemble the vectors from Delphi standard unit System.Math.Vectors, but are better in many ways. We offer many more types and with many more methods. And, frankly, our naming is more consistent, IMHO 🙂

Backward compatibility

While the change is for good, it is not 100% backward-compatible. I put a lot of effort to minimize the compatibility “disruption”, adding many “glue” functions and types to keep your existing code working (and merely warn about being “deprecated” during compilation). But there is still chance that you will need to change something in your code to make it compile with the engine >= 6.3 API. Read the hints below, and please ask on the forum if you’re unsure how to upgrade.

The changes that break compatibility (things that you will have to change in your code in order to compile):

  • When declaring constants, your old code may use this:

    const MyConstant: TVector2Single = (1.0, 2.0); // old code

    As the vectors, matrices, colors and triangles are now record types with a Data field, so it has to be changed to:

    const MyConstant: TVector2Single = (Data: (1.0, 2.0));
  • The TBox3D.Data, and your custom array of TVectorXxx types, are no longer “arrays of arrays”. They are now “arrays of records”. Your old code may use this:

    Box.Data[0, 0] := 0.0; // old code

    This has to be changed to:

    Box.Data[0][0] := 0.0;

    Or this, if you want to avoid any “getter” properties and just access the (addressable) variable directly:

    Box.Data[0].Data[0] := 0.0;
  • Although you can still access the vector component using V[0], this is now a “getter” property. It often doesn’t matter… unless you want to use it as var or out parameter for some function, or if you use C-like operators like +=. So if your old code has this:

    V[0] += 10; // old code

    This has to be changed to this:

    V.Data[0] += 10;

    Of course, in this case you can also just resign from using C-like operators, and have this:

    V[0] := V[0] + 10;

    Or this:

    V.X := V.X + 10;

Sidenote: I tried a different approach to backward-compatibility too (keeping both old and new APIs in parallel) but this was creating more compatibility than it was solving (gory details in the KEEP_OLD_VECTOR_API comments in this commit).

Have fun with the new API, and, as always, please ask on the forum if you have any questions!

Comment on this post ➤

Generics.Collections in the book and CGE

Posted on

To help you familiarize with the Generics.Collections unit, I wrote a nice chapter about it in the “Modern Pascal Introduction”. It’s a longer chapter, with 4 nice examples using containers from Generics.Collections.

In the related news: complete migration to Generics.Collections in Castle Game Engine is done 🙂 We now use Generics.Collections for all the purposes where we previously used the FGL and CastleGenericLists units. This brings us closer to Delphi compatibility, and it makes code better (Generics.Collections works with records out-of-the-box, it has a TDictionary with a much cleaner API, etc.)

Also, Michalis is back from vacations, with lots of new strength 🙂

Comment on this post ➤