Java OpenGL Math Library (JOML)

That’s nice to hear. :slight_smile: You might want to give joml-2d a try. So please do mention anything that you see is probably lacking there.

As for using joml-2d:
Do you intend to interface with OpenGL for rendering or do you want to use Java2D or JavaFX?
I’m asking since I observed that most people doing 2D do not want the high learning-curve of OpenGL and then reasonably choose Java2D or JavaFX for that.
JOML is rather geared towards integration with Java/OpenGL bindings, because everything is centered around matrices and NIO buffers, which are a perfect fit for OpenGL.
It may not be such a good fit for Java2D or JavaFX, which both provide their own facilities for representing transformations (AffineTransformation and such) and applying those transformations when drawing/rendering stuff.
With that I just want to see, whether joml-2d is really a fit for you.

As for a tutorial on how to use JOML:
I guess it boils down to understanding raw linear algebra, which JOML cannot help you with to be honest.
There are some few examples on JOML’s README and you can have a look at JOML’s Wiki (see the navigation bar to the right).
There is also the joml-lwjgl3-demos repository containing some example programs using LWJGL3 for rendering.
But in the end, what you might want to know is raw linear algebra. I am sure there are great tutorials or books out there, too, for that.

@Riven:
int x = Float.floatToRawIntBits(s[i]);
{do stuff}
f = Float.intBitsToFloat(x);

is being translated into (by C1): SSE register has float, moves to regular register…do stuff…move back to SSE. So they’re both move operations. So fast. The odd thing is even if do stuff could be performed in SSE. So I was indeed complete full of poop.

Finally home and ready to try out JOML stuff in WSW. Still missing three functions:

Matrix**.getScale(Vector**): gets the scaling of each axis. Used to calculate LOD level based on scale of model from a model’s matrix.

Vector3*.mulRotate(Matrix4*): multiplies the vector by the matrix with w=0, i.e. multiply by matrix but ignore translation. I use this to rotate directional and cone light direction vectors by the view matrix’s rotation.

Matrix4d.getFloat(int index, ByteBuffer buffer): stores the double matrix in a ByteBuffer as floats. Currently limited to doubles only for ByteBuffers. I use this to store model matrices in a mapped VBO (a ByteBuffer).

I really think that the “get” functions should be renamed to “store”. The name really makes no sense right now. LWJGL’s math library called it store(), since you store the matrix in the buffer. The “set” functions make more sense as you set the value of the matrix to that of the data in the buffer/array. LWJGL called this operation “load” which I prefer, but “set” is okay IMO.

EDIT: Minor note: the Vector**.distance() and distanceSquared() functions recalculate (x - this.x) etc twice (although the compiler probably caches that for you). Might want to compute those to local variables just to be sure. It makes the code more readable as well.

It should be either load/store or get/set, not store/set.

My two cents. :-*

It will remain get/set, as this is familiar with NIO and really is symmetric to all the other get*/set* methods. Let me explain:

Get means: Get all or some parts of ‘this’ into another representation
Set means: Set all or some parts of ‘this’ from another representation

The rationale could be:

  • get/set without any addition method name suffix is intended to transform ‘this’ to another equivalent representation, such as a Matrix3f to a ByteBuffer or a FloatBuffer
  • get*/set* methods with suffixes will represent transformations that extract “part of” ‘this’ into another representation, such as getTranslation() which extracts the translation

I know that there are currently set/get methods that violate the first enumeration point, namely set/get with Quaternion parameter. A quaternion is not a 1:1 full equivalent representation of a matrix, can’t be, but only of the rotation property. So, this could and should in fact be renamed to “getRotation”.

But I will not rename the get/set methods taking NIO Buffers to store/load.

I still vote for renaming it to store/load. I don’t think the get-set logic applies in this case since the JOML classes aren’t buffers. Getting in NIO implies reading data from a buffer, but getting in JOML implies writing to a buffer. Load/store makes sense as it mimics the old OpenGL functions as well, where glLoadMatrix(FloatBuffer) loads a matrix from a given buffer. The fact that the set() functions are filled with buffer.gets() is a bad sign IMO. >___>

G’morning. I just pulled the latest changes. getScale() works well, but removing Vector3*.mul(Matrix4*) broke a lot of my code. It’d be nice if we had mulPoint() and mulDirection() in Vector3* as well. I still need the function. >_<

In other news, we’re starting the switch to JOML for WSW today.

Vector3.mulPoint/.mulDirection is in, though it somehow feels wrong to ask a 3D vector to multiply itself by a 4x4 matrix. That’s what the Matrix4.transformPoint/.transformDirection were supposed to do. I added the vector methods nonetheless, but discourage you to use them, as it tingles in my fingers to mark them deprecated and remove them again. :slight_smile:

