Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Mesh rotation on Z axis — Gideros Forum

Mesh rotation on Z axis

I'm playing around with the 3D support in Gideros, and there seems to be a lack of rotation control, unless I'm missing something. A mesh has methods setRotationX() and setRotationY(), which both rotate the mesh around the world's axis, not the axis of the mesh itself. There's no setRotationZ() for a mesh, which means some orientations of the mesh relative to the world are impossible, unless there's some other method of rotation.

Using the 3D horse example, which shows a stationary horse mesh, as the camera moves around to view it from various angles. Suppose you make these changes:

In main.lua, replace this line:

frm=Core.frameStatistics().frameCounter

with

frm=0

That will leave the camera in a fixed position instead of orbiting around the horse. If you run the program, you'll see the horse facing you.

Now at the end of main.lua add:

horse:setRotationX(-60)

That method rotates the mesh around the world's X axis, so the part of the mesh closest to the viewer rises up. Since it's facing you, that means the horse is now rearing up on its hind legs.

Now remove that line, so you're back to the horse standing normally and facing you. Now add this line:

horse:SetRotationY(90)

That rotates the horse around the world's vertical axis, so now the horse is facing to the right.

Now here's the issue: Try to pose the horse so it's up on its hind legs, and facing to the right. Suppose you use both of those lines:

horse:setRotationX(-60)
horse:SetRotationY(90)

Since the rotations are done around world axis, and the Y rotation happens to be done first, the horse first turns towards the right, then rotates such that the part of the mesh nearest the viewer (the two right legs) lift off the ground, rather than the front legs. That is, the horse tips to its left.

Note that if the order of operations were reversed (rotating on the X axis, then Y axis), and the horse first raised up its front legs, then rotated to face right, we'd have the pose we were after, but then we'd lose the ability to make the horse face right and lean to its left or right.

Without a setRotationZ() method, it's impossible to rotate the horse so that it will be facing right with its front legs raised.

I've experimented with trying to manipulate the matrix of the mesh, with this:

m = mesh:getMatrix()
m:setRotationZ(-60)
mesh:setMatrix(z)

That has the effect of lowering the horse's two right legs, again, rotating on the world's X axis. Setting the X rotation of the matrix also rotates the mesh on the world's X axis. So manipulating the matrix rather than the mesh doesn't seem to offer a solution.

It is possible to generate a view of the horse facing right with its front legs raised by rotating it -60 degrees on the X axis, and then positioning the viewport to the left of the horse. That is, get the view you want by changing the position of the viewer rather than the orientation of the model. But that's not an effective workaround if you want a scene with more than one mesh that can be rotated independently. For example, you couldn't have two horses in the same view, one facing towards you and one to your right, both with front legs raised.

Am I missing something, or do we really need a mesh:setRotationZ()?

For context, I'm early in the process of building a new game using 3D models from a Windows game I developed years ago. I'm trying to decide whether to pose and pre-render those models in Blender, to make textures for 2D sprites, or to use the models directly in Gideros. I assume using pre-rendered sprites would have a performance advantage, and the advantage of being able to use more detailed models and render with anti-aliasing, etc. But using the models as meshes in Gideros has the advantage of freedom of perspective, and freedom to position objects at any angle without needing a huge number of textures. That assumes they can be rotated on 3 axis, though.

Paul

Likes: antix

+1 -1 (+1 / -0 )Share on Facebook

