diff options
author | NetherEran <55532075+NetherEran@users.noreply.github.com> | 2020-06-09 17:38:39 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-09 19:38:39 +0200 |
commit | 7148834440d10bc211628aa2652e31166bdd58a0 (patch) | |
tree | ddf86be920acc50f61925cfd2d256945ebebd225 /builtin/common/vector.lua | |
parent | b16f841756ef86e83710ad2fddf2cd5bafdf4bcc (diff) | |
download | minetest-7148834440d10bc211628aa2652e31166bdd58a0.tar.gz minetest-7148834440d10bc211628aa2652e31166bdd58a0.tar.bz2 minetest-7148834440d10bc211628aa2652e31166bdd58a0.zip |
Some vector functions useful for working with rotations (#9572)
* added vector.rotate
* added vector.forward_from_rotation and vector.up_from_rotation
* added vector.forward_up_to_rotatiton
* fixed some bugs and formatting with vector functions
* shortened name of some new vector functions and added documentation
* made vector.rotate not require a unit vector as axis
* fixed crash with vector.forward_up_to_rot
* renamed new vector functions, made vector.rotate apply a rotation matrix, old vector.rotate is now called vector.rotate_around_axis
* documented vector function changes
* removed some whitespace to appease luacheck
* implemented and fixed optimization of vector.rotate_around_axis by SmallJoker
* added some unit tests for rotation vector functions
* clarified that rotation vectors are in radians and according to the left hand rule
* hopefully appeased luacheck
* renamed rotation_to_horizontal to forward_at_rotation, rotation_to_vertical to up_at_rotation
* handled cases where sin or cos are 0 in rotation vector functions
* added more comments
* clarified documentation of rotation vector functions
* added more unit tests
* changed way in which vector.rotate_around_axis is adjusted for left handed coordinate systems
* made vector.rotate_around_axis actually left handed
* unrolled matrix multiplication
* removed vector.forward_at_rotation and vector.up_at_rotation
* prettified vector.rotate_around_axis, made previous commits not break anything
* removed references to removed vector.forward_at_rotation and vector.up_at_rotation
* removed documentation of removed vector functions
* clarified documentation and fixed styling of rotation vector functions
* restyled comments minorly
* spelling fixes and some hopefully better comments
* allowed 'up' to be missing from vector.directions_to_rotation and removed requirement for unit vectors as arguments
* made vector.rotate_around_axis() right handed again for consistency
* documented previous changes
* made matrix multiplication actually multiply
* renamed vector.directions_to_rotation() to vector.dir_to_rotation()
* optimized a distance comparison
* Fixed potential false positive in unit tests.
Co-authored-by: NetherEran <nethereran@hotmail.com>
Diffstat (limited to 'builtin/common/vector.lua')
-rw-r--r-- | builtin/common/vector.lua | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index ca6541eb4..1fd784ce2 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -141,3 +141,96 @@ function vector.sort(a, b) return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)}, {x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)} end + +local function sin(x) + if x % math.pi == 0 then + return 0 + else + return math.sin(x) + end +end + +local function cos(x) + if x % math.pi == math.pi / 2 then + return 0 + else + return math.cos(x) + end +end + +function vector.rotate_around_axis(v, axis, angle) + local cosangle = cos(angle) + local sinangle = sin(angle) + axis = vector.normalize(axis) + -- https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula + local dot_axis = vector.multiply(axis, vector.dot(axis, v)) + local cross = vector.cross(v, axis) + return vector.new( + cross.x * sinangle + (v.x - dot_axis.x) * cosangle + dot_axis.x, + cross.y * sinangle + (v.y - dot_axis.y) * cosangle + dot_axis.y, + cross.z * sinangle + (v.z - dot_axis.z) * cosangle + dot_axis.z + ) +end + +function vector.rotate(v, rot) + local sinpitch = sin(-rot.x) + local sinyaw = sin(-rot.y) + local sinroll = sin(-rot.z) + local cospitch = cos(rot.x) + local cosyaw = cos(rot.y) + local cosroll = math.cos(rot.z) + -- Rotation matrix that applies yaw, pitch and roll + local matrix = { + { + sinyaw * sinpitch * sinroll + cosyaw * cosroll, + sinyaw * sinpitch * cosroll - cosyaw * sinroll, + sinyaw * cospitch, + }, + { + cospitch * sinroll, + cospitch * cosroll, + -sinpitch, + }, + { + cosyaw * sinpitch * sinroll - sinyaw * cosroll, + cosyaw * sinpitch * cosroll + sinyaw * sinroll, + cosyaw * cospitch, + }, + } + -- Compute matrix multiplication: `matrix` * `v` + return vector.new( + matrix[1][1] * v.x + matrix[1][2] * v.y + matrix[1][3] * v.z, + matrix[2][1] * v.x + matrix[2][2] * v.y + matrix[2][3] * v.z, + matrix[3][1] * v.x + matrix[3][2] * v.y + matrix[3][3] * v.z + ) +end + +function vector.dir_to_rotation(forward, up) + forward = vector.normalize(forward) + local rot = {x = math.asin(forward.y), y = -math.atan2(forward.x, forward.z), z = 0} + if not up then + return rot + end + assert(vector.dot(forward, up) < 0.000001, + "Invalid vectors passed to vector.dir_to_rotation().") + up = vector.normalize(up) + -- Calculate vector pointing up with roll = 0, just based on forward vector. + local forwup = vector.rotate({x = 0, y = 1, z = 0}, rot) + -- 'forwup' and 'up' are now in a plane with 'forward' as normal. + -- The angle between them is the absolute of the roll value we're looking for. + rot.z = vector.angle(forwup, up) + + -- Since vector.angle never returns a negative value or a value greater + -- than math.pi, rot.z has to be inverted sometimes. + -- To determine wether this is the case, we rotate the up vector back around + -- the forward vector and check if it worked out. + local back = vector.rotate_around_axis(up, forward, -rot.z) + + -- We don't use vector.equals for this because of floating point imprecision. + if (back.x - forwup.x) * (back.x - forwup.x) + + (back.y - forwup.y) * (back.y - forwup.y) + + (back.z - forwup.z) * (back.z - forwup.z) > 0.0000001 then + rot.z = -rot.z + end + return rot +end |