Skip to content
/ MGL Public
forked from ImagicTheCat/MGL

Mathematics for Graphics in pure Lua.

License

Notifications You must be signed in to change notification settings

DUznanski/MGL

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MGL

Mathematics for Graphics in pure Lua (or Mathematics for OpenGL, also an anagram of GLM; an inspiration for the library) is a math library for graphics purposes.

It aims to be simple, generic and optimized (mostly for LuaJIT).

See examples.

Install

Concept

MGL design is around types and operators.

Operators

Operators have defined behavior for specific prototypes (a list of parameter types: MGL/special types).

Any operator prototype can be (re)defined.

⚠️
When an operator prototype is (re)defined, it will (re)generate the operator function. Previously referenced operators will still work, but without the updated behavior. listenOps can be used to update those references.
ℹ️
Operators will lazily check for expected parameters, additional arguments will not trigger an error if a prototype matches.

Types

MGL types can be Lua types (number, boolean, etc.) or table-based types from the metatable pool.

All types are recognized by a string: the name from the metatable pool or the result of the Lua type function.

ℹ️
The references from the metatable pool should normally be constants, but the content of the metatable can be modified.
Table-based types have pre-defined metamethods to call basic operators:
__tostring

tostring

__unm

unm

__add

add

__sub

sub

__mul

mul

__div

div

__mod

mod

__pow

pow

__eq

equal

__lt

lessThan

__le

lessEqual

Types can have specialized metamethods; for example, to implement accessors.

ℹ️
Accessors are implemented as simple as possible, they are check free.

Generic types

Generic types are used to generate types based on some parameters.

For example, vec(D) for a vector of dimension D or mat(N)x(M) / mat(N) (if square) for a matrix of size M x N.

API

Module

-- Loading the module will return (mgl, mglt).

-- MGL

-- Operator prototypes.
-- Registered functions taking parameters of specific types.
-- Map of operator id => nested tables. Each level contains parameter types as key
-- with t[1] as the final function and t[2] as the op table index.
mgl.ops

-- MGL types metatable pool.
-- Accessing a nil field will create the type.
-- map of MGL type => metatable
mgl.types

-- return MGL type (table-based type name or Lua type)
mgl.type(v)

-- Define operator prototype function.
-- It will replace the previous prototype function if identical.
-- Calling this function will mark the operator for update (old references will
-- work, but without the updated behavior).
--
-- func(...): called with operands
-- ...: strings, operator id and prototype (parameter types)
--- parameter types: MGL types or special types ("*": any non-nil)
mgl.defOp(func, ...)

-- Get operator prototype function.
-- ...: strings, operator id and prototype (parameter types)
-- return function or falsy if not found / invalid
mgl.getOp(...)

-- Listen to operator definitions.
-- callback(mgl, op): called when an operator definition changes (prototype update)
--- mgl: MGL handle
--- op: operator id
mgl.listenOps(callback)

-- Unlisten from operator definitions.
-- callback: previously registered callback
mgl.unlistenOps(callback)

-- Operators.
-- mgl.<...>

-- MGL tools

-- Generate function.
-- name: identify the generated function for debug
mglt.genfunc(code, name)

-- Generate "a1, a2, a3, a4..." list string.
-- t_element: string where "$" will be replaced by the element index
-- a: start index
-- b: end index
-- separator: (optional) default: ", "
mglt.genlist(t_element, a, b, separator)

-- Template substitution.
-- template: string with $... parameters
-- args: map of param => value
-- return processed template
mglt.tplsub(template, args)

-- Format prototype for debug.
-- id: operator id
-- ...: parameter types
mglt.format_proto(id, ...)

-- Format call prototype for debug.
-- id: operator id
-- ...: arguments
mglt.format_call(id, ...)

Types

vec(D)

Generic vector type of dimension D, stored as an array/list of scalars (table).

-- Generate vec(D) vector type.
-- D: dimension
mgl.gen_vec(D)

-- Accessors.
-- vec.x / vec.r (vec[1])
-- vec.y / vec.g (vec[2])
-- vec.z / vec.b (vec[3])
-- vec.w / vec.a (vec[4])

#vec -- dimension

mat(N)x(M) / mat(N)

Generic matrix type of dimension N x M, stored as an array/list of row-major ordered scalars (table). Columns are vectors.

ℹ️
The choice of the row-major order is about reading/writing a matrix content as we read/write text/code in English/Lua (left to right, top to bottom).
The choice of columns as vectors is about following mathematical conventions (M*v to transform a vector).
-- Generate mat(N)(M)/mat(N) vector type.
-- N: columns
-- M: (optional) rows (default: N)
mgl.gen_mat(N, M)

-- Vector accessor (get/set column vector).
-- idx: column index
-- vec: (optional) vec(M), set column
mat:v(idx, vec)

mat.M, mat.N -- dimensions

Operators

vec(D)

Vector constructor.

(number: scalars…​): vec(D)

Scalars constructor. #scalars…​ == D

(number: scalar): vec(D)

Scalar constructor.

(table: list): vec(D)

List constructor. #list >= D

(vec(D): a): vec(D)

Copy constructor.

(vec(D+1): a): vec(D)

Truncate constructor.

(vec(A): a, vec(B) or scalar: b): vec(D)

Composed constructor.
Vector and vector/scalar: 1 < A < D and B <= A.