Regarding the “storing a double-precision matrix as single-precision via a ByteBuffer”: I would request you to use a (premanent) FloatBuffer view on your ByteBuffer to achieve that.

I am using mapped VBOs, so generating a new FloatBuffer each time I map it is something I would like to avoid. In addition, I’m storing non-float stuff in it as well, so keeping it as ByteBuffer is optimal for me.

Edit:
Matrix4.get(Quaternion) should be renamed to Matrix4.getRotation(Quaternion), to complement getTranslation() and getScale().

Okay. I understand. Both your change requests are in now.

Thanks, works great!

To not pollute this thread even more: http://www.java-gaming.org/topics/case-studies/36562/msg/347355/view.html#msg347355

Matrix4d.set(Quaternion*) is missing. >_<

Just saw your post on phone, ran to the computer, turned it on, started Eclipse and emergency-added the missing method. :smiley:
It’s in now.

  • There’s no AxisAngle4d class it seems, and the Quaterniond constructor does not accept AxisAngle4fs. Currently need to do new Quaterniond().set(AxisAngle4f).

  • Matrix4d.translationRotateScale(Vector3d, Quaterniond, Vector3d) is missing, only the version with float Vectors exists (raw double argument version exists).

A problem I’m continuously getting is converting between float and double versions of objects.

Examples:

  • Getting the world position of bones. Object matrix is a Matrix4d, calculated local bone position is Vector3f. This works reasonably well as Matrix4d can take in a Vector3f.
  • Particles use float precision, but I have most matrices as double matrices. Matrix4f.set(Matrix4d) does not exist, which would be useful.

In my opinion, the float versions should at the minimum have setters for reading from the double versions (Vector3f.set(Vector3d), etc), but actually having all functions being able to take in double arguments (like how the double versions have functions that take in the float versions) would be really useful.

Also, Quaterniond seems to miss float versions of its functions.

Also badly missing the ability to initialize matrices and quaternions from Euler angles.

[quote]There’s no AxisAngle4d class it seems, and the Quaterniond constructor does not accept AxisAngle4fs. Currently need to do new Quaterniond().set(AxisAngle4f).
[/quote]
Thanks, I’ll take care of this.

[quote]Matrix4d.translationRotateScale(Vector3d, Quaterniond, Vector3d) is missing, only the version with float Vectors exists (raw double argument version exists).
[/quote]
I’ll also take care of that.

[quote]Getting the world position of bones. Object matrix is a Matrix4d, calculated local bone position is Vector3f. This works reasonably well as Matrix4d can take in a Vector3f.
[/quote]
Don’t understand what it is here that you want to have changed.

Or was it just meant as a transition to:

[quote]Matrix4f.set(Matrix4d) does not exist, which would be useful.
[/quote]
The reason why “most” floatPrecisionThing.set(doublePrecisionThing) does not exist, is, because you are losing precision and explicit casts are needed.

[quote]In my opinion, the float versions should at the minimum have setters for reading from the double versions (Vector3f.set(Vector3d), etc), but actually having all functions being able to take in double arguments (like how the double versions have functions that take in the float versions) would be really useful.
[/quote]
Okay. I’ll add those.

[quote]Also, Quaterniond seems to miss float versions of its functions.
[/quote]
What do you mean? If you mean functions that originally took double primitives, then there is no need for a float primitives overload, as float can always safely coalesced to double. Another thing it is of course with double-vector arguments. I’ll see where I can augment it with float-vector arguments.

[quote]Also badly missing the ability to initialize matrices and quaternions from Euler angles.
[/quote]
There is [icode]Quaternion.setEulerAnglesXYZ/setEulerAnglesZYX[/icode], depending on your wanted rotation order.
If you desperately need also that function in Matrix, then that could be added. Otherwise, you could initialize a quaternion with that and then set a matrix to that quaternion.

Thanks, you understood everything correctly. :wink:

There is [icode]Quaternion.setEulerAnglesXYZ/setEulerAnglesZYX[/icode], depending on your wanted rotation order.
If you desperately need also that function in Matrix, then that could be added. Otherwise, you could initialize a quaternion with that and then set a matrix to that quaternion.
[/quote]
It’d be really nice to have in Matrix** as well.

I’ve just manually “fixed” ~3000 errors over 2 days in WSW as a result of the switch. I literally dreamt about code last night.

Inject a proxy method, then refactor.

It’s not that simple as I’m selectively converting part of it to use doubles.