1 /*
2  * Portions of this file are copyright Rebirth contributors and licensed as
3  * described in COPYING.txt.
4  * Portions of this file are copyright Parallax Software and licensed
5  * according to the Parallax license below.
6  * See COPYING.txt for license details.
7 
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18 */
19 
20 /*
21  *
22  * Header file for vector/matrix library
23  *
24  */
25 
26 #pragma once
27 
28 #ifdef __cplusplus
29 #include <cassert>
30 #include <cstdint>
31 #include <utility>
32 #include "fwd-vecmat.h"
33 
34 namespace dcx {
35 
36 //The basic fixed-point vector.  Access elements by name or position
37 struct vms_vector
38 {
39 	fix x, y, z;
40 };
41 
42 class vm_distance
43 {
44 public:
45 	fix d = 0;
46 	/* Default constructor only required because Fcd_cache,SoundObjects
47 	 * have global scope instances of vm_distance.  They should be
48 	 * converted to construct as needed, then the default constructor
49 	 * should be removed.
50 	 */
51 	constexpr vm_distance() = default;
vm_distance(const fix & f)52 	constexpr explicit vm_distance(const fix &f) :
53 		d(f)
54 	{
55 	}
56 	template <typename T>
57 		vm_distance &operator+=(const T &rhs)
58 		{
59 			return *this = (*this + rhs);
60 		}
61 	template <typename T>
62 		vm_distance &operator*=(const T &rhs)
63 		{
64 			return *this = (*this * rhs);
65 		}
66 	template <typename T>
67 		vm_distance &operator/=(const T &rhs)
68 		{
69 			return *this = (*this / rhs);
70 		}
71 	constexpr vm_distance operator+(const vm_distance &rhs) const
72 	{
73 		return vm_distance{d + rhs.d};
74 	}
75 	constexpr vm_distance operator*(const int &f) const
76 	{
77 		return vm_distance{d * f};
78 	}
79 	constexpr vm_distance operator/(const int &f) const
80 	{
81 		return vm_distance{d / f};
82 	}
83 	constexpr bool operator<(const fix &f) const
84 	{
85 		return d < f;
86 	}
87 	constexpr bool operator<(const vm_distance &rhs) const
88 	{
89 		return d < rhs.d;
90 	}
91 	/* Clang chokes using a template operator> to swap the arguments.
92 	 * Use a non-template operator> for the one needed type.
93 	 */
94 	constexpr bool operator>(const fix &f) const
95 	{
96 		return d > f;
97 	}
98 	template <typename T>
99 		bool operator>(const T &t) const = delete;
100 	constexpr explicit operator bool() const { return d; }
101 #ifndef __clang__
102 	/* When this catch-all overload is defined, clang becomes confused
103 	 * and treats all arithmetic operations as ambiguous because it
104 	 * cannot choose between the deleted template function and the
105 	 * non-deleted operator fix().
106 	 */
107 	template <typename T>
108 		operator T() const = delete;
109 #endif
fix()110 	constexpr operator fix() const
111 	{
112 		return d;
113 	}
114 	constexpr vm_distance_squared operator*(const vm_distance &) const;
115 };
116 
117 class vm_magnitude : public vm_distance
118 {
119 public:
vm_magnitude(const uint32_t & f)120 	constexpr explicit vm_magnitude(const uint32_t &f) :
121 		vm_distance(f)
122 	{
123 	}
124 };
125 
126 class vm_distance_squared
127 {
128 public:
129 	fix64 d2;
130 	vm_distance_squared(const fix &) = delete;
vm_distance_squared(const fix64 & f2)131 	constexpr explicit vm_distance_squared(const fix64 &f2) :
132 		d2(f2)
133 	{
134 	}
135 	constexpr bool operator<(const vm_distance_squared &rhs) const
136 	{
137 		return d2 < rhs.d2;
138 	}
139 	constexpr bool operator>(const vm_distance_squared &rhs) const
140 	{
141 		return d2 > rhs.d2;
142 	}
143 	constexpr bool operator>=(const vm_distance_squared &rhs) const
144 	{
145 		return !(*this < rhs);
146 	}
147 	template <typename T>
148 		vm_distance_squared &operator-=(const T &rhs)
149 		{
150 			return *this = (*this - rhs);
151 		}
152 	constexpr vm_distance_squared operator-(const fix &) const = delete;
153 	constexpr vm_distance_squared operator-(const fix64 &f2) const
154 	{
155 		return vm_distance_squared{d2 - f2};
156 	}
157 	explicit operator bool() const { return d2; }
158 	template <typename T>
159 		constexpr operator T() const = delete;
fix64()160 	constexpr operator fix64() const
161 	{
162 		return d2;
163 	}
maximum_value()164 	static constexpr vm_distance_squared maximum_value()
165 	{
166 		return vm_distance_squared{INT64_MAX};
167 	}
minimum_value()168 	static constexpr vm_distance_squared minimum_value()
169 	{
170 		return vm_distance_squared{static_cast<fix64>(0)};
171 	}
172 };
173 
174 class vm_magnitude_squared : public vm_distance_squared
175 {
176 public:
vm_magnitude_squared(const uint64_t & f2)177 	constexpr explicit vm_magnitude_squared(const uint64_t &f2) :
178 		vm_distance_squared(static_cast<fix64>(f2))
179 	{
180 	}
181 };
182 
183 constexpr vm_distance_squared vm_distance::operator*(const vm_distance &rhs) const
184 {
185 	return vm_distance_squared{static_cast<fix64>(static_cast<fix>(*this)) * static_cast<fix64>(static_cast<fix>(rhs))};
186 }
187 
188 #define DEFINE_SERIAL_VMS_VECTOR_TO_MESSAGE()	\
189 	DEFINE_SERIAL_UDT_TO_MESSAGE(vms_vector, v, (v.x, v.y, v.z));	\
190 	ASSERT_SERIAL_UDT_MESSAGE_SIZE(vms_vector, 12)
191 
192 //Angle vector.  Used to store orientations
193 struct vms_angvec
194 {
195 	fixang p, b, h;
196 };
197 
198 
199 //A 3x3 rotation matrix.  Sorry about the numbering starting with one.
200 //Ordering is across then down, so <m1,m2,m3> is the first row
201 struct vms_matrix
202 {
203 	vms_vector rvec, uvec, fvec;
204 };
205 
206 // Quaternion structure
207 struct vms_quaternion
208 {
209     signed short w, x, y, z;
210 };
211 
212 
213 //Macros/functions to fill in fields of structures
214 
215 //macro to set a vector to zero.  we could do this with an in-line assembly
216 //macro, but it's probably better to let the compiler optimize it.
217 //Note: NO RETURN VALUE
vm_vec_zero(vms_vector & v)218 static inline void vm_vec_zero(vms_vector &v)
219 {
220 	v = {};
221 }
222 
223 //macro set set a matrix to the identity. Note: NO RETURN VALUE
224 
225 // DPH (18/9/98): Begin mod to fix linefeed problem under linux. Uses an
226 // inline function instead of a multi-line macro to fix CR/LF problems.
227 
228 // DPH (19/8/98): End changes.
229 
230 //Global constants
231 
232 //Here's a handy constant
233 
234 //negate a vector
vm_vec_negate(vms_vector & v)235 static inline void vm_vec_negate(vms_vector &v)
236 {
237 	v.x = -v.x;
238 	v.y = -v.y;
239 	v.z = -v.z;
240 }
241 
242 [[nodiscard]]
vm_vec_negated(vms_vector v)243 static inline vms_vector vm_vec_negated(vms_vector v)
244 {
245 	return vm_vec_negate(v), v;
246 }
247 
248 //Functions in library
249 
250 //adds two vectors, fills in dest, returns ptr to dest
251 //ok for dest to equal either source, but should use vm_vec_add2() if so
252 [[nodiscard]]
vm_vec_add(const vms_vector & src0,const vms_vector & src1)253 static inline vms_vector vm_vec_add (const vms_vector &src0, const vms_vector &src1)
254 {
255 	vms_vector dest;
256 	return vm_vec_add(dest, src0, src1), dest;
257 }
258 
259 
260 //subs two vectors, fills in dest, returns ptr to dest
261 //ok for dest to equal either source, but should use vm_vec_sub2() if so
vm_vec_sub(vms_vector & dest,const vms_vector & src0,const vms_vector & src1)262 static inline vms_vector &vm_vec_sub(vms_vector &dest, const vms_vector &src0, const vms_vector &src1)
263 {
264 #ifdef DXX_CONSTANT_TRUE
265 	if (DXX_CONSTANT_TRUE(&src0 == &src1))
266 		DXX_ALWAYS_ERROR_FUNCTION(vm_vec_sub_same_op, "vm_vec_sub with &src0 == &src1");
267 	else if (DXX_CONSTANT_TRUE(src0.x == src1.x && src0.y == src1.y && src0.z == src1.z))
268 		DXX_ALWAYS_ERROR_FUNCTION(vm_vec_sub_same_values, "vm_vec_sub with equal value inputs");
269 #endif
270 	return _vm_vec_sub(dest, src0, src1);
271 }
272 
273 [[nodiscard]]
vm_vec_sub(const vms_vector & src0,const vms_vector & src1)274 static inline vms_vector vm_vec_sub (const vms_vector &src0, const vms_vector &src1)
275 {
276 	vms_vector dest;
277 	return vm_vec_sub(dest, src0, src1), dest;
278 }
279 
280 //averages two vectors. returns ptr to dest
281 //dest can equal either source
282 [[nodiscard]]
vm_vec_avg(const vms_vector & src0,const vms_vector & src1)283 static inline vms_vector vm_vec_avg (const vms_vector &src0, const vms_vector &src1)
284 {
285 	vms_vector dest;
286 	return vm_vec_avg(dest, src0, src1), dest;
287 }
288 
289 //scales and copies a vector.  returns ptr to dest
290 #define vm_vec_copy_scale(A,B,...)	vm_vec_copy_scale(A, ## __VA_ARGS__, B)
291 [[nodiscard]]
vm_vec_copy_scale(vms_vector src,fix s)292 static inline vms_vector vm_vec_copy_scale(vms_vector src, fix s)
293 {
294 	return vm_vec_scale(src, s), src;
295 }
296 
297 //scales a vector, adds it to another, and stores in a 3rd vector
298 //dest = src1 + k * src2
299 [[nodiscard]]
vm_vec_scale_add(const vms_vector & src1,const vms_vector & src2,fix k)300 static inline vms_vector vm_vec_scale_add(const vms_vector &src1, const vms_vector &src2, fix k)
301 {
302 	vms_vector dest;
303 	return vm_vec_scale_add(dest, src1, src2, k), dest;
304 }
305 
306 [[nodiscard]]
vm_vec_normalized(vms_vector v)307 static inline vms_vector vm_vec_normalized(vms_vector v)
308 {
309 	return vm_vec_normalize(v), v;
310 }
311 
312 [[nodiscard]]
vm_vec_normalized_quick(vms_vector v)313 static inline vms_vector vm_vec_normalized_quick(vms_vector v)
314 {
315 	return vm_vec_normalize_quick(v), v;
316 }
317 
318 [[nodiscard]]
vm_vec_cross(const vms_vector & src0,const vms_vector & src1)319 static inline vms_vector vm_vec_cross(const vms_vector &src0, const vms_vector &src1)
320 {
321 	vms_vector dest;
322 	return vm_vec_cross(dest, src0, src1), dest;
323 }
324 
325 [[nodiscard]]
vm_vec_normal(const vms_vector & p0,const vms_vector & p1,const vms_vector & p2)326 static inline vms_vector vm_vec_normal(const vms_vector &p0, const vms_vector &p1, const vms_vector &p2)
327 {
328 	vms_vector dest;
329 	return vm_vec_normal(dest, p0, p1, p2), dest;
330 }
331 
332 [[nodiscard]]
vm_vec_perp(const vms_vector & p0,const vms_vector & p1,const vms_vector & p2)333 static inline vms_vector vm_vec_perp (const vms_vector &p0, const vms_vector &p1, const vms_vector &p2)
334 {
335 	vms_vector dest;
336 	return vm_vec_perp(dest, p0, p1, p2), dest;
337 }
338 
339 [[nodiscard]]
vm_angles_2_matrix(const vms_angvec & a)340 static inline vms_matrix vm_angles_2_matrix (const vms_angvec &a)
341 {
342 	vms_matrix m;
343 	return vm_angles_2_matrix(m, a), m;
344 }
345 
346 [[nodiscard]]
vm_vector_2_matrix(const vms_vector & fvec,const vms_vector * uvec,const vms_vector * rvec)347 static inline vms_matrix vm_vector_2_matrix (const vms_vector &fvec, const vms_vector *uvec, const vms_vector *rvec)
348 {
349 	vms_matrix m;
350 	return vm_vector_2_matrix(m, fvec, uvec, rvec), m;
351 }
352 
353 [[nodiscard]]
vm_vec_rotate(const vms_vector & src,const vms_matrix & m)354 static inline vms_vector vm_vec_rotate (const vms_vector &src, const vms_matrix &m)
355 {
356 	vms_vector dest;
357 	return vm_vec_rotate(dest, src, m), dest;
358 }
359 
360 //transpose a matrix in place. returns ptr to matrix
vm_transpose_matrix(vms_matrix & m)361 static inline void vm_transpose_matrix(vms_matrix &m)
362 {
363 	using std::swap;
364 	swap(m.uvec.x, m.rvec.y);
365 	swap(m.fvec.x, m.rvec.z);
366 	swap(m.fvec.y, m.uvec.z);
367 }
368 
369 [[nodiscard]]
vm_transposed_matrix(vms_matrix m)370 static inline vms_matrix vm_transposed_matrix(vms_matrix m)
371 {
372 	vm_transpose_matrix(m);
373 	return m;
374 }
375 
376 //mulitply 2 matrices, fill in dest.  returns ptr to dest
vm_matrix_x_matrix(vms_matrix & dest,const vms_matrix & src0,const vms_matrix & src1)377 static inline void vm_matrix_x_matrix(vms_matrix &dest, const vms_matrix &src0, const vms_matrix &src1)
378 {
379 #ifdef DXX_CONSTANT_TRUE
380 	if (DXX_CONSTANT_TRUE(&dest == &src0))
381 		DXX_ALWAYS_ERROR_FUNCTION(vm_matrix_x_matrix_dest_src0, "vm_matrix_x_matrix with &dest == &src0");
382 	else if (DXX_CONSTANT_TRUE(&dest == &src1))
383 		DXX_ALWAYS_ERROR_FUNCTION(vm_matrix_x_matrix_dest_src1, "vm_matrix_x_matrix with &dest == &src1");
384 #endif
385 	assert(&dest != &src0);
386 	assert(&dest != &src1);
387 	return _vm_matrix_x_matrix(dest, src0, src1);
388 }
389 
390 [[nodiscard]]
vm_matrix_x_matrix(const vms_matrix & src0,const vms_matrix & src1)391 static inline vms_matrix vm_matrix_x_matrix(const vms_matrix &src0, const vms_matrix &src1)
392 {
393 	vms_matrix dest;
394 	vm_matrix_x_matrix(dest, src0, src1);
395 	return dest;
396 }
397 
398 [[nodiscard]]
vm_extract_angles_matrix(const vms_matrix & m)399 static inline vms_angvec vm_extract_angles_matrix (const vms_matrix &m)
400 {
401 	vms_angvec a;
402 	return vm_extract_angles_matrix(a, m), a;
403 }
404 
405 //fills in fields of an angle vector
vm_angvec_make(vms_angvec * v,fixang p,fixang b,fixang h)406 static inline void vm_angvec_make(vms_angvec *v, fixang p, fixang b, fixang h)
407 {
408 	v->p = p;
409 	v->b = b;
410 	v->h = h;
411 }
412 
413 }
414 
415 #endif
416