|
| 1 | +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. |
| 2 | +// This file is part of the "Nabla Engine". |
| 3 | +// For conditions of distribution and use, see copyright notice in nabla.h |
| 4 | +#ifndef _NBL_BUILTIN_HLSL_MATH_QUATERNIONS_INCLUDED_ |
| 5 | +#define _NBL_BUILTIN_HLSL_MATH_QUATERNIONS_INCLUDED_ |
| 6 | + |
| 7 | +#include "nbl/builtin/hlsl/cpp_compat.hlsl" |
| 8 | +#include "nbl/builtin/hlsl/tgmath.hlsl" |
| 9 | + |
| 10 | +namespace nbl |
| 11 | +{ |
| 12 | +namespace hlsl |
| 13 | +{ |
| 14 | +namespace math |
| 15 | +{ |
| 16 | + |
| 17 | +template<typename T> |
| 18 | +struct truncated_quaternion |
| 19 | +{ |
| 20 | + using this_t = truncated_quaternion<T>; |
| 21 | + using scalar_type = T; |
| 22 | + using data_type = vector<T, 3>; |
| 23 | + |
| 24 | + static this_t create() |
| 25 | + { |
| 26 | + this_t q; |
| 27 | + q.data = data_type(0.0, 0.0, 0.0); |
| 28 | + return q; |
| 29 | + } |
| 30 | + |
| 31 | + data_type data; |
| 32 | +}; |
| 33 | + |
| 34 | +template <typename T> |
| 35 | +struct quaternion |
| 36 | +{ |
| 37 | + using this_t = quaternion<T>; |
| 38 | + using scalar_type = T; |
| 39 | + using data_type = vector<T, 4>; |
| 40 | + using vector3_type = vector<T, 3>; |
| 41 | + using matrix_type = matrix<T, 3, 3>; |
| 42 | + |
| 43 | + using AsUint = typename unsigned_integer_of_size<sizeof(scalar_type)>::type; |
| 44 | + |
| 45 | + static this_t create() |
| 46 | + { |
| 47 | + this_t q; |
| 48 | + q.data = data_type(0.0, 0.0, 0.0, 1.0); |
| 49 | + return q; |
| 50 | + } |
| 51 | + |
| 52 | + static this_t create(scalar_type x, scalar_type y, scalar_type z, scalar_type w) |
| 53 | + { |
| 54 | + this_t q; |
| 55 | + q.data = data_type(x, y, z, w); |
| 56 | + return q; |
| 57 | + } |
| 58 | + |
| 59 | + static this_t create(NBL_CONST_REF_ARG(this_t) other) |
| 60 | + { |
| 61 | + return other; |
| 62 | + } |
| 63 | + |
| 64 | + // angle: Rotation angle expressed in radians. |
| 65 | + // axis: Rotation axis, must be normalized. |
| 66 | + static this_t create(scalar_type angle, const vector3_type axis) |
| 67 | + { |
| 68 | + this_t q; |
| 69 | + const scalar_type sinTheta = hlsl::sin(angle * 0.5); |
| 70 | + const scalar_type cosTheta = hlsl::cos(angle * 0.5); |
| 71 | + q.data = data_type(axis * sinTheta, cosTheta); |
| 72 | + return q; |
| 73 | + } |
| 74 | + |
| 75 | + |
| 76 | + static this_t create(scalar_type pitch, scalar_type yaw, scalar_type roll) |
| 77 | + { |
| 78 | + const scalar_type rollDiv2 = roll * scalar_type(0.5); |
| 79 | + const scalar_type sr = hlsl::sin(rollDiv2); |
| 80 | + const scalar_type cr = hlsl::cos(rollDiv2); |
| 81 | + |
| 82 | + const scalar_type pitchDiv2 = pitch * scalar_type(0.5); |
| 83 | + const scalar_type sp = hlsl::sin(pitchDiv2); |
| 84 | + const scalar_type cp = hlsl::cos(pitchDiv2); |
| 85 | + |
| 86 | + const scalar_type yawDiv2 = yaw * scalar_type(0.5); |
| 87 | + const scalar_type sy = hlsl::sin(yawDiv2); |
| 88 | + const scalar_type cy = hlsl::cos(yawDiv2); |
| 89 | + |
| 90 | + this_t output; |
| 91 | + output.data[0] = cr * sp * cy + sr * cp * sy; // x |
| 92 | + output.data[1] = cr * cp * sy - sr * sp * cy; // y |
| 93 | + output.data[2] = sr * cp * cy - cr * sp * sy; // z |
| 94 | + output.data[3] = cr * cp * cy + sr * sp * sy; // w |
| 95 | + |
| 96 | + return output; |
| 97 | + } |
| 98 | + |
| 99 | + static this_t create(NBL_CONST_REF_ARG(matrix_type) m) |
| 100 | + { |
| 101 | + const scalar_type m00 = m[0][0], m11 = m[1][1], m22 = m[2][2]; |
| 102 | + const scalar_type neg_m00 = bit_cast<scalar_type>(bit_cast<AsUint>(m00)^0x80000000u); |
| 103 | + const scalar_type neg_m11 = bit_cast<scalar_type>(bit_cast<AsUint>(m11)^0x80000000u); |
| 104 | + const scalar_type neg_m22 = bit_cast<scalar_type>(bit_cast<AsUint>(m22)^0x80000000u); |
| 105 | + const data_type Qx = data_type(m00, m00, neg_m00, neg_m00); |
| 106 | + const data_type Qy = data_type(m11, neg_m11, m11, neg_m11); |
| 107 | + const data_type Qz = data_type(m22, neg_m22, neg_m22, m22); |
| 108 | + |
| 109 | + const data_type tmp = hlsl::promote<data_type>(1.0) + Qx + Qy + Qz; |
| 110 | + const data_type invscales = hlsl::promote<data_type>(0.5) / hlsl::sqrt(tmp); |
| 111 | + const data_type scales = tmp * invscales * hlsl::promote<data_type>(0.5); |
| 112 | + |
| 113 | + // TODO: speed this up |
| 114 | + this_t retval; |
| 115 | + if (tmp.x > scalar_type(0.0)) |
| 116 | + { |
| 117 | + retval.data.x = (m[2][1] - m[1][2]) * invscales.x; |
| 118 | + retval.data.y = (m[0][2] - m[2][0]) * invscales.x; |
| 119 | + retval.data.z = (m[1][0] - m[0][1]) * invscales.x; |
| 120 | + retval.data.w = scales.x; |
| 121 | + } |
| 122 | + else |
| 123 | + { |
| 124 | + if (tmp.y > scalar_type(0.0)) |
| 125 | + { |
| 126 | + retval.data.x = scales.y; |
| 127 | + retval.data.y = (m[0][1] + m[1][0]) * invscales.y; |
| 128 | + retval.data.z = (m[2][0] + m[0][2]) * invscales.y; |
| 129 | + retval.data.w = (m[2][1] - m[1][2]) * invscales.y; |
| 130 | + } |
| 131 | + else if (tmp.z > scalar_type(0.0)) |
| 132 | + { |
| 133 | + retval.data.x = (m[0][1] + m[1][0]) * invscales.z; |
| 134 | + retval.data.y = scales.z; |
| 135 | + retval.data.z = (m[0][2] - m[2][0]) * invscales.z; |
| 136 | + retval.data.w = (m[1][2] + m[2][1]) * invscales.z; |
| 137 | + } |
| 138 | + else |
| 139 | + { |
| 140 | + retval.data.x = (m[0][2] + m[2][0]) * invscales.w; |
| 141 | + retval.data.y = (m[1][2] + m[2][1]) * invscales.w; |
| 142 | + retval.data.z = scales.w; |
| 143 | + retval.data.w = (m[1][0] - m[0][1]) * invscales.w; |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + retval.data = hlsl::normalize(retval.data); |
| 148 | + return retval; |
| 149 | + } |
| 150 | + |
| 151 | + static this_t create(NBL_CONST_REF_ARG(truncated_quaternion<T>) first3Components) |
| 152 | + { |
| 153 | + this_t retval; |
| 154 | + retval.data.xyz = first3Components.data; |
| 155 | + retval.data.w = hlsl::sqrt(scalar_type(1.0) - hlsl::dot(first3Components.data, first3Components.data)); |
| 156 | + return retval; |
| 157 | + } |
| 158 | + |
| 159 | + this_t operator*(scalar_type scalar) |
| 160 | + { |
| 161 | + this_t output; |
| 162 | + output.data = data * scalar; |
| 163 | + return output; |
| 164 | + } |
| 165 | + |
| 166 | + this_t operator*(NBL_CONST_REF_ARG(this_t) other) |
| 167 | + { |
| 168 | + return this_t::create( |
| 169 | + data.w * other.data.w - data.x * other.x - data.y * other.data.y - data.z * other.data.z, |
| 170 | + data.w * other.data.x + data.x * other.w + data.y * other.data.z - data.z * other.data.y, |
| 171 | + data.w * other.data.y - data.x * other.z + data.y * other.data.w + data.z * other.data.x, |
| 172 | + data.w * other.data.z + data.x * other.y - data.y * other.data.x + data.z * other.data.w |
| 173 | + ); |
| 174 | + } |
| 175 | + |
| 176 | + static this_t lerp(const this_t start, const this_t end, const scalar_type fraction, const scalar_type totalPseudoAngle) |
| 177 | + { |
| 178 | + const AsUint negationMask = hlsl::bit_cast<AsUint>(totalPseudoAngle) & AsUint(0x80000000u); |
| 179 | + const data_type adjEnd = hlsl::bit_cast<scalar_type>(hlsl::bit_cast<AsUint>(end.data) ^ negationMask); |
| 180 | + |
| 181 | + this_t retval; |
| 182 | + retval.data = hlsl::mix(start.data, adjEnd, fraction); |
| 183 | + return retval; |
| 184 | + } |
| 185 | + |
| 186 | + static this_t lerp(const this_t start, const this_t end, const scalar_type fraction) |
| 187 | + { |
| 188 | + return lerp(start, end, fraction, hlsl::dot(start.data, end.data)); |
| 189 | + } |
| 190 | + |
| 191 | + static scalar_type __adj_interpolant(const scalar_type angle, const scalar_type fraction, const scalar_type interpolantPrecalcTerm2, const scalar_type interpolantPrecalcTerm3) |
| 192 | + { |
| 193 | + const scalar_type A = scalar_type(1.0904) + angle * (scalar_type(-3.2452) + angle * (scalar_type(3.55645) - angle * scalar_type(1.43519))); |
| 194 | + const scalar_type B = scalar_type(0.848013) + angle * (scalar_type(-1.06021) + angle * scalar_type(0.215638)); |
| 195 | + const scalar_type k = A * interpolantPrecalcTerm2 + B; |
| 196 | + return fraction + interpolantPrecalcTerm3 * k; |
| 197 | + } |
| 198 | + |
| 199 | + static this_t flerp(const this_t start, const this_t end, const scalar_type fraction) |
| 200 | + { |
| 201 | + const scalar_type pseudoAngle = hlsl::dot(start.data,end.data); |
| 202 | + const scalar_type interpolantPrecalcTerm = fraction - scalar_type(0.5); |
| 203 | + const scalar_type interpolantPrecalcTerm3 = fraction * interpolantPrecalcTerm * (fraction - scalar_type(1.0)); |
| 204 | + const scalar_type adjFrac = __adj_interpolant(hlsl::abs(pseudoAngle),fraction,interpolantPrecalcTerm*interpolantPrecalcTerm,interpolantPrecalcTerm3); |
| 205 | + |
| 206 | + this_t retval = lerp(start,end,adjFrac,pseudoAngle); |
| 207 | + retval.data = hlsl::normalize(retval.data); |
| 208 | + return retval; |
| 209 | + } |
| 210 | + |
| 211 | + vector3_type transformVector(const vector3_type v) |
| 212 | + { |
| 213 | + scalar_type scale = hlsl::length(data); |
| 214 | + vector3_type direction = data.xyz; |
| 215 | + return v * scale + hlsl::cross(direction, v * data.w + hlsl::cross(direction, v)) * scalar_type(2.0); |
| 216 | + } |
| 217 | + |
| 218 | + matrix_type constructMatrix() |
| 219 | + { |
| 220 | + matrix_type mat; |
| 221 | + mat[0] = data.yzx * data.ywz + data.zxy * data.zyw * vector3_type( 1.0, 1.0,-1.0); |
| 222 | + mat[1] = data.yzx * data.xzw + data.zxy * data.wxz * vector3_type(-1.0, 1.0, 1.0); |
| 223 | + mat[2] = data.yzx * data.wyx + data.zxy * data.xwy * vector3_type( 1.0,-1.0, 1.0); |
| 224 | + mat[0][0] = scalar_type(0.5) - mat[0][0]; |
| 225 | + mat[1][1] = scalar_type(0.5) - mat[1][1]; |
| 226 | + mat[2][2] = scalar_type(0.5) - mat[2][2]; |
| 227 | + mat *= scalar_type(2.0); |
| 228 | + return hlsl::transpose(mat); // TODO: double check transpose? |
| 229 | + } |
| 230 | + |
| 231 | + static vector3_type slerp_delta(const vector3_type start, const vector3_type preScaledWaypoint, scalar_type cosAngleFromStart) |
| 232 | + { |
| 233 | + vector3_type planeNormal = hlsl::cross(start,preScaledWaypoint); |
| 234 | + |
| 235 | + cosAngleFromStart *= scalar_type(0.5); |
| 236 | + const scalar_type sinAngle = hlsl::sqrt(scalar_type(0.5) - cosAngleFromStart); |
| 237 | + const scalar_type cosAngle = hlsl::sqrt(scalar_type(0.5) + cosAngleFromStart); |
| 238 | + |
| 239 | + planeNormal *= sinAngle; |
| 240 | + const vector3_type precompPart = hlsl::cross(planeNormal, start) * scalar_type(2.0); |
| 241 | + |
| 242 | + return precompPart * cosAngle + hlsl::cross(planeNormal, precompPart); |
| 243 | + } |
| 244 | + |
| 245 | + this_t inverse() |
| 246 | + { |
| 247 | + this_t retval; |
| 248 | + retval.data.x = bit_cast<scalar_type>(bit_cast<AsUint>(data.x)^0x80000000u); |
| 249 | + retval.data.y = bit_cast<scalar_type>(bit_cast<AsUint>(data.y)^0x80000000u); |
| 250 | + retval.data.z = bit_cast<scalar_type>(bit_cast<AsUint>(data.z)^0x80000000u); |
| 251 | + retval.data.w = data.w; |
| 252 | + return retval; |
| 253 | + } |
| 254 | + |
| 255 | + static this_t normalize(NBL_CONST_REF_ARG(this_t) q) |
| 256 | + { |
| 257 | + this_t retval; |
| 258 | + retval.data = hlsl::normalize(q.data); |
| 259 | + return retval; |
| 260 | + } |
| 261 | + |
| 262 | + data_type data; |
| 263 | +}; |
| 264 | + |
| 265 | +} |
| 266 | + |
| 267 | +namespace impl |
| 268 | +{ |
| 269 | + |
| 270 | +template<typename T> |
| 271 | +struct static_cast_helper<math::quaternion<T>, math::truncated_quaternion<T> > |
| 272 | +{ |
| 273 | + static inline math::quaternion<T> cast(math::truncated_quaternion<T> q) |
| 274 | + { |
| 275 | + return math::quaternion<T>::create(q); |
| 276 | + } |
| 277 | +}; |
| 278 | + |
| 279 | +template<typename T> |
| 280 | +struct static_cast_helper<math::truncated_quaternion<T>, math::quaternion<T> > |
| 281 | +{ |
| 282 | + static inline math::truncated_quaternion<T> cast(math::quaternion<T> q) |
| 283 | + { |
| 284 | + math::truncated_quaternion<T> t; |
| 285 | + t.data.x = t.data.x; |
| 286 | + t.data.y = t.data.y; |
| 287 | + t.data.z = t.data.z; |
| 288 | + return t; |
| 289 | + } |
| 290 | +}; |
| 291 | + |
| 292 | +template<typename T> |
| 293 | +struct static_cast_helper<matrix<T,3,3>, math::quaternion<T> > |
| 294 | +{ |
| 295 | + static inline matrix<T,3,3> cast(math::quaternion<T> q) |
| 296 | + { |
| 297 | + return q.constructMatrix(); |
| 298 | + } |
| 299 | +}; |
| 300 | +} |
| 301 | + |
| 302 | +} |
| 303 | +} |
| 304 | + |
| 305 | +#endif |
0 commit comments