1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #ifndef __IRR_POINT_3D_H_INCLUDED__
6 #define __IRR_POINT_3D_H_INCLUDED__
7 
8 #include "irrMath.h"
9 
10 namespace irr
11 {
12 namespace core
13 {
14 
15 	//! 3d vector template class with lots of operators and methods.
16 	/** The vector3d class is used in Irrlicht for three main purposes:
17 		1) As a direction vector (most of the methods assume this).
18 		2) As a position in 3d space (which is synonymous with a direction vector from the origin to this position).
19 		3) To hold three Euler rotations, where X is pitch, Y is yaw and Z is roll.
20 	*/
21 	template <class T>
22 	class vector3d
23 	{
24 	public:
25 		//! Default constructor (null vector).
vector3d()26 		vector3d() : X(0), Y(0), Z(0) {}
27 		//! Constructor with three different values
vector3d(T nx,T ny,T nz)28 		vector3d(T nx, T ny, T nz) : X(nx), Y(ny), Z(nz) {}
29 		//! Constructor with the same value for all elements
vector3d(T n)30 		explicit vector3d(T n) : X(n), Y(n), Z(n) {}
31 		//! Copy constructor
vector3d(const vector3d<T> & other)32 		vector3d(const vector3d<T>& other) : X(other.X), Y(other.Y), Z(other.Z) {}
33 
34 		// operators
35 
36 		vector3d<T> operator-() const { return vector3d<T>(-X, -Y, -Z); }
37 
38 		vector3d<T>& operator=(const vector3d<T>& other) { X = other.X; Y = other.Y; Z = other.Z; return *this; }
39 
40 		vector3d<T> operator+(const vector3d<T>& other) const { return vector3d<T>(X + other.X, Y + other.Y, Z + other.Z); }
41 		vector3d<T>& operator+=(const vector3d<T>& other) { X+=other.X; Y+=other.Y; Z+=other.Z; return *this; }
42 		vector3d<T> operator+(const T val) const { return vector3d<T>(X + val, Y + val, Z + val); }
43 		vector3d<T>& operator+=(const T val) { X+=val; Y+=val; Z+=val; return *this; }
44 
45 		vector3d<T> operator-(const vector3d<T>& other) const { return vector3d<T>(X - other.X, Y - other.Y, Z - other.Z); }
46 		vector3d<T>& operator-=(const vector3d<T>& other) { X-=other.X; Y-=other.Y; Z-=other.Z; return *this; }
47 		vector3d<T> operator-(const T val) const { return vector3d<T>(X - val, Y - val, Z - val); }
48 		vector3d<T>& operator-=(const T val) { X-=val; Y-=val; Z-=val; return *this; }
49 
50 		vector3d<T> operator*(const vector3d<T>& other) const { return vector3d<T>(X * other.X, Y * other.Y, Z * other.Z); }
51 		vector3d<T>& operator*=(const vector3d<T>& other) { X*=other.X; Y*=other.Y; Z*=other.Z; return *this; }
52 		vector3d<T> operator*(const T v) const { return vector3d<T>(X * v, Y * v, Z * v); }
53 		vector3d<T>& operator*=(const T v) { X*=v; Y*=v; Z*=v; return *this; }
54 
55 		vector3d<T> operator/(const vector3d<T>& other) const { return vector3d<T>(X / other.X, Y / other.Y, Z / other.Z); }
56 		vector3d<T>& operator/=(const vector3d<T>& other) { X/=other.X; Y/=other.Y; Z/=other.Z; return *this; }
57 		vector3d<T> operator/(const T v) const { T i=(T)1.0/v; return vector3d<T>(X * i, Y * i, Z * i); }
58 		vector3d<T>& operator/=(const T v) { T i=(T)1.0/v; X*=i; Y*=i; Z*=i; return *this; }
59 
60 		//! sort in order X, Y, Z. Equality with rounding tolerance.
61 		bool operator<=(const vector3d<T>&other) const
62 		{
63 			return 	(X<other.X || core::equals(X, other.X)) ||
64 					(core::equals(X, other.X) && (Y<other.Y || core::equals(Y, other.Y))) ||
65 					(core::equals(X, other.X) && core::equals(Y, other.Y) && (Z<other.Z || core::equals(Z, other.Z)));
66 		}
67 
68 		//! sort in order X, Y, Z. Equality with rounding tolerance.
69 		bool operator>=(const vector3d<T>&other) const
70 		{
71 			return 	(X>other.X || core::equals(X, other.X)) ||
72 					(core::equals(X, other.X) && (Y>other.Y || core::equals(Y, other.Y))) ||
73 					(core::equals(X, other.X) && core::equals(Y, other.Y) && (Z>other.Z || core::equals(Z, other.Z)));
74 		}
75 
76 		//! sort in order X, Y, Z. Difference must be above rounding tolerance.
77 		bool operator<(const vector3d<T>&other) const
78 		{
79 			return 	(X<other.X && !core::equals(X, other.X)) ||
80 					(core::equals(X, other.X) && Y<other.Y && !core::equals(Y, other.Y)) ||
81 					(core::equals(X, other.X) && core::equals(Y, other.Y) && Z<other.Z && !core::equals(Z, other.Z));
82 		}
83 
84 		//! sort in order X, Y, Z. Difference must be above rounding tolerance.
85 		bool operator>(const vector3d<T>&other) const
86 		{
87 			return 	(X>other.X && !core::equals(X, other.X)) ||
88 					(core::equals(X, other.X) && Y>other.Y && !core::equals(Y, other.Y)) ||
89 					(core::equals(X, other.X) && core::equals(Y, other.Y) && Z>other.Z && !core::equals(Z, other.Z));
90 		}
91 
92 		//! use weak float compare
93 		bool operator==(const vector3d<T>& other) const
94 		{
95 			return this->equals(other);
96 		}
97 
98 		bool operator!=(const vector3d<T>& other) const
99 		{
100 			return !this->equals(other);
101 		}
102 
103 		// functions
104 
105 		//! returns if this vector equals the other one, taking floating point rounding errors into account
106 		bool equals(const vector3d<T>& other, const T tolerance = (T)ROUNDING_ERROR_f32 ) const
107 		{
108 			return core::equals(X, other.X, tolerance) &&
109 				core::equals(Y, other.Y, tolerance) &&
110 				core::equals(Z, other.Z, tolerance);
111 		}
112 
set(const T nx,const T ny,const T nz)113 		vector3d<T>& set(const T nx, const T ny, const T nz) {X=nx; Y=ny; Z=nz; return *this;}
set(const vector3d<T> & p)114 		vector3d<T>& set(const vector3d<T>& p) {X=p.X; Y=p.Y; Z=p.Z;return *this;}
115 
116 		//! Get length of the vector.
getLength()117 		T getLength() const { return core::squareroot( X*X + Y*Y + Z*Z ); }
118 
119 		//! Get squared length of the vector.
120 		/** This is useful because it is much faster than getLength().
121 		\return Squared length of the vector. */
getLengthSQ()122 		T getLengthSQ() const { return X*X + Y*Y + Z*Z; }
123 
124 		//! Get the dot product with another vector.
dotProduct(const vector3d<T> & other)125 		T dotProduct(const vector3d<T>& other) const
126 		{
127 			return X*other.X + Y*other.Y + Z*other.Z;
128 		}
129 
130 		//! Get distance from another point.
131 		/** Here, the vector is interpreted as point in 3 dimensional space. */
getDistanceFrom(const vector3d<T> & other)132 		T getDistanceFrom(const vector3d<T>& other) const
133 		{
134 			return vector3d<T>(X - other.X, Y - other.Y, Z - other.Z).getLength();
135 		}
136 
137 		//! Returns squared distance from another point.
138 		/** Here, the vector is interpreted as point in 3 dimensional space. */
getDistanceFromSQ(const vector3d<T> & other)139 		T getDistanceFromSQ(const vector3d<T>& other) const
140 		{
141 			return vector3d<T>(X - other.X, Y - other.Y, Z - other.Z).getLengthSQ();
142 		}
143 
144 		//! Calculates the cross product with another vector.
145 		/** \param p Vector to multiply with.
146 		\return Crossproduct of this vector with p. */
crossProduct(const vector3d<T> & p)147 		vector3d<T> crossProduct(const vector3d<T>& p) const
148 		{
149 			return vector3d<T>(Y * p.Z - Z * p.Y, Z * p.X - X * p.Z, X * p.Y - Y * p.X);
150 		}
151 
152 		//! Returns if this vector interpreted as a point is on a line between two other points.
153 		/** It is assumed that the point is on the line.
154 		\param begin Beginning vector to compare between.
155 		\param end Ending vector to compare between.
156 		\return True if this vector is between begin and end, false if not. */
isBetweenPoints(const vector3d<T> & begin,const vector3d<T> & end)157 		bool isBetweenPoints(const vector3d<T>& begin, const vector3d<T>& end) const
158 		{
159 			const T f = (end - begin).getLengthSQ();
160 			return getDistanceFromSQ(begin) <= f &&
161 				getDistanceFromSQ(end) <= f;
162 		}
163 
164 		//! Normalizes the vector.
165 		/** In case of the 0 vector the result is still 0, otherwise
166 		the length of the vector will be 1.
167 		\return Reference to this vector after normalization. */
normalize()168 		vector3d<T>& normalize()
169 		{
170 			f64 length = X*X + Y*Y + Z*Z;
171 			if (length == 0 ) // this check isn't an optimization but prevents getting NAN in the sqrt.
172 				return *this;
173 			length = core::reciprocal_squareroot(length);
174 
175 			X = (T)(X * length);
176 			Y = (T)(Y * length);
177 			Z = (T)(Z * length);
178 			return *this;
179 		}
180 
181 		//! Sets the length of the vector to a new value
setLength(T newlength)182 		vector3d<T>& setLength(T newlength)
183 		{
184 			normalize();
185 			return (*this *= newlength);
186 		}
187 
188 		//! Inverts the vector.
invert()189 		vector3d<T>& invert()
190 		{
191 			X *= -1;
192 			Y *= -1;
193 			Z *= -1;
194 			return *this;
195 		}
196 
197 		//! Rotates the vector by a specified number of degrees around the Y axis and the specified center.
198 		/** \param degrees Number of degrees to rotate around the Y axis.
199 		\param center The center of the rotation. */
200 		void rotateXZBy(f64 degrees, const vector3d<T>& center=vector3d<T>())
201 		{
202 			degrees *= DEGTORAD64;
203 			f64 cs = cos(degrees);
204 			f64 sn = sin(degrees);
205 			X -= center.X;
206 			Z -= center.Z;
207 			set((T)(X*cs - Z*sn), Y, (T)(X*sn + Z*cs));
208 			X += center.X;
209 			Z += center.Z;
210 		}
211 
212 		//! Rotates the vector by a specified number of degrees around the Z axis and the specified center.
213 		/** \param degrees: Number of degrees to rotate around the Z axis.
214 		\param center: The center of the rotation. */
215 		void rotateXYBy(f64 degrees, const vector3d<T>& center=vector3d<T>())
216 		{
217 			degrees *= DEGTORAD64;
218 			f64 cs = cos(degrees);
219 			f64 sn = sin(degrees);
220 			X -= center.X;
221 			Y -= center.Y;
222 			set((T)(X*cs - Y*sn), (T)(X*sn + Y*cs), Z);
223 			X += center.X;
224 			Y += center.Y;
225 		}
226 
227 		//! Rotates the vector by a specified number of degrees around the X axis and the specified center.
228 		/** \param degrees: Number of degrees to rotate around the X axis.
229 		\param center: The center of the rotation. */
230 		void rotateYZBy(f64 degrees, const vector3d<T>& center=vector3d<T>())
231 		{
232 			degrees *= DEGTORAD64;
233 			f64 cs = cos(degrees);
234 			f64 sn = sin(degrees);
235 			Z -= center.Z;
236 			Y -= center.Y;
237 			set(X, (T)(Y*cs - Z*sn), (T)(Y*sn + Z*cs));
238 			Z += center.Z;
239 			Y += center.Y;
240 		}
241 
242 		//! Creates an interpolated vector between this vector and another vector.
243 		/** \param other The other vector to interpolate with.
244 		\param d Interpolation value between 0.0f (all the other vector) and 1.0f (all this vector).
245 		Note that this is the opposite direction of interpolation to getInterpolated_quadratic()
246 		\return An interpolated vector.  This vector is not modified. */
getInterpolated(const vector3d<T> & other,f64 d)247 		vector3d<T> getInterpolated(const vector3d<T>& other, f64 d) const
248 		{
249 			const f64 inv = 1.0 - d;
250 			return vector3d<T>((T)(other.X*inv + X*d), (T)(other.Y*inv + Y*d), (T)(other.Z*inv + Z*d));
251 		}
252 
253 		//! Creates a quadratically interpolated vector between this and two other vectors.
254 		/** \param v2 Second vector to interpolate with.
255 		\param v3 Third vector to interpolate with (maximum at 1.0f)
256 		\param d Interpolation value between 0.0f (all this vector) and 1.0f (all the 3rd vector).
257 		Note that this is the opposite direction of interpolation to getInterpolated() and interpolate()
258 		\return An interpolated vector. This vector is not modified. */
getInterpolated_quadratic(const vector3d<T> & v2,const vector3d<T> & v3,f64 d)259 		vector3d<T> getInterpolated_quadratic(const vector3d<T>& v2, const vector3d<T>& v3, f64 d) const
260 		{
261 			// this*(1-d)*(1-d) + 2 * v2 * (1-d) + v3 * d * d;
262 			const f64 inv = (T) 1.0 - d;
263 			const f64 mul0 = inv * inv;
264 			const f64 mul1 = (T) 2.0 * d * inv;
265 			const f64 mul2 = d * d;
266 
267 			return vector3d<T> ((T)(X * mul0 + v2.X * mul1 + v3.X * mul2),
268 					(T)(Y * mul0 + v2.Y * mul1 + v3.Y * mul2),
269 					(T)(Z * mul0 + v2.Z * mul1 + v3.Z * mul2));
270 		}
271 
272 		//! Sets this vector to the linearly interpolated vector between a and b.
273 		/** \param a first vector to interpolate with, maximum at 1.0f
274 		\param b second vector to interpolate with, maximum at 0.0f
275 		\param d Interpolation value between 0.0f (all vector b) and 1.0f (all vector a)
276 		Note that this is the opposite direction of interpolation to getInterpolated_quadratic()
277 		*/
interpolate(const vector3d<T> & a,const vector3d<T> & b,f64 d)278 		vector3d<T>& interpolate(const vector3d<T>& a, const vector3d<T>& b, f64 d)
279 		{
280 			X = (T)((f64)b.X + ( ( a.X - b.X ) * d ));
281 			Y = (T)((f64)b.Y + ( ( a.Y - b.Y ) * d ));
282 			Z = (T)((f64)b.Z + ( ( a.Z - b.Z ) * d ));
283 			return *this;
284 		}
285 
286 
287 		//! Get the rotations that would make a (0,0,1) direction vector point in the same direction as this direction vector.
288 		/** Thanks to Arras on the Irrlicht forums for this method.  This utility method is very useful for
289 		orienting scene nodes towards specific targets.  For example, if this vector represents the difference
290 		between two scene nodes, then applying the result of getHorizontalAngle() to one scene node will point
291 		it at the other one.
292 		Example code:
293 		// Where target and seeker are of type ISceneNode*
294 		const vector3df toTarget(target->getAbsolutePosition() - seeker->getAbsolutePosition());
295 		const vector3df requiredRotation = toTarget.getHorizontalAngle();
296 		seeker->setRotation(requiredRotation);
297 
298 		\return A rotation vector containing the X (pitch) and Y (raw) rotations (in degrees) that when applied to a
299 		+Z (e.g. 0, 0, 1) direction vector would make it point in the same direction as this vector. The Z (roll) rotation
300 		is always 0, since two Euler rotations are sufficient to point in any given direction. */
getHorizontalAngle()301 		vector3d<T> getHorizontalAngle() const
302 		{
303 			vector3d<T> angle;
304 
305 			const f64 tmp = (atan2((f64)X, (f64)Z) * RADTODEG64);
306 			angle.Y = (T)tmp;
307 
308 			if (angle.Y < 0)
309 				angle.Y += 360;
310 			if (angle.Y >= 360)
311 				angle.Y -= 360;
312 
313 			const f64 z1 = core::squareroot(X*X + Z*Z);
314 
315 			angle.X = (T)(atan2((f64)z1, (f64)Y) * RADTODEG64 - 90.0);
316 
317 			if (angle.X < 0)
318 				angle.X += 360;
319 			if (angle.X >= 360)
320 				angle.X -= 360;
321 
322 			return angle;
323 		}
324 
325 		//! Get the spherical coordinate angles
326 		/** This returns Euler degrees for the point represented by
327 		this vector.  The calculation assumes the pole at (0,1,0) and
328 		returns the angles in X and Y.
329 		*/
getSphericalCoordinateAngles()330 		vector3d<T> getSphericalCoordinateAngles() const
331 		{
332 			vector3d<T> angle;
333 			const f64 length = X*X + Y*Y + Z*Z;
334 
335 			if (length)
336 			{
337 				if (X!=0)
338 				{
339 					angle.Y = (T)(atan2((f64)Z,(f64)X) * RADTODEG64);
340 				}
341 				else if (Z<0)
342 					angle.Y=180;
343 
344 				angle.X = (T)(acos(Y * core::reciprocal_squareroot(length)) * RADTODEG64);
345 			}
346 			return angle;
347 		}
348 
349 		//! Builds a direction vector from (this) rotation vector.
350 		/** This vector is assumed to be a rotation vector composed of 3 Euler angle rotations, in degrees.
351 		The implementation performs the same calculations as using a matrix to do the rotation.
352 
353 		\param[in] forwards  The direction representing "forwards" which will be rotated by this vector.
354 		If you do not provide a direction, then the +Z axis (0, 0, 1) will be assumed to be forwards.
355 		\return A direction vector calculated by rotating the forwards direction by the 3 Euler angles
356 		(in degrees) represented by this vector. */
357 		vector3d<T> rotationToDirection(const vector3d<T> & forwards = vector3d<T>(0, 0, 1)) const
358 		{
359 			const f64 cr = cos( core::DEGTORAD64 * X );
360 			const f64 sr = sin( core::DEGTORAD64 * X );
361 			const f64 cp = cos( core::DEGTORAD64 * Y );
362 			const f64 sp = sin( core::DEGTORAD64 * Y );
363 			const f64 cy = cos( core::DEGTORAD64 * Z );
364 			const f64 sy = sin( core::DEGTORAD64 * Z );
365 
366 			const f64 srsp = sr*sp;
367 			const f64 crsp = cr*sp;
368 
369 			const f64 pseudoMatrix[] = {
370 				( cp*cy ), ( cp*sy ), ( -sp ),
371 				( srsp*cy-cr*sy ), ( srsp*sy+cr*cy ), ( sr*cp ),
372 				( crsp*cy+sr*sy ), ( crsp*sy-sr*cy ), ( cr*cp )};
373 
374 			return vector3d<T>(
375 				(T)(forwards.X * pseudoMatrix[0] +
376 					forwards.Y * pseudoMatrix[3] +
377 					forwards.Z * pseudoMatrix[6]),
378 				(T)(forwards.X * pseudoMatrix[1] +
379 					forwards.Y * pseudoMatrix[4] +
380 					forwards.Z * pseudoMatrix[7]),
381 				(T)(forwards.X * pseudoMatrix[2] +
382 					forwards.Y * pseudoMatrix[5] +
383 					forwards.Z * pseudoMatrix[8]));
384 		}
385 
386 		//! Fills an array of 4 values with the vector data (usually floats).
387 		/** Useful for setting in shader constants for example. The fourth value
388 		will always be 0. */
getAs4Values(T * array)389 		void getAs4Values(T* array) const
390 		{
391 			array[0] = X;
392 			array[1] = Y;
393 			array[2] = Z;
394 			array[3] = 0;
395 		}
396 
397 		//! Fills an array of 3 values with the vector data (usually floats).
398 		/** Useful for setting in shader constants for example.*/
getAs3Values(T * array)399 		void getAs3Values(T* array) const
400 		{
401 			array[0] = X;
402 			array[1] = Y;
403 			array[2] = Z;
404 		}
405 
406 
407 		//! X coordinate of the vector
408 		T X;
409 
410 		//! Y coordinate of the vector
411 		T Y;
412 
413 		//! Z coordinate of the vector
414 		T Z;
415 	};
416 
417 	//! partial specialization for integer vectors
418 	// Implementor note: inline keyword needed due to template specialization for s32. Otherwise put specialization into a .cpp
419 	template <>
420 	inline vector3d<s32> vector3d<s32>::operator /(s32 val) const {return core::vector3d<s32>(X/val,Y/val,Z/val);}
421 	template <>
422 	inline vector3d<s32>& vector3d<s32>::operator /=(s32 val) {X/=val;Y/=val;Z/=val; return *this;}
423 
424 	template <>
getSphericalCoordinateAngles()425 	inline vector3d<s32> vector3d<s32>::getSphericalCoordinateAngles() const
426 	{
427 		vector3d<s32> angle;
428 		const f64 length = X*X + Y*Y + Z*Z;
429 
430 		if (length)
431 		{
432 			if (X!=0)
433 			{
434 				angle.Y = round32((f32)(atan2((f64)Z,(f64)X) * RADTODEG64));
435 			}
436 			else if (Z<0)
437 				angle.Y=180;
438 
439 			angle.X = round32((f32)(acos(Y * core::reciprocal_squareroot(length)) * RADTODEG64));
440 		}
441 		return angle;
442 	}
443 
444 	//! Typedef for a f32 3d vector.
445 	typedef vector3d<f32> vector3df;
446 
447 	//! Typedef for an integer 3d vector.
448 	typedef vector3d<s32> vector3di;
449 
450 	//! Function multiplying a scalar and a vector component-wise.
451 	template<class S, class T>
452 	vector3d<T> operator*(const S scalar, const vector3d<T>& vector) { return vector*scalar; }
453 
454 } // end namespace core
455 } // end namespace irr
456 
457 #endif
458 
459