Hey, how important is this "3D" stuff anyway?



  • I'm a bit of a collector of 3D game engines. I like going through them and figuring out how each of them work. It's how I scratch my coding itch these days when my life is as much about PowerPoint as about MSVC or Eclipse...

    Anyway,  I've been having a spot of bother with one of the prominent Java game engines lately, and no matter how much I checked my code, I couldn't figure out where it was coming from. So I started tracing through the implementation of the game engine (which, btw, has been used for at least 2 released commercial games), and came across this wonderful piece of code in the Matrix4f class. Put on your linear algebra hat. And your protective glasses.



     

        public Matrix4f transpose() {
        	Matrix4f mat = new Matrix4f();
    
    	mat.m01 = m10;
    	mat.m02 = m20;
    	mat.m03 = m30;
    	
    	mat.m12 = m21;
    	mat.m13 = m31;
    	
    	mat.m23 = m32;
    	
    	return mat;
    }
    


  • So, where is the WTF?

    I really don't see it. I believe JME chose this method of implementing a Matrix for speed and memory purposes (this instead of double nested loops). Even so, this method makes perfect sense.

    (3x3 matrix for simplicity sake. 4x4 is a lot to type) 

    | m00 m01 m02 |

    | m10 m11 m12 |

    | m20 m21 m22 |

    transpose is (by definition):
     

    | m00 m10 m20 |

    | m01 m11 m21 |

    | m02 m12 m22 |
     

    Thus, there are only 6 changes for 9 elements. Much speedier to do than transpose everything, as this method can get called many, many times a frame. 

     

    Jason 



  • @JamesKilton said:

    So, where is the WTF?

     

    Only a portion of the transpose is actually performed. mat.m01 is assigned to, but mat.m01 is not. Further, there seems to be some assumption about mat being initialized to be a clone of the current object, but this doesn't seem to actually be the case.

     



  • @JamesKilton said:

    So, where is the WTF?

    It's only transposing half the matrix: 6 changes for 16 elements.



  • @JamesKilton said:

    So, where is the WTF?

    The empty matrix is being transposed. Wait, no, it's not actually being transposed at all.



  • @JamesKilton said:

    So, where is the WTF?

    The empty matrix is being transposed. Wait, no, it's not actually being transposed at all.



  • Nine transpositions for a 4x4 array.  I think it should be this:

        public Matrix4f transpose() {
    Matrix4f mat = new Matrix4f(this);

    mat.m01 = m10;
    mat.m02 = m20;
    mat.m03 = m30;
    mat.m10 = m01;
    mat.m12 = m21;
    mat.m13 = m31;
    mat.m20 = m02; mat.m21 = m12;
    mat.m23 = m32;

    return mat;
    }


  • Maybe this was a necessity and a VERY VERY poorly named function? Though it does not look right.

     

    :) What games is it used in? 



  • No, you need to transpose the translation values, too, for transpose to work right.

    Or, the story of how shoddy open source math libraries stole two evenings from my life! Tearing my hair out, trying to figure out why my normals weren't being rendered correctly.

    jMonkey is used in, a least, Bang! Howdy and one other game I forget the name of. I also think it's being used internally for some upcoming games at one or two publishers.

    For extra fun, see this thread:   http://www.jmonkeyengine.com/jmeforum/index.php?topic=6696.0

     



  • It's returning a copy of the matrix, so you have to assign EVERYTHING in the copy. (including the diagonal).

    I don't believe you can optimize this in java.  (anyone care to prove me wrong?)



  • @fluffy777 said:

    It's returning a copy of the matrix, so you have to assign EVERYTHING in the copy. (including the diagonal).

    I don't believe you can optimize this in java.  (anyone care to prove me wrong?)

    Won't the Matrix4f(Matrix4f) constructor take care of the deep copy? Wait, if it did, then that would take longer than the transpose method. So this is the wrong place to optimize - more time would be saved by removing the copy and instead assigning all necessary fields in this function.

    But yeah, I can see this transposition being used: It just flips a triangle matrix, ignoring the fields that may be filled in the other half. When you see code like this, you should investigate where it is called and if it might not be a victim of premature optimization. Of course, in 3d engines, some parts require copious amounts of number crunching, so it's possible this much optimization is in fact required.



  • @fluffy777 said:

    It's returning a copy of the matrix, so you have to assign EVERYTHING in the copy. (including the diagonal).

    I don't believe you can optimize this in java.  (anyone care to prove me wrong?)

    The only way I can think of to optimise this would be to use the swizzling MMX/SSE instructions. Are those accessible from Java? Offhand, I'm not sure. 



  • @Maciej said:

    @JamesKilton said:

    So, where is the WTF?


    Only a portion of the transpose is actually performed.

    Y'know, that would actually be ok if the only input matrices this function is fed are in triangular form. 

    @Maciej said:

    mat.m01 is assigned to, but mat.m01 is not.

    I think you mean FILE_NOT_FOUND? 

    @Maciej said:

    Further, there seems to be some assumption about mat being initialized to be a clone of the current object, but this doesn't seem to actually be the case.

    I'm not so sure, it looks to me like it intends to start with mat as a zero'd matrix, copy across the relevant elements from 'this' to their new locations, and return mat as the result? 



  • @DaveK said:

    I'm not so sure, it looks to me like it intends to start with mat as a zero'd matrix, copy across the relevant elements from 'this' to their new locations, and return mat as the result? 

    To me, but I'm stupid, it looks like it returns a fresh, transposed, empty matrix. Like rotating an empty box.



  • @dhromed said:

    @DaveK said:

    I'm not so sure, it looks to me like it intends to start with mat as a zero'd matrix, copy across the relevant elements from 'this' to their new locations, and return mat as the result? 

    To me, but I'm stupid, it looks like it returns a fresh, transposed, empty matrix. Like rotating an empty box.

    I don't think so.  Java may not be my strong point but I'm pretty confident that the right-hand sides of those assignment statements refer to elements of the 'this' object.  Compare to the left-hand sides and notice the lack of a 'mat.' reference.



  • @ebs2002 said:

    Nine transpositions for a 4x4 array.  I think it should be this:

        public Matrix4f transpose() {
    Matrix4f mat = new Matrix4f(this);

    mat.m01 = m10;
    mat.m02 = m20;
    mat.m03 = m30;

    mat.m10 = m01;
    mat.m12 = m21;
    mat.m13 = m31;

    mat.m20 = m02;
    mat.m21 = m12;
    mat.m23 = m32;

    return mat;
    }

    9/16ths. of that copy c-tor is wasted effort.  With the new-a-blank-matrix-and-copy-elements-over style, you could save that



  • @DaveK said:

    @dhromed said:
    To me, but I'm stupid, it looks like it returns a fresh, transposed, empty matrix. Like rotating an empty box.

    I don't think so.  Java may not be my strong point but I'm pretty confident that the right-hand sides of those assignment statements refer to elements of the 'this' object.  Compare to the left-hand sides and notice the lack of a 'mat.' reference.

    oh duh.



  • @DaveK said:

    9/16ths. of that copy c-tor is wasted effort.  With the new-a-blank-matrix-and-copy-elements-over style, you could save that

    I'm not sure how Java handles things behind the scenes, but in theory it could still be quicker. One bulk and 9 small copies could be faster than 16 small copies.



  • Wow, I really missed that. Yes, this is definitely a WTF.



  • @JamesKilton said:

    (this instead of double nested loops).

    The OTHER WTF is that this can't be done with loops since each of the 16 matrix elements is a separate member variable! This ought to make the class quite a pain to work with. Every other (sane) implementation uses either float[4][4] or float[16], right?



  • @Carnildo said:

    @JamesKilton said:
    So, where is the WTF?

    It's only transposing half the matrix: 6 changes for 16 elements.

    It's not a WTF.
    @dlikhten said:
    Maybe this was a necessity and a VERY VERY poorly named function?

    Exactly. This is an example of a poorly named function, or rather; a poorly named class. It shouldn't be called Matrix4f at all: it should be called AffineTransformMatrix.
    Affine transform matrices effectively only use one diagonal half of a 4x4 matrix.
    What's the quickest way to transpose such a matrix? You create the identity matrix (in your matrix class constructor...) and assign the values of the diagonal half that originally wasn't zeroed out to the one that originally was.



    PS:

    Is it just me or is this board's WYSIWYG editor a complete piece of crap? Bypassing it by using its builtin raw HTML source editor is just about the only sane way to type up a reply to a thread...


  • Discourse touched me in a no-no place

    @Ragnax said:

    Is it just me
    No.

    or is this board's WYSIWYG editor a complete piece of crap? Bypassing it by using its builtin raw HTML source editor is just about the only sane way to type up a reply to a thread...
    but complaining about it is seen to be a bit of a WTF in itself.



  • @Ragnax said:

    Bypassing it by using its builtin raw HTML source editor is just about the only sane way to type up a reply to a thread...

    The Real WTF, as the cool kids say, is that so many sites can be improved by disabling Javascript, and by extension some of the intended functionality. Functionality such as Google ads, tracking scripts, popups, etc...

    Or perhaps not a WTF at all, since it means someone at least got the "degrades gracefully" right.

    (The editor? I think I got it to work, once.)


Log in to reply