您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

285 行
7.2 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #pragma once
  13. #include <cassert>
  14. #include <cmath> // std::tan
  15. #include <algorithm> // std::max
  16. #include <format> // std::format
  17. namespace lol
  18. {
  19. template<>
  20. inline std::string mat2::tostring() const
  21. {
  22. mat2 const &p = *this;
  23. return std::format("[ {:6.6f} {:6.6f}\n", p[0][0], p[1][0]) +
  24. std::format(" {:6.6f} {:6.6f} ]\n", p[0][1], p[1][1]);
  25. }
  26. template<>
  27. inline std::string mat3::tostring() const
  28. {
  29. mat3 const &p = *this;
  30. return std::format("[ {:6.6f} {:6.6f} {:6.6f}\n", p[0][0], p[1][0], p[2][0]) +
  31. std::format(" {:6.6f} {:6.6f} {:6.6f}\n", p[0][1], p[1][1], p[2][1]) +
  32. std::format(" {:6.6f} {:6.6f} {:6.6f} ]\n", p[0][2], p[1][2], p[2][2]);
  33. }
  34. template<>
  35. inline std::string mat4::tostring() const
  36. {
  37. mat4 const &p = *this;
  38. return std::format("[ {:6.6f} {:6.6f} {:6.6f} {:6.6f}\n",
  39. p[0][0], p[1][0], p[2][0], p[3][0]) +
  40. std::format(" {:6.6f} {:6.6f} {:6.6f} {:6.6f}\n",
  41. p[0][1], p[1][1], p[2][1], p[3][1]) +
  42. std::format(" {:6.6f} {:6.6f} {:6.6f} {:6.6f}\n",
  43. p[0][2], p[1][2], p[2][2], p[3][2]) +
  44. std::format(" {:6.6f} {:6.6f} {:6.6f} {:6.6f} ]\n",
  45. p[0][3], p[1][3], p[2][3], p[3][3]);
  46. }
  47. template<typename T>
  48. mat_t<T,3,3> mat_t<T,3,3>::scale(T x, T y, T z)
  49. {
  50. mat_t<T,3,3> ret(T(1));
  51. ret[0][0] = x;
  52. ret[1][1] = y;
  53. ret[2][2] = z;
  54. return ret;
  55. }
  56. template<typename T>
  57. mat_t<T,3,3> mat_t<T,3,3>::scale(T x)
  58. {
  59. return scale(x, x, x);
  60. }
  61. template<typename T>
  62. mat_t<T,3,3> mat_t<T,3,3>::scale(vec_t<T,3> v)
  63. {
  64. return scale(v.x, v.y, v.z);
  65. }
  66. template<typename T>
  67. mat_t<T,4,4> mat_t<T,4,4>::translate(T x, T y, T z)
  68. {
  69. mat_t<T,4,4> ret(T(1));
  70. ret[3][0] = x;
  71. ret[3][1] = y;
  72. ret[3][2] = z;
  73. return ret;
  74. }
  75. template<typename T>
  76. mat_t<T,4,4> mat_t<T,4,4>::translate(vec_t<T,3> v)
  77. {
  78. return translate(v.x, v.y, v.z);
  79. }
  80. template<typename T>
  81. mat_t<T,2,2> mat_t<T,2,2>::rotate(T radians)
  82. {
  83. T st = sin(radians);
  84. T ct = cos(radians);
  85. mat_t<T,2,2> ret;
  86. ret[0][0] = ct;
  87. ret[0][1] = st;
  88. ret[1][0] = -st;
  89. ret[1][1] = ct;
  90. return ret;
  91. }
  92. template<typename T>
  93. mat_t<T,3,3> mat_t<T,3,3>::rotate(T radians, T x, T y, T z)
  94. {
  95. T st = sin(radians);
  96. T ct = cos(radians);
  97. T len = std::sqrt(x * x + y * y + z * z);
  98. T invlen = len ? T(1) / len : T(0);
  99. x *= invlen;
  100. y *= invlen;
  101. z *= invlen;
  102. T mtx = (T(1) - ct) * x;
  103. T mty = (T(1) - ct) * y;
  104. T mtz = (T(1) - ct) * z;
  105. mat_t<T,3,3> ret;
  106. ret[0][0] = x * mtx + ct;
  107. ret[0][1] = x * mty + st * z;
  108. ret[0][2] = x * mtz - st * y;
  109. ret[1][0] = y * mtx - st * z;
  110. ret[1][1] = y * mty + ct;
  111. ret[1][2] = y * mtz + st * x;
  112. ret[2][0] = z * mtx + st * y;
  113. ret[2][1] = z * mty - st * x;
  114. ret[2][2] = z * mtz + ct;
  115. return ret;
  116. }
  117. template<typename T>
  118. mat_t<T,3,3> mat_t<T,3,3>::rotate(T radians, vec_t<T,3> v)
  119. {
  120. return rotate(radians, v.x, v.y, v.z);
  121. }
  122. template<typename T>
  123. mat_t<T,3,3>::mat_t(quat_t<T> const &q)
  124. {
  125. T n = norm(q);
  126. if (!n)
  127. {
  128. for (int j = 0; j < 3; j++)
  129. for (int i = 0; i < 3; i++)
  130. (*this)[i][j] = (i == j) ? T(1) : T(0);
  131. return;
  132. }
  133. T s = T(2) / n;
  134. (*this)[0][0] = T(1) - s * (q.y * q.y + q.z * q.z);
  135. (*this)[0][1] = s * (q.x * q.y + q.z * q.w);
  136. (*this)[0][2] = s * (q.x * q.z - q.y * q.w);
  137. (*this)[1][0] = s * (q.x * q.y - q.z * q.w);
  138. (*this)[1][1] = T(1) - s * (q.z * q.z + q.x * q.x);
  139. (*this)[1][2] = s * (q.y * q.z + q.x * q.w);
  140. (*this)[2][0] = s * (q.x * q.z + q.y * q.w);
  141. (*this)[2][1] = s * (q.y * q.z - q.x * q.w);
  142. (*this)[2][2] = T(1) - s * (q.x * q.x + q.y * q.y);
  143. }
  144. template<typename T>
  145. mat_t<T,4,4>::mat_t(quat_t<T> const &q)
  146. {
  147. *this = mat_t<T,4,4>(mat_t<T,3,3>(q), T(1));
  148. }
  149. // These are only specialised for float type, but could be extended
  150. // to anything else. I’m just not sure it’s worth it.
  151. template<>
  152. inline mat4 mat4::lookat(vec3 eye, vec3 center, vec3 up)
  153. {
  154. vec3 v3 = normalize(eye - center);
  155. vec3 v1 = normalize(cross(up, v3));
  156. vec3 v2 = cross(v3, v1);
  157. return mat4(vec4(v1.x, v2.x, v3.x, 0.f),
  158. vec4(v1.y, v2.y, v3.y, 0.f),
  159. vec4(v1.z, v2.z, v3.z, 0.f),
  160. vec4(-dot(eye, v1), -dot(eye, v2), -dot(eye, v3), 1.f));
  161. }
  162. template<>
  163. inline mat4 mat4::ortho(float left, float right, float bottom,
  164. float top, float near, float far)
  165. {
  166. float invrl = (right != left) ? 1.0f / (right - left) : 0.0f;
  167. float invtb = (top != bottom) ? 1.0f / (top - bottom) : 0.0f;
  168. float invfn = (far != near) ? 1.0f / (far - near) : 0.0f;
  169. mat4 ret(0.0f);
  170. ret[0][0] = 2.0f * invrl;
  171. ret[1][1] = 2.0f * invtb;
  172. ret[2][2] = -2.0f * invfn;
  173. ret[3][0] = - (right + left) * invrl;
  174. ret[3][1] = - (top + bottom) * invtb;
  175. ret[3][2] = - (far + near) * invfn;
  176. ret[3][3] = 1.0f;
  177. return ret;
  178. }
  179. template<>
  180. inline mat4 mat4::ortho(float width, float height,
  181. float near, float far)
  182. {
  183. return mat4::ortho(-0.5f * width, 0.5f * width,
  184. -0.5f * height, 0.5f * height, near, far);
  185. }
  186. template<>
  187. inline mat4 mat4::frustum(float left, float right, float bottom,
  188. float top, float near, float far)
  189. {
  190. float invrl = (right != left) ? 1.0f / (right - left) : 0.0f;
  191. float invtb = (top != bottom) ? 1.0f / (top - bottom) : 0.0f;
  192. float invfn = (far != near) ? 1.0f / (far - near) : 0.0f;
  193. mat4 ret(0.0f);
  194. ret[0][0] = 2.0f * near * invrl;
  195. ret[1][1] = 2.0f * near * invtb;
  196. ret[2][0] = (right + left) * invrl;
  197. ret[2][1] = (top + bottom) * invtb;
  198. ret[2][2] = - (far + near) * invfn;
  199. ret[2][3] = -1.0f;
  200. ret[3][2] = -2.0f * far * near * invfn;
  201. return ret;
  202. }
  203. /*
  204. * Return a standard perspective matrix
  205. */
  206. template<>
  207. inline mat4 mat4::perspective(float fov_y, float width,
  208. float height, float near, float far)
  209. {
  210. using std::tan;
  211. float t2 = tan(fov_y * 0.5f);
  212. float t1 = t2 * width / height;
  213. return frustum(-near * t1, near * t1, -near * t2, near * t2, near, far);
  214. }
  215. /*
  216. * Return a perspective matrix with the camera location shifted to be on
  217. * the near plane
  218. */
  219. template<>
  220. inline mat4 mat4::shifted_perspective(float fov_y, float screen_size,
  221. float screen_ratio_yx,
  222. float near, float far)
  223. {
  224. using std::tan;
  225. float tan_y = tan(fov_y * .5f);
  226. assert(tan_y > 0.000001f);
  227. float dist_scr = (screen_size * screen_ratio_yx * .5f) / tan_y;
  228. return mat4::perspective(fov_y, screen_size, screen_size * screen_ratio_yx,
  229. std::max(.001f, dist_scr + near),
  230. std::max(.001f, dist_scr + far)) *
  231. mat4::translate(.0f, .0f, -dist_scr);
  232. }
  233. } // namespace lol