Linuxdirk wrote:entuland wrote:Minetest only provides a few spatial helpers to deal with vectors, lacks any proper matrix facilities which probably is the cause for other mods failing to properly handle arbitrary rotations.
This isn’t enough for vectors? What’s missing? Maybe create a feature request issue. And maybe check
this handy thing here.
You can get the position of the node in relation to the player, and the current rotation of the node from the node’s meta data, and the face the player is pointing and as well as the exact position the player is pointing to on the face. I never looked into this, but what else is needed to sanely rotate a node?
Oh well, technically no, you don't really need matrices to perform arbitrary rotations.
You don't need them just like you don't need multiplications to perform additions where all values are equal, but multiplications are faster to type and they express clearly what they're doing.
In Minetest rotations are expressed as a single number, ranging 0 to 23, where the total of 24 values is actually the combination of 6 semiaxes and 4 rotations around each of those semiaxes.
This is the current approach at dealing with the facedir values (extracted from the built-in screwdriver just because it's the one at hand, other mods do just the same):
Code: Select all
local facedir_tbl = {
[screwdriver.ROTATE_FACE] = {
[0] = 1, [1] = 2, [2] = 3, [3] = 0,
[4] = 5, [5] = 6, [6] = 7, [7] = 4,
[8] = 9, [9] = 10, [10] = 11, [11] = 8,
[12] = 13, [13] = 14, [14] = 15, [15] = 12,
[16] = 17, [17] = 18, [18] = 19, [19] = 16,
[20] = 21, [21] = 22, [22] = 23, [23] = 20,
},
[screwdriver.ROTATE_AXIS] = {
[0] = 4, [1] = 4, [2] = 4, [3] = 4,
[4] = 8, [5] = 8, [6] = 8, [7] = 8,
[8] = 12, [9] = 12, [10] = 12, [11] = 12,
[12] = 16, [13] = 16, [14] = 16, [15] = 16,
[16] = 20, [17] = 20, [18] = 20, [19] = 20,
[20] = 0, [21] = 0, [22] = 0, [23] = 0,
},
}
The above works, in the sense that it does what it is meant to do: map a facedir value to another facedir value according to a specific transformation (two transformations actually above, one for each table).
In the case of my mods (both [wesh] and [rhotator]) I _could_ use the same approach, and it would work. The only problem is that since I need all combinations, I should implement a table mapping all combinations of the 24 values against eachother (for a total of 576 pairs).
Using matrices instead I only need to write down 10 starting matrices (6 for the semiaxes, 4 for the rotations around the positive Y axis) and then I can combine them together without worring what is the actual orientation of the nodes, or of the face I'm pointing at (for the [rhotator] mod) or the capture / rebuild orientation of my canvases (for the [wesh] mod).
For example, the line that does the magic in [rhotator] is this:
Code: Select all
local stop = transform * rotation * transform:invert() * start
In the above all elements are matrices, not simple numbers. I take the "start" matrix, I bring the pointed face on the positive Y axis by multiplying for the inverse of the "transform" matrix, then I multiply for the "rotation" matrix to apply the actual desired rotation and finally I multiply for the regular "transform" matrix to bring that face back to where it was, correctly rotated around its axis - matrix multiplications are not commutative and they get applied right to left, so the order of factors there is meaningful.
I don't need to know what face I'm actually pointing at, or what rotation I need to apply or what orientation the node is, matrices make all of it "transparent" to the code, I just need to map the 24 facedir values to 24 matrices (and I don't even need to hardcode those 24 matrices, cause I can generate them combining the 6 "semiaxis" matrices and the 4 "rotation around Y" matrices).
Edit 1: of course I am using the handy thing...
https://github.com/entuland/rhotator/bl ... t.lua#L131
...that's what is making [rhotator] aware of the position of the crosshair relative to the face and to compute the closest edge used in "push" mode, but it's just one piece of the puzzle.
Edit 2: in particular, this is one of the functions one could eventually consider using to deal with such arbitrary transformations:
https://github.com/minetest/minetest/bl ... ua#L40-L91
The problem with working with vectors is though that they only convey the main direction the node is pointing to, not its rotation around that direction. That's why out of the 24 possible combinations of 6 directions and 4 rotations, that function only produces 8 values out of 24 (accounting for the Y value of the given vector) and 4 values out of 24 in case the Y value of the vector is to be ignored according to the caller of the function (the "is6d" flag).
Proper abritrary rotations in 3D require either a matrix, or an XYZ vector
plus an angle (
called axis-angle representation), or a quaternion (a vector with 4 values, none of which is X, Y or Z, it's simply another way to represent the datum about "direction + rotation").
(in the case of the dir_to_facedir() function it's easy to see how it's just an approximation at handling arbitrary directions / rotations for the simple fact that it operates only on a vector, it doesn't ask for an angle around that vector at all)
Quaternions and matrices can both be combined ignoring their "internals" (sort of, I'm simplifying) whereas axis-angle representations require far more juggling. Matrices in particular are the most powerful among them all because they can represent and combine spatial information far beyond the simple direction and rotation (scaling, skewing, mirroring and whatnot).
If I wanted to add some "mirror" flags to my [wesh] mod interface I could traverse the nested table and juggle the values (say, transforming 1 to 16 into 16 to 1) but using matrices I could simply apply a mirror+translate to the coords and be done with it.