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
Comments
Fragmenter - animated loop machine and IKONOMIKON - the memory game
Fragmenter - animated loop machine and IKONOMIKON - the memory game
Likes: keszegh
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.
Here's the horse, viewed along the Z axis, with no rotation:
Rotating only on X behaves as expected, with a negative value lifting the front of the horse:
Rotating only on Y behaves as expected, turning the horse to the right:
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.
If we move the -30 rotation from X to Z, we get the same pose, because the Z was done before the Y:
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)
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
Likes: MoKaLux
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
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
Likes: keszegh, MoKaLux, hgy29, SinisterSoft, antix