glm-js working draft
glm-js is an experimental JavaScript implementation of the OpenGL Mathematics (GLM) C++ Library.
Introduction
glm-js is imagined with generative qualities in mind — accessible, easy to learn, easy to master, provides good leverage and adaptable.
Rather than re-inventing the wheel, its lowest-level math functionality is delegated to existing libraries — which makes room to focus on higher-level abstractions like GLM and GLSL.
A limited (but growing) subset of GLM features are currently supported. Several "backend" math vendors have been integrated simultaneously — which makes glm-js one of the most consistent and verifiable ways to access math functionality from JavaScript to date.
- three.js - JavaScript 3D library. (☑)
- glMatrix - Javascript Matrix and Vector library for High Performance WebGL apps (☑)
- tdl-fast - A low-level WebGL library (☑)
Jump Start
To use the latest 'kitchen sink' build of glm-js (everything needed @ ~97k minified):
From the Browser:
http://humbletim.github.io/glm-js/code/build/glm-js.min.js
— github pageshttps://git.io/glm-js.min.js
— same, but shortened with git.iohttps://cdn.rawgit.com/humbletim/glm-js/31fd034b/build/glm-js.min.js
— rawgit CDN
<script src='https://git.io/glm-js.min.js'></script>
<script>
console.log('loaded glm-js version: ', glm.version);
console.log('vec3 example: ', glm.vec3(1,2,3));
</script>
(note: glm-js is also accessible from the current page you're viewing — just open browser's debug console and see glm
global)
From Node.js:
$ npm install glm-js
var glm = require('glm-js');
console.log('glm-js version: ', glm.version);
console.log('glm.vec3 example: ', glm.vec3(1,2,3));
Performance
Comparing performance across different JavaScript math libraries can easily lead to both false positives and false negatives, since testing artificial scenarios tends to differ wildly from later practical applications.
But glm-js offers a unique way to conduct such experiments — instead of hand-crafting test cases three times (across three different backends), you could write your scenarios once and then compare and contrast automatically across multiple, different glm-js backends.
For example, all of the testing (☑) links above go to the same page and differ only in terms of location hash, which just-in-time selects a backend to run the glm-js unit tests live against in your browser.
"GLMenetics"
A significant inspiration for glm-js is the original GLM C++ project, which effectively encourages mindset and code re-use by adopting the GLSL specification with purpose — or in the author's words:
GLM provides classes and functions designed and implemented with the same naming conventions and functionalities than GLSL so that when a programmer knows GLSL, he knows GLM as well which makes it really easy to use.
Similarly, glm-js aims to provide interfaces designed and implemented with the same naming conventions and functionalities as GLM — extending the reach of GLMenetics to JavaScript:
Library | Language | PU | Link |
---|---|---|---|
GLSL | C (like) | GPU | OpenGL Shading Language |
GLM | C++ | CPU | OpenGL Mathematics |
glm-js | JavaScript | JSPU* | glm-js |
* JavaScript Processing Unit
By using consistent conventions, math code be crafted in a more portable way across space, time, platform and environment.
Examples
To explore the latest glm-js at the shell prompt / using node:
$ git clone https://github.com/humbletim/glm-js.git
$ cd glm-js
$ node # or maybe: rlwrap -a node
> glm = require("./build/glm-js.min");
You can also open a browser debug console while on this web page — glm-js has been loaded for you, and can be accessed via browser global glm
≟.
Depending on browser you might need to append an .inspect() or .toString() for pretty-printed results — eg: glm.vec2(window.innerWidth,window.innerHeight).inspect()
≟.
And here are some relevant things to try typing ≟:
> glm.vec4(3,2,1,0)
{
"x": 3,
"y": 2,
"z": 1,
"w": 0
}
> v = glm.vec4(1), v.xyz = [.1,.2,.3], v.toString()
'fvec4(0.100000, 0.200000, 0.300000, 1.000000)'
> v['*='](5) // or v.mul_eq(5)
{
"x": 0.5,
"y": 1,
"z": 1.5,
"w": 5
}
> q = glm.angleAxis(glm.radians(45.0), glm.vec3(0,1,0));
{
"w": 0.9238795042037964,
"x": 0,
"y": 0.3826834261417389,
"z": 0
}
> glm.degrees(glm.eulerAngles(q))
{
"x": 0,
"y": 44.999996185302734,
"z": 0
}
> v['*'](q) // or v.mul(q)
{
"x": 1.4142135381698608,
"y": 1,
"z": 0.7071067690849304,
"w": 5
}
> glm.perspective(glm.radians(45.0), 4.0 / 3.0, 0.1, 100.0).toString()
mat4x4(
(1.810660, 0.000000, 0.000000, 0.000000),
(0.000000, 2.414214, 0.000000, 0.000000),
(0.000000, 0.000000, -1.002002, -1.000000),
(0.000000, 0.000000, -0.200200, 0.000000)
)
> glm.perspective(glm.radians(45.0), 4.0 / 3.0, 0.1, 100.0)
{
"0": {
"x": 1.8106601238250732,
"y": 0,
"z": 0,
"w": 0
},
"1": {
"x": 0,
"y": 2.4142136573791504,
"z": 0,
"w": 0
},
"2": {
"x": 0,
"y": 0,
"z": -1.0020020008087158,
"w": -1
},
"3": {
"x": 0,
"y": 0,
"z": -0.20020020008087158,
"w": 0
}
}
Trans-Porting 3D Math
The following three snippets are roughly the same despite spanning different "host" languages (GLSL, C++ and JavaScript, respectively).
GLSL (typically this would run on your graphics card):
GLSL | GLM C++ | glm-js | three-js
mat4 rotationMatrix(vec3 axis, float angle); // forward declaration
mat4 mrot = rotationMatrix(vec3(0.0,1.0,0.0), radians(45.0));
mat4 m1 = mat4(1.0);
mat4 m2 = mat4(2.0);
mat4 m3 = m1 * m2;
m3 *= mrot;
GLM and C++11 (typically this would run on one of your main processor cores):
GLSL | GLM C++ | glm-js | three-js
#include <glm/glm.hpp>
static auto mrot = glm::angleAxis(glm::radians(45.0f), glm::vec3(0,1,0));
auto m1 = glm::mat4(1.0f);
auto m2 = glm::mat4(2.0f);
auto m3 = m1 * m2;
m3 *= glm::toMat4(mrot);
glm-js and JavaScript (typically this would run in your web browser or on node):
GLSL | GLM C++ | glm-js | three-js
var glm = require('./glm');
this.mrot = this.mrot || glm.angleAxis(glm.radians(45.0), glm.vec3(0,1,0));
var m1 = glm.mat4(1.0);
var m2 = glm.mat4(2.0);
var m3 = m1['*'](m2);
m3['*='](glm.toMat4(this.mrot));
three-js and JavaScript:
GLSL | GLM C++ | glm-js | three-js
... here's the same math as above, implemented here using stock three.js:
var THREE = require('./three');
this.mrot = this.mrot ||
new THREE.Quaternion().setFromAxisAngle(
new THREE.Vector3(0,1,0), THREE.Math.degToRad(45.0));
var m1 = new THREE.Matrix4();
var m2 = new THREE.Matrix4();
// ... note: we just want a diagonal mat4(2) here, maybe there's a leaner way??
m2.scale(new THREE.Vector3(2.0,2.0,2.0)).elements[15] = 2.0;
var m3 = m1.clone().multiply(m2);
m3.multiply(new THREE.Matrix4().makeRotationFromQuaternion(this.mrot));
License
- glm-js itself is released under the MIT license - see glm-js/LICENSE
- "math vendor" backends are included per individual licenses - see glm-js/lib/LICENSE.*