maf.lua 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. -- maf
  2. -- https://github.com/bjornbytes/maf
  3. -- MIT License
  4. local ffi = type(jit) == 'table' and jit.status() and require 'ffi'
  5. local vec3, quat
  6. local forward
  7. local vtmp1
  8. local vtmp2
  9. local qtmp1
  10. vec3 = {
  11. __call = function(_, x, y, z)
  12. return setmetatable({ x = x or 0, y = y or 0, z = z or 0 }, vec3)
  13. end,
  14. __tostring = function(v)
  15. return string.format('(%f, %f, %f)', v.x, v.y, v.z)
  16. end,
  17. __add = function(v, u) return v:add(u, vec3()) end,
  18. __sub = function(v, u) return v:sub(u, vec3()) end,
  19. __mul = function(v, u)
  20. if vec3.isvec3(u) then return v:mul(u, vec3())
  21. elseif type(u) == 'number' then return v:scale(u, vec3())
  22. else error('vec3s can only be multiplied by vec3s and numbers') end
  23. end,
  24. __div = function(v, u)
  25. if vec3.isvec3(u) then return v:div(u, vec3())
  26. elseif type(u) == 'number' then return v:scale(1 / u, vec3())
  27. else error('vec3s can only be divided by vec3s and numbers') end
  28. end,
  29. __unm = function(v) return v:scale(-1) end,
  30. __len = function(v) return v:length() end,
  31. __index = {
  32. isvec3 = function(x)
  33. return ffi and ffi.istype('vec3', x) or getmetatable(x) == vec3
  34. end,
  35. clone = function(v)
  36. return vec3(v.x, v.y, v.z)
  37. end,
  38. unpack = function(v)
  39. return v.x, v.y, v.z
  40. end,
  41. set = function(v, x, y, z)
  42. if vec3.isvec3(x) then x, y, z = x.x, x.y, x.z end
  43. v.x = x
  44. v.y = y
  45. v.z = z
  46. return v
  47. end,
  48. add = function(v, u, out)
  49. out = out or v
  50. out.x = v.x + u.x
  51. out.y = v.y + u.y
  52. out.z = v.z + u.z
  53. return out
  54. end,
  55. sub = function(v, u, out)
  56. out = out or v
  57. out.x = v.x - u.x
  58. out.y = v.y - u.y
  59. out.z = v.z - u.z
  60. return out
  61. end,
  62. mul = function(v, u, out)
  63. out = out or v
  64. out.x = v.x * u.x
  65. out.y = v.y * u.y
  66. out.z = v.z * u.z
  67. return out
  68. end,
  69. div = function(v, u, out)
  70. out = out or v
  71. out.x = v.x / u.x
  72. out.y = v.y / u.y
  73. out.z = v.z / u.z
  74. return out
  75. end,
  76. scale = function(v, s, out)
  77. out = out or v
  78. out.x = v.x * s
  79. out.y = v.y * s
  80. out.z = v.z * s
  81. return out
  82. end,
  83. length = function(v)
  84. return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
  85. end,
  86. normalize = function(v, out)
  87. out = out or v
  88. local len = v:length()
  89. return len == 0 and v or v:scale(1 / len, out)
  90. end,
  91. distance = function(v, u)
  92. return vec3.sub(v, u, vtmp1):length()
  93. end,
  94. angle = function(v, u)
  95. return math.acos(v:dot(u) / (v:length() + u:length()))
  96. end,
  97. dot = function(v, u)
  98. return v.x * u.x + v.y * u.y + v.z * u.z
  99. end,
  100. cross = function(v, u, out)
  101. out = out or v
  102. local a, b, c = v.x, v.y, v.z
  103. out.x = b * u.z - c * u.y
  104. out.y = c * u.x - a * u.z
  105. out.z = a * u.y - b * u.x
  106. return out
  107. end,
  108. lerp = function(v, u, t, out)
  109. out = out or v
  110. out.x = v.x + (u.x - v.x) * t
  111. out.y = v.y + (u.y - v.y) * t
  112. out.z = v.z + (u.z - v.z) * t
  113. return out
  114. end,
  115. project = function(v, u, out)
  116. out = out or v
  117. local unorm = vtmp1
  118. u:normalize(unorm)
  119. local dot = v:dot(unorm)
  120. out.x = unorm.x * dot
  121. out.y = unorm.y * dot
  122. out.z = unorm.z * dot
  123. return out
  124. end,
  125. rotate = function(v, q, out)
  126. out = out or v
  127. local u, c, o = vtmp1, vtmp2, out
  128. u.x, u.y, u.z = q.x, q.y, q.z
  129. o.x, o.y, o.z = v.x, v.y, v.z
  130. u:cross(v, c)
  131. local uu = u:dot(u)
  132. local uv = u:dot(v)
  133. o:scale(q.w * q.w - uu)
  134. u:scale(2 * uv)
  135. c:scale(2 * q.w)
  136. return o:add(u:add(c))
  137. end
  138. }
  139. }
  140. quat = {
  141. __call = function(_, x, y, z, w)
  142. return setmetatable({ x = x, y = y, z = z, w = w }, quat)
  143. end,
  144. __tostring = function(q)
  145. return string.format('(%f, %f, %f, %f)', q.x, q.y, q.z, q.w)
  146. end,
  147. __add = function(q, r) return q:add(r, quat()) end,
  148. __sub = function(q, r) return q:sub(r, quat()) end,
  149. __mul = function(q, r)
  150. if quat.isquat(r) then return q:mul(r, quat())
  151. elseif vec3.isvec3(r) then return r:rotate(q, vec3())
  152. else error('quats can only be multiplied by quats and vec3s') end
  153. end,
  154. __unm = function(q) return q:scale(-1) end,
  155. __len = function(q) return q:length() end,
  156. __index = {
  157. isquat = function(x)
  158. return ffi and ffi.istype('quat', x) or getmetatable(x) == quat
  159. end,
  160. clone = function(q)
  161. return quat(q.x, q.y, q.z, q.w)
  162. end,
  163. unpack = function(q)
  164. return q.x, q.y, q.z, q.w
  165. end,
  166. set = function(q, x, y, z, w)
  167. if quat.isquat(x) then x, y, z, w = x.x, x.y, x.z, x.w end
  168. q.x = x
  169. q.y = y
  170. q.z = z
  171. q.w = w
  172. return q
  173. end,
  174. fromAngleAxis = function(angle, x, y, z)
  175. return quat():setAngleAxis(angle, x, y, z)
  176. end,
  177. setAngleAxis = function(q, angle, x, y, z)
  178. if vec3.isvec3(x) then x, y, z = x.x, x.y, x.z end
  179. local s = math.sin(angle * .5)
  180. local c = math.cos(angle * .5)
  181. q.x = x * s
  182. q.y = y * s
  183. q.z = z * s
  184. q.w = c
  185. return q
  186. end,
  187. getAngleAxis = function(q)
  188. if q.w > 1 or q.w < -1 then q:normalize() end
  189. local s = math.sqrt(1 - q.w * q.w)
  190. s = s < .0001 and 1 or 1 / s
  191. return 2 * math.acos(q.w), q.x * s, q.y * s, q.z * s
  192. end,
  193. between = function(u, v)
  194. return quat():setBetween(u, v)
  195. end,
  196. setBetween = function(q, u, v)
  197. local dot = u:dot(v)
  198. if dot > .99999 then
  199. q.x, q.y, q.z, q.w = 0, 0, 0, 1
  200. return q
  201. elseif dot < -.99999 then
  202. vtmp1.x, vtmp1.y, vtmp1.z = 1, 0, 0
  203. vtmp1:cross(u)
  204. if #vtmp1 < .00001 then
  205. vtmp1.x, vtmp1.y, vtmp1.z = 0, 1, 0
  206. vtmp1:cross(u)
  207. end
  208. vtmp1:normalize()
  209. return q:setAngleAxis(math.pi, vtmp1)
  210. end
  211. q.x, q.y, q.z = u.x, u.y, u.z
  212. vec3.cross(q, v)
  213. q.w = 1 + dot
  214. return q:normalize()
  215. end,
  216. fromDirection = function(x, y, z)
  217. return quat():setDirection(x, y, z)
  218. end,
  219. setDirection = function(q, x, y, z)
  220. if vec3.isvec3(x) then x, y, z = x.x, x.y, x.z end
  221. vtmp2.x, vtmp2.y, vtmp2.z = x, y, z
  222. return q:setBetween(forward, vtmp2)
  223. end,
  224. add = function(q, r, out)
  225. out = out or q
  226. out.x = q.x + r.x
  227. out.y = q.y + r.y
  228. out.z = q.z + r.z
  229. out.w = q.w + r.w
  230. return out
  231. end,
  232. sub = function(q, r, out)
  233. out = out or q
  234. out.x = q.x - r.x
  235. out.y = q.y - r.y
  236. out.z = q.z - r.z
  237. out.w = q.w - r.w
  238. return out
  239. end,
  240. mul = function(q, r, out)
  241. out = out or q
  242. local qx, qy, qz, qw = q:unpack()
  243. local rx, ry, rz, rw = r:unpack()
  244. out.x = qx * rw + qw * rx + qy * rz - qz * ry
  245. out.y = qy * rw + qw * ry + qz * rx - qx * rz
  246. out.z = qz * rw + qw * rz + qx * ry - qy * rx
  247. out.w = qw * rw - qx * rx - qy * ry - qz * rz
  248. return out
  249. end,
  250. scale = function(q, s, out)
  251. out = out or q
  252. out.x = q.x * s
  253. out.y = q.y * s
  254. out.z = q.z * s
  255. out.w = q.w * s
  256. return out
  257. end,
  258. length = function(q)
  259. return math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w)
  260. end,
  261. normalize = function(q, out)
  262. out = out or q
  263. local len = q:length()
  264. return len == 0 and q or q:scale(1 / len, out)
  265. end,
  266. lerp = function(q, r, t, out)
  267. out = out or q
  268. r:scale(t, qtmp1)
  269. q:scale(1 - t, out)
  270. return out:add(qtmp1)
  271. end,
  272. slerp = function(q, r, t, out)
  273. out = out or q
  274. local dot = q.x * r.x + q.y * r.y + q.z * r.z + q.w * r.w
  275. if dot < 0 then
  276. dot = -dot
  277. r:scale(-1)
  278. end
  279. if 1 - dot < .0001 then
  280. return q:lerp(r, t, out)
  281. end
  282. local theta = math.acos(dot)
  283. q:scale(math.sin((1 - t) * theta), out)
  284. r:scale(math.sin(t * theta), qtmp1)
  285. return out:add(qtmp1):scale(1 / math.sin(theta))
  286. end
  287. }
  288. }
  289. if ffi then
  290. ffi.cdef [[
  291. typedef struct { double x, y, z; } vec3;
  292. typedef struct { double x, y, z, w; } quat;
  293. ]]
  294. vec3 = ffi.metatype('vec3', vec3)
  295. quat = ffi.metatype('quat', quat)
  296. else
  297. setmetatable(vec3, vec3)
  298. setmetatable(quat, quat)
  299. end
  300. forward = vec3(0, 0, -1)
  301. vtmp1 = vec3()
  302. vtmp2 = vec3()
  303. qtmp1 = quat()
  304. return {
  305. vec3 = vec3,
  306. quat = quat,
  307. vector = vec3,
  308. rotation = quat
  309. }