Examples
  • vec3(vec2(1), 1)

  • vec4(vec2(1), vec2(1))

  • vec4(vec3(1), 1)

mat(N)x(M) / mat(N)

Matrix constructor.

(number: scalar): mat(N)x(M)

Scalar constructor. Create matrix with scalar along the identity diagonal.

(table: list): mat(N)x(M)

List constructor. #list >= N*M

(mat(N)x(M): a): mat(N)x(M)

Copy constructor.

(vec(M): columns…​): mat(N)x(M)

Column vectors constructor. #columns…​ == N

(mat(N-1): a): mat(N)

Square extend constructor. Fill with identity.

(mat(N+1): a): mat(N)

Square truncate constructor.

copy

(vec(D): dst, vec(D): src)

-

(mat(N)x(M): dst, mat(N)x(M): src)

-

tostring

(vec(D): a): string

-

(mat(N)x(M): a): string

-

equal

(vec(D): a, vec(D): b): boolean

-

(mat(N)x(M): a, mat(N)x(M): b): boolean

-

unm

Unary minus.

(vec(D): a): vec(D)

-

(mat(N)x(M): a): mat(N)x(M)

-

add

(vec(D): a, vec(D): b): vec(D)

-

(mat(N)x(M): a, mat(N)x(M): b): mat(N)x(M)

-

sub

(vec(D): a, vec(D): b): vec(D)

-

(mat(N)x(M): a, mat(N)x(M): b): mat(N)x(M)

-

mul

(vec(D): a, vec(D): b): vec(D)

Component-wise multiplication.

(vec(D): a, number: b): vec(D)

-

(number: a, vec(D): b): vec(D)

-

(mat(N): a, mat(N): b): mat(N)

Square matrix multiplication.

(mat(N): a, vec(N): b): vec(N)

Square matrix/vector multiplication.

(mat(N)x(M): a, mat(O)x(N) or vec(N): b): mat(O)x(M) or vec(M)

Matrix/vector general multiplication (implemented with the special type * for the second parameter).
Will return a vector if the result has a single column.

(mat(N)x(M): a, number: b): mat(N)x(M)

-

(number: a, mat(N)x(M): b): mat(N)x(M)

-

div

(vec(D): a, vec(D): b): vec(D)

Component-wise division.

(vec(D): a, number: b): vec(D)

-

(mat(N)x(M): a, number: b): mat(N)x(M)

-

length

(vec(D): a): number

Vector length (Euclidean).

normalize

(vec(D): a): vec(D), number

Vector normalization. Also returns length.

dot

(vec(D): a, vec(D): b): number

Dot product.

cross

(vec3: a, vec3: b): vec3

Cross product.

transpose

(mat(N)x(M): a): mat(M)x(N)

-

determinant

(mat2: a): number

-

(mat3: a): number

-

(mat4: a): number

-

inverse

(mat2: a): mat2, number

Compute inverse matrix. Also returns determinant.

(mat3: a): mat3, number

Compute inverse matrix. Also returns determinant.

(mat4: a): mat4, number

Compute inverse matrix. Also returns determinant.

translate

(vec2: a): mat3

Translate identity (2D homogeneous).

(vec3: a): mat4

Translate identity (3D homogeneous).

rotate

(number: theta): mat3

Rotate identity (2D homogeneous). theta is in radians.

(vec3: axis, number: theta): mat4

Rotate identity (3D homogeneous). axis is a unit vector; theta is in radians.

scale

(vec2: a): mat3

Scale identity (2D homogeneous).

(vec3: a): mat4

Scale identity (3D homogeneous).

orthographic

Orthographic projection.

(number: left, number: right, number: bottom, number: top, number: near, number: far): mat4

Build GL compatible orthographic projection.

perspective

Perspective projection.

(number: hfov, number: aspect, number: near, number: far): mat4

Build GL compatible perspective projection. hfov is in radians.

Performances

Notes
  • Operators branch to operator prototypes with a generated type-checking function.

  • More an operator has prototypes, more it has type-checking code: best to only generate required types.

  • This has an overhead, but it is probably less significant with the LuaJIT interpreter than PUC Lua.

  • Furthermore, the LuaJIT compiler may eliminate all of the overhead.

  • In any case, the operator prototype can be retrieved and cached with getOp when optimizations are needed.

Comparisons

Here are some comparisons with other libraries (only aims to give clues about MGL performances).

Measures are made on a x86_64 i5-6500 3.6GHz 8Go DDR4 machine.
The minimum time and the maximum memory (RSS) of multiple measures is kept.
For total times, allocation of entities is taken into account.

Table 1. Transform 10 000 entities at 60 FPS for 10s (600 ticks).
name time (s) CPU utime (s) memory (kB) ~ ms/tick ~ frame % code

GLM GCC -O2

0.366

0.362

4040

0.597

4

compare/glm/bench_transform.cpp

MGL LuaJIT (JIT on)

2.477

2.371

19000

4.112

25

examples/bench_transform.lua

CPML LuaJIT (JIT on)

4.224

4.205

8984

7.048

43

compare/cpml/bench_transform.lua

MGL is around 6-7x slower than GLM in this benchmark. It seems fine considering that MGL works on raw tables with a straightforward API (thanks to LuaJIT optimizations like Allocation Sinking).

About

Mathematics for Graphics in pure Lua.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Lua 95.7%
  • C++ 4.3%