KrightGameTools consists of several independent modules in Scala and C++ for 2d and 3d applications: math and physics simulation. Most Scala modules support both JVM and Scala.js.
It contains some basic classes like vectors, rotors, matrices, and rigid body physics built on top of them. All of this is implemented from scratch and doesn't depend on other libraries. So I hope the library could be used within GraalVM or from Kotlin or Java.
All the code is under MIT license. Contributions are welcome, feel free to send a pull request.
- Examples
- Getting started
- Library modules
- Tests
- How to change this library and try changes locally in another project
- examples/precession: Scala + sbt, simulate body precession
- examples/nbody: Gradle + Kotlin + Java on JVM and GraalVM, simulation of N-bodies in space.
- examples/indigodemo: Indigo game engine with scala js.
- cpp: C++ code for 3d physics simulation.
Requires Scala 3.9 or newer (use version 0.10.1 for older Scala 3 versions).
You don't need all the modules listed below: pick the ones you use, see module dependencies.
libraryDependencies ++= Seq(
"me.kright" %% "gametools-mathutil" % "0.12.0",
"me.kright" %% "gametools-flatarray" % "0.12.0",
"me.kright" %% "gametools-vector" % "0.12.0",
"me.kright" %% "gametools-matrix" % "0.12.0",
"me.kright" %% "gametools-pga2d" % "0.12.0",
"me.kright" %% "gametools-pga2dgeom" % "0.12.0",
"me.kright" %% "gametools-pga3d" % "0.12.0",
"me.kright" %% "gametools-pga3dgeom" % "0.12.0",
"me.kright" %% "gametools-physics1d" % "0.12.0",
"me.kright" %% "gametools-pga3dphysics" % "0.12.0"
)For Scala.js use %%% instead of %%:
libraryDependencies += "me.kright" %%% "gametools-pga3d" % "0.12.0"Note: suffix _3 is for Scala 3.
dependencies {
implementation 'me.kright:gametools-mathutil_3:0.12.0'
implementation 'me.kright:gametools-flatarray_3:0.12.0'
implementation 'me.kright:gametools-vector_3:0.12.0'
implementation 'me.kright:gametools-matrix_3:0.12.0'
implementation 'me.kright:gametools-pga2d_3:0.12.0'
implementation 'me.kright:gametools-pga2dgeom_3:0.12.0'
implementation 'me.kright:gametools-pga3d_3:0.12.0'
implementation 'me.kright:gametools-pga3dgeom_3:0.12.0'
implementation 'me.kright:gametools-physics1d_3:0.12.0'
implementation 'me.kright:gametools-pga3dphysics_3:0.12.0'
}Initially, it was a repo for simple math with matrices and vectors. I implemented physics for 3d on top of that math. During my development, I figured out that plane-based geometric algebra is a fantastic way to describe physics equations. So there is a code with geometric algebra, which includes math and physics too.
I'm inspired by https://bivector.net/PGADYN.html
I rewrote physics equations in PGA, it looks like PGA is a better way of describing physics.
If you are new to geometric algebra, start with my article Plane-based geometric algebra for rigid body motion: it explains the ideas behind this library, from the basics of geometric algebra to how rigid body motion and physics are expressed in PGA.
The concepts shared by the pga2d and pga3d modules (the products, bulk and weight, duality, exp/log, the narrowest-result-type rule) are described in pga-concepts.md.
- mathutil: small utilities shared by the other modules - fast math operations, fast ranges for loop iterations, precision-related helpers.
- flatarray: flat storage of small all-Double structs (PGA points, vectors, rotors,
triangles, etc.) as runs of
Doubles in anArray[Double]: theFlatDoubleSerializertypeclass (derivable, recursing into nested case classes) plusFlatArray/FlatBuffer/FlatViewwith a compile-time-inlined hot path, so bulk transforms avoid materializing the element objects (see the FlatArray benchmarks). - symbolic: simple implementation for AST like
(1.0 + ("y" * "x"))with simplification rules. Used by the code generators to derive formulas symbolically. - ga: experimental support for geometric algebra (GA) and plane-based geometric algebra (PGA). See https://bivector.net for more details. Suitable for any dimensions and signatures, represents a multivector as a map from basis blade to coefficient. Flexible, but slower than the generated pga2d/pga3d classes; used as the source of truth for the code generators.
- vector: Vector2d, Vector3d, Vector4d - simple immutable vector classes with the usual operations.
- matrix: linear algebra over
arrayview2d views of doubles - there is no matrix class, any view (including a lazy transposed one or a slice) is a matrix; multiplication, determinants, the Jacobi eigendecomposition, Cholesky, projection matrices. - pga2d: efficient library for 2d PGA with generated code and some common cases—Pga2dLine, Pga2dPoint, Pga2dRotor, Pga2dMotor, etc. The 2d counterpart of pga3d with the same style and terminology, except that a grade-1 element is called a line instead of a plane, and the analog of Pga3dRotor is Pga2dRotor with just 2 fields (cos and sin of the half-angle).
- pga3d: efficient library for 3d PGA with generated code and some common cases—Pga3dPlane, Pga3dPoint, Pga3dRotor, Pga3dBivector, etc. There is a huge number of similar methods (for each pair of classes for each type of multiplication). Because of generated methods for each case it's possible to know at compile time that, for example, dot product of two bivectors is a scalar or geometric product of two planes is a motor.
- pgaNdCodeGen: hand-made code generator for the pga2d and pga3d modules. It does operations in symbolic form, and searches the most narrow subclass of multivector for the result. Generates Scala code for both, plus C++ code for 3d.
- physics1d: physics independent of PGA. Scalar 1d force models - the SpringElasticity
curve (a soft zone blending into a stiff branch) and rate-independent hysteretic friction for rubber bushings
(HystereticFriction: Dahl, Berg, Iwan models) with an integrator-friendly pure-evaluation contract. And the
universal ODE solvers (OdeSolverRK4 and others) over
Integrable- an object with a state as a run of doubles and a place for normalization - so your own objects are integrated together with the rigid bodies of pga3dphysics. - pga3dphysics: some helper classes for implementing physics engine - body inertia, physics solvers, etc.
- pga3dgeom: classes for geometry - edges, triangles, spheres, rays, axis-aligned bounding boxes
- pga2dgeom: the 2d counterpart of pga3dgeom - edges, triangles, circles, rays, axis-aligned bounding boxes
An arrow means "depends on". Only the published modules are shown, and transitive dependencies are omitted. The dashed arrayview is my own library too, under the same MIT license, it just lives in a separate repository. vector is standalone: no other published module depends on it.
graph TD
subgraph g3d ["3d"]
pga3dphysics --> pga3d
pga3dgeom --> pga3d
end
subgraph g2d ["2d"]
pga2dgeom --> pga2d
end
vector --> mathutil
pga3dphysics --> physics1d
pga3d --> flatarray
pga3d --> matrix
pga2d --> flatarray
pga2d --> matrix
physics1d --> mathutil
flatarray --> mathutil
matrix --> mathutil
matrix --> arrayview["arrayview (separate repo)"]
style arrayview stroke-dasharray: 5 5
It is enough to add the topmost module you need, the build tool fetches the rest. For example,
gametools-pga3dphysics brings in pga3d, physics1d, matrix, flatarray, mathutil and arrayview.
- cpp: same code as in pga3d and pga3dphysics in Scala, but for C++.
The current implementation is experimental and may be changed in the future. I use C++ 20 because of Unreal Engine 5 requirements.
- Immutable API;
Seq(...)literals are created asArraySeq(...)(array-backed),Seqstays as the interface type. - Generated code (pga2d, pga3d, cpp/pga3d) is never edited by hand - change pgaNdCodeGen and regenerate; CI checks for drift.
- Queries that may find nothing (
intersection,deepestContactin the geom modules) returnT | Nullwithnullfor "no result" instead ofOption[T]- no allocation on the hot path. The library is compiled with-Yexplicit-nulls, so the nullability is part of the type;
sbt testI use scalaCheck and property-based approach. It goes well with checking math properties such as addition or multiplication associativity, zero and id elements, morphisms between rotors and corresponding matrices. For physics, it's ok to check that total energy and impulse are constant in body systems without friction.
Change lib code, publish to local ivy repo:
sbt publishLocalOr to the local maven:
sbt publishM2In my case "/.ivy2/local/me.kright/gametools-pga3d_3/0.12.1-SNAPSHOT"
and "/.m2/repository/me/kright/gametools-pga3d_3/0.12.1-SNAPSHOT"
After that, add the local library to another project. In my case, it was for sbt:
libraryDependencies += "me.kright" %% "gametools-pga3d" % "0.12.1-SNAPSHOT"And for Gradle:
implementation "me.kright:gametools-pga3d_3:0.12.1-SNAPSHOT"Maybe you will need to remove cached lib, it will be placed in path like "~/.cache/coursier/v1/https/repo1.maven.org/maven2/me/kright/".