Skip to content

Commit 781df86

Browse files
committed
split out new quaternion hlsl stuff over from hlsl path tracer example
1 parent 407051c commit 781df86

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
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

src/nbl/builtin/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/geometry.hlsl")
225225
LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/intutil.hlsl")
226226
LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/polar.hlsl")
227227
LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/angle_adding.hlsl")
228+
LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/quaternions.hlsl")
228229
LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/quadratic.hlsl")
229230
LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/cubic.hlsl")
230231
LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/quartic.hlsl")

0 commit comments

Comments
 (0)