Lightweight vector and matrix math library for OpenGL programming in Scala.
Inspired by glm, Subspace handles the vector and matrix computations that need to be performed on the CPU. It provides convenience features from shader programming like swizzle operators as well as a comprehensive set of operations for graphics programming, including replacements for functions that have been deprecated by OpenGL such as building a perspective transformation matrix.
Is intended for use with OpenGL, via JVM bindings such as LWJGL, but could be used with any graphics API.
To minimize it's footprint, this library has no dependencies.
To get started, just add subspace as a dependency to your scala project.
libraryDependencies += "com.github.jpbetz" % "subspace" % "0.1.0"
All vector classes are case classes with companion objects that provide additional functions and constructors.
val position = Vector3(0, 0, 1) val origin = Vector3.fill(0)
Mathematical operators can be used for operations that make sense mathematically, e.g.:
val v1 = Vector3(3.2f, 1.5f, 0) val v2 = Vector3(5, 0.5f, 0) val v3 = -(v1/3f + v2)
And all mathematical operators have an equivalent method. E.g.
v1 + v2 can also be written as
Where mathematical operators cannot be overloaded in a clear and unambiguous way, the operator is not
overloaded. For example, to multiply a vector with a scalar use:
vec3 * 3.0f, but to compute the product of two
* is not available. Instead, use
(for component-wise multiplication).
Vector classes also contain a variety of convenience methods, e.g.:
Matrices are usually constructed using the convenience methods on the companion object.
val perspectiveMatrix = Matrix4x4.forPerspective(scala.math.Pi.toFloat/3f, 1f, 1f, 0.001f, 1000f) val worldToViewMatrix = Matrix4x4.forRotation(cameraRotation)
Matrices can be combined using matrix multiplication.
val modelToWorldMatrix = Matrix4x4.forTranslation(modelPosition) * Matrix4x4.forRotation(modelRotation) val modelViewMatrix = modelToWorldMatrix * worldToViewMatrix
Matrices also have convenience methods for common graphics operations:
val normalViewMatrix = modelViewMatrix.normalMatrix // same as modelViewMatrix.inverse.transpose
Rotations can be managed using any of the following:
Internally, Quaternions are used to handle all rotations. But Quaternions can be constructed from an axis angle or Euler angles.
For example, to create a transformation matrix from an axis angle:
And to create one from Euler angles:
Matrix4x4.forRotation(Quaternion.forEuler(Vector3(scala.math.Pi.toFloat/2, 0, 0)))
All angles are in radians.
Swizzle operators allow a new vector to be created from an existing vector by specifying the dimensions from the existing vector to use to create the new vector. Dimensions can be specified in any order and can be repeated. If few dimensions are specified, a lower dimension vector is created.
|Swizzle Operator||Equivalent code|
Vectors can be constructed from other vectors. Similar to GLSL constructors:
|Convenience Constructors||Equivalent code|
Constructors and swizzle operators can be used together to reshape and resize vectors:
|Swizzle Operators + Constructors||Equivalent code|
Integrating with graphics API bindings for the JVM, such as LWJGL, may require allocating byte buffers for vectors and matrices. Usually so they can be passed to shaders as uniforms.
To allocate a new byte buffer:
val cameraPosition = Vector3(10, 10, 5) ... val cameraPositionBuffer = cameraPosition.allocateBuffer
To update an existing byte buffer:
To read from a byte buffer:
Subspace works well with LWJGL, a library providing access the full OpenGL API from the JVM.
This library can be used with version 2 and 3 of LWJGL. While LWJGL 2 provides a utility library with vector and matrix classes, it is rather incomplete. And in LWJGL 3, they are removing the utility library entirely.
To use this library with LWJGL, simply build whatever types are needed and then use the toBuffer methods to produce the ByteBuffers needed by LWJGL. E.g.:
val modelViewMatrixBuffer = Matrix4x4.allocateEmptyBuffer ... val modelViewMatrix = Matrix4x4.forTranslationRotationScale(modelPosition, modelQuaternion, modelScale) modelViewMatrix.updateBuffer(modelViewMatrixBuffer) ... glUniformMatrix4(modelViewMatrixUniform, false, modelViewMatrixBuffer)
Buffers can also be allocated from existing objects, e.g.:
val perspectiveMatrixBuffer = perspectiveMatrix.allocateBuffer ... glUniformMatrix4(perspectiveMatrixUniform, false, perspectiveMatrixBuffer)
And buffers can be read using companion objects, e.g.:
val position = Vector3.fromBuffer(byteBuffer)
Please open github issues with any questions or feedback. Contributions welcome in the form of pull requests for issues/features. Please open an issue explaining a planned change so it can be discussed before coding up and submitting a pull request.