Comments

  • keszeghkeszegh Member
    edited November 2019
    without reading your full message, you can always rotate a mesh around any point by translating it so that the point moves to the origin, then rotating around the origin, then translating back the mesh to the original position. if these three transformation matrices are multiplied then you only need one matrix to do the same, but it just makes the operation a bit faster, there is no real difference.
  • Can you explain how one would do that with the 3D horse example project?
  • maybe someone else can explain, i never did 3d in gideros, so that's only the theory what i wrote.
  • hgy29hgy29 Maintainer
    @PaulH, of course there is a setRotationZ(), but it is just called setRotation(), actually the same setRotation you use in 2D. About the rotation origin, it is the same as in 2D: use setAnchorPosition to define it.

    Likes: keszegh

    +1 -1 (+1 / -0 )Share on Facebook
  • PaulHPaulH Member
    edited November 2019
    I initially assumed that would be the method for rotating on Z, but when rotating on Y by 90, setRotation() and setRotationX() seem to cancel each other out. They're both rotating about the X axis, just in opposite directions.

    That is, doing this:

    setRotation(60)
    setRotationX(-60)
    setRotationY(90)

    has the same result as just doing setRotationY(90).

    This only seems to be an issue after rotating Y by 90. Perhaps I'm still missing something (my 3D programming is rusty) but I can't find any combination of rotations that result in the horse facing right but with the front legs raised.
  • PaulHPaulH Member
    edited November 2019
    It almost seems as though setRotationY and setRotationX are rotating around those axis of the world, while setRotation rotates around the mesh's original Z axis, rather than the Z axis of the world. That is, setRotationY and setRotationX are absolute while setRotation is relative. I'm still trying to wrap my head around what's going on here.

  • hgy29hgy29 Maintainer
    Accepted Answer
    All three setRotation() are absolute, if you want to apply multiple transforms one after the other, you can try with Matrix ops: there is a combining rotate() that is relative instead of absolute.
  • I think I finally understand what I'm seeing. Yes, they're all absolute, but the order of operations seems to make some positions of a mesh impossible to achieve using only setRotation(), setRotationX() and setRotationY(). It seems they are applied in the order Z, Y, X. That means that if there's a 90 degree Y rotation, both setRotation() and setRotationX() effectively rotate the mesh on the X axis.

    Here's the horse, viewed along the Z axis, with no rotation:
    image

    Rotating only on X behaves as expected, with a negative value lifting the front of the horse:
    image

    Rotating only on Y behaves as expected, turning the horse to the right:
    image

    Doing both shows that the horse was rotated first on the Y axis (facing right), followed by the X rotation, which now leans the horse to its left.
    image

    If we move the -30 rotation from X to Z, we get the same pose, because the Z was done before the Y:
    image

    With the rotations applied in the order Z, Y, X, any time you have a 90 degree rotation on Y, rotations on X and Z both effectively rotate on the X axis. With this order of rotations, using these 3 methods, I don't think it's possible to orient the horse facing right with the front legs raised.

    Thanks, @hgy29, for pointing out the matrix rotate() method. That gives the coder control of the order of operations. These lines turn the horse right and then lift its legs, rotating on Y, then Z:

    m = Matrix.new(1, 0, 0, 1, 0, 0)
    -- Turn horse to the right by rotating on Y:
    m:rotate(90, 0, 1, 0)
    -- Raise front legs by rotating on Z, since horse is now facing right:
    m:rotate(30, 0, 0, 1)
    horse:setMatrix(m)

    image

    The same orientation can be achieved in the other order by rotating on X, then Y:

    m = Matrix.new(1, 0, 0, 1, 0, 0)
    -- Raise horse's front legs by rotating on X
    m:rotate(-30, 1, 0, 0)
    -- Turn horse to the right by rotating on Y:
    m:rotate(90, 0, 1, 0)
    horse:setMatrix(m)

    So I think the lesson I've learned from this is that not every combination of rotations is possible using the setRotation*() methods, but manipulating the matrix provides complete control.

    This function applies rotations in the order ZXY, which seems to allow for every orientation:

    function setRotationZXYOrder(mesh, x, y, z)
    m = Matrix.new(1, 0, 0, 1, 0, 0)
    m:rotate(z, 0, 0, 1)
    m:rotate(x, 1, 0, 0)
    m:rotate(y, 0, 1, 0)
    mesh:setMatrix(m)
    end

    All is well. =)

    Thanks,

    Paul

    r1.jpg
    367 x 493 - 28K
    r2.jpg
    533 x 527 - 33K
    r3.jpg
    381 x 478 - 32K
    r4.jpg
    500 x 465 - 35K
    r5.jpg
    656 x 448 - 37K

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • The above didn't turn out to be quite the solution I'd hoped. Yes, those matrix manipulations can orient the mesh in any angle, but setMatrix() overrides the scale, and vice versa. That is, if I do this:

    obj:setScale(0.5)
    setRotationZXYOrder(obj, 0, 90, 0)

    The object is rotated, but the scale reverts to 1.0. If I call obj:setScale(0.5) again after the rotation, the the rotation reverts to 0, 0, 0. The same happens if I call setScaleX(), setScaleY(), or setScaleZ().

    If I look at the matrix following a setScale(), like this:

    obj:setScale(0.5)
    print(obj:getMatrix():getElements())

    I get a matrix of 0.5, 0, 0, 0.5, 0, 0. But if I create an equivalent matrix and apply it, like this:

    obj:setMatrix(Matrix.new(0.5, 0, 0, 0.5, 0, 0))

    that scales the mesh on X and Y but not Z.

    So it seems if I create a new matrix to apply to the mesh with setMatrix(), even if the matrix contains the same values as the one I get with obj:getMatrix(), setting that new matrix overrides some other attributes of the mesh.

    In my function setRotationZXYOrder(), if rather than creating a new matrix, I retrieve the existing matrix and rotate that, then the scale is preserved. But then the rotations become cumulative. If for every frame I try to I rotate an object to it's appropriate orientation, it rotate further with each frame and spin. A workaround is to undo the previous rotation before applying each new one. That requires a method to apply the matrix rotations in the opposite order. So if for each frame I want to orient an object to "heading" degrees on the Y axis, I could do this:

    setRotationYXZOrder(obj, 0, 0 - last_heading, 0)
    setRotationZXYOrder(obj, 0, heading, 0)
    last_heading = heading

    More generally, I can track the last X, Y, and Z rotations applied, and undo them before applying any new ones.

    This works, but seems rather inelegant.

    Likes: antix, MoKaLux

    +1 -1 (+2 / -0 )Share on Facebook
  • hgy29hgy29 Maintainer
    You can isolate some transforms from the others by nesting your mesh into another sprite. That way you can apply a set of transform to the mesh and another to the surrounding sprite
  • That makes sense. Thanks. It's seems it will be useful to have a parent sprite for the mesh, to make it easy to swap out different meshes in different poses.

    For now I'm using a method that cancels out previous rotations on the matrix, then applies the new ones.

    With that working, I've made some progress on my experiments, and I've got to say, I'm really impressed with how Gideros handles 3D graphics. I've got a working landscape from a heightfield, with nested skyboxes to create aerial perspective, an animated water surface, and a fishing boat one can drive from various views including first-person. It's nowhere near complete (no boat wake, temporary water effects, no trees or vegetation, the boat bounces excessively while changing speed, etc.) but I'm really encouraged with how easy it is to reuse my existing models and landscapes. Of course it's not a full featured 3D system, but it's more powerful than I expected.

    I'm also really pleased with the performance, even with really detailed meshes, and even on an older device.

    Here's what it looks like so far - enough of a proof of concept that I'm satisfied Gideros can do what I need it to for the new game:





    Paul
    +1 -1 (+5 / -0 )Share on Facebook
  • wonderful stuff you have here! Congrats!
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • Thanks! As things come together I may share some code that might be useful for others, like the heightfield landscape functions.
Sign In or Register to comment.