openMSX
gl_vec.hh
Go to the documentation of this file.
1#ifndef GL_VEC_HH
2#define GL_VEC_HH
3
4// This code implements a mathematical vector, comparable in functionality
5// and syntax to the vector types in GLSL.
6//
7// Only vector sizes 2, 3 and 4 are supported. Though when it doesn't
8// complicate stuff the code was written to support any size.
9//
10// Most basic functionality is already there, but this is not meant to be a
11// full set of GLSL functions. We can always extend the functionality if/when
12// required.
13//
14// In the past we had (some) manual SSE optimizations in this code. Though for
15// the functions that matter (matrix-vector and matrix-matrix multiplication are
16// built on top of the functions in this file), the compiler's
17// auto-vectorization has become as good as the manually vectorized code.
18
19#include "Math.hh"
20#include "narrow.hh"
21#include "xrange.hh"
22#include <algorithm>
23#include <array>
24#include <cassert>
25#include <cmath>
26#include <iostream>
27#include <utility>
28
29namespace gl {
30
31// Vector with N components of type T.
32template<int N, typename T> class vecN
33{
34public:
35 // Default copy-constructor and assignment operator.
36
37 // Construct vector containing all zeros.
38 constexpr vecN()
39 {
40 for (auto i : xrange(N)) e[i] = T(0);
41 }
42
43 // Construct vector containing the same value repeated N times.
44 constexpr explicit vecN(T x)
45 {
46 for (auto i : xrange(N)) e[i] = x;
47 }
48
49 // Conversion constructor from vector of same size but different type
50 template<typename T2>
51 constexpr explicit vecN(const vecN<N, T2>& x)
52 {
53 for (auto i : xrange(N)) e[i] = T(x[i]);
54 }
55
56 // Construct from larger vector (higher order elements are dropped).
57 template<int N2> constexpr explicit vecN(const vecN<N2, T>& x)
58 {
59 static_assert(N2 > N, "wrong vector length in constructor");
60 for (auto i : xrange(N)) e[i] = x[i];
61 }
62
63 // Construct vector from 2 given values (only valid when N == 2).
64 constexpr vecN(T x, T y)
65 : e{x, y}
66 {
67 static_assert(N == 2, "wrong #constructor arguments");
68 }
69
70 // Construct vector from 3 given values (only valid when N == 3).
71 constexpr vecN(T x, T y, T z)
72 : e{x, y, z}
73 {
74 static_assert(N == 3, "wrong #constructor arguments");
75 }
76
77 // Construct vector from 4 given values (only valid when N == 4).
78 constexpr vecN(T x, T y, T z, T w)
79 : e{x, y, z, w}
80 {
81 static_assert(N == 4, "wrong #constructor arguments");
82 }
83
84 // Construct vector from concatenating a scalar and a (smaller) vector.
85 template<int N2>
86 constexpr vecN(T x, const vecN<N2, T>& y)
87 {
88 static_assert((1 + N2) == N, "wrong vector length in constructor");
89 e[0] = x;
90 for (auto i : xrange(N2)) e[i + 1] = y[i];
91 }
92
93 // Construct vector from concatenating a (smaller) vector and a scalar.
94 template<int N1>
95 constexpr vecN(const vecN<N1, T>& x, T y)
96 {
97 static_assert((N1 + 1) == N, "wrong vector length in constructor");
98 for (auto i : xrange(N1)) e[i] = x[i];
99 e[N1] = y;
100 }
101
102 // Construct vector from concatenating two (smaller) vectors.
103 template<int N1, int N2>
104 constexpr vecN(const vecN<N1, T>& x, const vecN<N2, T>& y)
105 {
106 static_assert((N1 + N2) == N, "wrong vector length in constructor");
107 for (auto i : xrange(N1)) e[i ] = x[i];
108 for (auto i : xrange(N2)) e[i + N1] = y[i];
109 }
110
111 // Access the i-th element of this vector.
112 [[nodiscard]] constexpr T operator[](unsigned i) const {
113 #ifdef DEBUG
114 assert(i < N);
115 #endif
116 return e[i];
117 }
118 [[nodiscard]] constexpr T& operator[](unsigned i) {
119 #ifdef DEBUG
120 assert(i < N);
121 #endif
122 return e[i];
123 }
124
125 // For structured bindings
126 template<size_t I> [[nodiscard]] constexpr T get() const noexcept { return (*this)[I]; }
127 template<size_t I> [[nodiscard]] constexpr T& get() noexcept { return (*this)[I]; }
128
129 // Assignment version of the +,-,* operations defined below.
130 constexpr vecN& operator+=(const vecN& x) { *this = *this + x; return *this; }
131 constexpr vecN& operator-=(const vecN& x) { *this = *this - x; return *this; }
132 constexpr vecN& operator*=(const vecN& x) { *this = *this * x; return *this; }
133 constexpr vecN& operator*=(T x) { *this = *this * x; return *this; }
134
135 // gcc-10 mis-compiles this (fixed in gcc-11):
136 // [[nodiscard]] constexpr bool operator==(const vecN&) const = default;
137 // For now still manually implement it.
138 [[nodiscard]] friend constexpr bool operator==(const vecN& x, const vecN& y)
139 {
140 for (auto i : xrange(N)) if (x[i] != y[i]) return false;
141 return true;
142 }
143
144private:
145 std::array<T, N> e;
146};
147
148
149// Convenience typedefs (same names as used by GLSL).
156
157
158// -- Scalar functions --
159
160// reciprocal square root
161[[nodiscard]] inline float rsqrt(float x)
162{
163 return 1.0f / sqrtf(x);
164}
165[[nodiscard]] inline double rsqrt(double x)
166{
167 return 1.0 / sqrt(x);
168}
169
170// convert radians <-> degrees
171template<typename T> [[nodiscard]] constexpr T radians(T d)
172{
173 return d * T(Math::pi / 180.0);
174}
175template<typename T> [[nodiscard]] constexpr T degrees(T r)
176{
177 return r * T(180.0 / Math::pi);
178}
179
180
181// -- Vector functions --
182
183// vector negation
184template<int N, typename T>
185[[nodiscard]] constexpr vecN<N, T> operator-(const vecN<N, T>& x)
186{
187 return vecN<N, T>() - x;
188}
189
190// vector + vector
191template<int N, typename T>
192[[nodiscard]] constexpr vecN<N, T> operator+(const vecN<N, T>& x, const vecN<N, T>& y)
193{
194 vecN<N, T> r;
195 for (auto i : xrange(N)) r[i] = x[i] + y[i];
196 return r;
197}
198
199// vector - vector
200template<int N, typename T>
201[[nodiscard]] constexpr vecN<N, T> operator-(const vecN<N, T>& x, const vecN<N, T>& y)
202{
203 vecN<N, T> r;
204 for (auto i : xrange(N)) r[i] = x[i] - y[i];
205 return r;
206}
207
208// scalar * vector
209template<int N, typename T>
210[[nodiscard]] constexpr vecN<N, T> operator*(T x, const vecN<N, T>& y)
211{
212 vecN<N, T> r;
213 for (auto i : xrange(N)) r[i] = x * y[i];
214 return r;
215}
216
217// vector * scalar
218template<int N, typename T>
219[[nodiscard]] constexpr vecN<N, T> operator*(const vecN<N, T>& x, T y)
220{
221 vecN<N, T> r;
222 for (auto i : xrange(N)) r[i] = x[i] * y;
223 return r;
224}
225
226// vector * vector
227template<int N, typename T>
228[[nodiscard]] constexpr vecN<N, T> operator*(const vecN<N, T>& x, const vecN<N, T>& y)
229{
230 vecN<N, T> r;
231 for (auto i : xrange(N)) r[i] = x[i] * y[i];
232 return r;
233}
234
235// element-wise reciprocal
236template<int N, typename T>
237[[nodiscard]] constexpr vecN<N, T> recip(const vecN<N, T>& x)
238{
239 vecN<N, T> r;
240 for (auto i : xrange(N)) r[i] = T(1) / x[i];
241 return r;
242}
243
244// scalar / vector
245template<int N, typename T>
246[[nodiscard]] constexpr vecN<N, T> operator/(T x, const vecN<N, T>& y)
247{
248 return x * recip(y);
249}
250
251// vector / scalar
252template<int N, typename T>
253[[nodiscard]] constexpr vecN<N, T> operator/(const vecN<N, T>& x, T y)
254{
255 return x * (T(1) / y);
256}
257
258// vector / vector
259template<int N, typename T>
260[[nodiscard]] constexpr vecN<N, T> operator/(const vecN<N, T>& x, const vecN<N, T>& y)
261{
262 return x * recip(y);
263}
264
265// min(vector, vector)
266template<int N, typename T>
267[[nodiscard]] constexpr vecN<N, T> min(const vecN<N, T>& x, const vecN<N, T>& y)
268{
269 vecN<N, T> r;
270 for (auto i : xrange(N)) r[i] = std::min(x[i], y[i]);
271 return r;
272}
273
274// min(vector, vector)
275template<int N, typename T>
276[[nodiscard]] constexpr T min_component(const vecN<N, T>& x)
277{
278 T r = x[0];
279 for (auto i : xrange(1, N)) r = std::min(r, x[i]);
280 return r;
281}
282
283// max(vector, vector)
284template<int N, typename T>
285[[nodiscard]] constexpr vecN<N, T> max(const vecN<N, T>& x, const vecN<N, T>& y)
286{
287 vecN<N, T> r;
288 for (auto i : xrange(N)) r[i] = std::max(x[i], y[i]);
289 return r;
290}
291
292// clamp(vector, vector, vector)
293template<int N, typename T>
294[[nodiscard]] constexpr vecN<N, T> clamp(const vecN<N, T>& x, const vecN<N, T>& minVal, const vecN<N, T>& maxVal)
295{
296 return min(maxVal, max(minVal, x));
297}
298
299// clamp(vector, scalar, scalar)
300template<int N, typename T>
301[[nodiscard]] constexpr vecN<N, T> clamp(const vecN<N, T>& x, T minVal, T maxVal)
302{
303 return clamp(x, vecN<N, T>(minVal), vecN<N, T>(maxVal));
304}
305
306// sum of components
307template<int N, typename T>
308[[nodiscard]] constexpr T sum(const vecN<N, T>& x)
309{
310 T result(0);
311 for (auto i : xrange(N)) result += x[i];
312 return result;
313}
314template<int N, typename T>
315[[nodiscard]] constexpr vecN<N, T> sum_broadcast(const vecN<N, T>& x)
316{
317 return vecN<N, T>(sum(x));
318}
319
320// dot product
321template<int N, typename T>
322[[nodiscard]] constexpr T dot(const vecN<N, T>& x, const vecN<N, T>& y)
323{
324 return sum(x * y);
325}
326template<int N, typename T>
327[[nodiscard]] constexpr vecN<N, T> dot_broadcast(const vecN<N, T>& x, const vecN<N, T>& y)
328{
329 return sum_broadcast(x * y);
330}
331
332// squared length (norm-2)
333template<int N, typename T>
334[[nodiscard]] constexpr T length2(const vecN<N, T>& x)
335{
336 return dot(x, x);
337}
338
339// length (norm-2)
340template<int N, typename T>
341[[nodiscard]] inline T length(const vecN<N, T>& x)
342{
343 return std::sqrt(length2(x));
344}
345
346// normalize vector
347template<int N, typename T>
348[[nodiscard]] inline vecN<N, T> normalize(const vecN<N, T>& x)
349{
350 return x * rsqrt(length2(x));
351}
352
353// cross product (only defined for vectors of length 3)
354template<typename T>
355[[nodiscard]] constexpr vecN<3, T> cross(const vecN<3, T>& x, const vecN<3, T>& y)
356{
357 return vecN<3, T>(x[1] * y[2] - x[2] * y[1],
358 x[2] * y[0] - x[0] * y[2],
359 x[0] * y[1] - x[1] * y[0]);
360}
361
362// round each component to the nearest integer (returns a vector of integers)
363template<int N, typename T>
364[[nodiscard]] inline vecN<N, int> round(const vecN<N, T>& x)
365{
366 vecN<N, int> r;
367 // note: std::lrint() is more generic (e.g. also works with double),
368 // but Dingux doesn't seem to have std::lrint().
369 for (auto i : xrange(N)) r[i] = narrow_cast<int>(lrintf(narrow_cast<float>(x[i])));
370 return r;
371}
372
373// truncate each component to the nearest integer that is not bigger in
374// absolute value (returns a vector of integers)
375template<int N, typename T>
376[[nodiscard]] constexpr vecN<N, int> trunc(const vecN<N, T>& x)
377{
378 vecN<N, int> r;
379 for (auto i : xrange(N)) r[i] = int(x[i]);
380 return r;
381}
382
383// Textual representation. (Only) used to debug unittest.
384template<int N, typename T>
385std::ostream& operator<<(std::ostream& os, const vecN<N, T>& x)
386{
387 os << "[ ";
388 for (auto i : xrange(N)) {
389 os << x[i] << ' ';
390 }
391 os << ']';
392 return os;
393}
394
395} // namespace gl
396
397// Support for structured bindings
398namespace std {
399// On some platforms tuple_size is a class and on others it is a struct.
400// Such a mismatch is only a problem when targeting the Microsoft C++ ABI,
401// which we don't do when compiling with Clang.
402#if defined(__clang__)
403#pragma clang diagnostic push
404#pragma clang diagnostic ignored "-Wmismatched-tags"
405#endif
406 template<int N, typename T> class tuple_size<gl::vecN<N, T>>
407 : public std::integral_constant<size_t, N> {};
408#if defined(__clang__)
409#pragma clang diagnostic pop
410#endif
411 template<size_t I, int N, typename T> class tuple_element<I, gl::vecN<N, T>> {
412 public:
413 using type = T;
414 };
415}
416
417#endif // GL_VEC_HH
constexpr T operator[](unsigned i) const
Definition: gl_vec.hh:112
constexpr vecN & operator*=(const vecN &x)
Definition: gl_vec.hh:132
constexpr vecN(T x, T y)
Definition: gl_vec.hh:64
constexpr T get() const noexcept
Definition: gl_vec.hh:126
constexpr vecN & operator*=(T x)
Definition: gl_vec.hh:133
constexpr vecN(T x, T y, T z, T w)
Definition: gl_vec.hh:78
friend constexpr bool operator==(const vecN &x, const vecN &y)
Definition: gl_vec.hh:138
constexpr vecN(const vecN< N1, T > &x, const vecN< N2, T > &y)
Definition: gl_vec.hh:104
constexpr T & get() noexcept
Definition: gl_vec.hh:127
constexpr vecN(const vecN< N1, T > &x, T y)
Definition: gl_vec.hh:95
constexpr vecN(const vecN< N2, T > &x)
Definition: gl_vec.hh:57
constexpr T & operator[](unsigned i)
Definition: gl_vec.hh:118
constexpr vecN(T x, const vecN< N2, T > &y)
Definition: gl_vec.hh:86
constexpr vecN(const vecN< N, T2 > &x)
Definition: gl_vec.hh:51
constexpr vecN(T x)
Definition: gl_vec.hh:44
constexpr vecN & operator-=(const vecN &x)
Definition: gl_vec.hh:131
constexpr vecN & operator+=(const vecN &x)
Definition: gl_vec.hh:130
constexpr vecN()
Definition: gl_vec.hh:38
constexpr vecN(T x, T y, T z)
Definition: gl_vec.hh:71
constexpr double pi
Definition: Math.hh:24
constexpr double sqrt(double x)
Definition: cstd.hh:261
Definition: gl_mat.hh:23
constexpr vecN< N, T > recip(const vecN< N, T > &x)
Definition: gl_vec.hh:237
constexpr vecN< 3, T > cross(const vecN< 3, T > &x, const vecN< 3, T > &y)
Definition: gl_vec.hh:355
constexpr T min_component(const vecN< N, T > &x)
Definition: gl_vec.hh:276
constexpr T dot(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:322
constexpr vecN< N, int > trunc(const vecN< N, T > &x)
Definition: gl_vec.hh:376
constexpr matMxN< M, N, T > operator*(T x, const matMxN< M, N, T > &A)
Definition: gl_mat.hh:155
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:341
constexpr vecN< N, T > operator/(T x, const vecN< N, T > &y)
Definition: gl_vec.hh:246
vecN< N, int > round(const vecN< N, T > &x)
Definition: gl_vec.hh:364
constexpr T length2(const vecN< N, T > &x)
Definition: gl_vec.hh:334
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:267
constexpr vecN< N, T > dot_broadcast(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:327
constexpr T degrees(T r)
Definition: gl_vec.hh:175
constexpr vecN< N, T > sum_broadcast(const vecN< N, T > &x)
Definition: gl_vec.hh:315
constexpr T sum(const vecN< N, T > &x)
Definition: gl_vec.hh:308
std::ostream & operator<<(std::ostream &os, const matMxN< M, N, T > &A)
Definition: gl_mat.hh:330
constexpr T radians(T d)
Definition: gl_vec.hh:171
constexpr matMxN< M, N, T > operator-(const matMxN< M, N, T > &A, const matMxN< M, N, T > &B)
Definition: gl_mat.hh:139
vecN< N, T > normalize(const vecN< N, T > &x)
Definition: gl_vec.hh:348
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:285
float rsqrt(float x)
Definition: gl_vec.hh:161
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
Definition: gl_vec.hh:294
constexpr matMxN< M, N, T > operator+(const matMxN< M, N, T > &A, const matMxN< M, N, T > &B)
Definition: gl_mat.hh:130
STL namespace.
constexpr auto xrange(T e)
Definition: xrange.hh:132