OXYGENHUB
◆ main openmsx / src / video / gl_vec.hh
16.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 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 "unreachable.hh"
22
#include "xrange.hh"
23
#include <algorithm>
24
#include <array>
25
#include <cmath>
26
#include <iostream>
27
#include <utility>
28
29
namespace gl {
30
31
// Vector with N components of type T.
32
template<int N, typename T> class vecN;
33
34
// Specialization for N=2.
35
template<typename T> class vecN<2, T>
36
{
37
public:
38
	// Default copy-constructor and assignment operator.
39
40
	// Construct vector containing all zeros.
41
	constexpr vecN() : x(T(0)), y(T(0)) {}
42
43
	// Construct vector containing the same value repeated N times.
44
	constexpr explicit vecN(T t) : x(t), y(t) {}
45
46
	// Conversion constructor from vector of same size but different type.
47
	template<typename T2>
48
	constexpr explicit vecN(const vecN<2, T2>& v) : x(T(v.x)), y(T(v.y)) {}
49
50
	// Construct from larger vector (higher order elements are dropped).
51
	template<int N2> constexpr explicit vecN(const vecN<N2, T>& v) : x(v.x), y(v.y) {}
52
53
	// Construct vector from 2 given values.
54
	constexpr vecN(T a, T b) : x(a), y(b) {}
55
56
	// Access the i-th element of this vector.
57
	[[nodiscard]] constexpr T  operator[](unsigned i) const {
58
		if (i == 0) return x;
59
		if (i == 1) return y;
60
		UNREACHABLE;
61
	}
62
	[[nodiscard]] constexpr T& operator[](unsigned i) {
63
		if (i == 0) return x;
64
		if (i == 1) return y;
65
		UNREACHABLE;
66
	}
67
68
	[[nodiscard]] constexpr const T* data() const { return &x; }
69
	[[nodiscard]] constexpr       T* data()       { return &x; }
70
71
	// For structured bindings
72
	template<size_t I> [[nodiscard]] constexpr T  get() const noexcept { return (*this)[I]; }
73
	template<size_t I> [[nodiscard]] constexpr T& get()       noexcept { return (*this)[I]; }
74
75
	// Assignment version of the +,-,* operations defined below.
76
	constexpr vecN& operator+=(const vecN& v) { *this = *this + v; return *this; }
77
	constexpr vecN& operator-=(const vecN& v) { *this = *this - v; return *this; }
78
	constexpr vecN& operator*=(const vecN& v) { *this = *this * v; return *this; }
79
	constexpr vecN& operator*=(T           t) { *this = *this * t; return *this; }
80
81
	[[nodiscard]] constexpr bool operator==(const vecN&) const = default;
82
83
	// vector + vector
84
	[[nodiscard]] constexpr friend vecN operator+(const vecN& v1, const vecN& v2) {
85
		vecN r;
86
		for (auto i : xrange(2)) r[i] = v1[i] + v2[i];
87
		return r;
88
	}
89
90
	// vector - vector
91
	[[nodiscard]] constexpr friend vecN operator-(const vecN& v1, const vecN& v2) {
92
		vecN r;
93
		for (auto i : xrange(2)) r[i] = v1[i] - v2[i];
94
		return r;
95
	}
96
97
	// scalar * vector
98
	[[nodiscard]] constexpr friend vecN operator*(T a, const vecN& v) {
99
		vecN r;
100
		for (auto i : xrange(2)) r[i] = a * v[i];
101
		return r;
102
	}
103
104
	// vector * scalar
105
	[[nodiscard]] constexpr friend vecN operator*(const vecN& v, T a) {
106
		vecN r;
107
		for (auto i : xrange(2)) r[i] = v[i] * a;
108
		return r;
109
	}
110
111
	// vector * vector
112
	[[nodiscard]] constexpr friend vecN operator*(const vecN& v1, const vecN& v2)
113
	{
114
		vecN r;
115
		for (auto i : xrange(2)) r[i] = v1[i] * v2[i];
116
		return r;
117
	}
118
119
	// element-wise reciprocal
120
	[[nodiscard]] constexpr friend vecN recip(const vecN& v) {
121
		vecN r;
122
		for (auto i : xrange(2)) r[i] = T(1) / v[i];
123
		return r;
124
	}
125
126
	// scalar / vector
127
	[[nodiscard]] constexpr friend vecN operator/(T a, const vecN& v) {
128
		if constexpr (std::is_floating_point_v<T>) {
129
			return a * recip(v);
130
		} else {
131
			vecN r;
132
			for (auto i : xrange(2)) r[i] = a / v[i];
133
			return r;
134
		}
135
	}
136
137
	// vector / scalar
138
	[[nodiscard]] constexpr friend vecN operator/(const vecN& v, T a) {
139
		if constexpr (std::is_floating_point_v<T>) {
140
			return v * (T(1) / a);
141
		} else {
142
			vecN r;
143
			for (auto i : xrange(2)) r[i] = v[i] / a;
144
			return r;
145
		}
146
	}
147
148
	// vector / vector
149
	[[nodiscard]] constexpr friend vecN operator/(const vecN& v1, const vecN& v2) {
150
		if constexpr (std::is_floating_point_v<T>) {
151
			return v1 * recip(v2);
152
		} else {
153
			vecN r;
154
			for (auto i : xrange(2)) r[i] = v1[i] / v2[i];
155
			return r;
156
		}
157
	}
158
159
	// Textual representation. (Only) used to debug unittest.
160
	friend std::ostream& operator<<(std::ostream& os, const vecN& v) {
161
		os << "[ ";
162
		for (auto i : xrange(2)) {
163
			os << v[i] << ' ';
164
		}
165
		os << ']';
166
		return os;
167
	}
168
169
public:
170
	T x, y;
171
};
172
173
// Specialization for N=3.
174
template<typename T> class vecN<3, T>
175
{
176
public:
177
	constexpr vecN() : x(T(0)), y(T(0)), z(T(0)) {}
178
	constexpr explicit vecN(T t) : x(t), y(t), z(t) {}
179
	template<typename T2>
180
	constexpr explicit vecN(const vecN<3, T2>& v) : x(T(v.x)), y(T(v.y)), z(T(v.z)) {}
181
	constexpr explicit vecN(const vecN<4, T>& v) : x(v.x), y(v.y), z(v.z) {}
182
	constexpr vecN(T a, T b, T c) : x(a), y(b), z(c) {}
183
	constexpr vecN(T a, const vecN<2, T>& b) : x(a), y(b.x), z(b.y) {}
184
	constexpr vecN(const vecN<2, T>& a, T b) : x(a.x), y(a.y), z(b) {}
185
186
	[[nodiscard]] constexpr T  operator[](unsigned i) const {
187
		if (i == 0) return x;
188
		if (i == 1) return y;
189
		if (i == 2) return z;
190
		UNREACHABLE;
191
	}
192
	[[nodiscard]] constexpr T& operator[](unsigned i) {
193
		if (i == 0) return x;
194
		if (i == 1) return y;
195
		if (i == 2) return z;
196
		UNREACHABLE;
197
	}
198
199
	[[nodiscard]] constexpr const T* data() const { return &x; }
200
	[[nodiscard]] constexpr       T* data()       { return &x; }
201
202
	template<size_t I> [[nodiscard]] constexpr T  get() const noexcept { return (*this)[I]; }
203
	template<size_t I> [[nodiscard]] constexpr T& get()       noexcept { return (*this)[I]; }
204
205
	constexpr vecN& operator+=(const vecN& v) { *this = *this + v; return *this; }
206
	constexpr vecN& operator-=(const vecN& v) { *this = *this - v; return *this; }
207
	constexpr vecN& operator*=(const vecN& v) { *this = *this * v; return *this; }
208
	constexpr vecN& operator*=(T           t) { *this = *this * t; return *this; }
209
210
	[[nodiscard]] constexpr bool operator==(const vecN&) const = default;
211
212
	// vector + vector
213
	[[nodiscard]] constexpr friend vecN operator+(const vecN& v1, const vecN& v2) {
214
		vecN r;
215
		for (auto i : xrange(3)) r[i] = v1[i] + v2[i];
216
		return r;
217
	}
218
219
	// vector - vector
220
	[[nodiscard]] constexpr friend vecN operator-(const vecN& v1, const vecN& v2) {
221
		vecN r;
222
		for (auto i : xrange(3)) r[i] = v1[i] - v2[i];
223
		return r;
224
	}
225
226
	// scalar * vector
227
	[[nodiscard]] constexpr friend vecN operator*(T a, const vecN& v) {
228
		vecN r;
229
		for (auto i : xrange(3)) r[i] = a * v[i];
230
		return r;
231
	}
232
233
	// vector * scalar
234
	[[nodiscard]] constexpr friend vecN operator*(const vecN& v, T a) {
235
		vecN r;
236
		for (auto i : xrange(3)) r[i] = v[i] * a;
237
		return r;
238
	}
239
240
	// vector * vector
241
	[[nodiscard]] constexpr friend vecN operator*(const vecN& v1, const vecN& v2)
242
	{
243
		vecN r;
244
		for (auto i : xrange(3)) r[i] = v1[i] * v2[i];
245
		return r;
246
	}
247
248
	// element-wise reciprocal
249
	[[nodiscard]] constexpr friend vecN recip(const vecN& v) {
250
		vecN r;
251
		for (auto i : xrange(3)) r[i] = T(1) / v[i];
252
		return r;
253
	}
254
255
	// scalar / vector
256
	[[nodiscard]] constexpr friend vecN operator/(T a, const vecN& v) {
257
		return a * recip(v);
258
	}
259
260
	// vector / scalar
261
	[[nodiscard]] constexpr friend vecN operator/(const vecN& v, T a) {
262
		return v * (T(1) / a);
263
	}
264
265
	// vector / vector
266
	[[nodiscard]] constexpr friend vecN operator/(const vecN& v1, const vecN& v2) {
267
		return v1 * recip(v2);
268
	}
269
270
	// Textual representation. (Only) used to debug unittest.
271
	friend std::ostream& operator<<(std::ostream& os, const vecN& v) {
272
		os << "[ ";
273
		for (auto i : xrange(3)) {
274
			os << v[i] << ' ';
275
		}
276
		os << ']';
277
		return os;
278
	}
279
280
public:
281
	T x, y, z;
282
};
283
284
// Specialization for N=4.
285
template<typename T> class vecN<4, T>
286
{
287
public:
288
	constexpr vecN() : x(T(0)), y(T(0)), z(T(0)), w(T(0)) {}
289
	constexpr explicit vecN(T t) : x(t), y(t), z(t), w(t) {}
290
	template<typename T2>
291
	constexpr explicit vecN(const vecN<4, T2>& v) : x(T(v.x)), y(T(v.y)), z(T(v.z)), w(T(v.w)) {}
292
	constexpr vecN(T a, T b, T c, T d) : x(a), y(b), z(c), w(d) {}
293
	constexpr vecN(T a, const vecN<3, T>& b) : x(a), y(b.x), z(b.y), w(b.z) {}
294
	constexpr vecN(const vecN<3, T>& a, T b) : x(a.x), y(a.y), z(a.z), w(b) {}
295
	constexpr vecN(const vecN<2, T>& a, const vecN<2, T>& b) : x(a.x), y(a.y), z(b.x), w(b.y) {}
296
297
	[[nodiscard]] constexpr T  operator[](unsigned i) const {
298
		if (i == 0) return x;
299
		if (i == 1) return y;
300
		if (i == 2) return z;
301
		if (i == 3) return w;
302
		UNREACHABLE;
303
	}
304
	[[nodiscard]] constexpr T& operator[](unsigned i) {
305
		if (i == 0) return x;
306
		if (i == 1) return y;
307
		if (i == 2) return z;
308
		if (i == 3) return w;
309
		UNREACHABLE;
310
	}
311
312
	[[nodiscard]] constexpr const T* data() const { return &x; }
313
	[[nodiscard]] constexpr       T* data()       { return &x; }
314
315
	template<size_t I> [[nodiscard]] constexpr T  get() const noexcept { return (*this)[I]; }
316
	template<size_t I> [[nodiscard]] constexpr T& get()       noexcept { return (*this)[I]; }
317
318
	// Assignment version of the +,-,* operations defined below.
319
	constexpr vecN& operator+=(const vecN& v) { *this = *this + v; return *this; }
320
	constexpr vecN& operator-=(const vecN& v) { *this = *this - v; return *this; }
321
	constexpr vecN& operator*=(const vecN& v) { *this = *this * v; return *this; }
322
	constexpr vecN& operator*=(T           t) { *this = *this * t; return *this; }
323
324
	[[nodiscard]] constexpr bool operator==(const vecN&) const = default;
325
326
	// vector + vector
327
	[[nodiscard]] constexpr friend vecN operator+(const vecN& v1, const vecN& v2) {
328
		vecN r;
329
		for (auto i : xrange(4)) r[i] = v1[i] + v2[i];
330
		return r;
331
	}
332
333
	// vector - vector
334
	[[nodiscard]] constexpr friend vecN operator-(const vecN& v1, const vecN& v2) {
335
		vecN r;
336
		for (auto i : xrange(4)) r[i] = v1[i] - v2[i];
337
		return r;
338
	}
339
340
	// scalar * vector
341
	[[nodiscard]] constexpr friend vecN operator*(T a, const vecN& v) {
342
		vecN r;
343
		for (auto i : xrange(4)) r[i] = a * v[i];
344
		return r;
345
	}
346
347
	// vector * scalar
348
	[[nodiscard]] constexpr friend vecN operator*(const vecN& v, T a) {
349
		vecN r;
350
		for (auto i : xrange(4)) r[i] = v[i] * a;
351
		return r;
352
	}
353
354
	// vector * vector
355
	[[nodiscard]] constexpr friend vecN operator*(const vecN& v1, const vecN& v2)
356
	{
357
		vecN r;
358
		for (auto i : xrange(4)) r[i] = v1[i] * v2[i];
359
		return r;
360
	}
361
362
	// element-wise reciprocal
363
	[[nodiscard]] constexpr friend vecN recip(const vecN& v) {
364
		vecN r;
365
		for (auto i : xrange(4)) r[i] = T(1) / v[i];
366
		return r;
367
	}
368
369
	// scalar / vector
370
	[[nodiscard]] constexpr friend vecN operator/(T a, const vecN& v) {
371
		return a * recip(v);
372
	}
373
374
	// vector / scalar
375
	[[nodiscard]] constexpr friend vecN operator/(const vecN& v, T a) {
376
		return v * (T(1) / a);
377
	}
378
379
	// vector / vector
380
	[[nodiscard]] constexpr friend vecN operator/(const vecN& v1, const vecN& v2) {
381
		return v1 * recip(v2);
382
	}
383
384
	// Textual representation. (Only) used to debug unittest.
385
	friend std::ostream& operator<<(std::ostream& os, const vecN& v) {
386
		os << "[ ";
387
		for (auto i : xrange(4)) {
388
			os << v[i] << ' ';
389
		}
390
		os << ']';
391
		return os;
392
	}
393
394
public:
395
	T x, y, z, w;
396
};
397
398
399
// Convenience typedefs (same names as used by GLSL).
400
using  vec2 = vecN<2, float>;
401
using  vec3 = vecN<3, float>;
402
using  vec4 = vecN<4, float>;
403
using ivec2 = vecN<2, int>;
404
using ivec3 = vecN<3, int>;
405
using ivec4 = vecN<4, int>;
406
407
static_assert(sizeof( vec2) == 2 * sizeof(float));
408
static_assert(sizeof( vec3) == 3 * sizeof(float));
409
static_assert(sizeof( vec4) == 4 * sizeof(float));
410
static_assert(sizeof(ivec2) == 2 * sizeof(int));
411
static_assert(sizeof(ivec3) == 3 * sizeof(int));
412
static_assert(sizeof(ivec4) == 4 * sizeof(int));
413
414
415
// -- Scalar functions --
416
417
// reciprocal square root
418
[[nodiscard]] inline float rsqrt(float x)
419
{
420
	return 1.0f / sqrtf(x);
421
}
422
[[nodiscard]] inline double rsqrt(double x)
423
{
424
	return 1.0 / sqrt(x);
425
}
426
427
// convert radians <-> degrees
428
template<typename T> [[nodiscard]] constexpr T radians(T d)
429
{
430
	return d * T(Math::pi / 180.0);
431
}
432
template<typename T> [[nodiscard]] constexpr T degrees(T r)
433
{
434
	return r * T(180.0 / Math::pi);
435
}
436
437
438
// -- Vector functions --
439
440
// vector negation
441
template<int N, typename T>
442
[[nodiscard]] constexpr vecN<N, T> operator-(const vecN<N, T>& x)
443
{
444
	return vecN<N, T>() - x;
445
}
446
447
// min(vector, vector)
448
template<int N, typename T>
449
[[nodiscard]] constexpr vecN<N, T> min(const vecN<N, T>& x, const vecN<N, T>& y)
450
{
451
	vecN<N, T> r;
452
	for (auto i : xrange(N)) r[i] = std::min(x[i], y[i]);
453
	return r;
454
}
455
456
// max(vector, vector)
457
template<int N, typename T>
458
[[nodiscard]] constexpr vecN<N, T> max(const vecN<N, T>& x, const vecN<N, T>& y)
459
{
460
	vecN<N, T> r;
461
	for (auto i : xrange(N)) r[i] = std::max(x[i], y[i]);
462
	return r;
463
}
464
465
// min(vector.x, vector.y, ...)
466
template<int N, typename T>
467
[[nodiscard]] constexpr T min_component(const vecN<N, T>& x)
468
{
469
	T r = x[0];
470
	for (auto i : xrange(1, N)) r = std::min(r, x[i]);
471
	return r;
472
}
473
474
// max(vector.x, vector.y, ...)
475
template<int N, typename T>
476
[[nodiscard]] constexpr T max_component(const vecN<N, T>& x)
477
{
478
	T r = x[0];
479
	for (auto i : xrange(1, N)) r = std::max(r, x[i]);
480
	return r;
481
}
482
483
// clamp(vector, vector, vector)
484
template<int N, typename T>
485
[[nodiscard]] constexpr vecN<N, T> clamp(const vecN<N, T>& x, const vecN<N, T>& minVal, const vecN<N, T>& maxVal)
486
{
487
	return min(maxVal, max(minVal, x));
488
}
489
490
// clamp(vector, scalar, scalar)
491
template<int N, typename T>
492
[[nodiscard]] constexpr vecN<N, T> clamp(const vecN<N, T>& x, T minVal, T maxVal)
493
{
494
	return clamp(x, vecN<N, T>(minVal), vecN<N, T>(maxVal));
495
}
496
497
// sum of components
498
template<int N, typename T>
499
[[nodiscard]] constexpr T sum(const vecN<N, T>& x)
500
{
501
	T result(0);
502
	for (auto i : xrange(N)) result += x[i];
503
	return result;
504
}
505
template<int N, typename T>
506
[[nodiscard]] constexpr vecN<N, T> sum_broadcast(const vecN<N, T>& x)
507
{
508
	return vecN<N, T>(sum(x));
509
}
510
511
// dot product
512
template<int N, typename T>
513
[[nodiscard]] constexpr T dot(const vecN<N, T>& x, const vecN<N, T>& y)
514
{
515
	return sum(x * y);
516
}
517
template<int N, typename T>
518
[[nodiscard]] constexpr vecN<N, T> dot_broadcast(const vecN<N, T>& x, const vecN<N, T>& y)
519
{
520
	return sum_broadcast(x * y);
521
}
522
523
// squared length (norm-2)
524
template<int N, typename T>
525
[[nodiscard]] constexpr T length2(const vecN<N, T>& x)
526
{
527
	return dot(x, x);
528
}
529
530
// length (norm-2)
531
template<int N, typename T>
532
[[nodiscard]] inline T length(const vecN<N, T>& x)
533
{
534
	return std::sqrt(length2(x));
535
}
536
537
// normalize vector
538
template<int N, typename T>
539
[[nodiscard]] inline vecN<N, T> normalize(const vecN<N, T>& x)
540
{
541
	return x * gl::rsqrt(length2(x));
542
}
543
544
// cross product (only defined for vectors of length 3)
545
template<typename T>
546
[[nodiscard]] constexpr vecN<3, T> cross(const vecN<3, T>& a, const vecN<3, T>& b)
547
{
548
	return vecN<3, T>(a.y * b.z - a.z * b.y,
549
	                  a.z * b.x - a.x * b.z,
550
	                  a.x * b.y - a.y * b.x);
551
}
552
553
// round each component to the nearest integer (returns a vector of integers)
554
template<int N, typename T>
555
[[nodiscard]] inline vecN<N, int> round(const vecN<N, T>& x)
556
{
557
	vecN<N, int> r;
558
	for (auto i : xrange(N)) r[i] = narrow_cast<int>(std::lrint(x[i]));
559
	return r;
560
}
561
562
// truncate each component to the nearest integer that is not bigger in
563
// absolute value (returns a vector of integers)
564
template<int N, typename T>
565
[[nodiscard]] constexpr vecN<N, int> trunc(const vecN<N, T>& x)
566
{
567
	vecN<N, int> r;
568
	for (auto i : xrange(N)) r[i] = int(x[i]);
569
	return r;
570
}
571
// Similar, but floor.
572
template<int N, typename T>
573
[[nodiscard]] inline vecN<N, int> floor(const vecN<N, T>& x)
574
{
575
	vecN<N, int> r;
576
	for (auto i : xrange(N)) r[i] = int(std::floor(x[i]));
577
	return r;
578
}
579
// Similar, but ceil.
580
template<int N, typename T>
581
[[nodiscard]] inline vecN<N, int> ceil(const vecN<N, T>& x)
582
{
583
	vecN<N, int> r;
584
	for (auto i : xrange(N)) r[i] = int(std::ceil(x[i]));
585
	return r;
586
}
587
588
} // namespace gl
589
590
// Support for structured bindings
591
namespace std {
592
// On some platforms tuple_size is a class and on others it is a struct.
593
// Such a mismatch is only a problem when targeting the Microsoft C++ ABI,
594
// which we don't do when compiling with Clang.
595
#ifdef __clang__
596
#pragma clang diagnostic push
597
#pragma clang diagnostic ignored "-Wmismatched-tags"
598
#endif
599
	template<int N, typename T> class tuple_size<gl::vecN<N, T>>
600
		: public std::integral_constant<size_t, N> {};
601
#ifdef __clang__
602
#pragma clang diagnostic pop
603
#endif
604
	template<size_t I, int N, typename T> class tuple_element<I, gl::vecN<N, T>> {
605
	public:
606
		using type = T;
607
	};
608
}
609
610
#endif // GL_VEC_HH