------------------------------------------------ --作者: 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