jerome Posted February 7, 2017 Share Posted February 7, 2017 Hi people, I'm about to implement a new useful feature to the core. For now, it's called addRotation(). What is it for ? Well, usually the BJS users aren't big fans of quaternions to manage their mesh rotations, although the quaternions are the best tools to achieve it. Quaternion aren't intuitive, so people prefer the Euler angles, right ? Moreover BJS imposes the rotation order, what is, if I'm not wrong, YXZ. This means that, when you set a mesh rotation (x, y, z), it is rotated first around Y, then around X and finally around Z in its local space. This is important because the rotation is not commutative : if you rotate a mesh for, say, 30° around Y and then for 20° around X, you won't get the same final orientation than if you rotate it first for 20° around X and then only for 30° around Y. In brief, the rotation order really matters ! So, you want to use only the Euler angles and you are constrained by the engine rotation order : no surprise you can hardly achieve the rotation you really want. Here's comes addRotation(x, y, z) to the rescue ! addRotation() will do 3 things for you : - to let you use only Euler angles, whatever the internal mesh rotation is Euler or quaternion based (note : under the hood, addRotation() works with quaternions, but it's hidden for your convenience) - to allow you to decompose your rotation by steps in your custom order to achieve your final orientation : you can rotate a mesh first around X, then around Y, then around X again, then around Z, etc. - to update the initial mesh rotation values for you : if the rotation is Euler based, it's updated as Euler angles (mesh.rotation), if it's a quaternionRotation (mesh.rotationQuaternion), the quaternion is updated. How does it work ? Just give the mesh an initial rotation, or none, (this rotation can be Euler angles or a quaternion, as you want), then add your rotation steps to achieve the wanted final orientation : mesh.rotation.x = Math.PI / 4.0; // initial rotation around x mesh.addRotation(0, Math.PI / 3.0, 0); // the mesh is fisrt rotated around X, then only around Y for PI/3 // you can even link all the rotation steps // here X first, then Z, finally Y mesh.addRotation(x1, 0, 0).addRotation(0, 0, z2).addRotation(0, y3, 0); // the mesh rotation property is computed for you console.log(mesh.rotation); First demo :http://www.babylonjs-playground.com/#1PON40 The left box is the model box. The central box is rotated as usual for PI/3 around X and PI/2 around Y. The BJS rotation order makes it rotate first around Y, then around X. The right box is given an initial rotation for PI/3 around X, then a rotation step is added for PI/2 around Y. You can see that the final orientation differs. If you have a look at the console where the box2 rotation is displayed : http://www.babylonjs-playground.com/#1PON40#1 you will notice that the Euler angles needed to achieve this final orientation have a z value, although you didn't specify anything about Z. Simply because this is the Euler rotation to be done in the required BJS rotation order to achieve this final orientation. Something you wouldn't probably have found by yourself just playing with Euler angles ... Let's go further with the torus wheel challenge. Remember the number of times that, in this forum, people tried out to use the BJS provided shape called torus and to use it as a car wheel. It's damned complex because the torus is designed horizontally in its local space and we need to make rotate either around its central axis (rolling), either around a vertical axis (wheel direction). So some head hache with quaternions or mesh.rotate() once in the local space, once in the world space. Let's try an easier way. Here's the just born torus : http://www.babylonjs-playground.com/#1PON40#2 Let's set it vertical as an initial rotation around Z : http://www.babylonjs-playground.com/#1PON40#3 Too bad the method debugLayer.shouldDisplayAxis() doesn't currently display anything, just keep in mind the torus local axis at this step : Y is the torus central axis (so now horizontal after this first rotation around Z) X is, now the torus is vertical, the vertical axis, Z keeps unchanged along the world Z axis. So in order to make the wheel roll, we will give each frame, from this initial rotation, an extra rotation around X first (wheel direction) and then a rotation around the torus central axis Y http://www.babylonjs-playground.com/#1PON40#4 As you can see, the code is quite short and intuitive : I don't know the BJS required rotation, but I know how to set my torus orientation step by step using only Euler angles. No need for quaternion or space switching here. I just "build" each frame the way to achieve the final rotation with my own rotation steps and my own order. Then it's quite easy to animate car wheels : http://www.babylonjs-playground.com/#1PON40#5 Have fun JohnK, ian, Sebavan and 5 others 8 Quote Link to comment Share on other sites More sharing options...
jerome Posted February 7, 2017 Author Share Posted February 7, 2017 PRed and merged \o/ Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted February 7, 2017 Share Posted February 7, 2017 Congrats!!! Best name ever jerome 1 Quote Link to comment Share on other sites More sharing options...
JohnK Posted February 7, 2017 Share Posted February 7, 2017 Brilliant work as ever Jerome. 7 hours ago, jerome said: Moreover BJS imposes the rotation order, what is, if I'm not wrong, YXZ Having done quite a bit of work on rotations the imposed order is ZXY, you can check this out at http://babylonjsguide.github.io/advanced/Applying_Rotations.html Obviously this makes no difference to your new addRotation function and I am being very pedantic. I can see the new function being extremely useful. jerome 1 Quote Link to comment Share on other sites More sharing options...
jerome Posted February 7, 2017 Author Share Posted February 7, 2017 I was quite certain it was YXZ. I really need to check this back for my own curiosity. I had the mnemonic way to match the order with the human head movements : say no first (shaking head left to right and back) : Y then yes (nodding) : X then twist it, if you can, like an owl : Z GameMonetize and JohnK 2 Quote Link to comment Share on other sites More sharing options...
jerome Posted February 8, 2017 Author Share Posted February 8, 2017 I just read accurately your documentation, John. Well, I've omitted to precise that mesh.addRotation() only modifies the mesh properties .rotation or .rotationQuaternion, what both sets the mesh rotation in its local space only. So, it's coherent with your doc here : http://babylonjsguide.github.io/advanced/Applying_Rotations.html#yxz-local-axes-yaw-pitch-roll JohnK 1 Quote Link to comment Share on other sites More sharing options...
JohnK Posted February 8, 2017 Share Posted February 8, 2017 OK got it now. When x, y, z are considered as rotations in LOCAL space then mesh.rotation(x, y, z) applies the rotations in the order y, x, z see http://www.babylonjs-playground.com/#1ST43U#3 When x, y, z are considered as rotations in WORLD space then mesh.rotation(x, y, z) applies the rotations in the order z, x, y see http://www.babylonjs-playground.com/#1ST43U#2 Therefore addRotation is accumulating rotations in LOCAL space. EDIT I work out this stuff then forget all about it. Quote Link to comment Share on other sites More sharing options...
jerome Posted February 8, 2017 Author Share Posted February 8, 2017 Actually both methods, addRotation() and rotate(), accumulates the rotation. BJS only knows the order YXZ when it takes in account the mesh property values .rotation or .rotationQuaternion. BJS reads these values and computes the Wolrd Matrix from them before passing it to the GPU. If you use addRotation(), you modify the initial .rotation or .rotationQuaternion values by accumulating your own values each call. At the end, you've got new values for .rotation or .rotationQuaternion. BJS will read them and compute the World Matrix using the YXZ order only. The same if you use rotate() : you accumulate new rotation values, so the order of your call matters, because this modifies each call the value of mesh.quaternionRotation. So if you call mesh.rotate(z) before mesh.rotate(y), you won't get the same final result than the inverse order. Whatever the order you choose, what has a big importance for the final rotation, BJS just get the final state, it is to say the final value of .quaternionRotation, then computes the World Matrix ... using the YXZ order only again (if this order matters when talking about quaternions). Example : I changed the order of the calls to rotate() here http://www.babylonjs-playground.com/#1ST43U#16 As you said, addRotation() is a helper tool, working directly with Euler angles and only in the local space whereas rotate() works only with quaternions, rotation axis but either in the local and the world spaces. Quote Link to comment Share on other sites More sharing options...
jerome Posted February 8, 2017 Author Share Posted February 8, 2017 I rewrote the rotation part here : https://github.com/BabylonJS/Documentation/blob/master/content/overviews/Standard/How_Rotations_and_Translations_Work.md adam 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.