Comments (36)
Thank you for your feedback! :)
Actually this is not a "bug". At least I'm pretty sure it's not.
Rotation properties in 3D are ambiguous for many reasons. This is one of them: gimbal lock (it's not the classic symptom of what we call "gimbal lock" but both problems have the same root):
http://en.wikipedia.org/wiki/Gimbal_lock
Here is the explanation: 3D matrix decomposition is ambiguous. Extracting the translation is not an issue since it is stored in the 4th row/column. But there are several ways to extract the rotation and the scale. The main issue is that both the rotation and the scale are stored in the upper left 3x3 matrix. And there is now way to make the difference between a Math.PI rotation and a -1 scale (to summarize).
When you read one of the Matrix4x4.rotation* properties, it will try to decompose the matrix. But it can fail at preserving the consistency of the rotation values: some will be clamped to [-PI .. PI] when others will be negated for example. So setting rotation values work: it's the getter that returns ambiguous values.
For example, this works as expected:
node.rotationY = 2.;
But if you want to increment/decrement the rotation and keep the consistency, you should use the Matrix4x4.appendRotation() and Matrix4x4.preprendRotation() methods instead. Those methods use axis angles and are not ambiguous:
private function onKeyDown(e:KeyboardEvent):void { switch (e.keyCode) { case Keyboard.LEFT: _node.transform.appendRotation(.1, Vector4.Y_AXIS); break; case Keyboard.RIGHT: _node.transform.appendRotation(-.1, Vector4.Y_AXIS); break; default: } }
You could also do something like this:
_rotation += .1; node.rotationY = _rotation;
But it will burn a lot (lot) more CPU than append/prependRotation for the very same result...
A workaround would be to store the roation aside from the Matrix3D object. But it would be pretty hard to maintain this property and quite cumbersome. And I guess it won't help with actual gimbal lock so you'll still have to learn more about rotation in a 3D space anyway :)
To help people, I will write a sample and a tutorial today about this very problem.
from minko.
in fact, in our project we where doing it like
_mesh.rotationY = Number(_settings.getProperties(Settings3D.PROPERTIES_ROTATION_Y) / (180 / Math.PI))
and it caused mesh flipping upside down.
prependRotation is not that good because it's cumulative, but setRotation worked for me (however, it seems to be much slower than modifying object's "rotation" properties directly)
from minko.
prependRotation is not that good because it's cumulative
But the bug should only exist when you want the rotation to be cumulative...
_mesh.rotationY = Number(_settings.getProperties(Settings3D.PROPERTIES_ROTATION_Y) / (180 / Math.PI))
Are you sure it's not because your local Y axis is not the up axis?
What do you get in the console if you do this:
_mesh.localToWorld.transformVector(Vector4.Y_AXIS);
?
from minko.
But the bug should only exists when you want the rotation to be cumulative...
Not entirely true, the bug could be seen when ever the user can compare two rotations, for example if he uses angle slider.
from minko.
he uses angle slider
What kind of "angle slider"? If the slider simply sets the rotation it works fine.
If you want to compare rotations, you should use quaternions because euler angles are ambiguous.
Maybe we should add a Matrix4x4.getRotationQuaternion() method...
from minko.
what would be the difference between doing .rotationY += 0.1 and moving the slider by 0.1 and then .rotationY = slider.value? do you mean rotationY getter makes the jump?
from minko.
do you mean rotationY getter makes the jump?
Exactly. As I explained earlier, the decomposition is ambiguous. The composition is not.
Therefore, rotationY = slider.value should work without any problem just like the last sample I provided.
At least it will rotate on the Y axis. Now that Y axis might not be the UP axis and that's what I suspect :)
It is really a math VS programming problem... And this is one of the reasons I wanted to avoid the rotation* properties in the scene node itself in the first place: I'd rather have people take some time to learn about 3D transformations, matrices and the associated pitfalls.
In Minko Studio, the rotation gizmo uses append/prependRotation() and it really works fine.
If someone adds Matrix4x4.getRotationQuaternion() I'll be more than happy to merge it of course :)
from minko.
Here you go, Matrix4x4.getRotationQuaternion() 371c748
This should make it easier to compare rotations using Vector4.equals()
from minko.
actually, we do use slider (we are developing editor), and our meshes and lights are flipped upside down in that cases (angle between 90 and 270), but with setRotation it does not flip (works as intended, but SLOW)
from minko.
we are developing editor
Why don't you use Minko Studio? I can give you access to the private beta.
and our meshes and lights are flipped upside down in that cases, but with setRotation it does not flip
The rotation setter uses setRotation. So they have the very same behavior...
Are you sure they behave differently?
Can you provide some sample code?
Thank you for your feedback :)
works as intended, but SLOW
Setting the rotation directly implies decomposing/recomposing the matrix.
This is why you should use append/prependRotation() instead.
from minko.
We do following:
var rotationX:Number = Number(_settings.getProperties(Settings3D.PROPERTIES_ROTATION_X)) / (180 / Math.PI) + Math.PI / 2;
var rotationY:Number = Number(_settings.getProperties(Settings3D.PROPERTIES_ROTATION_Y)) / (180 / Math.PI);
var rotationZ:Number = Number(_settings.getProperties(Settings3D.PROPERTIES_ROTATION_Z)) / (180 / Math.PI) + Math.PI;
_mesh.transform.setRotation(rotationX, rotationY, rotationZ);
This code is actually working correct, but when we set all 3 properties on mesh independently (in same order as they are calculated) it flips
About Minko studio - it would be great to have a look at it (and we are really interested in ShaderLab - is it included in studio or it comes as separate product?)
from minko.
About Minko studio - it would be great to have a look at it (and we are really interested in ShaderLab - is it included in studio or it comes as separate product?)
Just contact us: http://aerys.in/contact
ShaderLab open beta is here : http://hub.aerys.in/index.php/Minko:Tools
But it's an old release.
A new version will be directly integrated inside Minko Studio before the end of the year.
This code is actually working correct, but when we set all 3 properties on mesh independently (in same order as they are calculated) it flips
Yes. Because the rotation* setter uses setRotation(), which has to decompose the matrix to know the value of the other 2 rotations (ie. if you set only Y, we have to get the value of the X and Z rotations).
If you call setRotation() with the 3 rotations, the euler angle values extracted from the matrix are replaced with the 3 values you provide. Thus the issue does not show.
Also, make sure you always lock() and unlock() matrices if you perform multiple operations. Read the Locking/unlocking the transform section here:
http://hub.aerys.in/index.php/Minko:Moving,_Rotating_And_Scaling_Objects
from minko.
Yes. Because the rotation* setter uses setRotation(), which has to decompose the matrix to know the value of the other 2 rotations
lol so it does not work with the slider, after all.
from minko.
Indeed :) My mistake...
Another way to do it is to use append/prependRotation() with push/pop:
// init the matrix with initial translation/scale/rotation // push it to "save" its state matrix.push(); // on enterframe // restore the matrix at its init. state matrix.pop(); // save it again to make sure we don't lose the init state matrix.push(); matrix.appendRotation(_rotationX, Vector4.Y_AXIS);
Matrix4x4.push() will save the matrix.
Matrix4x4.pop() will restore it.
It's like somekind of "states" stack directly in the Matrix4x4 object.
It's pretty cool for a lot of use cases :)
from minko.
Indeed, very nice feature
from minko.
by the way, following code that was suggested by you as working is broken too (it actually reproduces that "flip" bug I got when trying to rotate my mesh by sliders)
private var _rot:Number = 0;
private function onKeyDown(e:KeyboardEvent):void
{
switch (e.keyCode)
{
case Keyboard.LEFT:
_rot += Math.PI / 8;
break;
case Keyboard.RIGHT:
_rot -= Math.PI / 8;
break;
default:
}
_node.rotationX = 0;
_node.rotationY = _rot;
_node.rotationZ = 0;
}
from minko.
And that's why I said "Indeed :) My mistake..." :p
from minko.
well, then forcing users to work with transform seems to be way better than providing ambiguous methods - personally I'm OK with matrices and only reason why I used that rotation* because it's there and lays on surface (and in most engines which recalculate matrix completely on-demand it works just OK) ;)
from minko.
in most engines which recalculate matrix completely on-demand it works just OK
But that's simply not possible.
As soon as you multiply to matrices for example, you'll have to decompose the result to get the rotation.
I've never heard of any other solution than working with quaternions or axis angles...
If you have an implementation example I'll be happy to have a look :)
from minko.
to give one quick example - it's Away3D, matrix there is duplicated by set of fields (x,y,z, rotX, rotY, rotZ, scaleX, scaleY, scaleZ), is marked as dirty when field changes and then recalculated when matrix getter is accessed
from minko.
And they have the exact same bug, don't they?
I found this: http://away3d.com/forum/viewthread/3299/
When those properties are "dirty", you have to decompose the matrix to refresh them.
And decomposition will return those ambiguous values.
from minko.
/**
* Defines the euler angle of rotation of the 3d object around the x-axis, relative to the local coordinates of the parent <code>ObjectContainer3D</code>.
*/
public function get rotationX() : Number
{
return _rotationX * MathConsts.RADIANS_TO_DEGREES;
}
public function set rotationX(val:Number) : void
{
if (rotationX == val)
return;
_rotationX = val * MathConsts.DEGREES_TO_RADIANS;
invalidateRotation();
}
from minko.
@promethe42 well you could make a matrix class with all these angles tracked internally (along with scales and translations). while this would limit the range of matrices you could use for transformation, it's not necessary tight limit - software like Maya are doing exactly that and noone complains.
from minko.
and in the end they're doing
protected function updateTransform() : void
{
_pos.x = _x;
_pos.y = _y;
_pos.z = _z;
_rot.x = _rotationX;
_rot.y = _rotationY;
_rot.z = _rotationZ;
_sca.x = _scaleX;
_sca.y = _scaleY;
_sca.z = _scaleZ;
_transform.recompose(_transformComponents);
if (!_pivotZero) {
_transform.prependTranslation(-_pivotPoint.x, -_pivotPoint.y, -_pivotPoint.z);
_transform.appendTranslation(_pivotPoint.x, _pivotPoint.y, _pivotPoint.z);
}
_transformDirty = false;
_positionDirty = false;
_rotationDirty = false;
_scaleDirty = false;
}
where "transformComponents" is following:
_transformComponents = new Vector.<Vector3D>(3, true);
_transformComponents[0] = _pos;
_transformComponents[1] = _rot;
_transformComponents[2] = _sca;
from minko.
If you have the actual link to the source file it would be very helpful :)
Away3D 4 doesn't have its own Matrix type so it's sure they have the same issue.
well you could make a matrix class with all these angles tracked internally
Ok. But what do you do when those private properties have to be refreshed?
The only way is to decompose the matrix and you get ambiguous values.
and in the end they're doing
The thing is their matrices are polled.
Ours are pushed (this is mainly for performances reasons and it works great.) so the behavior has to be a little bit different.
I'm investigating a bug in minko-lighting and I'll give it a try...
from minko.
what do you do when those private properties have to be refreshed?
they just dont :) you can't transform the node in Maya in any other way than that formula allows.
from minko.
they just dont :) you can't transform the node in Maya in any other way than that formula allows.
That means goodbye for 99% of what you actually need to do when you do "real" 3D programming stuff.
A 3D editor and a 3D engine are used in very different ways. For example, you could not have lookAt() or orientTo() methods. You would not be able to multiply two matrices, which happen 100x more than incrementing rotation* properties...
Look at minko studio: the rotation gizmo works perfectly well and yes, the tool "limits" what you can do with the matrix since you can't affect it directly. But in the code, you absolutely need to...
from minko.
https://github.com/away3d/away3d-core-fp11/blob/master/src/away3d/core/base/Object3D.as
and by the way, I'll vote for just removing that rotation* stuff at all as broken - as you've said earlier it's actually impossible to decompose matrix without any ambiguity involved
from minko.
If it can't be fixed we will. I wasn't too happy with this in the first place.
You know it's always the same thing: people complain it's not here despite they don't know how much they actually don't need it/should not use it :p
We usually don't do this kind of things... we've been to compromising on this one :)
from minko.
A 3D editor and a 3D engine are used in very different ways.
You could still make such a matrix class (extending the current one) and move all rotation* there, just in case of that 1% (and as you guessed this thread is exactly one of them)
from minko.
I get it. But it doesn't sound very elegant :)
I'd rather have people learn things...
from minko.
It's ugly compromise. As I've said earlier I'm happy with matrices, I like math and I even can write my own lightning system with bare AGAL (already done to replace that crappy stuff in Away3D). But well, I work in team and not everyone in team know anything about math - most people just don't care (as long as they don't see obvious bugs, and even if they see they just say something like "that minko of yours is broken piece of shit!").
I'm currently fixing bugs left after such team member :(
from minko.
the point is that forcing everybody to do it the right way is not that bad at all :)
from minko.
the point is that forcing everybody to do it the right way is not that bad at all :)
100% pro!
And I think that's what we don't with the rest of the framework.
from minko.
fixed in f861b24
please confirm that fix works for you
from minko.
Yes, no more flipping now.
from minko.
Related Issues (20)
- where is minko shaderlab and shaderStudio HOT 5
- minko compile 1 error generated HOT 3
- Collision detection HOT 5
- when will you provide minko3 flash version? HOT 1
- Vulkan API HOT 1
- README Linux section is confuse HOT 2
- Collada model picking? HOT 11
- Chinese characters cannot display on minko-plugin-html-overlay HOT 3
- Mouse events unusable on MacBook HOT 1
- Integration with UIKit HOT 1
- Invalid NDK Path
- Compile Minko SDK android HOT 4
- CylinderGeometry HOT 1
- Using a webserver in the compiled application for an html overlay
- Has minko been abandaned by original developers? HOT 7
- I want use haxe to reimplement the minko source code. HOT 2
- haxe to reimplement the minko source code. HOT 2
- Android build instructions out of date HOT 1
- Integration into Qt HOT 2
- Any chance of enabling discussions on this repo?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from minko.