LCOV - code coverage report
Current view: top level - core/geom - SPQuaternion.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 155 168 92.3 %
Date: 2024-05-12 00:16:13 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /**
       2             : Copyright 2013 BlackBerry Inc.
       3             : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
       4             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       5             : 
       6             : Licensed under the Apache License, Version 2.0 (the "License");
       7             : you may not use this file except in compliance with the License.
       8             : You may obtain a copy of the License at
       9             : 
      10             : http://www.apache.org/licenses/LICENSE-2.0
      11             : 
      12             : Unless required by applicable law or agreed to in writing, software
      13             : distributed under the License is distributed on an "AS IS" BASIS,
      14             : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             : See the License for the specific language governing permissions and
      16             : limitations under the License.
      17             : 
      18             : Original file from GamePlay3D: http://gameplay3d.org
      19             : 
      20             : This file was modified to fit the cocos2d-x project
      21             : This file was modified for stappler project
      22             : */
      23             : 
      24             : #include "SPQuaternion.h"
      25             : #include "SPMat4.h"
      26             : 
      27             : namespace STAPPLER_VERSIONIZED stappler::geom {
      28             : 
      29         100 : static void Quaternion_slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t,
      30             :                 float* dstx, float* dsty, float* dstz, float* dstw) {
      31             :         // Fast slerp implementation by kwhatmough:
      32             :         // It contains no division operations, no trig, no inverse trig
      33             :         // and no sqrt. Not only does this code tolerate small constraint
      34             :         // errors in the input quaternions, it actually corrects for them.
      35         100 :         assert(dstx && dsty && dstz && dstw);
      36         100 :         assert(!(t < 0.0f || t > 1.0f));
      37             : 
      38         100 :         if (t == 0.0f) {
      39          25 :                 *dstx = q1x;
      40          25 :                 *dsty = q1y;
      41          25 :                 *dstz = q1z;
      42          25 :                 *dstw = q1w;
      43          25 :                 return;
      44          75 :         } else if (t == 1.0f) {
      45          25 :                 *dstx = q2x;
      46          25 :                 *dsty = q2y;
      47          25 :                 *dstz = q2z;
      48          25 :                 *dstw = q2w;
      49          25 :                 return;
      50             :         }
      51             : 
      52          50 :         if (q1x == q2x && q1y == q2y && q1z == q2z && q1w == q2w) {
      53          25 :                 *dstx = q1x;
      54          25 :                 *dsty = q1y;
      55          25 :                 *dstz = q1z;
      56          25 :                 *dstw = q1w;
      57          25 :                 return;
      58             :         }
      59             : 
      60             :         float halfY, alpha, beta;
      61             :         float u, f1, f2a, f2b;
      62             :         float ratio1, ratio2;
      63             :         float halfSecHalfTheta, versHalfTheta;
      64             :         float sqNotU, sqU;
      65             : 
      66          25 :         float cosTheta = q1w * q2w + q1x * q2x + q1y * q2y + q1z * q2z;
      67             : 
      68             :         // As usual in all slerp implementations, we fold theta.
      69          25 :         alpha = cosTheta >= 0 ? 1.0f : -1.0f;
      70          25 :         halfY = 1.0f + alpha * cosTheta;
      71             : 
      72             :         // Here we bisect the interval, so we need to fold t as well.
      73          25 :         f2b = t - 0.5f;
      74          25 :         u = f2b >= 0 ? f2b : -f2b;
      75          25 :         f2a = u - f2b;
      76          25 :         f2b += u;
      77          25 :         u += u;
      78          25 :         f1 = 1.0f - u;
      79             : 
      80             :         // One iteration of Newton to get 1-cos(theta / 2) to good accuracy.
      81          25 :         halfSecHalfTheta = 1.09f - (0.476537f - 0.0903321f * halfY) * halfY;
      82          25 :         halfSecHalfTheta *= 1.5f - halfY * halfSecHalfTheta * halfSecHalfTheta;
      83          25 :         versHalfTheta = 1.0f - halfY * halfSecHalfTheta;
      84             : 
      85             :         // Evaluate series expansions of the coefficients.
      86          25 :         sqNotU = f1 * f1;
      87          25 :         ratio2 = 0.0000440917108f * versHalfTheta;
      88          25 :         ratio1 = -0.00158730159f + (sqNotU - 16.0f) * ratio2;
      89          25 :         ratio1 = 0.0333333333f + ratio1 * (sqNotU - 9.0f) * versHalfTheta;
      90          25 :         ratio1 = -0.333333333f + ratio1 * (sqNotU - 4.0f) * versHalfTheta;
      91          25 :         ratio1 = 1.0f + ratio1 * (sqNotU - 1.0f) * versHalfTheta;
      92             : 
      93          25 :         sqU = u * u;
      94          25 :         ratio2 = -0.00158730159f + (sqU - 16.0f) * ratio2;
      95          25 :         ratio2 = 0.0333333333f + ratio2 * (sqU - 9.0f) * versHalfTheta;
      96          25 :         ratio2 = -0.333333333f + ratio2 * (sqU - 4.0f) * versHalfTheta;
      97          25 :         ratio2 = 1.0f + ratio2 * (sqU - 1.0f) * versHalfTheta;
      98             : 
      99             :         // Perform the bisection and resolve the folding done earlier.
     100          25 :         f1 *= ratio1 * halfSecHalfTheta;
     101          25 :         f2a *= ratio2;
     102          25 :         f2b *= ratio2;
     103          25 :         alpha *= f1 + f2a;
     104          25 :         beta = f1 + f2b;
     105             : 
     106             :         // Apply final coefficients to a and b as usual.
     107          25 :         const float w = alpha * q1w + beta * q2w;
     108          25 :         const float x = alpha * q1x + beta * q2x;
     109          25 :         const float y = alpha * q1y + beta * q2y;
     110          25 :         const float z = alpha * q1z + beta * q2z;
     111             : 
     112             :         // This final adjustment to the quaternion's length corrects for
     113             :         // any small constraint error in the inputs q1 and q2 But as you
     114             :         // can see, it comes at the cost of 9 additional multiplication
     115             :         // operations. If this error-correcting feature is not required,
     116             :         // the following code may be removed.
     117          25 :         f1 = 1.5f - 0.5f * (w * w + x * x + y * y + z * z);
     118          25 :         *dstw = w * f1;
     119          25 :         *dstx = x * f1;
     120          25 :         *dsty = y * f1;
     121          25 :         *dstz = z * f1;
     122             : }
     123             : 
     124         225 : static void Quaternion_slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst) {
     125         225 :         assert(dst);
     126             : 
     127             :         // cos(omega) = q1 * q2;
     128             :         // slerp(q1, q2, t) = (q1*sin((1-t)*omega) + q2*sin(t*omega))/sin(omega);
     129             :         // q1 = +- q2, slerp(q1,q2,t) = q1.
     130             :         // This is a straight-forward implementation of the formula of slerp. It does not do any sign switching.
     131         225 :         const float c = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
     132             : 
     133         225 :         if (fabs(c) >= 1.0f) {
     134           0 :                 dst->x = q1.x;
     135           0 :                 dst->y = q1.y;
     136           0 :                 dst->z = q1.z;
     137           0 :                 dst->w = q1.w;
     138           0 :                 return;
     139             :         }
     140             : 
     141         225 :         float omega = acos(c);
     142         225 :         float s = sqrt(1.0f - c * c);
     143         225 :         if (fabs(s) <= 0.00001f) {
     144           0 :                 dst->x = q1.x;
     145           0 :                 dst->y = q1.y;
     146           0 :                 dst->z = q1.z;
     147           0 :                 dst->w = q1.w;
     148           0 :                 return;
     149             :         }
     150             : 
     151         225 :         float r1 = sin((1 - t) * omega) / s;
     152         225 :         float r2 = sin(t * omega) / s;
     153         225 :         dst->x = (q1.x * r1 + q2.x * r2);
     154         225 :         dst->y = (q1.y * r1 + q2.y * r2);
     155         225 :         dst->z = (q1.z * r1 + q2.z * r2);
     156         225 :         dst->w = (q1.w * r1 + q2.w * r2);
     157             : }
     158             : 
     159          25 : Quaternion::Quaternion(const Mat4& m) {
     160          25 :         m.getRotation(this);
     161          25 : }
     162             : 
     163          75 : bool Quaternion::inverse() {
     164          75 :         float n = x * x + y * y + z * z + w * w;
     165          75 :         if (n == 1.0f) {
     166          50 :                 x = -x;
     167          50 :                 y = -y;
     168          50 :                 z = -z;
     169             :                 //w = w;
     170             : 
     171          50 :                 return true;
     172             :         }
     173             : 
     174             :         // Too close to zero.
     175          25 :         if (n < 0.000001f) {
     176           0 :                 return false;
     177             :         }
     178             : 
     179          25 :         n = 1.0f / n;
     180          25 :         x = -x * n;
     181          25 :         y = -y * n;
     182          25 :         z = -z * n;
     183          25 :         w = w * n;
     184             : 
     185          25 :         return true;
     186             : }
     187             : 
     188          50 : Quaternion Quaternion::getInversed() const {
     189          50 :         Quaternion q(*this);
     190          50 :         q.inverse();
     191          50 :         return q;
     192             : }
     193             : 
     194          25 : void Quaternion::multiply(const Quaternion& q) {
     195          25 :         multiply(*this, q, this);
     196          25 : }
     197             : 
     198          50 : void Quaternion::multiply(const Quaternion& q1, const Quaternion& q2, Quaternion* dst) {
     199          50 :         assert(dst);
     200             : 
     201          50 :         float x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
     202          50 :         float y = q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x;
     203          50 :         float z = q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w;
     204          50 :         float w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
     205             : 
     206          50 :         dst->x = x;
     207          50 :         dst->y = y;
     208          50 :         dst->z = z;
     209          50 :         dst->w = w;
     210          50 : }
     211             : 
     212          75 : void Quaternion::normalize() {
     213          75 :         float n = x * x + y * y + z * z + w * w;
     214             : 
     215             :         // Already normalized.
     216          75 :         if (n == 1.0f)
     217           0 :                 return;
     218             : 
     219          75 :         n = sqrt(n);
     220             :         // Too close to zero.
     221          75 :         if (n < 0.000001f)
     222           0 :                 return;
     223             : 
     224          75 :         n = 1.0f / n;
     225          75 :         x *= n;
     226          75 :         y *= n;
     227          75 :         z *= n;
     228          75 :         w *= n;
     229             : }
     230             : 
     231          50 : Quaternion Quaternion::getNormalized() const {
     232          50 :         Quaternion q(*this);
     233          50 :         q.normalize();
     234          50 :         return q;
     235             : }
     236             : 
     237          25 : float Quaternion::toAxisAngle(Vec3* axis) const {
     238          25 :         assert(axis);
     239             : 
     240          25 :         Quaternion q(x, y, z, w);
     241          25 :         q.normalize();
     242          25 :         axis->x = q.x;
     243          25 :         axis->y = q.y;
     244          25 :         axis->z = q.z;
     245          25 :         axis->normalize();
     246             : 
     247          25 :         return (2.0f * acos(q.w));
     248             : }
     249             : 
     250         100 : void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst) {
     251         100 :         assert(dst);
     252         100 :         assert(!(t < 0.0f || t > 1.0f));
     253             : 
     254         100 :         if (t == 0.0f) {
     255          25 :                 memcpy((void *)dst, (const void *)&q1, sizeof(float) * 4);
     256          25 :                 return;
     257          75 :         } else if (t == 1.0f) {
     258          25 :                 memcpy((void *)dst, (const void *)&q2, sizeof(float) * 4);
     259          25 :                 return;
     260             :         }
     261             : 
     262          50 :         const float t1 = 1.0f - t;
     263             : 
     264          50 :         dst->x = t1 * q1.x + t * q2.x;
     265          50 :         dst->y = t1 * q1.y + t * q2.y;
     266          50 :         dst->z = t1 * q1.z + t * q2.z;
     267          50 :         dst->w = t1 * q1.w + t * q2.w;
     268             : }
     269             : 
     270         100 : void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst) {
     271         100 :         assert(dst);
     272         100 :         Quaternion_slerp(q1.x, q1.y, q1.z, q1.w, q2.x, q2.y, q2.z, q2.w, t, &dst->x, &dst->y, &dst->z, &dst->w);
     273         100 : }
     274             : 
     275          75 : void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst) {
     276          75 :         assert(!(t < 0.0f || t > 1.0f));
     277             : 
     278          75 :         Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
     279          75 :         Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
     280             : 
     281          75 :         Quaternion_slerpForSquad(q1, q2, t, &dstQ);
     282          75 :         Quaternion_slerpForSquad(s1, s2, t, &dstS);
     283          75 :         Quaternion_slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
     284          75 : }
     285             : 
     286             : #ifdef __LCC__
     287             : 
     288             : constexpr const Quaternion Quaternion::IDENTITY(0.0f, 0.0f, 0.0f, 1.0f);
     289             : constexpr const Quaternion Quaternion::ZERO(0.0f, 0.0f, 0.0f, 0.0f);
     290             : 
     291             : #endif
     292             : 
     293             : }

Generated by: LCOV version 1.14