Non-uniform scale in jME

While trying to fix some glitches in the 3DS importer we found out an interesting thing about jME transformations: The application of non-uniform scale does not match the 3ds formats expectations.

Most scenegraphs seem to apply local scale, rotation and translation into a matrix and multiply these to get world coordinates. jME does not do that. It handles scale and rotation independently. This causes some wierd effects when using non-uniform scale. You can try it yourself by scaling the rootNode in TestScenegraph by [2,1,1] -> the line that should connect the boxes ends outside the boxes when rotating them…



So - what can we do about it? Would it be wise to replace world-rotation and -scale by matrix? Would be quite a big change! What would be the desired behaviour of non-uniform scale in jME? Or should we state that non-uniform scale is not handled correctly? Or even forbid it…

Hmm, the rough thing about putting it in a matrix is trying to read it back out…  Otherwise I would say, let's replace rot,scale,trans with a 3x4 matrix (bottom row is usually superfulous, but we could do 4x4 for completeness)

renanse said:

Hmm, the rough thing about putting it in a matrix is trying to read it back out...

exactly :| - would be quite more expensive (memory and/or cpu wise) to offer getWorldRotation() and getWorldScale()...

Is it even possible to read out a scale from a matrix when a rotation is also set?  I tried looking up how to do that in the past and found nothing on that… plenty on setting it (diagonals, of course)

It is possible but really expensive: see e.g. the post from Dave Eberly here.



I think if we really want to go with a matrix 8) the question would be if getWorldRotation/Scale is really needed or can it always be replaced by the whole transformation matrix. On a quick glance into jME and the projects I have here it is only used to transform from local into world coordinates and vice versa. (We could still support them and note they work only with uniform scale and just deprecate them - to avoid breaking peoples code)



An option for the importer btw. would be to convert the non-uniform scaled models by using matrix multiplication and decompose them afterwards. This would allow loading all models while jME does not support correct non-uniform scale.

It looks like the link you posted for Eberly is for extracting translation not scale?



One thing I like about using a matrix is that we can make a call to glLoadMatrix, getting rid of some card math per rendered object.  On the other hand, losing direct access to scale would not allow us to only call GL_NORMALIZE when a non-identity scale was used (perhaps not a big loss though)

renanse said:

It looks like the link you posted for Eberly is for extracting translation not scale?

both afaik, translation, scale and rotation are decomposed
irrisor said:

renanse said:

It looks like the link you posted for Eberly is for extracting translation not scale?

both afaik, translation, scale and rotation are decomposed


After all my searches to find this (and seeing time and again a "non-deterministic" flag on getting scale from matrix) i'd have to see it work to believe it.  :?

Hehe, feel free to grab a c-compiler of your choice to compile this:

http://www.cse.ucsc.edu/~pang/160/f98/Gems/GemsIV/polar_decomp/Decompose.c

http://www.cse.ucsc.edu/~pang/160/f98/Gems/GemsIV/polar_decomp/Decompose.h

As you can see in that source (polar_decomp function) it is deterministic but iterative and thus really expensive (but less costly than a matrix inversion btw).



or read a paper about it:

http://www.cs.wisc.edu/graphics/Courses/cs-838-2002/Papers/polar-decomp.pdf



But that's not what we should discuss, I think. More important is whether we would like to have those matrices and if yes whether we need getWorldRotation/Scale or not.

Interesting paper…  but as the paper says the technique is non-deterministic if you don't know what order the components (scale, rotation, translation and shear) are applied in.  Could we control that order if we only stored a Matrix4f and users were free to setLocalXXX in whatever order they chose?  It would seem not.  But perhaps I am missing something simple.



Anyhow, we originally based our transform code on Eberly's and he has since updated to support non-uniform scales, so perhaps we should have a look at that before moving to a new system altogether.

renanse said:
Could we control that order

sure, it's the order they are applied in (currently 1. rotation 2. scale 3. translation) not the order they are set
renanse said:

Anyhow, we originally based our transform code on Eberly's and he has since updated to support non-uniform scales, so perhaps we should have a look at that before moving to a new system altogether.

good idea - didn't know he updated it. Is it in newer versions of his book already?

But if we only had set methods and did not store the Quaternion and Vectors, how would we apply them in any way except when set was called, see what I mean?  Otherwise we have to store both the individual components and the Matrix4f.



He updated his code in ver. 3.0 of WildMagic which is covered in his 2005 book.  See the Transformation class, especially the Product method.

We should still store local vectors/quats and only replace the world stuff (which cannot be set).



I will have a look into the book tomorrow (don't have it here).

ah, good point.  :)  Hopefully we can just adopt eberly's.  We could keep our memory down (10floats vs 16floats - or 12 if we use a 3x4 mat).  If we do go with mat4, I do not want to personally port any of that decomp code…  ugh.

renanse said:

We could keep our memory down (10floats vs 16floats - or 12 if we use a 3x4 mat). 

Not sure what you mean - do you mean it would raise with matrix? Actually memory footprint would stay the same (or even reduce) from 3 objects, 3 pointers, 10 floats to 1 object, 1 pointer, 16/12 floats. This means from 76 byte to 76/56 bytes on 32bit system (even better on 64).


If we do go with mat4, I do not want to personally port any of that decomp code...  ugh.

hehe, maybe that can avoided some way (if we decide not to support getWorldRotation/Scale) - if not we could do that here.

hehe, why was this topic moved here? don't want jME users to panic? :wink:



If you only want to suppress superflous comments, moving it here won't help - darkfrog is developer now :stuck_out_tongue: XD

ok, to be serious: I thought about posting here initially, but thought there might be users that have good ideas… why did you want this discussion to be internal (just curious to decide better next time).

Because this is internal discussion about a major aspect of the library. Therefore, it needs to be kept as focused as possible by those that make the decisions (Darkfrog being the exception). Once the basics get resolved, and a decision is made, discussion with the community is fine.

Hmm, my bad.



Matrix4f = 8+64bytes or 72bytes  (am I missing how you got 76?)



Quaternion = 8+16 or 24 bytes

Vector3f = 8+12 or 20 bytes (actually 24 bytes, since Java allocates in 8 byte blocks)



But yeah, 4x4 matrix would equal what we currently use (both 72 bytes)…  Maybe we should write that Matrix3x4f class. :slight_smile:

btw, I'm not trying to be difficult, I'm just practiced at playing the role of devil's advocate.  :P  Having a working system is most important to me in the end.