JOML-CI / JOML

A Java math library for OpenGL rendering calculations
MIT License
729 stars 104 forks source link

Can't figure out interesting behavior by Matrix4f.transpose() #335

Closed terraflops1048576 closed 1 year ago

terraflops1048576 commented 1 year ago

Hi, I've come across some weird behavior in my Java project related to transposing matrices, and I'm not sure how to go about debugging it.

public static void main(String[] args) throws Exception {
        Matrix4f matrix = new Matrix4f();
        for (int row = 0; row < 4; row++) {
            for (int col = 0; col < 4; col++) {
                matrix.set(col, row, row + col * 4);
            }
        }
        System.out.println("Original");
        System.out.println(matrix);
        System.out.println("Transposed");
        System.out.println(matrix.transpose(new Matrix4f()));
 }

This gives me the output:

 Original
 0.000E+0  4.000E+0  8.000E+0  1.200E+1
 1.000E+0  5.000E+0  9.000E+0  1.300E+1
 2.000E+0  6.000E+0  1.000E+1  1.400E+1
 3.000E+0  7.000E+0  1.100E+1  1.500E+1

Transposed
 1.000E+0  0.000E+0  0.000E+0  0.000E+0
 0.000E+0  1.000E+0  0.000E+0  0.000E+0
 0.000E+0  0.000E+0  1.000E+0  0.000E+0
 0.000E+0  0.000E+0  0.000E+0  1.000E+0

which is unexpected (expected behavior is to see the matrix transposed).

But if I do this:

public static void main(String[] args) throws Exception {
        Matrix4f matrix = new Matrix4f();
        for (int row = 0; row < 4; row++) {
            for (int col = 0; col < 4; col++) {
                matrix.set(col, row, row + col * 4);
            }
        }
        System.out.println("Original");
        System.out.println(matrix);
        System.out.println("Transposed");
        System.out.println(matrix.transpose());
    }

I get this:

Original
 0.000E+0  4.000E+0  8.000E+0  1.200E+1
 1.000E+0  5.000E+0  9.000E+0  1.300E+1
 2.000E+0  6.000E+0  1.000E+1  1.400E+1
 3.000E+0  7.000E+0  1.100E+1  1.500E+1

Transposed
 0.000E+0  4.000E+0  8.000E+0  1.200E+1
 1.000E+0  5.000E+0  9.000E+0  1.300E+1
 2.000E+0  6.000E+0  1.000E+1  1.400E+1
 3.000E+0  7.000E+0  1.100E+1  1.500E+1

Expected behavior is that the matrix is transposed.

However, if I initialize the matrix like so:

Matrix4f matrix = new Matrix4f(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
        System.out.println("Original");
        System.out.println(matrix);
        System.out.println("Transposed");
        System.out.println(matrix.transpose());

then I get the expected behavior:

Original
 0.000E+0  4.000E+0  8.000E+0  1.200E+1
 1.000E+0  5.000E+0  9.000E+0  1.300E+1
 2.000E+0  6.000E+0  1.000E+1  1.400E+1
 3.000E+0  7.000E+0  1.100E+1  1.500E+1

Transposed
 0.000E+0  1.000E+0  2.000E+0  3.000E+0
 4.000E+0  5.000E+0  6.000E+0  7.000E+0
 8.000E+0  9.000E+0  1.000E+1  1.100E+1
 1.200E+1  1.300E+1  1.400E+1  1.500E+1

My build.gradle includes this: implementation 'org.joml:joml:1.10.5'

Can anyone reproduce / tell me what's going on?

httpdigest commented 1 year ago

Thanks for reporting! And, sorry about that. It is a bug about the Matrix4f.set(col, row, value) method not resetting the assumptions about the matrix properties, that are stored in the properties field in order to route/accelerate further operations. It has been fixed for 1.10.6, which you can use right now with 1.10.6-SNAPSHOT or in the actual 1.10.6 release later.

Generally, manually setting any matrix field is highly discouraged, because it will break optimized matrix operations afterwards, which then always have to make no assumptions anymore and use the most general operation implementations.

In order to "reestablish" the properties of a matrix, there is the method determineProperties(), but this is not able to determine all properties of a matrix, such as orthogonality. So, this can be called after setting all matrix fields manually, to get back at least some performance for further operations.