Files
Main/Assets/GameAssets/Resources/Lua/Common/CustomLib/Utility/Quaternion.lua

390 lines
10 KiB
Lua
Raw Normal View History

2025-01-25 04:38:09 +08:00
------------------------------------------------
--作者: xihan
--日期: 2019-05-10
--文件: Quaternion.lua
--模块: Quaternion
--描述: Quaternion的运算
------------------------------------------------
local Vector3 = require("Common.CustomLib.Utility.Vector3")
local L_Rawget = rawget
local L_Rawset = rawset
local L_Setmetatable = setmetatable
local L_Getmetatable = getmetatable
local L_Math = math
local L_Sin = L_Math.sin
local L_Cos = L_Math.cos
local L_Acos = L_Math.acos
local L_Asin = L_Math.asin
local L_Sqrt = L_Math.sqrt
local L_Min = L_Math.min
local L_Max = L_Math.max
local L_Sign = L_Math.Sign
local L_Atan = L_Math.atan
local L_Clamp = L_Math.Clamp
local L_Abs = L_Math.abs
local L_Rad2Deg = L_Math.Rad2Deg
local L_HalfDegToRad = 0.5 * L_Math.Deg2Rad
local L_Forward = Vector3.forward
local L_Up = Vector3.up
local L_Next = {2, 3, 1}
local Quaternion = {}
local function New(x, y, z, w)
return L_Setmetatable({x = x or 0, y = y or 0, z = z or 0, w = w or 0}, Quaternion)
end
local function NewByEuler(x, y, z)
if y == nil and z == nil then
y = x.y
z = x.z
x = x.x
end
x = x * L_HalfDegToRad
y = y * L_HalfDegToRad
z = z * L_HalfDegToRad
local _sinX = L_Sin(x)
local _cosX = L_Cos(x)
local _sinY = L_Sin(y)
local _cosY = L_Cos(y)
local _sinZ = L_Sin(z)
local _cosZ = L_Cos(z)
local _w = _cosY * _cosX * _cosZ + _sinY * _sinX * _sinZ
x = _cosY * _sinX * _cosZ + _sinY * _cosX * _sinZ
y = _sinY * _cosX * _cosZ - _cosY * _sinX * _sinZ
z = _cosY * _cosX * _sinZ - _sinY * _sinX * _cosZ
return New(x, y, z, _w)
end
function Quaternion:Set(x, y, z, w)
self.x = x or 0
self.y = y or 0
self.z = z or 0
self.w = w or 0
end
function Quaternion:Get()
return self.x, self.y, self.z, self.w
end
function Quaternion:Clone()
return New(self.x, self.y, self.z, self.w)
end
function Quaternion:GetNormalize()
return self:Clone():Normalize()
end
function Quaternion:Normalize()
local _n = self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
if _n ~= 1 and _n > 0 then
_n = 1 / L_Sqrt(_n)
self.x = self.x * _n
self.y = self.y * _n
self.z = self.z * _n
self.w = self.w * _n
end
return self
end
function Quaternion:SetIdentity()
self.x = 0
self.y = 0
self.z = 0
self.w = 1
end
function Quaternion:Inverse()
local _quat = New()
_quat.x = -self.x
_quat.y = -self.y
_quat.z = -self.z
_quat.w = self.w
return _quat
end
local function Approximately(f0, f1)
return L_Abs(f0 - f1) < 1e-6
end
function Quaternion:ToAngleAxis()
local _angle = 2 * L_Acos(self.w)
if Approximately(_angle, 0) then
return _angle * 57.29578, Vector3(1, 0, 0)
end
local _div = 1 / L_Sqrt(1 - L_Sqrt(self.w))
return _angle * 57.29578, Vector3(self.x * _div, self.y * _div, self.z * _div)
end
local L_PI = L_Math.pi
local L_Half_PI = L_PI * 0.5
local L_Two_PI = 2 * L_PI
local L_NegativeFlip = -0.0001
local L_PositiveFlip = L_Two_PI - 0.0001
local function SanitizeEuler(euler)
if euler.x < L_NegativeFlip then
euler.x = euler.x + L_Two_PI
elseif euler.x > L_PositiveFlip then
euler.x = euler.x - L_Two_PI
end
if euler.y < L_NegativeFlip then
euler.y = euler.y + L_Two_PI
elseif euler.y > L_PositiveFlip then
euler.y = euler.y - L_Two_PI
end
if euler.z < L_NegativeFlip then
euler.z = euler.z + L_Two_PI
elseif euler.z > L_PositiveFlip then
euler.z = euler.z + L_Two_PI
end
end
function Quaternion:ToEulerAngles()
local _x = self.x
local _y = self.y
local _z = self.z
local _w = self.w
local _check = 2 * (_y * _z - _w * _x)
if _check < 0.999 then
if _check > -0.999 then
local _v =
Vector3(
-L_Asin(_check),
L_Atan(2 * (_x * _z + _w * _y), 1 - 2 * (_x * _x + _y * _y)),
L_Atan(2 * (_x * _y + _w * _z), 1 - 2 * (_x * _x + _z * _z))
)
SanitizeEuler(_v)
_v:Mul(L_Rad2Deg)
return _v
else
local _v = Vector3(L_Half_PI, L_Atan(2 * (_x * _y - _w * _z), 1 - 2 * (_y * _y + _z * _z)), 0)
SanitizeEuler(_v)
_v:Mul(L_Rad2Deg)
return _v
end
else
local _v = Vector3(-L_Half_PI, L_Atan(-2 * (_x * _y - _w * _z), 1 - 2 * (_y * _y + _z * _z)), 0)
SanitizeEuler(_v)
_v:Mul(L_Rad2Deg)
return _v
end
end
function Quaternion:Forward()
return self:MulVec3(Vector3.forward)
end
function Quaternion:MulVec3(point)
local _vec = Vector3()
local _num = self.x * 2
local _num2 = self.y * 2
local _num3 = self.z * 2
local _num4 = self.x * _num
local _num5 = self.y * _num2
local _num6 = self.z * _num3
local _num7 = self.x * _num2
local _num8 = self.x * _num3
local _num9 = self.y * _num3
local _num10 = self.w * _num
local _num11 = self.w * _num2
local _num12 = self.w * _num3
_vec.x = (((1 - (_num5 + _num6)) * point.x) + ((_num7 - _num12) * point.y)) + ((_num8 + _num11) * point.z)
_vec.y = (((_num7 + _num12) * point.x) + ((1 - (_num4 + _num6)) * point.y)) + ((_num9 - _num10) * point.z)
_vec.z = (((_num8 - _num11) * point.x) + ((_num9 + _num10) * point.y)) + ((1 - (_num4 + _num5)) * point.z)
return _vec
end
local function MatrixToQuaternion(rot, quat)
local _trace = rot[1][1] + rot[2][2] + rot[3][3]
if _trace > 0 then
local _s = L_Sqrt(_trace + 1)
quat.w = 0.5 * _s
_s = 0.5 / _s
quat.x = (rot[3][2] - rot[2][3]) * _s
quat.y = (rot[1][3] - rot[3][1]) * _s
quat.z = (rot[2][1] - rot[1][2]) * _s
--]]
quat:Normalize()
else
local _i = 1
local _q = {0, 0, 0}
if rot[2][2] > rot[1][1] then
_i = 2
end
if rot[3][3] > rot[_i][_i] then
_i = 3
end
local _j = L_Next[_i]
local _k = L_Next[_j]
local _t = rot[_i][_i] - rot[_j][_j] - rot[_k][_k] + 1
local _s = 0.5 / L_Sqrt(_t)
_q[_i] = _s * _t
local _w = (rot[_k][_j] - rot[_j][_k]) * _s
_q[_j] = (rot[_j][_i] + rot[_i][_j]) * _s
_q[_k] = (rot[_k][_i] + rot[_i][_k]) * _s
quat:Set(_q[1], _q[2], _q[3], _w)
quat:Normalize()
end
end
--创建一个从formDirection到toDirection的Quaternion实例
function Quaternion:GetFromToRotation(from, to)
local _q = self:Clone()
_q:FromToRotation(from, to)
return _q
end
--从from到to的四元数
function Quaternion:FromToRotation(from, to)
from = from:Normalize()
to = to:Normalize()
local _e = Utils.Dot(from, to)
if _e > 1 - 1e-6 then
self:Set(0, 0, 0, 1)
elseif _e < -1 + 1e-6 then
local _left = {0, from.z, from.y}
local _mag = _left[2] * _left[2] + _left[3] * _left[3] --+ left[1] * left[1] = 0
if _mag < 1e-6 then
_left[1] = -from.z
_left[2] = 0
_left[3] = from.x
_mag = _left[1] * _left[1] + _left[3] * _left[3]
end
local _invlen = 1 / L_Sqrt(_mag)
_left[1] = _left[1] * _invlen
_left[2] = _left[2] * _invlen
_left[3] = _left[3] * _invlen
local _up = {0, 0, 0}
_up[1] = _left[2] * from.z - _left[3] * from.y
_up[2] = _left[3] * from.x - _left[1] * from.z
_up[3] = _left[1] * from.y - _left[2] * from.x
local _fxx = -from.x * from.x
local _fyy = -from.y * from.y
local _fzz = -from.z * from.z
local _fxy = -from.x * from.y
local _fxz = -from.x * from.z
local _fyz = -from.y * from.z
local _uxx = _up[1] * _up[1]
local _uyy = _up[2] * _up[2]
local _uzz = _up[3] * _up[3]
local _uxy = _up[1] * _up[2]
local _uxz = _up[1] * _up[3]
local _uyz = _up[2] * _up[3]
local _lxx = -_left[1] * _left[1]
local _lyy = -_left[2] * _left[2]
local _lzz = -_left[3] * _left[3]
local _lxy = -_left[1] * _left[2]
local _lxz = -_left[1] * _left[3]
local _lyz = -_left[2] * _left[3]
local _rot = {
{_fxx + _uxx + _lxx, _fxy + _uxy + _lxy, _fxz + _uxz + _lxz},
{_fxy + _uxy + _lxy, _fyy + _uyy + _lyy, _fyz + _uyz + _lyz},
{_fxz + _uxz + _lxz, _fyz + _uyz + _lyz, _fzz + _uzz + _lzz}
}
MatrixToQuaternion(_rot, self)
else
local _v = Utils.Cross(from, to)
local _h = (1 - _e) / Utils.Dot(_v, _v)
local _hx = _h * _v.x
local _hz = _h * _v.z
local _hxy = _hx * _v.y
local _hxz = _hx * _v.z
local _hyz = _hz * _v.y
local _rot = {
{_e + _hx * _v.x, _hxy - _v.z, _hxz + _v.y},
{_hxy + _v.z, _e + _h * _v.y * _v.y, _hyz - _v.x},
{_hxz - _v.y, _hyz + _v.x, _e + _hz * _v.z}
}
MatrixToQuaternion(_rot, self)
end
end
Quaternion.__mul =
function(lhs, rhs)
if Quaternion == L_Getmetatable(rhs) then
return New(
(((lhs.w * rhs.x) + (lhs.x * rhs.w)) + (lhs.y * rhs.z)) - (lhs.z * rhs.y),
(((lhs.w * rhs.y) + (lhs.y * rhs.w)) + (lhs.z * rhs.x)) - (lhs.x * rhs.z),
(((lhs.w * rhs.z) + (lhs.z * rhs.w)) + (lhs.x * rhs.y)) - (lhs.y * rhs.x),
(((lhs.w * rhs.w) - (lhs.x * rhs.x)) - (lhs.y * rhs.y)) - (lhs.z * rhs.z)
)
elseif Vector3 == L_Getmetatable(rhs) then
return lhs:MulVec3(rhs)
end
end
Quaternion.__unm = function(q)
return New(-q.x, -q.y, -q.z, -q.w)
end
Quaternion.__eq = function(lhs, rhs)
return Utils.Dot(lhs, rhs) > 0.999999
end
Quaternion.__tostring = function(self)
return "[" .. self.x .. "," .. self.y .. "," .. self.z .. "," .. self.w .. "]"
end
Quaternion.__index = function(t, k)
return L_Rawget(Quaternion, k)
end
local L_Metatable = {
identity = function()
return New(0, 0, 0, 1)
end
}
L_Metatable.__index = function(t, k)
if L_Metatable[k] then
return L_Metatable[k]()
end
end
L_Metatable.__call = function(t, x, y, z, w)
if type(x) == "number" or type(x) == "nil" then
return New(x, y, z, w)
else
if x.w then
return New(x.x, x.y, x.z, x.w)
else
return NewByEuler(x.x, x.y, x.z)
end
end
end
L_Setmetatable(Quaternion, L_Metatable)
return Quaternion