Add matrix & rotation Lua APIs (2nd try)#16870
Add matrix & rotation Lua APIs (2nd try)#16870appgurueu wants to merge 37 commits intoluanti-org:masterfrom
Conversation
|
Thanks for the review! I'll address it soon. Another thing that came to my mind, I'll want a rotation constructor similar to Currently considering a I should probably also add some devtest test entities that make use of these APIs to prove visually that everything works out in the end. |
- Rename "all" to "full" - Move reflection constructor implementation to matrix4.h - Fix up and reword docs a bit
000ddbd to
215d4e8
Compare
215d4e8 to
caba0fa
Compare
|
I've now addressed the review, so now I'm waiting on further replies / comments, and maybe will implement the additional |
|
Okay, about non-finite floats: For rotations, it's clear that we don't want them. We effectively have as invariant that we want a normalized quaternion (a well-defined rotation), and that only works with finite floats. So I don't permit them there. For matrices, we can allow them, and most operations (e.g. applying matrices to vectors, matrix multiplication, etc.) are still well-defined. In fact it is possible to get matrices with infinities through matrix multiplication if things go out of range. It's also possible to put in large doubles which get cast to inf floats. Thoughts? Scream if you think this is a problem. |
|
Two meta-thoughts:
|
Desour
left a comment
There was a problem hiding this comment.
Sorry, idk why I haven't left any comments on this yet, for 8? months. Must have forgotten.
Only some inputs on doc for now (hopefully not too annoying).
| * `vector.cross(v1, v2)`: | ||
| * Returns the cross product of `v1` and `v2`. | ||
| * Returns the *right-handed* cross product of `v1` and `v2`. | ||
| * To get the left-handed cross product | ||
| (e.g. for use with rotations), swap `v1` and `v2`. |
There was a problem hiding this comment.
cross((1,0,0), (0,1,0)) is (0,0,1).
In a right-handed coord system this is right-handed.
But luanti worlds use a left-handed coord system, so it's left-handed.
Doc needs to be clear. Just saying "right-handed" is ambiguous.
| The precision of the implementation may change (improve) in the future. | ||
| Rotations currently use 32-bit floats (*less* precision than the Lua number type). | ||
|
|
||
| Rotations use **left-handed** conventions. |
| Rotations currently use 32-bit floats (*less* precision than the Lua number type). | ||
|
|
||
| Rotations use **left-handed** conventions. | ||
|
|
There was a problem hiding this comment.
| In the current implementation, Rotations are just an abstraction over quaternions. | |
Would be helpful info (and grep endpoint) imo for users who want to use quaternions.
| * This is consistent with the Euler angles that can be used for entities. | ||
| You can do `Rotation.euler_zxy((-rotation):unpack())` | ||
| to convert an entity rotation vector (note the handedness conversion). | ||
| * `Rotation.mapsto(dir_from, dir_to)`: |
There was a problem hiding this comment.
Sound like a function that returns a bool.
Would prefer mappingto or similar.
Also, why no snake case? maps_to
| * `Rotation.identity()`: Constructs a no-op rotation. | ||
| * `Rotation.quaternion(x, y, z, w)`: | ||
| Constructs a rotation from a quaternion (which need not be normalized). | ||
| * `Rotation.axis_angle(axis, angle)`: |
There was a problem hiding this comment.
(
Also here, sounds like it's measuring the axis of an angle (yes this makes no sense, but that's how I read x), the thinking comes afterwards).
from_axis_angle would be more clear.
Similar for other ctors.
Well, I guess one can get accustomed to this kind of naming.
)
| ======== | ||
|
|
||
| Luanti uses 4x4 matrices to represent linear transformations of 3D vectors. | ||
| For this, 3D vectors are embedded into 4D space. |
There was a problem hiding this comment.
into 3D projective space using homogeneous coordinates.
| * `mat:compose(...)`: Shorthand for `Matrix4.compose(mat, ...)`. | ||
| * `mat:determinant()`: Returns the determinant. | ||
| * `mat:invert()`: Returns a newly created inverse, or `nil` if the matrix is (close to being) singular. | ||
| * `mat:transpose()`: Returns a transposed copy of the matrix. |
There was a problem hiding this comment.
(
Would like to also have the adjucate (adj) (essentially a cheaper but scaled inverse). (And combination of adj and transposed.)
(Probably will only get relevant once we have 4d vectors though.)
(Could also have even more ctors then, i.e. for projection matrices.)
)
| * `mat:is_affine_transform([tolerance = 0])`: | ||
| Whether the matrix is an affine transformation in 3d space, | ||
| meaning it is a 3d linear transformation plus a translation. | ||
| (This is the case if the last column is approximately 0, 0, 0, 1.) |
There was a problem hiding this comment.
| (This is the case if the last column is approximately 0, 0, 0, 1.) | |
| (This is the case if the last row is approximately 0, 0, 0, 1.) |
(Also, technically you can also have affine transformations with other last rows (0,0,0,a), but you have to divide by the w component (by a) when dehomogenizing (equivalent: divide the whole matrix by a). But idk if doc formulation could include this easily, or should.)
| let `mat = Matrix4.compose(Matrix4.translation(t), Matrix4.rotation(r), Matrix4.scale(s))`. | ||
| Then we can decompose `mat` further. Note that `mat` must not shear or reflect. | ||
|
|
||
| * `rotation, scale = mat:get_rs()`: |
There was a problem hiding this comment.
When reading mat:get_rs() somewhere, I wouldn't know what it means. Is it that common that it needs such a short name?
get_rot_scale?
There was a problem hiding this comment.
TRS is a common thing, but I'm not sure if RS is clear to people
There was a problem hiding this comment.
I will give this a longer name.
| meaning it is a 3d linear transformation plus a translation. | ||
| (This is the case if the last column is approximately 0, 0, 0, 1.) | ||
|
|
||
| For working with affine transforms, the following methods are available: |
There was a problem hiding this comment.
What if not, methods will still exist, right? UB? Or unspecified result? (I.e. may it crash?)
No worries, I appreciate the review; there's no rush. I'll address it in a bit. |
| * `mat:transform_pos(pos)`: | ||
| * Apply the matrix to a vector representing a position. | ||
| * Applies the transformation as if w = 1 and discards the resulting w component. |
There was a problem hiding this comment.
So ... does this effectively do M * T(v.append(1)) where T means transposing?
There was a problem hiding this comment.
If you take v.append(1) to be the row vector (x, y, z, 1), and then discard the w component: Yes.
This PR exposes userdata wrappers around Irrlicht 4x4 matrices and rotation quaternions. This is already useful as-is; I expect it to be even more useful once we eventually give proper control over client rendering to modders via SSCSM.
Closes #7478, closes #15985.
How to test
Unit tests are included; the bindings should work as expected. Testing more error cases (e.g. wrong types) could be considered.
Follow up
After this is merged, functions that take rotations (e.g. bone overrides, entity rotations) should be updated to accept
Rotationobjects. There should perhaps be new getters that produceRotationobjects.To discuss
This PR is Ready for Review.
I am grateful for @grorp's and everyone else's comments on the last PR. I think I have dealt with everything decently here.
Supersedes #16212. (I wanted to reopen that PR, but it turns out funny things happen once you rebase and force push.)
Full disclosure: I did experiment with GitHub copilot in the making of this PR since it is quite a chore-y job of writing bindings. It was quite underwhelming though, so I ended up writing most of it by hand still.