TransformPrimitive.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2014 Markus Schöngart
  7. * Copyright (c) 2019 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #include "precompiled.h"
  29. #include "../../Include/RmlUi/Core/TransformPrimitive.h"
  30. #include "../../Include/RmlUi/Core/TypeConverter.h"
  31. #include <iostream>
  32. #include <unordered_map>
  33. namespace Rml {
  34. namespace Core {
  35. namespace Transforms {
  36. static Vector3f Combine(const Vector3f& a, const Vector3f& b, float a_scale, float b_scale)
  37. {
  38. Vector3f result;
  39. result.x = a_scale * a.x + b_scale * b.x;
  40. result.y = a_scale * a.y + b_scale * b.y;
  41. result.z = a_scale * a.z + b_scale * b.z;
  42. return result;
  43. }
  44. // Interpolate two quaternions a, b with weight alpha [0, 1]
  45. static Vector4f QuaternionSlerp(const Vector4f& a, const Vector4f& b, float alpha)
  46. {
  47. using namespace Math;
  48. const float eps = 0.9995f;
  49. float dot = a.DotProduct(b);
  50. dot = Clamp(dot, -1.f, 1.f);
  51. if (dot > eps)
  52. return a;
  53. float theta = ACos(dot);
  54. float w = Sin(alpha * theta) / SquareRoot(1.f - dot * dot);
  55. float a_scale = Cos(alpha*theta) - dot * w;
  56. Vector4f result;
  57. for (int i = 0; i < 4; i++)
  58. {
  59. result[i] = a[i] * a_scale + b[i] * w;
  60. }
  61. return result;
  62. }
  63. NumericValue::NumericValue() noexcept
  64. : number(), unit(Property::UNKNOWN)
  65. {
  66. }
  67. NumericValue::NumericValue(float number, Property::Unit unit) noexcept
  68. : number(number), unit(unit)
  69. {
  70. }
  71. float NumericValue::ResolveLengthPercentage(Element& e, float base) const noexcept
  72. {
  73. Property prop;
  74. prop.value = Variant(number);
  75. prop.unit = unit;
  76. return e.ResolveNumericProperty(&prop, base);
  77. }
  78. float NumericValue::ResolveWidth(Element& e) const noexcept
  79. {
  80. if(unit & (Property::PX | Property::NUMBER)) return number;
  81. return ResolveLengthPercentage(e, e.GetBox().GetSize(Box::BORDER).x);
  82. }
  83. float NumericValue::ResolveHeight(Element& e) const noexcept
  84. {
  85. if (unit & (Property::PX | Property::NUMBER)) return number;
  86. return ResolveLengthPercentage(e, e.GetBox().GetSize(Box::BORDER).y);
  87. }
  88. float NumericValue::ResolveDepth(Element& e) const noexcept
  89. {
  90. if (unit & (Property::PX | Property::NUMBER)) return number;
  91. Vector2f size = e.GetBox().GetSize(Box::BORDER);
  92. return ResolveLengthPercentage(e, Math::Max(size.x, size.y));
  93. }
  94. float NumericValue::ResolveAbsoluteUnit(Property::Unit base_unit) const noexcept
  95. {
  96. if(base_unit == Property::RAD)
  97. {
  98. switch (unit)
  99. {
  100. case Property::NUMBER:
  101. case Property::DEG:
  102. return Math::DegreesToRadians(number);
  103. case Property::RAD:
  104. return number;
  105. case Property::PERCENT:
  106. return number * 0.01f * 2.0f * Math::RMLUI_PI;
  107. default:
  108. break;
  109. }
  110. }
  111. return number;
  112. }
  113. String NumericValue::ToString() const noexcept
  114. {
  115. Property prop;
  116. prop.value = Variant(number);
  117. prop.unit = unit;
  118. return prop.ToString();
  119. }
  120. struct ResolveTransformVisitor
  121. {
  122. Matrix4f& m;
  123. Element& e;
  124. bool operator()(const Matrix2D& p)
  125. {
  126. m = Matrix4f::FromRows(
  127. Vector4f(p.values[0], p.values[2], 0, p.values[4]),
  128. Vector4f(p.values[1], p.values[3], 0, p.values[5]),
  129. Vector4f(0, 0, 1, 0),
  130. Vector4f(0, 0, 0, 1)
  131. );
  132. return true;
  133. }
  134. bool operator()(const Matrix3D& p)
  135. {
  136. m = Matrix4f::FromColumns(
  137. Vector4f(p.values[0], p.values[1], p.values[2], p.values[3]),
  138. Vector4f(p.values[4], p.values[5], p.values[6], p.values[7]),
  139. Vector4f(p.values[8], p.values[9], p.values[10], p.values[11]),
  140. Vector4f(p.values[12], p.values[13], p.values[14], p.values[15])
  141. );
  142. return true;
  143. }
  144. bool operator()(const TranslateX& p)
  145. {
  146. m = Matrix4f::TranslateX(p.values[0].ResolveWidth(e));
  147. return true;
  148. }
  149. bool operator()(const TranslateY& p)
  150. {
  151. m = Matrix4f::TranslateY(p.values[0].ResolveHeight(e));
  152. return true;
  153. }
  154. bool operator()(const TranslateZ& p)
  155. {
  156. m = Matrix4f::TranslateZ(p.values[0].ResolveDepth(e));
  157. return true;
  158. }
  159. bool operator()(const Translate2D& p)
  160. {
  161. m = Matrix4f::Translate(
  162. p.values[0].ResolveWidth(e),
  163. p.values[1].ResolveHeight(e),
  164. 0
  165. );
  166. return true;
  167. }
  168. bool operator()(const Translate3D& p)
  169. {
  170. m = Matrix4f::Translate(
  171. p.values[0].ResolveWidth(e),
  172. p.values[1].ResolveHeight(e),
  173. p.values[2].ResolveDepth(e)
  174. );
  175. return true;
  176. }
  177. bool operator()(const ScaleX& p)
  178. {
  179. m = Matrix4f::ScaleX(p.values[0]);
  180. return true;
  181. }
  182. bool operator()(const ScaleY& p)
  183. {
  184. m = Matrix4f::ScaleY(p.values[0]);
  185. return true;
  186. }
  187. bool operator()(const ScaleZ& p)
  188. {
  189. m = Matrix4f::ScaleZ(p.values[0]);
  190. return true;
  191. }
  192. bool operator()(const Scale2D& p)
  193. {
  194. m = Matrix4f::Scale(p.values[0], p.values[1], 1);
  195. return true;
  196. }
  197. bool operator()(const Scale3D& p)
  198. {
  199. m = Matrix4f::Scale(p.values[0], p.values[1], p.values[2]);
  200. return true;
  201. }
  202. bool operator()(const RotateX& p)
  203. {
  204. m = Matrix4f::RotateX(p.values[0]);
  205. return true;
  206. }
  207. bool operator()(const RotateY& p)
  208. {
  209. m = Matrix4f::RotateY(p.values[0]);
  210. return true;
  211. }
  212. bool operator()(const RotateZ& p)
  213. {
  214. m = Matrix4f::RotateZ(p.values[0]);
  215. return true;
  216. }
  217. bool operator()(const Rotate2D& p)
  218. {
  219. m = Matrix4f::RotateZ(p.values[0]);
  220. return true;
  221. }
  222. bool operator()(const Rotate3D& p)
  223. {
  224. m = Matrix4f::Rotate(Vector3f(p.values[0], p.values[1], p.values[2]), p.values[3]);
  225. return true;
  226. }
  227. bool operator()(const SkewX& p)
  228. {
  229. m = Matrix4f::SkewX(p.values[0]);
  230. return true;
  231. }
  232. bool operator()(const SkewY& p)
  233. {
  234. m = Matrix4f::SkewY(p.values[0]);
  235. return true;
  236. }
  237. bool operator()(const Skew2D& p)
  238. {
  239. m = Matrix4f::Skew(p.values[0], p.values[1]);
  240. return true;
  241. }
  242. bool operator()(const DecomposedMatrix4& p)
  243. {
  244. m = Matrix4f::Compose(p.translation, p.scale, p.skew, p.perspective, p.quaternion);
  245. return true;
  246. }
  247. bool operator()(const Perspective& p)
  248. {
  249. m = Matrix4f::Perspective(p.values[0].ResolveDepth(e));
  250. return true;
  251. }
  252. bool run(const PrimitiveVariant& primitive)
  253. {
  254. switch (primitive.type)
  255. {
  256. case PrimitiveVariant::MATRIX2D: return this->operator()(primitive.matrix_2d);
  257. case PrimitiveVariant::MATRIX3D: return this->operator()(primitive.matrix_3d);
  258. case PrimitiveVariant::TRANSLATEX: return this->operator()(primitive.translate_x);
  259. case PrimitiveVariant::TRANSLATEY: return this->operator()(primitive.translate_y);
  260. case PrimitiveVariant::TRANSLATEZ: return this->operator()(primitive.translate_z);
  261. case PrimitiveVariant::TRANSLATE2D: return this->operator()(primitive.translate_2d);
  262. case PrimitiveVariant::TRANSLATE3D: return this->operator()(primitive.translate_3d);
  263. case PrimitiveVariant::SCALEX: return this->operator()(primitive.scale_x);
  264. case PrimitiveVariant::SCALEY: return this->operator()(primitive.scale_y);
  265. case PrimitiveVariant::SCALEZ: return this->operator()(primitive.scale_z);
  266. case PrimitiveVariant::SCALE2D: return this->operator()(primitive.scale_2d);
  267. case PrimitiveVariant::SCALE3D: return this->operator()(primitive.scale_3d);
  268. case PrimitiveVariant::ROTATEX: return this->operator()(primitive.rotate_x);
  269. case PrimitiveVariant::ROTATEY: return this->operator()(primitive.rotate_y);
  270. case PrimitiveVariant::ROTATEZ: return this->operator()(primitive.rotate_z);
  271. case PrimitiveVariant::ROTATE2D: return this->operator()(primitive.rotate_2d);
  272. case PrimitiveVariant::ROTATE3D: return this->operator()(primitive.rotate_3d);
  273. case PrimitiveVariant::SKEWX: return this->operator()(primitive.skew_x);
  274. case PrimitiveVariant::SKEWY: return this->operator()(primitive.skew_y);
  275. case PrimitiveVariant::SKEW2D: return this->operator()(primitive.skew_2d);
  276. case PrimitiveVariant::PERSPECTIVE: return this->operator()(primitive.perspective);
  277. case PrimitiveVariant::DECOMPOSEDMATRIX4: return this->operator()(primitive.decomposed_matrix_4);
  278. default:
  279. break;
  280. }
  281. RMLUI_ASSERT(false);
  282. return false;
  283. }
  284. };
  285. bool Primitive::ResolveTransform(Matrix4f & m, Element & e) const noexcept
  286. {
  287. ResolveTransformVisitor visitor{ m, e };
  288. bool result = visitor.run(primitive);
  289. return result;
  290. }
  291. struct SetIdentityVisitor
  292. {
  293. template <size_t N>
  294. void operator()(ResolvedPrimitive<N>& p)
  295. {
  296. for (auto& value : p.values)
  297. value = 0.0f;
  298. }
  299. template <size_t N>
  300. void operator()(UnresolvedPrimitive<N>& p)
  301. {
  302. for (auto& value : p.values)
  303. value.number = 0.0f;
  304. }
  305. void operator()(Matrix2D& p)
  306. {
  307. for (int i = 0; i < 6; i++)
  308. p.values[i] = ((i == 0 || i == 3) ? 1.0f : 0.0f);
  309. }
  310. void operator()(Matrix3D& p)
  311. {
  312. for (int i = 0; i < 16; i++)
  313. p.values[i] = ((i % 5) == 0 ? 1.0f : 0.0f);
  314. }
  315. void operator()(ScaleX& p)
  316. {
  317. p.values[0] = 1;
  318. }
  319. void operator()(ScaleY& p)
  320. {
  321. p.values[0] = 1;
  322. }
  323. void operator()(ScaleZ& p)
  324. {
  325. p.values[0] = 1;
  326. }
  327. void operator()(Scale2D& p)
  328. {
  329. p.values[0] = p.values[1] = 1;
  330. }
  331. void operator()(Scale3D& p)
  332. {
  333. p.values[0] = p.values[1] = p.values[2] = 1;
  334. }
  335. void operator()(DecomposedMatrix4& p)
  336. {
  337. p.perspective = Vector4f(0, 0, 0, 1);
  338. p.quaternion = Vector4f(0, 0, 0, 1);
  339. p.translation = Vector3f(0, 0, 0);
  340. p.scale = Vector3f(1, 1, 1);
  341. p.skew = Vector3f(0, 0, 0);
  342. }
  343. void run(PrimitiveVariant& primitive)
  344. {
  345. switch (primitive.type)
  346. {
  347. case PrimitiveVariant::MATRIX2D: this->operator()(primitive.matrix_2d); break;
  348. case PrimitiveVariant::MATRIX3D: this->operator()(primitive.matrix_3d); break;
  349. case PrimitiveVariant::TRANSLATEX: this->operator()(primitive.translate_x); break;
  350. case PrimitiveVariant::TRANSLATEY: this->operator()(primitive.translate_y); break;
  351. case PrimitiveVariant::TRANSLATEZ: this->operator()(primitive.translate_z); break;
  352. case PrimitiveVariant::TRANSLATE2D: this->operator()(primitive.translate_2d); break;
  353. case PrimitiveVariant::TRANSLATE3D: this->operator()(primitive.translate_3d); break;
  354. case PrimitiveVariant::SCALEX: this->operator()(primitive.scale_x); break;
  355. case PrimitiveVariant::SCALEY: this->operator()(primitive.scale_y); break;
  356. case PrimitiveVariant::SCALEZ: this->operator()(primitive.scale_z); break;
  357. case PrimitiveVariant::SCALE2D: this->operator()(primitive.scale_2d); break;
  358. case PrimitiveVariant::SCALE3D: this->operator()(primitive.scale_3d); break;
  359. case PrimitiveVariant::ROTATEX: this->operator()(primitive.rotate_x); break;
  360. case PrimitiveVariant::ROTATEY: this->operator()(primitive.rotate_y); break;
  361. case PrimitiveVariant::ROTATEZ: this->operator()(primitive.rotate_z); break;
  362. case PrimitiveVariant::ROTATE2D: this->operator()(primitive.rotate_2d); break;
  363. case PrimitiveVariant::ROTATE3D: this->operator()(primitive.rotate_3d); break;
  364. case PrimitiveVariant::SKEWX: this->operator()(primitive.skew_x); break;
  365. case PrimitiveVariant::SKEWY: this->operator()(primitive.skew_y); break;
  366. case PrimitiveVariant::SKEW2D: this->operator()(primitive.skew_2d); break;
  367. case PrimitiveVariant::PERSPECTIVE: this->operator()(primitive.perspective); break;
  368. case PrimitiveVariant::DECOMPOSEDMATRIX4: this->operator()(primitive.decomposed_matrix_4); break;
  369. default:
  370. RMLUI_ASSERT(false);
  371. break;
  372. }
  373. }
  374. };
  375. void Primitive::SetIdentity() noexcept
  376. {
  377. SetIdentityVisitor{}.run(primitive);
  378. }
  379. struct PrepareVisitor
  380. {
  381. Element& e;
  382. bool operator()(TranslateX& p)
  383. {
  384. p.values[0] = NumericValue{ p.values[0].ResolveWidth(e), Property::PX };
  385. return true;
  386. }
  387. bool operator()(TranslateY& p)
  388. {
  389. p.values[0] = NumericValue{ p.values[0].ResolveHeight(e), Property::PX };
  390. return true;
  391. }
  392. bool operator()(TranslateZ& p)
  393. {
  394. p.values[0] = NumericValue{ p.values[0].ResolveDepth(e), Property::PX };
  395. return true;
  396. }
  397. bool operator()(Translate2D& p)
  398. {
  399. p.values[0] = NumericValue{ p.values[0].ResolveWidth(e), Property::PX };
  400. p.values[1] = NumericValue{ p.values[1].ResolveHeight(e), Property::PX };
  401. return true;
  402. }
  403. bool operator()(Translate3D& p)
  404. {
  405. p.values[0] = NumericValue{ p.values[0].ResolveWidth(e), Property::PX };
  406. p.values[1] = NumericValue{ p.values[1].ResolveHeight(e), Property::PX };
  407. p.values[2] = NumericValue{ p.values[2].ResolveDepth(e), Property::PX };
  408. return true;
  409. }
  410. template <size_t N>
  411. bool operator()(ResolvedPrimitive<N>& p)
  412. {
  413. // No conversion needed for resolved transforms (with some exceptions below)
  414. return true;
  415. }
  416. bool operator()(DecomposedMatrix4& p)
  417. {
  418. return true;
  419. }
  420. bool operator()(Rotate3D& p)
  421. {
  422. // Rotate3D can be interpolated if and only if their rotation axes point in the same direction.
  423. // We normalize the rotation vector here for easy comparison, and return true here. Later on we make the
  424. // pair-wise check in 'TryConvertToMatchingGenericType' to see if we need to decompose.
  425. Vector3f vec = Vector3f(p.values[0], p.values[1], p.values[2]).Normalise();
  426. p.values[0] = vec.x;
  427. p.values[1] = vec.y;
  428. p.values[2] = vec.z;
  429. return true;
  430. }
  431. bool operator()(Matrix3D& p)
  432. {
  433. // Matrices must be decomposed for interpolation
  434. return false;
  435. }
  436. bool operator()(Matrix2D& p)
  437. {
  438. // Matrix2D can also be optimized for interpolation, but for now we decompose it to a full DecomposedMatrix4
  439. return false;
  440. }
  441. bool operator()(Perspective& p)
  442. {
  443. // Perspective must be decomposed
  444. return false;
  445. }
  446. bool run(PrimitiveVariant& primitive)
  447. {
  448. switch (primitive.type)
  449. {
  450. case PrimitiveVariant::MATRIX2D: return this->operator()(primitive.matrix_2d);
  451. case PrimitiveVariant::MATRIX3D: return this->operator()(primitive.matrix_3d);
  452. case PrimitiveVariant::TRANSLATEX: return this->operator()(primitive.translate_x);
  453. case PrimitiveVariant::TRANSLATEY: return this->operator()(primitive.translate_y);
  454. case PrimitiveVariant::TRANSLATEZ: return this->operator()(primitive.translate_z);
  455. case PrimitiveVariant::TRANSLATE2D: return this->operator()(primitive.translate_2d);
  456. case PrimitiveVariant::TRANSLATE3D: return this->operator()(primitive.translate_3d);
  457. case PrimitiveVariant::SCALEX: return this->operator()(primitive.scale_x);
  458. case PrimitiveVariant::SCALEY: return this->operator()(primitive.scale_y);
  459. case PrimitiveVariant::SCALEZ: return this->operator()(primitive.scale_z);
  460. case PrimitiveVariant::SCALE2D: return this->operator()(primitive.scale_2d);
  461. case PrimitiveVariant::SCALE3D: return this->operator()(primitive.scale_3d);
  462. case PrimitiveVariant::ROTATEX: return this->operator()(primitive.rotate_x);
  463. case PrimitiveVariant::ROTATEY: return this->operator()(primitive.rotate_y);
  464. case PrimitiveVariant::ROTATEZ: return this->operator()(primitive.rotate_z);
  465. case PrimitiveVariant::ROTATE2D: return this->operator()(primitive.rotate_2d);
  466. case PrimitiveVariant::ROTATE3D: return this->operator()(primitive.rotate_3d);
  467. case PrimitiveVariant::SKEWX: return this->operator()(primitive.skew_x);
  468. case PrimitiveVariant::SKEWY: return this->operator()(primitive.skew_y);
  469. case PrimitiveVariant::SKEW2D: return this->operator()(primitive.skew_2d);
  470. case PrimitiveVariant::PERSPECTIVE: return this->operator()(primitive.perspective);
  471. case PrimitiveVariant::DECOMPOSEDMATRIX4: return this->operator()(primitive.decomposed_matrix_4);
  472. default:
  473. break;
  474. }
  475. RMLUI_ASSERT(false);
  476. return false;
  477. }
  478. };
  479. bool Primitive::PrepareForInterpolation(Element & e) noexcept
  480. {
  481. return PrepareVisitor{ e }.run(primitive);
  482. }
  483. enum class GenericType { None, Scale3D, Translate3D, Rotate3D };
  484. struct GetGenericTypeVisitor
  485. {
  486. GenericType run(const PrimitiveVariant& primitive)
  487. {
  488. switch (primitive.type)
  489. {
  490. case PrimitiveVariant::TRANSLATEX: return GenericType::Translate3D;
  491. case PrimitiveVariant::TRANSLATEY: return GenericType::Translate3D;
  492. case PrimitiveVariant::TRANSLATEZ: return GenericType::Translate3D;
  493. case PrimitiveVariant::TRANSLATE2D: return GenericType::Translate3D;
  494. case PrimitiveVariant::TRANSLATE3D: return GenericType::Translate3D;
  495. case PrimitiveVariant::SCALEX: return GenericType::Scale3D;
  496. case PrimitiveVariant::SCALEY: return GenericType::Scale3D;
  497. case PrimitiveVariant::SCALEZ: return GenericType::Scale3D;
  498. case PrimitiveVariant::SCALE2D: return GenericType::Scale3D;
  499. case PrimitiveVariant::SCALE3D: return GenericType::Scale3D;
  500. case PrimitiveVariant::ROTATEX: return GenericType::Rotate3D;
  501. case PrimitiveVariant::ROTATEY: return GenericType::Rotate3D;
  502. case PrimitiveVariant::ROTATEZ: return GenericType::Rotate3D;
  503. case PrimitiveVariant::ROTATE2D: return GenericType::Rotate3D;
  504. case PrimitiveVariant::ROTATE3D: return GenericType::Rotate3D;
  505. default:
  506. break;
  507. }
  508. return GenericType::None;
  509. }
  510. };
  511. struct ConvertToGenericTypeVisitor
  512. {
  513. Translate3D operator()(const TranslateX& p) { return Translate3D{ p.values[0], {0.0f, Property::PX}, {0.0f, Property::PX} }; }
  514. Translate3D operator()(const TranslateY& p) { return Translate3D{ {0.0f, Property::PX}, p.values[0], {0.0f, Property::PX} }; }
  515. Translate3D operator()(const TranslateZ& p) { return Translate3D{ {0.0f, Property::PX}, {0.0f, Property::PX}, p.values[0] }; }
  516. Translate3D operator()(const Translate2D& p) { return Translate3D{ p.values[0], p.values[1], {0.0f, Property::PX} }; }
  517. Scale3D operator()(const ScaleX& p) { return Scale3D{ p.values[0], 1.0f, 1.0f }; }
  518. Scale3D operator()(const ScaleY& p) { return Scale3D{ 1.0f, p.values[0], 1.0f }; }
  519. Scale3D operator()(const ScaleZ& p) { return Scale3D{ 1.0f, 1.0f, p.values[0] }; }
  520. Scale3D operator()(const Scale2D& p) { return Scale3D{ p.values[0], p.values[1], 1.0f }; }
  521. Rotate3D operator()(const RotateX& p) { return Rotate3D{ 1, 0, 0, p.values[0], Property::RAD }; }
  522. Rotate3D operator()(const RotateY& p) { return Rotate3D{ 0, 1, 0, p.values[0], Property::RAD }; }
  523. Rotate3D operator()(const RotateZ& p) { return Rotate3D{ 0, 0, 1, p.values[0], Property::RAD }; }
  524. Rotate3D operator()(const Rotate2D& p) { return Rotate3D{ 0, 0, 1, p.values[0], Property::RAD }; }
  525. template <typename T>
  526. PrimitiveVariant operator()(const T& p) { RMLUI_ERROR; return p; }
  527. PrimitiveVariant run(const PrimitiveVariant& primitive)
  528. {
  529. PrimitiveVariant result = primitive;
  530. switch (primitive.type)
  531. {
  532. case PrimitiveVariant::TRANSLATEX: result.type = PrimitiveVariant::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_x); break;
  533. case PrimitiveVariant::TRANSLATEY: result.type = PrimitiveVariant::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_y); break;
  534. case PrimitiveVariant::TRANSLATEZ: result.type = PrimitiveVariant::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_z); break;
  535. case PrimitiveVariant::TRANSLATE2D: result.type = PrimitiveVariant::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_2d); break;
  536. case PrimitiveVariant::TRANSLATE3D: break;
  537. case PrimitiveVariant::SCALEX: result.type = PrimitiveVariant::SCALE3D; result.scale_3d = this->operator()(primitive.scale_x); break;
  538. case PrimitiveVariant::SCALEY: result.type = PrimitiveVariant::SCALE3D; result.scale_3d = this->operator()(primitive.scale_y); break;
  539. case PrimitiveVariant::SCALEZ: result.type = PrimitiveVariant::SCALE3D; result.scale_3d = this->operator()(primitive.scale_z); break;
  540. case PrimitiveVariant::SCALE2D: result.type = PrimitiveVariant::SCALE3D; result.scale_3d = this->operator()(primitive.scale_2d); break;
  541. case PrimitiveVariant::SCALE3D: break;
  542. case PrimitiveVariant::ROTATEX: result.type = PrimitiveVariant::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_x); break;
  543. case PrimitiveVariant::ROTATEY: result.type = PrimitiveVariant::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_y); break;
  544. case PrimitiveVariant::ROTATEZ: result.type = PrimitiveVariant::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_z); break;
  545. case PrimitiveVariant::ROTATE2D: result.type = PrimitiveVariant::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_2d); break;
  546. case PrimitiveVariant::ROTATE3D: break;
  547. default:
  548. RMLUI_ASSERT(false);
  549. break;
  550. }
  551. return result;
  552. }
  553. };
  554. static bool CanInterpolateRotate3D(const Rotate3D& p0, const Rotate3D& p1)
  555. {
  556. // Rotate3D can only be interpolated if and only if their rotation axes point in the same direction.
  557. // Assumes each rotation axis has already been normalized.
  558. auto& v0 = p0.values;
  559. auto& v1 = p1.values;
  560. return v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2];
  561. }
  562. bool Primitive::TryConvertToMatchingGenericType(Primitive & p0, Primitive & p1) noexcept
  563. {
  564. if (p0.primitive.type == p1.primitive.type)
  565. {
  566. if(p0.primitive.type == PrimitiveVariant::ROTATE3D && !CanInterpolateRotate3D(p0.primitive.rotate_3d, p1.primitive.rotate_3d))
  567. return false;
  568. return true;
  569. }
  570. GenericType c0 = GetGenericTypeVisitor{}.run(p0.primitive);
  571. GenericType c1 = GetGenericTypeVisitor{}.run(p1.primitive);
  572. if (c0 == c1 && c0 != GenericType::None)
  573. {
  574. PrimitiveVariant new_p0 = ConvertToGenericTypeVisitor{}.run(p0.primitive);
  575. PrimitiveVariant new_p1 = ConvertToGenericTypeVisitor{}.run(p1.primitive);
  576. RMLUI_ASSERT(new_p0.type == new_p1.type);
  577. if (new_p0.type == PrimitiveVariant::ROTATE3D && !CanInterpolateRotate3D(new_p0.rotate_3d, new_p1.rotate_3d))
  578. return false;
  579. p0.primitive = new_p0;
  580. p1.primitive = new_p1;
  581. return true;
  582. }
  583. return false;
  584. }
  585. struct InterpolateVisitor
  586. {
  587. const PrimitiveVariant& other_variant;
  588. float alpha;
  589. template <size_t N>
  590. bool Interpolate(ResolvedPrimitive<N>& p0, const ResolvedPrimitive<N>& p1)
  591. {
  592. for (size_t i = 0; i < N; i++)
  593. p0.values[i] = p0.values[i] * (1.0f - alpha) + p1.values[i] * alpha;
  594. return true;
  595. }
  596. template <size_t N>
  597. bool Interpolate(UnresolvedPrimitive<N>& p0, const UnresolvedPrimitive<N>& p1)
  598. {
  599. // Assumes that the underlying units have been resolved (e.g. to pixels)
  600. for (size_t i = 0; i < N; i++)
  601. p0.values[i].number = p0.values[i].number*(1.0f - alpha) + p1.values[i].number * alpha;
  602. return true;
  603. }
  604. bool Interpolate(Rotate3D& p0, const Rotate3D& p1)
  605. {
  606. RMLUI_ASSERT(CanInterpolateRotate3D(p0, p1));
  607. // We can only interpolate rotate3d if their rotation axes align. That should be the case if we get here,
  608. // otherwise the generic type matching should decompose them. Thus, we only need to interpolate
  609. // the angle value here.
  610. p0.values[3] = p0.values[3] * (1.0f - alpha) + p1.values[3] * alpha;
  611. return true;
  612. }
  613. bool Interpolate(Matrix2D& p0, const Matrix2D& p1) { return false; /* Error if we get here, see PrepareForInterpolation() */ }
  614. bool Interpolate(Matrix3D& p0, const Matrix3D& p1) { return false; /* Error if we get here, see PrepareForInterpolation() */ }
  615. bool Interpolate(Perspective& p0, const Perspective& p1) { return false; /* Error if we get here, see PrepareForInterpolation() */ }
  616. bool Interpolate(DecomposedMatrix4& p0, const DecomposedMatrix4& p1)
  617. {
  618. p0.perspective = p0.perspective * (1.0f - alpha) + p1.perspective * alpha;
  619. p0.quaternion = QuaternionSlerp(p0.quaternion, p1.quaternion, alpha);
  620. p0.translation = p0.translation * (1.0f - alpha) + p1.translation * alpha;
  621. p0.scale = p0.scale* (1.0f - alpha) + p1.scale* alpha;
  622. p0.skew = p0.skew* (1.0f - alpha) + p1.skew* alpha;
  623. return true;
  624. }
  625. bool run(PrimitiveVariant& variant)
  626. {
  627. RMLUI_ASSERT(variant.type == other_variant.type);
  628. switch (variant.type)
  629. {
  630. case PrimitiveVariant::MATRIX2D: return Interpolate(variant.matrix_2d, other_variant.matrix_2d);
  631. case PrimitiveVariant::MATRIX3D: return Interpolate(variant.matrix_3d, other_variant.matrix_3d);
  632. case PrimitiveVariant::TRANSLATEX: return Interpolate(variant.translate_x, other_variant.translate_x);
  633. case PrimitiveVariant::TRANSLATEY: return Interpolate(variant.translate_y, other_variant.translate_y);
  634. case PrimitiveVariant::TRANSLATEZ: return Interpolate(variant.translate_z, other_variant.translate_z);
  635. case PrimitiveVariant::TRANSLATE2D: return Interpolate(variant.translate_2d, other_variant.translate_2d);
  636. case PrimitiveVariant::TRANSLATE3D: return Interpolate(variant.translate_3d, other_variant.translate_3d);
  637. case PrimitiveVariant::SCALEX: return Interpolate(variant.scale_x, other_variant.scale_x);
  638. case PrimitiveVariant::SCALEY: return Interpolate(variant.scale_y, other_variant.scale_y);
  639. case PrimitiveVariant::SCALEZ: return Interpolate(variant.scale_z, other_variant.scale_z);
  640. case PrimitiveVariant::SCALE2D: return Interpolate(variant.scale_2d, other_variant.scale_2d);
  641. case PrimitiveVariant::SCALE3D: return Interpolate(variant.scale_3d, other_variant.scale_3d);
  642. case PrimitiveVariant::ROTATEX: return Interpolate(variant.rotate_x, other_variant.rotate_x);
  643. case PrimitiveVariant::ROTATEY: return Interpolate(variant.rotate_y, other_variant.rotate_y);
  644. case PrimitiveVariant::ROTATEZ: return Interpolate(variant.rotate_z, other_variant.rotate_z);
  645. case PrimitiveVariant::ROTATE2D: return Interpolate(variant.rotate_2d, other_variant.rotate_2d);
  646. case PrimitiveVariant::ROTATE3D: return Interpolate(variant.rotate_3d, other_variant.rotate_3d);
  647. case PrimitiveVariant::SKEWX: return Interpolate(variant.skew_x, other_variant.skew_x);
  648. case PrimitiveVariant::SKEWY: return Interpolate(variant.skew_y, other_variant.skew_y);
  649. case PrimitiveVariant::SKEW2D: return Interpolate(variant.skew_2d, other_variant.skew_2d);
  650. case PrimitiveVariant::PERSPECTIVE: return Interpolate(variant.perspective, other_variant.perspective);
  651. case PrimitiveVariant::DECOMPOSEDMATRIX4: return Interpolate(variant.decomposed_matrix_4, other_variant.decomposed_matrix_4);
  652. default:
  653. break;
  654. }
  655. RMLUI_ASSERT(false);
  656. return false;
  657. }
  658. };
  659. bool Primitive::InterpolateWith(const Primitive & other, float alpha) noexcept
  660. {
  661. if (primitive.type != other.primitive.type)
  662. return false;
  663. bool result = InterpolateVisitor{ other.primitive, alpha }.run(primitive);
  664. return result;
  665. }
  666. template<size_t N>
  667. static inline String ToString(const Transforms::ResolvedPrimitive<N>& p, String unit, bool rad_to_deg = false, bool only_unit_on_last_value = false) noexcept {
  668. float multiplier = 1.0f;
  669. String tmp;
  670. String result = "(";
  671. for (size_t i = 0; i < N; i++)
  672. {
  673. if (only_unit_on_last_value && i < N - 1)
  674. multiplier = 1.0f;
  675. else if (rad_to_deg)
  676. multiplier = 180.f / Math::RMLUI_PI;
  677. if (TypeConverter<float, String>::Convert(p.values[i] * multiplier, tmp))
  678. result += tmp;
  679. if (!unit.empty() && (!only_unit_on_last_value || (i == N - 1)))
  680. result += unit;
  681. if (i < N - 1)
  682. result += ", ";
  683. }
  684. result += ")";
  685. return result;
  686. }
  687. template<size_t N>
  688. static inline String ToString(const Transforms::UnresolvedPrimitive<N> & p) noexcept {
  689. String result = "(";
  690. for (size_t i = 0; i < N; i++)
  691. {
  692. result += p.values[i].ToString();
  693. if (i != N - 1)
  694. result += ", ";
  695. }
  696. result += ")";
  697. return result;
  698. }
  699. static inline String ToString(const Transforms::DecomposedMatrix4& p) noexcept {
  700. static const DecomposedMatrix4 d{
  701. Vector4f(0, 0, 0, 1),
  702. Vector4f(0, 0, 0, 1),
  703. Vector3f(0, 0, 0),
  704. Vector3f(1, 1, 1),
  705. Vector3f(0, 0, 0)
  706. };
  707. String tmp;
  708. String result;
  709. if(p.perspective != d.perspective && TypeConverter< Vector4f, String >::Convert(p.perspective, tmp))
  710. result += "perspective(" + tmp + "), ";
  711. if (p.quaternion != d.quaternion && TypeConverter< Vector4f, String >::Convert(p.quaternion, tmp))
  712. result += "quaternion(" + tmp + "), ";
  713. if (p.translation != d.translation && TypeConverter< Vector3f, String >::Convert(p.translation, tmp))
  714. result += "translation(" + tmp + "), ";
  715. if (p.scale != d.scale && TypeConverter< Vector3f, String >::Convert(p.scale, tmp))
  716. result += "scale(" + tmp + "), ";
  717. if (p.skew != d.skew && TypeConverter< Vector3f, String >::Convert(p.skew, tmp))
  718. result += "skew(" + tmp + "), ";
  719. if (result.size() > 2)
  720. result.resize(result.size() - 2);
  721. result = "decomposedMatrix3d{ " + result + " }";
  722. return result;
  723. }
  724. String ToString(const Transforms::Matrix2D & p) noexcept { return "matrix" + ToString(static_cast<const Transforms::ResolvedPrimitive< 6 >&>(p), ""); }
  725. String ToString(const Transforms::Matrix3D & p) noexcept { return "matrix3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 16 >&>(p), ""); }
  726. String ToString(const Transforms::TranslateX & p) noexcept { return "translateX" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  727. String ToString(const Transforms::TranslateY & p) noexcept { return "translateY" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  728. String ToString(const Transforms::TranslateZ & p) noexcept { return "translateZ" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  729. String ToString(const Transforms::Translate2D & p) noexcept { return "translate" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 2 >&>(p)); }
  730. String ToString(const Transforms::Translate3D & p) noexcept { return "translate3d" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 3 >&>(p)); }
  731. String ToString(const Transforms::ScaleX & p) noexcept { return "scaleX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  732. String ToString(const Transforms::ScaleY & p) noexcept { return "scaleY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  733. String ToString(const Transforms::ScaleZ & p) noexcept { return "scaleZ" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  734. String ToString(const Transforms::Scale2D & p) noexcept { return "scale" + ToString(static_cast<const Transforms::ResolvedPrimitive< 2 >&>(p), ""); }
  735. String ToString(const Transforms::Scale3D & p) noexcept { return "scale3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 3 >&>(p), ""); }
  736. String ToString(const Transforms::RotateX & p) noexcept { return "rotateX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  737. String ToString(const Transforms::RotateY & p) noexcept { return "rotateY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  738. String ToString(const Transforms::RotateZ & p) noexcept { return "rotateZ" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  739. String ToString(const Transforms::Rotate2D & p) noexcept { return "rotate" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  740. String ToString(const Transforms::Rotate3D & p) noexcept { return "rotate3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 4 >&>(p), "deg", true, true); }
  741. String ToString(const Transforms::SkewX & p) noexcept { return "skewX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  742. String ToString(const Transforms::SkewY & p) noexcept { return "skewY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  743. String ToString(const Transforms::Skew2D & p) noexcept { return "skew" + ToString(static_cast<const Transforms::ResolvedPrimitive< 2 >&>(p), "deg", true); }
  744. String ToString(const Transforms::Perspective & p) noexcept { return "perspective" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  745. struct ToStringVisitor
  746. {
  747. String run(const PrimitiveVariant& variant)
  748. {
  749. switch (variant.type)
  750. {
  751. case PrimitiveVariant::MATRIX2D: return ToString(variant.matrix_2d);
  752. case PrimitiveVariant::MATRIX3D: return ToString(variant.matrix_3d);
  753. case PrimitiveVariant::TRANSLATEX: return ToString(variant.translate_x);
  754. case PrimitiveVariant::TRANSLATEY: return ToString(variant.translate_y);
  755. case PrimitiveVariant::TRANSLATEZ: return ToString(variant.translate_z);
  756. case PrimitiveVariant::TRANSLATE2D: return ToString(variant.translate_2d);
  757. case PrimitiveVariant::TRANSLATE3D: return ToString(variant.translate_3d);
  758. case PrimitiveVariant::SCALEX: return ToString(variant.scale_x);
  759. case PrimitiveVariant::SCALEY: return ToString(variant.scale_y);
  760. case PrimitiveVariant::SCALEZ: return ToString(variant.scale_z);
  761. case PrimitiveVariant::SCALE2D: return ToString(variant.scale_2d);
  762. case PrimitiveVariant::SCALE3D: return ToString(variant.scale_3d);
  763. case PrimitiveVariant::ROTATEX: return ToString(variant.rotate_x);
  764. case PrimitiveVariant::ROTATEY: return ToString(variant.rotate_y);
  765. case PrimitiveVariant::ROTATEZ: return ToString(variant.rotate_z);
  766. case PrimitiveVariant::ROTATE2D: return ToString(variant.rotate_2d);
  767. case PrimitiveVariant::ROTATE3D: return ToString(variant.rotate_3d);
  768. case PrimitiveVariant::SKEWX: return ToString(variant.skew_x);
  769. case PrimitiveVariant::SKEWY: return ToString(variant.skew_y);
  770. case PrimitiveVariant::SKEW2D: return ToString(variant.skew_2d);
  771. case PrimitiveVariant::PERSPECTIVE: return ToString(variant.perspective);
  772. case PrimitiveVariant::DECOMPOSEDMATRIX4: return ToString(variant.decomposed_matrix_4);
  773. default:
  774. break;
  775. }
  776. RMLUI_ASSERT(false);
  777. return String();
  778. }
  779. };
  780. String Primitive::ToString() const noexcept
  781. {
  782. String result = ToStringVisitor{}.run(primitive);
  783. return result;
  784. }
  785. bool DecomposedMatrix4::Decompose(const Matrix4f & m)
  786. {
  787. // Follows the procedure given in https://drafts.csswg.org/css-transforms-2/#interpolation-of-3d-matrices
  788. const float eps = 0.0005f;
  789. if (Math::AbsoluteValue(m[3][3]) < eps)
  790. return false;
  791. // Perspective matrix
  792. Matrix4f p = m;
  793. for (int i = 0; i < 3; i++)
  794. p[i][3] = 0;
  795. p[3][3] = 1;
  796. if (Math::AbsoluteValue(p.Determinant()) < eps)
  797. return false;
  798. if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0)
  799. {
  800. auto rhs = m.GetColumn(3);
  801. Matrix4f p_inv = p;
  802. if (!p_inv.Invert())
  803. return false;
  804. auto& p_inv_trans = p.Transpose();
  805. perspective = p_inv_trans * rhs;
  806. }
  807. else
  808. {
  809. perspective[0] = perspective[1] = perspective[2] = 0;
  810. perspective[3] = 1;
  811. }
  812. for (int i = 0; i < 3; i++)
  813. translation[i] = m[3][i];
  814. Vector3f row[3];
  815. for (int i = 0; i < 3; i++)
  816. {
  817. row[i][0] = m[i][0];
  818. row[i][1] = m[i][1];
  819. row[i][2] = m[i][2];
  820. }
  821. scale[0] = row[0].Magnitude();
  822. row[0] = row[0].Normalise();
  823. skew[0] = row[0].DotProduct(row[1]);
  824. row[1] = Combine(row[1], row[0], 1, -skew[0]);
  825. scale[1] = row[1].Magnitude();
  826. row[1] = row[1].Normalise();
  827. skew[0] /= scale[1];
  828. skew[1] = row[0].DotProduct(row[2]);
  829. row[2] = Combine(row[2], row[0], 1, -skew[1]);
  830. skew[2] = row[1].DotProduct(row[2]);
  831. row[2] = Combine(row[2], row[1], 1, -skew[2]);
  832. scale[2] = row[2].Magnitude();
  833. row[2] = row[2].Normalise();
  834. skew[1] /= scale[2];
  835. skew[2] /= scale[2];
  836. // Check if we need to flip coordinate system
  837. auto pdum3 = row[1].CrossProduct(row[2]);
  838. if (row[0].DotProduct(pdum3) < 0.0f)
  839. {
  840. for (int i = 0; i < 3; i++)
  841. {
  842. scale[i] *= -1.f;
  843. row[i] *= -1.f;
  844. }
  845. }
  846. quaternion[0] = 0.5f * Math::SquareRoot(Math::Max(1.f + row[0][0] - row[1][1] - row[2][2], 0.0f));
  847. quaternion[1] = 0.5f * Math::SquareRoot(Math::Max(1.f - row[0][0] + row[1][1] - row[2][2], 0.0f));
  848. quaternion[2] = 0.5f * Math::SquareRoot(Math::Max(1.f - row[0][0] - row[1][1] + row[2][2], 0.0f));
  849. quaternion[3] = 0.5f * Math::SquareRoot(Math::Max(1.f + row[0][0] + row[1][1] + row[2][2], 0.0f));
  850. if (row[2][1] > row[1][2])
  851. quaternion[0] *= -1.f;
  852. if (row[0][2] > row[2][0])
  853. quaternion[1] *= -1.f;
  854. if (row[1][0] > row[0][1])
  855. quaternion[2] *= -1.f;
  856. return true;
  857. }
  858. }
  859. }
  860. }