subspace

Lightweight vector and matrix math library for OpenGL programming in Scala.

License

License

GroupId

GroupId

com.github.jpbetz
ArtifactId

ArtifactId

subspace
Last Version

Last Version

0.1.0
Release Date

Release Date

Type

Type

jar
Description

Description

subspace
Lightweight vector and matrix math library for OpenGL programming in Scala.
Project URL

Project URL

http://jpbetz.github.io/subspace/
Source Code Management

Source Code Management

https://github.com/jpbetz/subspace/tree/master/

Download subspace

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.jpbetz/subspace/ -->
<dependency>
    <groupId>com.github.jpbetz</groupId>
    <artifactId>subspace</artifactId>
    <version>0.1.0</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.jpbetz/subspace/
implementation 'com.github.jpbetz:subspace:0.1.0'
// https://jarcasting.com/artifacts/com.github.jpbetz/subspace/
implementation ("com.github.jpbetz:subspace:0.1.0")
'com.github.jpbetz:subspace:jar:0.1.0'
<dependency org="com.github.jpbetz" name="subspace" rev="0.1.0">
  <artifact name="subspace" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.jpbetz', module='subspace', version='0.1.0')
)
libraryDependencies += "com.github.jpbetz" % "subspace" % "0.1.0"
[com.github.jpbetz/subspace "0.1.0"]

Dependencies

compile (1)

Group / Artifact Type Version
org.scala-lang : scala-library jar 2.11.5

test (1)

Group / Artifact Type Version
org.testng : testng jar 6.1.1

Project Modules

There are no modules declared in this project.

Subspace

Lightweight vector and matrix math library for OpenGL programming in Scala.

For more details, see: http://jpbetz.github.io/subspace/

Inspired by glm, Subspace handles the graphics programming vector and matrix computations that often need to be performed on the CPU. It provides convenience features inspired by shader programming language like swizzle operators as well as a comprehensive set of core operations, including replacements for operations that have been deprecated by OpenGL such as a convenient routine for 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.

Usage

To get started, just add subspace as a dependency to your scala project.

Scala SBT:

libraryDependencies += "com.github.jpbetz" % "subspace" % "0.1.0"

Gradle:

compile 'com.github.jpbetz:subspace:0.1.0'

Vectors

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 v1.add(v2).

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 vectors, * is not available. Instead, use v1.dotProduct(v2), v1.crossProduct(v2) or v1.scale(v2) (for component-wise multiplication).

Vector classes also contain a variety of convenience methods, e.g.:

  • v1.distanceTo(v2)
  • v1.lerp(v2, 0.5f)
  • v1.clamp(min, max)

Matrices

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

Rotations assume a left hand oriented coordinate system (same as OpenGL) and all angles are in radians.

Rotations can be represented using any of the following:

Internally, Quaternions are used to handle all rotations. But Quaternions can easily be constructed from an axis angle or Euler angles.

Euler angle example:

Matrix4x4.forRotation(Quaternion.forEuler(Vector3(scala.math.Pi.toFloat/2, 0, 0)))

Axis angle example:

Matrix4x4.forRotation(Quaternion.forAxisAngle(Orientation.x, scala.math.Pi.toFloat/4))

Swizzle operators

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 Longform
vec3.zxy Vector3(vec3.z, vec3.y, vec3.z)
vec4.xz Vector2(vec4.x, vec4.z)
vec4.yyy Vector3(vec4.y, vec4.y, vec4.y)

Vectors can be constructed from other vectors. Similar to GLSL constructors:

Convenience Constructors Equivalent Longform
Vector4(vec3, 1) Vector4(vec3.x, vec3.y, vec3.z, 1)
Vector4(1, vec2, 1) Vector4(1, vec2.x, vec2.y, 1)

Constructors and swizzle operators can be used together to reshape and resize vectors:

Swizzle Operators + Constructors Equivalent Longform
Vector4(0, vec2.yx, 1) Vector4(0, vec2.y, vec2.x, 1)
Vector4(vec3.zxy, 1) Vector4(vec3.z, vec3.y, vec3.x, 1)

ByteBuffers

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:

cameraPosition.updateBuffer(cameraPositionBuffer)

To read from a byte buffer:

Matrix4x4.fromBuffer(transformBuffer)

OpenGL Programming in Scala

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)

Questions and Feedback

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.

Design

Goals:

  • Be good at one thing. Provide the vector and matrix types and operations to drive a GPU from the CPU, and nothing else.
  • Consistent and complete. Shaders and libraries for other languages have been studied to make sure all the convenience operations developers expect have been included.
  • Scala idomatic. Immutable case classes for all vector and matrix types. Carefully defined operator overloading for natural looking mathematical expressions.
  • Minimal footprint. No dependencies.

Non-goals:

  • Provide Scala bindings for OpenGL
  • Write a graphics/game engine
  • Write a general purpose linear algebra library

Current Limitations

  • All types are currently reference types. This may have negative performance implications. While scala does allow stack allocated value types to be defined by extending AnyVal, AnyVal can only be used for single field types, not multi field types like Vector2. Maybe if/when Java adds value types, scala will provide a way to stack allocate these types?

TODO

  • Flesh out scaladoc
  • Integrate with scala collection types (Product, Seq, ??)
  • Implement projection/reflection convenience methods on Vector3
  • Add Color and UV coordinate related conveniences. Might be as simple as adding swizzle operators (rgba, stpq) to Vector3 and Vector4.

References

Versions

Version
0.1.0