1 /*!
2  \brief Eye Candy extension for Eternal Lands
3 
4  Introduction:
5 
6  The Eye Candy object files were designed with the intent of adding a
7  new suite of realistic special effects to Eternal Lands.  Developed in
8  C++, they are connected to the game (written in C) through a wrapper,
9  eye_candy_wrapper (.cpp and .h).
10 
11  Eye Candy effects are largely based on textured point sprites, although
12  certain effects use polygons as well.  The point sprites can have multiple
13  frames, although none are currently setup as frame-by-frame animations (the
14  infrastructure allows for this, however).  Instead, existing point sprites
15  randomly pick a frame from a range of possible choices.  The sprites can be
16  drawn using one of three methods, two of which are wrapped: OpenGL point
17  sprites (not supported on all cards, but theoretically faster), fast
18  billboarded quads, and accurate billboarded quads (not wrapped).  There is
19  not that much difference between "fast" and "accurate" billboarded quads in
20  terms of visual accuracy and speed, so only one of those is wrapped.
21 
22  The advantage to using point sprite-based effects is that you can cram a
23  great deal of detail into a particle without using many polygons.  Even the
24  most basic approximation of a sphere (a tetrahedron) takes four polygons,
25  and textures will not wrap well around such a boxy shape.  Textured polys
26  allow for wispy, translucent features, smooth curves, and all kinds of other
27  effects.  Additionally, they lend themselves naturally to looking as though
28  they are filled when tranlucency is used; translucent polygons look like
29  shells.
30 
31  The primary disadvantage to point sprites is that they are, quite simply,
32  sprites.  Thus, they tend to work better for objects that don't vary much
33  depending on which angle you look at them from, and appear most realistic
34  when there are many on the screen at once, all behaving according to some
35  realistic movement rules.
36 
37  Actual polygons are used in a few locations in the Eye Candy package, in
38  places where point sprites are not suitable.  These include teleporation
39  effects (for a translucent column of light) and blowing leaves/flower
40  petals.  Fireflies, however, are point sprites, as they are expected to only
41  be used in the dark when you wouldn't expect to see the insect body in
42  detail.
43 
44  Translucency is done in the package using two blend methods.  The default,
45  and more common, is "glowing" particles.  These blend accumulatively.  A
46  glowing particle will never make its background darker.  Infinite
47  accumulation of glowing particles, assuming that there is at least some R,
48  G, and B in them, will always result in white.  They work well for magic and
49  light sources.  "Non-glowing" particles blend with an average (as with
50  glowing, weighted proportional to transparency).  Infinite accumulation of
51  non-glowing particles results purely in the color of the particle itself.
52  They work well for things like dust, debris, and smoke.
53 
54  All effects (except fireflies and leaves/petals, which don't need it) have a
55  level of detail flag.  This is the maximum level of detail to use.  The
56  number of particles that the effect will use is roughly proportional to its
57  level of detail, although different effects may use more or less particles
58  than others.  Naturally, lower level of detail is faster but poorer quality.
59  Eye Candy also has a built-in particle limit.  As you near this limit, it
60  automatically tells effects to lower their level of detail.  Any new
61  particles that they create will be done according to the new LOD.  Lastly,
62  the framerate of eternallands will also automatically adjust the level of
63  detail.  When the total particle count is too high, eye candy will start to
64  kill off particles.  Effects that are far enough away that they become
65  "inactive" will eventually have their particle counts pruned away to
66  nothing.
67 
68  The main, controlling object is EyeCandy.  There is only ever one EyeCandy
69  object (in our case, it is defined in eye_candy_wrapper.cpp).  It acts as
70  the control mechanism for all of the effects and particles, and it is how
71  the wrapper interfaces with them.  Effect objects are created manually, but
72  destroy themselves -- either automatically when the effect finishes, or when
73  told to by having their "recall" flag set (after making sure that their
74  particles expire peacefully).  Particle objects are created by the effect,
75  and handle all of the details of their drawing.  Particles frequently are
76  positioned by Spawners, which pick coordinates in 3-space based on various
77  rules, and are moved by Movers, which can simulate things like gravity and
78  wind.  Each specific effect class has its own effect_*.cpp file, and is
79  based on an object that inherits from Effect (and typically uses particles
80  that inherit from Particle).  An additional object used by the system is
81  math_cache.cpp, which speeds up certain mathematics functions.
82 
83  Note that the Eye Candy system uses a different coordinate system
84  than Eternal Lands (Y is up/down in Eye Candy, while in Eternal Lands, Z is
85  up/down).  The wrapper takes care of hiding this from the user -- and even
86  from Eye Candy itself.
87 
88  Lastly, one guiding principle when editing this code: independence.  Eye
89  Candy is a completely indepenent piece of code.  It's *only* interface with
90  the rest of Eternal Lands is eye_candy_wrapper.  This is by design.  Please
91  do not put includes to any other EL code (and thus use any EL globals,
92  functions, etc) in Eye Candy.  Rather, work through the wrapper to exchange
93  any information you feel is necessary.  This way, Eye Candy remains clean
94  and project-independent.
95  */
96 
97 #ifndef EYE_CANDY_H
98 #define EYE_CANDY_H
99 
100 // I N C L U D E S ////////////////////////////////////////////////////////////
101 
102 #if defined(_WIN32) || defined(_WIN64)
103 #ifndef NOMINMAX
104 #define NOMINMAX
105 #endif
106 #include <windows.h>
107 #include <float.h>
108 #else
109 #include <sys/time.h>
110 #include <time.h>
111 #endif
112 
113 #include <algorithm>
114 #include <string>
115 #include <vector>
116 #include <map>
117 #include <iostream>
118 #include <limits>
119 #include <cassert>
120 #include <SDL.h>
121 #include <stdlib.h>
122 #include <cmath>
123 
124 #include "types.h"
125 #include "math_cache.h"
126 #include "../platform.h"
127 
128 #ifdef CLUSTER_INSIDES
129 #include "../cluster.h"
130 #endif
131 #include <memory>
132 #include "../engine/hardwarebuffer.hpp"
133 
134 namespace el = eternal_lands;
135 
136 namespace ec
137 {
138 
139 	// P R O T O T Y P E S ////////////////////////////////////////////////////////
140 
141 #define randdouble MathCache::randdouble	// Aliases to static functions to make things easier to type.
142 #define randfloat MathCache::randfloat
143 #define randdouble MathCache::randdouble
144 #define randfloat MathCache::randfloat
145 #define randint MathCache::randint
146 #define rand8 Uint8 rand8();	//Functions to ensure a minimum entropy range for the rand function.
147 #define rand16 MathCache::rand16
148 #define rand32 MathCache::rand32
149 #define rand64 MathCache::rand64
150 #define rand7 MathCache::rand7
151 #define rand15 MathCache::rand15
152 #define rand31 MathCache::rand31
153 #define rand63 MathCache::rand63
154 #define randcoord MathCache::randcoord
155 #define randcoord MathCache::randcoord
156 #define randcoord_non_zero MathCache::randcoord_non_zero
157 #define pow_randfloat MathCache::pow_randfloat
158 #define randcolor MathCache::randcolor
159 #define randcolor MathCache::randcolor
160 #define randalpha MathCache::randalpha
161 #define randalpha MathCache::randalpha
162 #define randenergy MathCache::randenergy
163 #define randenergy MathCache::randenergy
164 #define randlight MathCache::randlight
165 #define randlight MathCache::randlight
166 #define randpercent MathCache::randpercent
167 #define randpercent MathCache::randpercent
168 #define randangle MathCache::randangle
169 #define randangle MathCache::randangle
170 #define square MathCache::square
171 #define square MathCache::square
172 #define square MathCache::square
173 #define cube MathCache::cube
174 #define cube MathCache::cube
175 #define cube MathCache::cube
176 #define invsqrt MathCache::invsqrt
177 #define fastsqrt MathCache::fastsqrt
178 
179 #ifdef _MSC_VER
180 #define copysign _copysign
181 #define isnan _isnan
182 #define isinf !_finite
183 #define isfinite _finite
fmax(const float a,const float b)184 	inline float fmax(const float a, const float b)
185 	{	return ((a < b) ? b : a);};
round(const float a)186 	inline float round(const float a)
187 	{	return (a - floor(a) < 0.5f ? floor(a) : ceil(a));};
remainderf(const float a,const float b)188 	inline float remainderf(const float a, const float b)
189 	{	return (a - (float)round(a / b) * b);};
usleep(const unsigned long a)190 	inline void usleep(const unsigned long a)
191 	{	Sleep(a / 1000);};
192 
193 #pragma warning (disable : 4100) // Unreferenced formal parameter (Justification: I may have a parameter passed for later use.  No harm done.)
194 #pragma warning (disable : 4127) // Conditional expression is constant (Justification: Needed for sizeof() checks that will be optimized out; allows for type polymorphism)
195 #pragma warning (disable : 4244) // Conversion from type1 to type2 (Justification: This occurs on cases like "float f=1.0;".  In such cases, I don't want to hear a peep out of the compiler; that code does just what I want it to do.  Specifying 1.0f would hurt type polymorphism.)
196 #pragma warning (disable : 4305) // Truncation from type1 to type2 (Justification: Like above.)
197 #pragma warning (disable : 4311) // Explicitly asting a pointer to a non-pointer type (Justification: Occasionally I use the pointer to an object as salt in a function -- salt that's consistant across that object but not between objects)
198 #endif
199 
200 	Uint64 get_time();
201 	void hsv_to_rgb(const color_t h, const color_t s, const color_t v,
202 		color_t& r, color_t& g, color_t& b);
203 
204 	// M E M B E R S //////////////////////////////////////////////////////////////
205 
206 #ifdef DEBUG
207 #ifndef EC_DEBUG
208 	const int EC_DEBUG = 1;
209 #endif // EC_DEBUG
210 #else // DEBUG
211 #ifndef EC_DEBUG
212 	const int EC_DEBUG = 0;
213 #endif // EC_DEBUG
214 #endif // DEBUG
215 	const float PI = 3.141592654;
216 	const energy_t G = 6.673e-11;
217 	const int MaxMotionBlurPoints = 5;
218 	const coord_t MAX_DRAW_DISTANCE = 24.0;
219 	const coord_t MAX_DRAW_DISTANCE_SQUARED = MAX_DRAW_DISTANCE
220 		* MAX_DRAW_DISTANCE;
221 
222 	// E X T E R N S //////////////////////////////////////////////////////////////
223 
224 	extern MathCache math_cache;
225 
226 	class Obstruction;
227 	extern std::vector<Obstruction*> null_obstructions; // Used where we don't want to have to pass a list.
228 
229 	// E N U M S //////////////////////////////////////////////////////////////////
230 
231 	// Keep in sync with eye_candy_wrapper.h!
232 	enum EffectEnum
233 	{
234 		EC_LAMP = 0,
235 		EC_CAMPFIRE = 1,
236 		EC_FOUNTAIN = 2,
237 		EC_TELEPORTER = 3,
238 		EC_FIREFLY = 4,
239 		EC_SWORD = 5,
240 		EC_SUMMON = 6,
241 		EC_SELFMAGIC = 7,
242 		EC_TARGETMAGIC = 8,
243 		EC_ONGOING = 9,
244 		EC_IMPACT = 10,
245 		EC_SMOKE = 11,
246 		EC_BAG = 12,
247 		EC_CLOUD = 13,
248 		EC_HARVESTING = 14,
249 		EC_WIND = 15,
250 		EC_BREATH = 16,
251 		EC_CANDLE = 17,
252 		EC_MINES = 18,
253 		EC_GLOW = 19,
254 		EC_MISSILE = 20,
255 		EC_STAFF
256 	};
257 
258 	enum TextureEnum
259 	{
260 		EC_CRYSTAL,
261 		EC_FLARE,
262 		EC_INVERSE,
263 		EC_SHIMMER,
264 		EC_SIMPLE,
265 		EC_TWINFLARE,
266 		EC_VOID,
267 		EC_WATER,
268 		EC_LEAF_ASH,
269 		EC_LEAF_MAPLE,
270 		EC_LEAF_OAK,
271 		EC_PETAL,
272 		EC_SNOWFLAKE
273 	};
274 
275 	// C L A S S E S //////////////////////////////////////////////////////////////
276 
277 	/*!
278 	 \brief Vec3: A three-coordinate vector
279 
280 	 Vec3 contains an x, y, and z coordinate and nothing else.  Unlike
281 	 std::vectors, which are for data storage, these are a fixed-size, fixed-type
282 	 structure used for mathematics vector operations -- namely, for particle
283 	 coordinates and velocities.
284 
285 	 Possible speed improvement: use SSE like in the math cache's invsqrt to
286 	 group the variables together into a single 128-bit structure for collective
287 	 math ops.
288 	 */
289 	class Vec3
290 	{
291 		public:
Vec3()292 			Vec3()
293 			{
294 				x = 0.0f;
295 				y = 0.0f;
296 				z = 0.0f;
297 			}
Vec3(coord_t _x,coord_t _y,coord_t _z)298 			Vec3(coord_t _x, coord_t _y, coord_t _z)
299 			{
300 				x = _x;
301 				y = _y;
302 				z = _z;
303 			}
304 			Vec3(const Vec3& v) = default;
~Vec3()305 			~Vec3()
306 			{
307 			}
308 			;
309 
310 			Vec3 operator+=(const Vec3& rhs)
311 			{
312 				x += rhs.x;
313 				y += rhs.y;
314 				z += rhs.z;
315 
316 				if (!is_valid())
317 				{
318 					x = 0.0;
319 					y = 0.0;
320 					z = 0.0;
321 				}
322 
323 				return *this;
324 			}
325 			;
326 
327 			Vec3 operator-=(const Vec3& rhs)
328 			{
329 				x -= rhs.x;
330 				y -= rhs.y;
331 				z -= rhs.z;
332 
333 				if (!is_valid())
334 				{
335 					x = 0.0;
336 					y = 0.0;
337 					z = 0.0;
338 				}
339 
340 				return *this;
341 			}
342 			;
343 
344 			Vec3 operator+(const Vec3& rhs) const
345 			{
346 				Vec3 lhs(x, y, z);
347 				lhs += rhs;
348 
349 				return lhs;
350 			}
351 			;
352 
353 			Vec3 operator-(const Vec3& rhs) const
354 			{
355 				Vec3 lhs(x, y, z);
356 				lhs -= rhs;
357 
358 				return lhs;
359 			}
360 			;
361 
362 			Vec3 operator*=(const coord_t d)
363 			{
364 				x *= d;
365 				y *= d;
366 				z *= d;
367 
368 				return *this;
369 			}
370 			;
371 
372 			Vec3 operator/=(const coord_t d)
373 			{
374 				x /= d;
375 				y /= d;
376 				z /= d;
377 
378 				return *this;
379 			}
380 			;
381 
382 			Vec3 operator*(const coord_t d) const
383 			{
384 				Vec3 lhs(x, y, z);
385 				lhs *= d;
386 
387 				return lhs;
388 			}
389 			;
390 
391 			Vec3 operator/(const coord_t d) const
392 			{
393 				Vec3 lhs(x, y, z);
394 				lhs /= d;
395 
396 				return lhs;
397 			}
398 			;
399 
400 			bool operator==(const Vec3& rhs) const
401 			{
402 				if ((x == rhs.x) && (y == rhs.y) && (z == rhs.z))
403 					return true;
404 				else
405 					return false;
406 			}
407 			;
408 
409 			bool operator!=(const Vec3& rhs) const
410 			{
411 				return !(*this == rhs);
412 			}
413 			;
414 
415 			Vec3& operator=(const Vec3& rhs) = default;
416 
417 			Vec3 operator-()
418 			{
419 				return Vec3(-x, -y, -z);
420 			}
421 			;
422 
magnitude()423 			coord_t magnitude() const
424 			{
425 				return std::sqrt(square(x) + square(y) + square(z));
426 			}
427 			;
428 
magnitude_squared()429 			coord_t magnitude_squared() const
430 			{
431 				return square(x) + square(y) + square(z);
432 			}
433 			;
434 
planar_magnitude()435 			coord_t planar_magnitude() const
436 			{
437 				return std::sqrt(square(x) + square(z));
438 			}
439 			;
440 
planar_magnitude_squared()441 			coord_t planar_magnitude_squared() const
442 			{
443 				return square(x) + square(z);
444 			}
445 			;
446 
447 			Vec3 normalize(const coord_t scale = 1.0f)
448 			{
449 				coord_t tmp;
450 
451 				tmp = std::max(magnitude_squared(), 0.0001f);
452 
453 				(*this) *= (scale / std::sqrt(tmp));
454 
455 				return *this;
456 			}
457 			;
458 
459 			void randomize(const coord_t scale = 1.0)
460 			{
461 				angle_t a, b;
462 
463 				a = randfloat() * 2.0f * M_PI;
464 				b = randfloat() * 2.0f * M_PI;
465 
466 				x = scale * std::sin(a) * std::cos(b);
467 				y = scale * std::sin(a) * std::sin(b);
468 				z = scale * std::cos(a);
469 			}
470 			;
471 
dot(const Vec3 rhs)472 			angle_t dot(const Vec3 rhs) const
473 			{
474 				return x * rhs.x + y * rhs.y + z * rhs.z;
475 			}
476 			;
477 
angle_to(const Vec3 rhs)478 			angle_t angle_to(const Vec3 rhs) const
479 			{
480 				Vec3 lhs_normal = *this;
481 				lhs_normal.normalize();
482 				Vec3 rhs_normal = rhs;
483 				rhs_normal.normalize();
484 
485 				return std::acos(lhs_normal.x * rhs_normal.x + lhs_normal.y
486 					* rhs_normal.y + lhs_normal.z * rhs_normal.z);
487 			}
488 			;
489 
angle_to_prenormalized(const Vec3 rhs)490 			angle_t angle_to_prenormalized(const Vec3 rhs) const
491 			{
492 				return std::acos(x * rhs.x + y * rhs.y + z * rhs.z);
493 			}
494 			;
495 
cross(const Vec3 rhs)496 			Vec3 cross(const Vec3 rhs) const
497 			{
498 				return Vec3(y * rhs.z - z * rhs.y, z * rhs.x -
499 					x * rhs.z, x * rhs.y - y * rhs.x);
500 			}
501 
is_valid(const float value)502 			static bool is_valid(const float value)
503 			{
504 				return (std::abs(value) <
505 					std::numeric_limits<float>::infinity())
506 					// test for NaN
507 					&& (value == value);
508 			}
509 
is_valid()510 			bool is_valid() const
511 			{
512 				return is_valid(x) && is_valid(y) &&
513 					is_valid(z);
514 			}
515 
as_el()516 			Vec3 as_el()
517 			{
518 				return Vec3(x, y, z);
519 			}
520 
as_ec()521 			Vec3 as_ec()
522 			{
523 				return Vec3(x, z, -y);
524 			}
525 
526 			coord_t x, y, z;
527 	};
528 
529 	inline std::ostream& operator<<(std::ostream& lhs, const Vec3 rhs)
530 	{
531 		return lhs << "<" << rhs.x << ", " << rhs.y << ", " << rhs.z << ">";
532 	}
533 	;
534 
535 	/*!
536 	 This class is a standard quaternion.  Think of a quaternion ("quat") as a
537 	 vector to rotate around and an angle.  That's not exactly correct, but close
538 	 enough.  The standard method of storing an x rotation, y rotation, and z
539 	 rotation us subject to a phenominon called "Gimbal locking" and doesn't
540 	 accumulate well.
541 
542 	 Like with Vec3s, this class could potentially be sped up by grouping its
543 	 x, y, and z into a single 128-bit element for aggregate SSE ops.
544 	 */
545 	class Quaternion
546 	{
547 		public:
Quaternion()548 			Quaternion()
549 			{
550 				vec.x = 0.0;
551 				vec.y = 0.0;
552 				vec.z = 0.0;
553 				scalar = 1.0;
554 			}
555 			;
Quaternion(angle_t _x,angle_t _y,angle_t _z,angle_t _scalar)556 			Quaternion(angle_t _x, angle_t _y, angle_t _z, angle_t _scalar)
557 			{
558 				vec.x = _x;
559 				vec.y = _y;
560 				vec.z = _z;
561 				scalar = _scalar;
562 			}
563 			;
Quaternion(angle_t _scalar,const Vec3 _vec)564 			Quaternion(angle_t _scalar, const Vec3 _vec)
565 			{
566 				scalar = _scalar;
567 				vec = _vec;
568 			}
569 			;
~Quaternion()570 			~Quaternion()
571 			{
572 			}
573 			;
574 
conjugate()575 			Quaternion conjugate() const
576 			{
577 				return Quaternion(-vec.x, -vec.y, -vec.z, scalar);
578 			}
579 			;
580 
inverse(const Quaternion rhs)581 			Quaternion inverse(const Quaternion rhs) const
582 			{
583 				return rhs.conjugate();
584 			}
585 			;
586 
magnitude()587 			angle_t magnitude() const
588 			{
589 				return std::sqrt(square(vec.x) + square(vec.y) + square(vec.z) + square(scalar));
590 			}
591 			;
592 
normalize()593 			Quaternion normalize()
594 			{
595 				const angle_t inv_sqrt= 1.0f / std::sqrt(vec.magnitude_squared() + square(scalar));
596 				vec *= inv_sqrt;
597 				scalar *= inv_sqrt;
598 				return *this;
599 			}
600 			;
601 
602 			Quaternion operator*(const Quaternion rhs) const
603 			{
604 				Quaternion ret;
605 				ret.scalar = vec.dot(rhs.vec);
606 				ret.vec = vec.cross(rhs.vec) + vec * rhs.scalar + rhs.vec
607 					* scalar;
608 				ret.normalize();
609 				return ret;
610 			}
611 			;
612 
613 			Quaternion operator*=(const Quaternion rhs)
614 			{
615 				(*this) = (*this) * rhs;
616 				return *this;
617 			}
618 			;
619 
get_matrix(GLfloat * ret)620 			GLfloat* get_matrix(GLfloat* ret) const
621 			{
622 				const angle_t xx = vec.x * vec.x;
623 				const angle_t xy = vec.x * vec.y;
624 				const angle_t xz = vec.x * vec.z;
625 				const angle_t xw = vec.x * scalar;
626 				const angle_t yy = vec.y * vec.y;
627 				const angle_t yz = vec.y * vec.z;
628 				const angle_t yw = vec.y * scalar;
629 				const angle_t zz = vec.z * vec.z;
630 				const angle_t zw = vec.z * scalar;
631 
632 				ret[0] = 1 - 2 * (yy + zz);
633 				ret[1] = 2 * (xy - zw);
634 				ret[2] = 2 * (xz + yw);
635 				ret[3] = 0;
636 
637 				ret[4] = 2 * (xy + zw);
638 				ret[5] = 1 - 2 * (xx - zz);
639 				ret[6] = 2 * (yz + xw);
640 				ret[7] = 0;
641 
642 				ret[8] = 2 * (xz + yw);
643 				ret[9] = 2 * (yz - xw);
644 				ret[10] = 1 - 2 * (xx + yy);
645 				ret[11] = 0;
646 
647 				ret[12] = 0;
648 				ret[13] = 0;
649 				ret[14] = 0;
650 				ret[15] = 1;
651 
652 				return ret;
653 			}
654 			;
655 
from_matrix(const GLfloat * matrix)656 			void from_matrix(const GLfloat* matrix)
657 			{
658 #if 0		// Assumedly slower but equivalent version
659 				const angle_t trace = matrix[0] + matrix[5] + matrix[10] + 1;
660 				if (trace> 0)
661 				{
662 					const angle_t s = 0.5 / std::sqrt(trace);
663 					scalar = 0.25 / s;
664 					vec = Vec3(matrix[9] - matrix[6], matrix[2] - matrix[8], matrix[4] - matrix[1]) * s;
665 				}
666 				else
667 				{
668 					if ((matrix[0]> matrix[5]) && (matrix[0]> matrix[10]))
669 					{
670 						const angle_t s = 2.0 * std::sqrt(1.0 + matrix[0] - matrix[5] - matrix[10]);
671 						vec.x = 0.5 / s;
672 						vec.y = (matrix[1] + matrix[4]) / s;
673 						vec.z = (matrix[2] + matrix[8]) / s;
674 						scalar = (matrix[6] + matrix[9]) / s;
675 					}
676 					else if (matrix[5]> matrix[10])
677 					{
678 						const angle_t s = 2.0 * std::sqrt(1.0 + matrix[5] - matrix[0] - matrix[10]);
679 						vec.x = (matrix[1] + matrix[4]) / s;
680 						vec.y = 0.5 / s;
681 						vec.z = (matrix[6] + matrix[9]) / s;
682 						scalar = (matrix[2] + matrix[8]) / s;
683 					}
684 					else
685 					{
686 						const angle_t s = 2.0 * std::sqrt(1.0 + matrix[10] - matrix[0] - matrix[5]);
687 						vec.x = (matrix[2] + matrix[8]) / s;
688 						vec.y = (matrix[6] + matrix[9]) / s;
689 						vec.z = 0.5 / s;
690 						scalar = (matrix[1] + matrix[4]) / s;
691 					}
692 				}
693 #else
694 				scalar = std::sqrt(fmax( 0, 1 + matrix[0] + matrix[5] + matrix[10])) / 2;
695 				vec.x = std::sqrt(fmax( 0, 1 + matrix[0] - matrix[5] - matrix[10])) / 2;
696 				vec.y = std::sqrt(fmax( 0, 1 - matrix[0] + matrix[5] - matrix[10])) / 2;
697 				vec.z = std::sqrt(fmax( 0, 1 - matrix[0] - matrix[5] + matrix[10])) / 2;
698 				vec.x = copysign(vec.x, matrix[9] - matrix[6]);
699 				vec.y = copysign(vec.y, matrix[2] - matrix[8]);
700 				vec.z = copysign(vec.z, matrix[4] - matrix[1]);
701 #endif
702 			}
703 			;
704 
from_axis_and_angle(const Vec3 axis,const angle_t angle)705 			void from_axis_and_angle(const Vec3 axis, const angle_t angle)
706 			{
707 				vec = axis * sin(angle * 0.5);
708 				scalar = cos(angle * 0.5);
709 				normalize();
710 			}
711 			;
712 
get_axis_and_angle(Vec3 & axis,angle_t & angle)713 			void get_axis_and_angle(Vec3& axis, angle_t& angle) const
714 			{
715 				angle = acos(scalar);
716 				angle_t sin_angle= std::sqrt(1.0 - square(scalar));
717 
718 				if (fabs(sin_angle) < 0.0001)
719 					sin_angle = 1;
720 
721 				axis = vec / sin_angle;
722 			}
723 			;
724 
725 			Vec3 vec;
726 			angle_t scalar;
727 	};
728 
729 	inline std::ostream& operator<<(std::ostream& lhs, const Quaternion rhs)
730 	{
731 		return lhs << "[" << rhs.vec << ", " << rhs.scalar << "]";
732 	}
733 	;
734 
735 	/*!
736 	 \brief The base class for drawing untextured geometric primitives
737 
738 	 A variety of geometric primitives inherit from Shape.  They all make use
739 	 of its draw routine, but set their vertex data on their own.  Shapes are
740 	 used for things like columns of light.
741 	 */
742 	class EyeCandy;
743 
744 	class Shape
745 	{
746 		public:
Shape(EyeCandy * _base)747 			Shape(EyeCandy* _base)
748 			{
749 				base = _base;
750 			}
751 			;
752 			~Shape();
753 			void draw();
754 
755 			Vec3 pos;
756 			Vec3 color;
757 			alpha_t alpha;
758 			EyeCandy* base;
759 
760 		protected:
761 			int vertex_count;
762 			int facet_count;
763 			el::HardwareBuffer vertex_buffer;
764 			el::HardwareBuffer index_buffer;
765 
766 			class Facet
767 			{
768 				public:
Facet(int f1,int f2,int f3)769 					Facet(int f1, int f2, int f3)
770 					{
771 						f[0] = f1;
772 						f[1] = f2;
773 						f[2] = f3;
774 					}
775 					;
~Facet()776 					~Facet()
777 					{
778 					}
779 					;
780 
781 					int f[3];
782 			};
783 	};
784 
785 	class CaplessCylinder : public Shape
786 	{
787 		public:
788 			CaplessCylinder(EyeCandy* _base, const Vec3 _start,
789 				const Vec3 _end, const Vec3 _color, const alpha_t _alpha,
790 				const coord_t _radius, const int polys);
~CaplessCylinder()791 			~CaplessCylinder()
792 			{
793 			}
794 			;
795 
796 			coord_t radius;
797 			Vec3 start;
798 			Vec3 end;
799 			Vec3 orig_start;
800 			Vec3 orig_end;
801 	};
802 
803 	class Cylinder : public Shape
804 	{
805 		public:
806 			Cylinder(EyeCandy* _base, const Vec3 _start, const Vec3 _end,
807 				const Vec3 _color, const alpha_t _alpha, const coord_t _radius,
808 				const int polys);
~Cylinder()809 			~Cylinder()
810 			{
811 			}
812 			;
813 
814 			coord_t radius;
815 			Vec3 start;
816 			Vec3 end;
817 			Vec3 orig_start;
818 			Vec3 orig_end;
819 	};
820 
821 	class Sphere : public Shape
822 	{
823 		public:
824 			Sphere(EyeCandy* _base, const Vec3 pos, const Vec3 _color,
825 				const alpha_t _alpha, const coord_t _radius, const int polys);
~Sphere()826 			~Sphere()
827 			{
828 			}
829 			;
830 
831 			void average_points(const coord_t p1_first, const coord_t p2_first,
832 				const coord_t p1_second, const coord_t p2_second, coord_t& p,
833 				coord_t& q);
834 
835 			coord_t radius;
836 	};
837 
838 
839 	class CaplessCylinders
840 	{
841 		public:
842 			class CaplessCylinderItem
843 			{
844 				public:
CaplessCylinderItem(const Vec3 _start,const Vec3 _end,const Vec3 _color,const alpha_t _alpha,const coord_t _radius,const int _polys)845 					CaplessCylinderItem(const Vec3 _start,
846 						const Vec3 _end,
847 						const Vec3 _color,
848 						const alpha_t _alpha,
849 						const coord_t _radius,
850 						const int _polys)
851 					{
852 						start = _start;
853 						end = _end;
854 						color = _color;
855 						alpha = _alpha;
856 						radius = _radius;
857 						polys = _polys;
858 					}
859 
860 					Vec3 start;
861 					Vec3 end;
862 					Vec3 color;
863 					alpha_t alpha;
864 					coord_t radius;
865 					int polys;
866 			};
867 
868 			CaplessCylinders(EyeCandy* _base,
869 				const std::vector<CaplessCylinderItem> &items);
870 
871 			void draw(const float alpha_scale);
872 
873 		protected:
874 			struct CaplessCylindersVertex
875 			{
876 				GLfloat x, y, z;
877 				GLfloat nx, ny, nz;
878 				GLubyte r, g, b, a;
879 			};
880 
881 			EyeCandy* base;
882 			int vertex_count;
883 			int facet_count;
884 			el::HardwareBuffer vertex_buffer;
885 			el::HardwareBuffer index_buffer;
886 
887 	};
888 
889 	/*!
890 	 \brief The basic element of a geometric boundary comprised of sinous polar
891 	 coordinates elements.
892 	 */
893 	class PolarCoordElement
894 	{
895 		public:
896 			PolarCoordElement(const coord_t _frequency, const coord_t _offset,
897 				const coord_t _scalar, const coord_t _power);
~PolarCoordElement()898 			~PolarCoordElement()
899 			{
900 			}
901 			;
902 
903 			coord_t get_radius(const angle_t angle) const;
904 
905 			coord_t frequency;
906 			coord_t offset;
907 			coord_t scalar;
908 			coord_t power;
909 	};
910 
911 	/*!
912 	 \brief The basic element of a geometric boundary comprised of sinous polar
913 	 coordinates elements.
914 	 */
915 	class SmoothPolygonElement
916 	{
917 		public:
SmoothPolygonElement(const angle_t _angle,const coord_t _radius)918 			SmoothPolygonElement(const angle_t _angle, const coord_t _radius)
919 			{
920 				angle = _angle;
921 				radius = _radius;
922 			}
923 			;
~SmoothPolygonElement()924 			~SmoothPolygonElement()
925 			{
926 			}
927 			;
928 
929 			coord_t get_radius(const angle_t angle) const;
930 
931 			angle_t angle;
932 			coord_t radius;
933 	};
934 
935 	class ParticleMover;
936 	class Effect;
937 
938 	/*!
939 	 \brief An ultra-simplified particle element
940 
941 	 Used for a cheap kind of motion blur, if desired.  Contains only the most
942 	 elementary drawing information.
943 	 */
944 	class ParticleHistory
945 	{
946 		public:
ParticleHistory()947 			ParticleHistory()
948 			{
949 				size = 0.00001;
950 				texture = 0;
951 				color[0] = 0.0;
952 				color[1] = 0.0;
953 				color[2] = 0.0;
954 				alpha = 0.0;
955 				pos = Vec3(0.0, 0.0, 0.0);
956 			}
957 			;
ParticleHistory(const coord_t _size,const GLuint _texture,const color_t _red,const color_t _green,const color_t _blue,const alpha_t _alpha,const Vec3 _pos)958 			ParticleHistory(const coord_t _size, const GLuint _texture,
959 				const color_t _red, const color_t _green, const color_t _blue,
960 				const alpha_t _alpha, const Vec3 _pos)
961 			{
962 				size = _size;
963 				texture = _texture;
964 				color[0] = _red;
965 				color[1] = _green;
966 				color[2] = _blue;
967 				alpha = _alpha;
968 				pos = _pos;
969 			}
970 			;
~ParticleHistory()971 			~ParticleHistory()
972 			{
973 			}
974 			;
975 
976 			coord_t size;
977 			GLuint texture;
978 			color_t color[3];
979 			alpha_t alpha;
980 			Vec3 pos;
981 	};
982 
983 	/*!
984 	 \brief That which adds the term "particle" to the term "particle effect"
985 
986 	 Apart from the occasional Shape, Particles are what you see when an effect
987 	 is going off.  Particles are created in Effects, and are referenced both in
988 	 the effect and the base EyeCandy object.  A particle can order itself to be
989 	 deleted by returning false in its idle function, but the EyeCandy object
990 	 retains the right to terminate it at any time without warning.  Particles
991 	 can be set to "flare" randomly as they move through space, and they can
992 	 optionally use a basic form of motion blur (not usually worth it).
993 	 */
994 	class Particle
995 	{
996 		public:
997 			Particle(Effect* _effect, ParticleMover* _mover, const Vec3 _pos,
998 				const Vec3 _velocity, const coord_t _size = 1.0f);
999 			virtual ~Particle();
1000 
1001 			virtual bool idle(const Uint64 delta_t) = 0;
1002 			virtual Uint32 get_texture() = 0;
1003 			virtual light_t estimate_light_level() const = 0;
get_light_level()1004 			virtual light_t get_light_level()
1005 			{
1006 				return alpha * size / 1500;
1007 			}
1008 			;
deletable()1009 			virtual bool deletable()
1010 			{
1011 				return true;
1012 			}
1013 			;
1014 
get_burn()1015 			virtual float get_burn() const
1016 			{
1017 				return 1.0f;
1018 			}
1019 
1020 			void draw(const Uint64 usec);
1021 			virtual coord_t flare() const;
1022 
1023 			ParticleMover* mover;
1024 			Vec3 velocity;
1025 			Vec3 pos;
1026 			color_t color[3];
1027 			alpha_t alpha;
1028 			coord_t size;
1029 			Uint64 born;
1030 			energy_t energy;
1031 			coord_t flare_max; // Bigger values mean bigger flares.  1.0 to max particle size.
1032 			coord_t flare_exp; // Lower values mean rarer flares.  0.0 to 1.0.
1033 			coord_t flare_frequency; // Geographic scalar between flares.
1034 			Uint16 state;
1035 			Effect* effect;
1036 			EyeCandy* base;
1037 
1038 			ParticleHistory* motion_blur;
1039 			int cur_motion_blur_point;
1040 
1041 	};
1042 
1043 	/*!
1044 	 \brief A base class for classes that can move particles around
1045 
1046 	 Movers take in a particle and a length of time, and put the particle in its
1047 	 new position.  The most basic particle mover simply follows your typical
1048 	 "position += velocity * time" algorithm.
1049 	 */
1050 	class ParticleMover
1051 	{
1052 		public:
1053 			ParticleMover(Effect* _effect);
~ParticleMover()1054 			virtual ~ParticleMover()
1055 			{
1056 			}
1057 			;
1058 
move(Particle & p,Uint64 usec)1059 			virtual void move(Particle& p, Uint64 usec)
1060 			{
1061 				p.pos += p.velocity * (usec / 1000000.0);
1062 			}
1063 			;
calculate_energy(const Particle & p)1064 			virtual energy_t calculate_energy(const Particle& p) const
1065 			{
1066 				return 0;
1067 			}
1068 			;
1069 
1070 			Vec3 vec_shift(const Vec3 src, const Vec3 dest,
1071 				const percent_t percent) const;
1072 			Vec3 vec_shift_amount(const Vec3 src, const Vec3 dest,
1073 				const coord_t amount) const;
1074 			Vec3 nonpreserving_vec_shift(const Vec3 src, const Vec3 dest,
1075 				const percent_t percent) const;
1076 			Vec3 nonpreserving_vec_shift_amount(const Vec3 src,
1077 				const Vec3 dest, const percent_t amount) const;
1078 
1079 			Effect* effect;
1080 			EyeCandy* base;
1081 
attachParticle(Particle *)1082 			virtual void attachParticle(Particle *)
1083 			{
1084 			}
1085 			;
detachParticle(Particle *)1086 			virtual void detachParticle(Particle *)
1087 			{
1088 			}
1089 			;
1090 	};
1091 
1092 	/*!
1093 	 \brief A Mover which applies an acceleration gradient.
1094 	 */
1095 	class GradientMover : public ParticleMover
1096 	{
1097 		public:
GradientMover(Effect * _effect)1098 			GradientMover(Effect* _effect) :
1099 				ParticleMover(_effect)
1100 			{
1101 			}
1102 			;
~GradientMover()1103 			virtual ~GradientMover()
1104 			{
1105 			}
1106 			;
1107 
1108 			virtual void move(Particle& p, Uint64 usec);
1109 
1110 			virtual Vec3 get_force_gradient(Particle& p) const;
1111 			virtual Vec3 get_obstruction_gradient(Particle& p) const;
1112 	};
1113 
1114 	/*!
1115 	 \brief A simple example of a gradient mover, used for simple smoke effects.
1116 	 */
1117 	class SmokeMover : public GradientMover
1118 	{
1119 		public:
SmokeMover(Effect * _effect)1120 			SmokeMover(Effect* _effect) :
1121 				GradientMover(_effect)
1122 			{
1123 				strength = 1.0;
1124 			}
1125 			;
SmokeMover(Effect * _effect,const coord_t _strength)1126 			SmokeMover(Effect* _effect, const coord_t _strength) :
1127 				GradientMover(_effect)
1128 			{
1129 				strength = _strength;
1130 			}
1131 			;
~SmokeMover()1132 			virtual ~SmokeMover()
1133 			{
1134 			}
1135 			;
1136 
1137 			//  virtual void move(Particle& p, Uint64 usec);
1138 			virtual Vec3 get_force_gradient(Particle& p) const;
1139 
1140 			coord_t strength;
1141 	};
1142 
1143 	/*!
1144 	 \brief A gradient mover which whirls/pinches particles.
1145 
1146 	 If spiral speed is equal to -pinch_rate, particles will follow a rough
1147 	 circle.  A greater magnitude pinch and they'll go inwards.  A lesser
1148 	 magnitude pinch and they'll spiral outwards.
1149 	 */
1150 	class SpiralMover : public GradientMover
1151 	{
1152 		public:
SpiralMover(Effect * _effect,Vec3 * _center,const coord_t _spiral_speed,const coord_t _pinch_rate)1153 			SpiralMover(Effect* _effect, Vec3* _center,
1154 				const coord_t _spiral_speed, const coord_t _pinch_rate) :
1155 				GradientMover(_effect)
1156 			{
1157 				center = _center;
1158 				spiral_speed = _spiral_speed;
1159 				pinch_rate = _pinch_rate;
1160 			}
1161 			;
~SpiralMover()1162 			virtual ~SpiralMover()
1163 			{
1164 			}
1165 			;
1166 
1167 			virtual Vec3 get_force_gradient(Particle& p) const;
1168 
1169 			Vec3* center;
1170 			coord_t spiral_speed;
1171 			coord_t pinch_rate;
1172 	};
1173 
1174 	/*!
1175 	 \brief Defines a range to be used by bounding movers and spawners.
1176 	 */
1177 	class BoundingRange
1178 	{
1179 		public:
BoundingRange()1180 			BoundingRange()
1181 			{
1182 			}
1183 			;
~BoundingRange()1184 			virtual ~BoundingRange()
1185 			{
1186 			}
1187 			;
1188 
1189 			virtual coord_t get_radius(const angle_t angle) const = 0;
1190 	};
1191 
1192 	/*!
1193 	 \brief A bounding range composed of a sum of sinusoidal elements in polar
1194 	 coordinates space.
1195 	 */
1196 
1197 	class PolarCoordsBoundingRange : public BoundingRange
1198 	{
1199 		public:
PolarCoordsBoundingRange()1200 			PolarCoordsBoundingRange()
1201 			{
1202 			}
1203 			;
~PolarCoordsBoundingRange()1204 			~PolarCoordsBoundingRange()
1205 			{
1206 			}
1207 			;
1208 
1209 			virtual coord_t get_radius(const angle_t angle) const;
1210 
1211 			std::vector<PolarCoordElement> elements;
1212 	};
1213 
1214 	/*!
1215 	 \brief A bounding range composed of a series of angles and radii forming a
1216 	 polygon that is smoothly interpolated.
1217 	 */
1218 
1219 	class SmoothPolygonBoundingRange : public BoundingRange
1220 	{
1221 		public:
SmoothPolygonBoundingRange()1222 			SmoothPolygonBoundingRange()
1223 			{
1224 			}
1225 			;
~SmoothPolygonBoundingRange()1226 			~SmoothPolygonBoundingRange()
1227 			{
1228 			}
1229 			;
1230 
1231 			virtual coord_t get_radius(const angle_t angle) const;
1232 
1233 			std::vector<SmoothPolygonElement> elements;
1234 	};
1235 
1236 	/*!
1237 	 \brief A gradient mover that confines particles to a bounding range
1238 
1239 	 This is a base class for specific bounding movers.
1240 	 */
1241 	class BoundingMover : public GradientMover
1242 	{
1243 		public:
1244 			BoundingMover(Effect* _effect, const Vec3 _center_pos,
1245 				BoundingRange* _bounding_range, const coord_t _force);
~BoundingMover()1246 			~BoundingMover()
1247 			{
1248 			}
1249 			;
1250 
1251 			virtual Vec3 get_force_gradient(Particle& p) const;
1252 
1253 			coord_t force;
1254 			Vec3 center_pos;
1255 			BoundingRange* bounding_range;
1256 	};
1257 
1258 	/*!
1259 	 \brief A simple gradient mover that implements downward-only gravitational
1260 	 acceleration.
1261 	 */
1262 	class SimpleGravityMover : public GradientMover // Your basic downward acceleration.
1263 	{
1264 		public:
SimpleGravityMover(Effect * _effect)1265 			SimpleGravityMover(Effect* _effect) :
1266 				GradientMover(_effect)
1267 			{
1268 			}
1269 			;
~SimpleGravityMover()1270 			virtual ~SimpleGravityMover()
1271 			{
1272 			}
1273 			;
1274 
1275 			virtual Vec3 get_force_gradient(Particle& p) const;
1276 	};
1277 
1278 	/*!
1279 	 \brief A true gravity mover
1280 
1281 	 This mover implements a near physics-sim-quality gravitational attraction
1282 	 mechanism (single source gravity only, though).  This includes things like
1283 	 tracking how much potential/kinetic energy a particle has in order to
1284 	 compensate for the uneven acceleration that occurs between frames -- a
1285 	 particle passing right past the center may be 5 units away on one frame, and
1286 	 0.1 units on the next frame.  The latter frame will experience a much
1287 	 greater gravitational attraction, even though it really should be taking a
1288 	 smooth curve.  The only better way to keep particles moving properly than
1289 	 our energy-tracking mechanism would be to actually integrate across the path,
1290 	 and there's no way we'd have the CPU time for that.
1291 	 */
1292 	class GravityMover : public GradientMover // A full-featured gravity simulator.
1293 	{
1294 		public:
1295 			GravityMover(Effect* _effect, Vec3* _center);
1296 			GravityMover(Effect* _effect, Vec3* _center, const energy_t _mass);
~GravityMover()1297 			virtual ~GravityMover()
1298 			{
1299 			}
1300 			;
1301 
1302 			void set_gravity_center(Vec3* _gravity_center);
1303 			virtual void move(Particle& p, Uint64 usec);
1304 			energy_t calculate_velocity_energy(const Particle& p) const;
1305 			energy_t calculate_position_energy(const Particle& p) const;
1306 			coord_t gravity_dist(const Particle& p, const Vec3& center) const;
1307 			virtual energy_t calculate_energy(const Particle& p) const;
1308 
1309 			Vec3 old_gravity_center;
1310 			Vec3* gravity_center_ptr;
1311 			Vec3 gravity_center;
1312 			energy_t mass;
1313 			energy_t max_gravity;
1314 	};
1315 
1316 	/*!
1317 	 \brief The base class for particle spawners
1318 
1319 	 Particle spawners are effort-saving objects that can be called to determine
1320 	 coordinates for placement of new particles.
1321 	 */
1322 	class ParticleSpawner
1323 	{
1324 		public:
ParticleSpawner()1325 			ParticleSpawner()
1326 			{
1327 			}
1328 			;
~ParticleSpawner()1329 			virtual ~ParticleSpawner()
1330 			{
1331 			}
1332 			;
1333 
1334 			virtual Vec3 get_new_coords() = 0;
1335 	};
1336 
1337 	/*!
1338 	 \brief Base class for objects used in setting up an IFS (Iterative Function
1339 	 System) particle spawner's shape.
1340 	 */
1341 	class IFSParticleElement
1342 	{
1343 		public:
IFSParticleElement(const coord_t _scale)1344 			IFSParticleElement(const coord_t _scale)
1345 			{
1346 				scale = _scale;
1347 				inv_scale = 1.0 - _scale;
1348 			}
1349 			;
~IFSParticleElement()1350 			virtual ~IFSParticleElement()
1351 			{
1352 			}
1353 			;
1354 
1355 			virtual Vec3 get_new_coords(const Vec3& center) = 0;
1356 
1357 			coord_t scale;
1358 			coord_t inv_scale;
1359 	};
1360 
1361 	/*!
1362 	 \brief Your standard IFS linear-interpolation between points
1363 	 */
1364 	class IFSLinearElement : public IFSParticleElement
1365 	{
1366 		public:
IFSLinearElement(const Vec3 _center,const coord_t _scale)1367 			IFSLinearElement(const Vec3 _center, const coord_t _scale) :
1368 				IFSParticleElement(_scale)
1369 			{
1370 				center = _center;
1371 			}
1372 			;
~IFSLinearElement()1373 			virtual ~IFSLinearElement()
1374 			{
1375 			}
1376 			;
1377 
1378 			virtual Vec3 get_new_coords(const Vec3& pos);
1379 			Vec3 center;
1380 	};
1381 
1382 	/*!
1383 	 \brief A "flame" extension to IFS.  Only works so-so in this case.
1384 	 */
1385 	class IFSSinusoidalElement : public IFSParticleElement
1386 	{
1387 		public:
IFSSinusoidalElement(const coord_t _scale,const Vec3 _offset,const Vec3 _scalar,const Vec3 _scalar2)1388 			IFSSinusoidalElement(const coord_t _scale, const Vec3 _offset,
1389 				const Vec3 _scalar, const Vec3 _scalar2) :
1390 				IFSParticleElement(_scale)
1391 			{
1392 				offset = _offset;
1393 				scalar = _scalar;
1394 				scalar2 = _scalar2;
1395 			}
1396 			;
~IFSSinusoidalElement()1397 			virtual ~IFSSinusoidalElement()
1398 			{
1399 			}
1400 			;
1401 
1402 			virtual Vec3 get_new_coords(const Vec3& pos);
1403 
1404 			Vec3 offset;
1405 			Vec3 scalar;
1406 			Vec3 scalar2;
1407 	};
1408 
1409 	/*!
1410 	 \brief A "flame" extension to IFS.  Only works so-so in this case.
1411 	 */
1412 	class IFSSphericalElement : public IFSParticleElement
1413 	{
1414 		public:
IFSSphericalElement(const coord_t _scale,const Vec3 _numerator_adjust,const Vec3 _denominator_adjust)1415 			IFSSphericalElement(const coord_t _scale,
1416 				const Vec3 _numerator_adjust, const Vec3 _denominator_adjust) :
1417 				IFSParticleElement(_scale)
1418 			{
1419 				numerator_adjust = _numerator_adjust;
1420 				denominator_adjust = _denominator_adjust;
1421 			}
1422 			;
~IFSSphericalElement()1423 			virtual ~IFSSphericalElement()
1424 			{
1425 			}
1426 			;
1427 
1428 			virtual Vec3 get_new_coords(const Vec3& pos);
1429 			Vec3 numerator_adjust;
1430 			Vec3 denominator_adjust;
1431 	};
1432 
1433 	/*!
1434 	 \brief A "flame" extension to IFS.  Only works so-so in this case.
1435 	 */
1436 	class IFSRingElement : public IFSParticleElement
1437 	{
1438 		public:
IFSRingElement(const coord_t _scale,const Vec3 _numerator_adjust,const Vec3 _denominator_adjust)1439 			IFSRingElement(const coord_t _scale, const Vec3 _numerator_adjust,
1440 				const Vec3 _denominator_adjust) :
1441 				IFSParticleElement(_scale)
1442 			{
1443 				numerator_adjust = _numerator_adjust;
1444 				denominator_adjust = _denominator_adjust;
1445 			}
1446 			;
~IFSRingElement()1447 			virtual ~IFSRingElement()
1448 			{
1449 			}
1450 			;
1451 
1452 			virtual Vec3 get_new_coords(const Vec3& pos);
1453 			Vec3 numerator_adjust;
1454 			Vec3 denominator_adjust;
1455 	};
1456 
1457 	/*!
1458 	 \brief A "flame" extension to IFS.  Only works so-so in this case.
1459 	 */
1460 	class IFSSwirlElement : public IFSParticleElement
1461 	{
1462 		public:
IFSSwirlElement(const coord_t _scale)1463 			IFSSwirlElement(const coord_t _scale) :
1464 				IFSParticleElement(_scale)
1465 			{
1466 			}
1467 			;
~IFSSwirlElement()1468 			virtual ~IFSSwirlElement()
1469 			{
1470 			}
1471 			;
1472 
1473 			virtual Vec3 get_new_coords(const Vec3& pos);
1474 	};
1475 
1476 	/*!
1477 	 \brief A "flame" extension to IFS.  Only works so-so in this case.
1478 	 */
1479 	class IFS2DSwirlElement : public IFSParticleElement
1480 	{
1481 		public:
IFS2DSwirlElement(const coord_t _scale)1482 			IFS2DSwirlElement(const coord_t _scale) :
1483 				IFSParticleElement(_scale)
1484 			{
1485 			}
1486 			;
~IFS2DSwirlElement()1487 			virtual ~IFS2DSwirlElement()
1488 			{
1489 			}
1490 			;
1491 
1492 			virtual Vec3 get_new_coords(const Vec3& pos);
1493 	};
1494 
1495 	/*!
1496 	 \brief A "flame" extension to IFS.  Only works so-so in this case.
1497 	 */
1498 	class IFSHorseshoeElement : public IFSParticleElement
1499 	{
1500 		public:
IFSHorseshoeElement(const coord_t _scale)1501 			IFSHorseshoeElement(const coord_t _scale) :
1502 				IFSParticleElement(_scale)
1503 			{
1504 			}
1505 			;
~IFSHorseshoeElement()1506 			virtual ~IFSHorseshoeElement()
1507 			{
1508 			}
1509 			;
1510 
1511 			virtual Vec3 get_new_coords(const Vec3& pos);
1512 	};
1513 
1514 	/*!
1515 	 \brief A "flame" extension to IFS.  Only works so-so in this case.
1516 	 */
1517 	class IFS2DHorseshoeElement : public IFSParticleElement
1518 	{
1519 		public:
IFS2DHorseshoeElement(const coord_t _scale)1520 			IFS2DHorseshoeElement(const coord_t _scale) :
1521 				IFSParticleElement(_scale)
1522 			{
1523 			}
1524 			;
~IFS2DHorseshoeElement()1525 			virtual ~IFS2DHorseshoeElement()
1526 			{
1527 			}
1528 			;
1529 
1530 			virtual Vec3 get_new_coords(const Vec3& pos);
1531 	};
1532 
1533 	/*!
1534 	 \brief An IFS (Iterative Function System) particle spawner, for fractaline
1535 	 spawning shapes.
1536 	 */
1537 	class IFSParticleSpawner : public ParticleSpawner
1538 	{
1539 		public:
IFSParticleSpawner()1540 			IFSParticleSpawner()
1541 			{
1542 				pos = Vec3(0.0, 0.0, 0.0);
1543 			}
1544 			;
1545 			IFSParticleSpawner(const int count, const coord_t size);
1546 			IFSParticleSpawner(const int count, const Vec3 scale);
1547 			virtual ~IFSParticleSpawner();
1548 
1549 			virtual void generate(const int count, const Vec3 scale);
1550 			virtual Vec3 get_new_coords();
1551 
1552 			std::vector<IFSParticleElement*> ifs_elements;
1553 			Vec3 pos;
1554 	};
1555 
1556 	/*!
1557 	 \brief A sample IFS spawner: spawns particles in a Sierpinski tetrahedron.
1558 	 */
1559 	class SierpinskiIFSParticleSpawner : public IFSParticleSpawner // Just a sample.
1560 	{
1561 		public:
SierpinskiIFSParticleSpawner()1562 			SierpinskiIFSParticleSpawner()
1563 			{
1564 				ifs_elements.push_back(new IFSLinearElement(Vec3(0.0, 1, 0.0), 0.5));
1565 				ifs_elements.push_back(new IFSLinearElement(Vec3(1, -1, -1), 0.5));
1566 				ifs_elements.push_back(new IFSLinearElement(Vec3(-1.155, -1, -1.155), 0.5));
1567 				ifs_elements.push_back(new IFSLinearElement(Vec3(0.0, -1, 1), 0.5));
1568 			}
1569 			;
SierpinskiIFSParticleSpawner(float scale)1570 			SierpinskiIFSParticleSpawner(float scale)
1571 			{
1572 				ifs_elements.push_back(new IFSLinearElement(Vec3(0.0 * scale, 1 * scale, 0.0 * scale), 0.5 * scale));
1573 				ifs_elements.push_back(new IFSLinearElement(Vec3(1 * scale, -1 * scale, -1 * scale), 0.5 * scale));
1574 				ifs_elements.push_back(new IFSLinearElement(Vec3(-1.155 * scale, -1 * scale, -1.155 * scale), 0.5 * scale));
1575 				ifs_elements.push_back(new IFSLinearElement(Vec3(0.0 * scale, -1 * scale, 1 * scale), 0.5 * scale));
1576 			}
1577 			;
1578 	};
1579 
1580 	/*!
1581 	 \brief Spawns particles throughout a sphere.
1582 	 */
1583 	class FilledSphereSpawner : public ParticleSpawner
1584 	{
1585 		public:
FilledSphereSpawner(const coord_t _radius)1586 			FilledSphereSpawner(const coord_t _radius)
1587 			{
1588 				radius = _radius;
1589 			}
1590 			;
~FilledSphereSpawner()1591 			virtual ~FilledSphereSpawner()
1592 			{
1593 			}
1594 			;
1595 
1596 			virtual Vec3 get_new_coords();
1597 
1598 			coord_t radius;
1599 	};
1600 
1601 	/*!
1602 	 \brief Spawns particles throughout an ellipsoid.
1603 	 */
1604 	class FilledEllipsoidSpawner : public ParticleSpawner
1605 	{
1606 		public:
FilledEllipsoidSpawner(const Vec3 _radius)1607 			FilledEllipsoidSpawner(const Vec3 _radius)
1608 			{
1609 				radius = _radius;
1610 			}
1611 			;
~FilledEllipsoidSpawner()1612 			virtual ~FilledEllipsoidSpawner()
1613 			{
1614 			}
1615 			;
1616 
1617 			virtual Vec3 get_new_coords();
1618 
1619 			Vec3 radius;
1620 	};
1621 
1622 	/*!
1623 	 \brief Spawns particles on the rim of a sphere.
1624 	 */
1625 	class HollowSphereSpawner : public ParticleSpawner
1626 	{
1627 		public:
HollowSphereSpawner(const coord_t _radius)1628 			HollowSphereSpawner(const coord_t _radius)
1629 			{
1630 				radius = _radius;
1631 			}
1632 			;
~HollowSphereSpawner()1633 			virtual ~HollowSphereSpawner()
1634 			{
1635 			}
1636 			;
1637 
1638 			virtual Vec3 get_new_coords();
1639 
1640 			coord_t radius;
1641 	};
1642 
1643 	/*!
1644 	 \brief Spawns particles on the rim of an ellipsoid.
1645 	 */
1646 	class HollowEllipsoidSpawner : public ParticleSpawner
1647 	{
1648 		public:
HollowEllipsoidSpawner(const Vec3 _radius)1649 			HollowEllipsoidSpawner(const Vec3 _radius)
1650 			{
1651 				radius = _radius;
1652 			}
1653 			;
~HollowEllipsoidSpawner()1654 			virtual ~HollowEllipsoidSpawner()
1655 			{
1656 			}
1657 			;
1658 
1659 			virtual Vec3 get_new_coords();
1660 
1661 			Vec3 radius;
1662 	};
1663 
1664 	/*!
1665 	 \brief Spawns particles throughout a two-dimensional disc.
1666 	 */
1667 	class FilledDiscSpawner : public ParticleSpawner
1668 	{
1669 		public:
FilledDiscSpawner(const coord_t _radius)1670 			FilledDiscSpawner(const coord_t _radius)
1671 			{
1672 				radius = _radius;
1673 			}
1674 			;
~FilledDiscSpawner()1675 			virtual ~FilledDiscSpawner()
1676 			{
1677 			}
1678 			;
1679 
1680 			virtual Vec3 get_new_coords();
1681 
1682 			coord_t radius;
1683 	};
1684 
1685 	/*!
1686 	 \brief Spawns particles on the rim of a two-dimensional disc.
1687 	 */
1688 	class HollowDiscSpawner : public ParticleSpawner
1689 	{
1690 		public:
HollowDiscSpawner(const coord_t _radius)1691 			HollowDiscSpawner(const coord_t _radius)
1692 			{
1693 				radius = _radius;
1694 			}
1695 			;
~HollowDiscSpawner()1696 			virtual ~HollowDiscSpawner()
1697 			{
1698 			}
1699 			;
1700 
1701 			virtual Vec3 get_new_coords();
1702 
1703 			coord_t radius;
1704 	};
1705 
1706 	/*!
1707 	 \brief Spawns particles within a range
1708 	 */
1709 	class FilledBoundingSpawner : public ParticleSpawner
1710 	{
1711 		public:
1712 			FilledBoundingSpawner(BoundingRange* _bounding_range,
1713 				Vec3* _center, Vec3* _base_center, float _range_scalar = 1.0)
1714 			{
1715 				bounding_range = _bounding_range;
1716 				center = _center;
1717 				base_center = _base_center;
1718 				range_scalar = _range_scalar;
1719 			}
1720 			;
~FilledBoundingSpawner()1721 			virtual ~FilledBoundingSpawner()
1722 			{
1723 			}
1724 			;
1725 
1726 			virtual Vec3 get_new_coords();
1727 			coord_t get_area() const;
1728 
1729 			BoundingRange* bounding_range;
1730 			Vec3* center;
1731 			Vec3* base_center;
1732 			float range_scalar;
1733 	};
1734 
1735 	/*!
1736 	 \brief Spawns particles within a range
1737 	 */
1738 	class NoncheckingFilledBoundingSpawner : public ParticleSpawner
1739 	{
1740 		public:
NoncheckingFilledBoundingSpawner(BoundingRange * _bounding_range)1741 			NoncheckingFilledBoundingSpawner(BoundingRange* _bounding_range)
1742 			{
1743 				bounding_range = _bounding_range;
1744 			}
1745 			;
~NoncheckingFilledBoundingSpawner()1746 			virtual ~NoncheckingFilledBoundingSpawner()
1747 			{
1748 			}
1749 			;
1750 
1751 			virtual Vec3 get_new_coords();
1752 			coord_t get_area() const;
1753 
1754 			BoundingRange* bounding_range;
1755 	};
1756 
1757 	/*!
1758 	 \brief Spawns particles on the edge of a range
1759 	 */
1760 	class HollowBoundingSpawner : public ParticleSpawner
1761 	{
1762 		public:
HollowBoundingSpawner(BoundingRange * _bounding_range)1763 			HollowBoundingSpawner(BoundingRange* _bounding_range)
1764 			{
1765 				bounding_range = _bounding_range;
1766 			}
1767 			;
~HollowBoundingSpawner()1768 			virtual ~HollowBoundingSpawner()
1769 			{
1770 			}
1771 			;
1772 
1773 			virtual Vec3 get_new_coords();
1774 			coord_t get_area() const;
1775 
1776 			BoundingRange* bounding_range;
1777 	};
1778 
1779 	/*!
1780 	 \brief Base class for obstructions -- objects that deflect particles.
1781 	 */
1782 	class Obstruction
1783 	{
1784 		public:
1785 			Obstruction(const coord_t _max_distance, const coord_t _force);
~Obstruction()1786 			virtual ~Obstruction()
1787 			{
1788 			}
1789 			;
1790 
1791 			virtual Vec3 get_force_gradient(Particle& p) const = 0;
1792 
1793 			coord_t max_distance;
1794 			coord_t max_distance_squared;
1795 			coord_t force;
1796 	};
1797 
1798 	/*!
1799 	 \brief An obstruction shaped like a vertical cylinder of infinite length (fast)
1800 	 */
1801 	class SimpleCylinderObstruction : public Obstruction // Vertical and infinite.  Speeds up the math if you don't need the extra detail.
1802 	{
1803 		public:
SimpleCylinderObstruction(Vec3 * _pos,const coord_t _max_distance,const coord_t _force)1804 			SimpleCylinderObstruction(Vec3* _pos, const coord_t _max_distance,
1805 				const coord_t _force) :
1806 				Obstruction(_max_distance, _force)
1807 			{
1808 				pos = _pos;
1809 			}
1810 			;
~SimpleCylinderObstruction()1811 			virtual ~SimpleCylinderObstruction()
1812 			{
1813 			}
1814 			;
1815 
1816 			virtual Vec3 get_force_gradient(Particle& p) const;
1817 
1818 			Vec3* pos;
1819 	};
1820 
1821 	/*!
1822 	 \brief Like above, but with top and bottom caps
1823 	 */
1824 	class CappedSimpleCylinderObstruction : public Obstruction
1825 	{
1826 		public:
CappedSimpleCylinderObstruction(Vec3 * _pos,const coord_t _max_distance,const coord_t _force,const coord_t _bottom,const coord_t _top)1827 			CappedSimpleCylinderObstruction(Vec3* _pos,
1828 				const coord_t _max_distance, const coord_t _force,
1829 				const coord_t _bottom, const coord_t _top) :
1830 				Obstruction(_max_distance, _force)
1831 			{
1832 				pos = _pos;
1833 				bottom = _bottom;
1834 				top = _top;
1835 			}
1836 			;
~CappedSimpleCylinderObstruction()1837 			virtual ~CappedSimpleCylinderObstruction()
1838 			{
1839 			}
1840 			;
1841 
1842 			virtual Vec3 get_force_gradient(Particle& p) const;
1843 
1844 			Vec3* pos;
1845 			coord_t bottom;
1846 			coord_t top;
1847 	};
1848 
1849 	/*!
1850 	 \brief An obstruction shaped like a cylinder of finite length at any angle (slow)
1851 	 */
1852 	class CylinderObstruction : public Obstruction // Note: assumes that (*end - *start) doesn't change.
1853 	{
1854 		public:
1855 			CylinderObstruction(Vec3* _start, Vec3* _end,
1856 				const coord_t _max_distance, const coord_t _force);
~CylinderObstruction()1857 			virtual ~CylinderObstruction()
1858 			{
1859 			}
1860 			;
1861 
1862 			virtual Vec3 get_force_gradient(Particle& p) const;
1863 
1864 			Vec3* start;
1865 			Vec3* end;
1866 			Vec3 length_vec;
1867 			coord_t length_vec_mag;
1868 	};
1869 
1870 	/*!
1871 	 \brief An obstruction shaped like a sphere (fast)
1872 	 */
1873 	class SphereObstruction : public Obstruction
1874 	{
1875 		public:
SphereObstruction(Vec3 * _pos,const coord_t _max_distance,const coord_t _force)1876 			SphereObstruction(Vec3* _pos, const coord_t _max_distance,
1877 				const coord_t _force) :
1878 				Obstruction(_max_distance, _force)
1879 			{
1880 				pos = _pos;
1881 			}
1882 			;
~SphereObstruction()1883 			virtual ~SphereObstruction()
1884 			{
1885 			}
1886 			;
1887 
1888 			virtual Vec3 get_force_gradient(Particle& p) const;
1889 
1890 			Vec3* pos;
1891 	};
1892 
1893 	/*!
1894 	 \brief An obstruction shaped like a box at any angle (slow)
1895 	 */
1896 	class BoxObstruction : public Obstruction
1897 	{
1898 		public:
BoxObstruction(const Vec3 _start,const Vec3 _end,Vec3 * _center,float * _sin_rot_x,float * _cos_rot_x,float * _sin_rot_y,float * _cos_rot_y,float * _sin_rot_z,float * _cos_rot_z,float * _sin_rot_x2,float * _cos_rot_x2,float * _sin_rot_y2,float * _cos_rot_y2,float * _sin_rot_z2,float * _cos_rot_z2,const coord_t _force)1899 			BoxObstruction(const Vec3 _start, const Vec3 _end, Vec3* _center,
1900 				float* _sin_rot_x, float*_cos_rot_x, float* _sin_rot_y,
1901 				float* _cos_rot_y, float* _sin_rot_z, float* _cos_rot_z,
1902 				float* _sin_rot_x2, float*_cos_rot_x2, float* _sin_rot_y2,
1903 				float* _cos_rot_y2, float* _sin_rot_z2, float* _cos_rot_z2,
1904 				const coord_t _force) :
1905 				Obstruction(1.0, _force)
1906 			{
1907 				start = _start;
1908 				end = _end;
1909 				midpoint = (start + end) / 2;
1910 				size = end - start;
1911 				max_distance_squared = size.magnitude_squared() / 4;
1912 				center = _center;
1913 				sin_rot_x = _sin_rot_x;
1914 				cos_rot_x = _cos_rot_x;
1915 				sin_rot_y = _sin_rot_y;
1916 				cos_rot_y = _cos_rot_y;
1917 				sin_rot_z = _sin_rot_z;
1918 				cos_rot_z = _cos_rot_z;
1919 				sin_rot_x2 = _sin_rot_x2;
1920 				cos_rot_x2 = _cos_rot_x2;
1921 				sin_rot_y2 = _sin_rot_y2;
1922 				cos_rot_y2 = _cos_rot_y2;
1923 				sin_rot_z2 = _sin_rot_z2;
1924 				cos_rot_z2 = _cos_rot_z2;
1925 			}
1926 			;
~BoxObstruction()1927 			virtual ~BoxObstruction()
1928 			{
1929 			}
1930 			;
1931 
1932 			virtual Vec3 get_force_gradient(Particle& p) const;
1933 
1934 			Vec3 start;
1935 			Vec3 end;
1936 			Vec3 midpoint; // Local coordinates
1937 			Vec3 size;
1938 			Vec3* center; // World coordinates
1939 			float max_distance_squared; // The squared radius of a verticle cylinder that describes the maximum area of effect of this bounded object.
1940 			float* sin_rot_x;
1941 			float* cos_rot_x;
1942 			float* sin_rot_y;
1943 			float* cos_rot_y;
1944 			float* sin_rot_z;
1945 			float* cos_rot_z;
1946 			float* sin_rot_x2;
1947 			float* cos_rot_x2;
1948 			float* sin_rot_y2;
1949 			float* cos_rot_y2;
1950 			float* sin_rot_z2;
1951 			float* cos_rot_z2;
1952 	};
1953 
1954 	/*!
1955 	 \brief The base element for every kind of eye candy effect
1956 
1957 	 An eye candy effect is a single visual phenominon composed of many particles.
1958 	 Example effects would include things like a "fountain" or a "fire".  Effects
1959 	 can run their course and then expire (simply by returning false in their
1960 	 idle function) or be persistent.  Effects are manually created by the caller
1961 	 (using the c++ "new" operator), but delete themselves.  For the caller to
1962 	 stop an effect, they need to flag it's recall flag; an effect must respect
1963 	 the recall flag when it sees it and clean up its state, then return false
1964 	 in its idle function.  Effects can also draw non-particle elements if they
1965 	 wish in their draw function.
1966 	 */
1967 	class Effect
1968 	{
1969 		public:
Effect()1970 			Effect()
1971 			{
1972 				state = 0;
1973 				motion_blur_points = 0;
1974 				motion_blur_fade_rate = 0.001;
1975 				born = get_time();
1976 				recall = false; //NOTE: All effects *must* respect recall!  If this is flagged, all of its particles should disappear ASAP, and the effect should then return false.
1977 				desired_LOD = 10;
1978 				LOD = desired_LOD;
1979 				active = true;
1980 				obstructions = &null_obstructions;
1981 				bounds = NULL;
1982 				particle_max_count = 0;
1983 				particle_count = 0;
1984 				buffer = 0;
1985 			}
1986 			;
~Effect()1987 			virtual ~Effect()
1988 			{
1989 				*dead = true;
1990 			}
1991 			;
1992 
1993 			void draw_particle(const coord_t size,
1994 				const Uint32 texture, const color_t r,
1995 				const color_t g, const color_t b,
1996 				const alpha_t alpha, const Vec3 pos,
1997 				const alpha_t burn);
1998 			void build_particle_buffer(const Uint64 time_diff);
1999 			void draw_particle_buffer();
2000 
register_particle(Particle * p)2001 			void register_particle(Particle* p)
2002 			{
2003 				particles[p] = true;
2004 			}
2005 			;
unregister_particle(Particle * p)2006 			void unregister_particle(Particle* p)
2007 			{
2008 				particles.erase(particles.find(p));
2009 			}
2010 			;
2011 
2012 			virtual EffectEnum get_type() = 0;
2013 			virtual bool idle(const Uint64 usec) = 0;
draw(const Uint64 usec)2014 			virtual void draw(const Uint64 usec)
2015 			{
2016 				for (auto& iter2: particles)
2017 				{
2018 					for (auto obstruction: *obstructions)
2019 					{
2020 						obstruction->get_force_gradient(*iter2.first);
2021 					}
2022 				}
2023 			}
2024 			;
request_LOD(const float _LOD)2025 			virtual void request_LOD(const float _LOD)
2026 			{
2027 				if (fabs(_LOD - (float)LOD) < 1.0)
2028 					return;
2029 				const Uint16 rounded_LOD = (Uint16)round(_LOD);
2030 				if (rounded_LOD <= desired_LOD)
2031 					LOD = rounded_LOD;
2032 				else
2033 					LOD = desired_LOD;
2034 			}
2035 			;
get_max_end_time()2036 			static Uint64 get_max_end_time()
2037 			{
2038 				return 0x8000000000000000ull;};
get_expire_time()2039 			virtual Uint64 get_expire_time()
2040 			{	return 0x8000000000000000ull;};
2041 
2042 #ifdef CLUSTER_INSIDES
getCluster()2043 			short getCluster () const
2044 			{
2045 				if (!pos)
2046 				return 0;
2047 				return get_cluster (int (pos->x / 0.5f), int (-pos->z / 0.5f));
2048 			}
2049 
belongsToCluster(short cluster)2050 			bool belongsToCluster (short cluster) const
2051 			{
2052 				short my_cluster = getCluster ();
2053 				return my_cluster == cluster || my_cluster == 0;
2054 			}
2055 #endif
2056 
2057 			EyeCandy* base;
2058 			int motion_blur_points;
2059 			percent_t motion_blur_fade_rate; //0 to 1; higher means less fade.
2060 			Uint16 state;
2061 			Uint64 born;
2062 			bool* dead; //Provided by the effect caller; set when this effect is going away.
2063 			Vec3* pos;
2064 			std::vector<Obstruction*>* obstructions;
2065 			std::map<Particle*, bool> particles;
2066 			BoundingRange* bounds;
2067 			bool active;
2068 			bool recall;
2069 			Uint16 desired_LOD;
2070 			Uint16 LOD;
2071 			protected:
2072 			el::HardwareBuffer particle_vertex_buffer;
2073 			Uint32 particle_max_count;
2074 			Uint32 particle_count;
2075 			float* buffer;
2076 		};
2077 
2078 		/*!
2079 		 \brief The core object of all eye candy
2080 
2081 		 The EyeCandy object (there should only ever be one) encapsulates all of the
2082 		 effects and particles that will occur in a program.  There are numerous
2083 		 options, flags, and settings that can be set for the eye candy object.
2084 		 A few critical notes:
2085 
2086 		 1) When initializing the object, be sure to load_textures().
2087 		 2) Optimially, give it a few lights (the add_light() function).
2088 		 3) Between idle calls, set how much time has passed (time_diff) and the
2089 		 camera's location (set_camera()).
2090 		 4) The key functions to call every cycle are ec_idle() and ec_draw().
2091 		 5) When not drawing, don't call ec_draw().  If you wish to save CPU time,
2092 		 you can skip calling ec_idle() most of the time, but if you ever delete
2093 		 effects, you'll want to let it run once to help clear out the system.
2094 
2095 		 */
2096 		class EyeCandy
2097 		{
2098 			public:
2099 
2100 			EyeCandy();
2101 			EyeCandy(int _max_particles);
2102 			~EyeCandy();
2103 
2104 			void set_thresholds(const int _max_particles, const float min_framerate, const float max_framerate);
2105 			void load_textures();
2106 			void push_back_effect(Effect* e);
2107 			bool push_back_particle(Particle* p);
set_camera(const Vec3 & _camera)2108 			void set_camera(const Vec3& _camera)
2109 			{	camera = _camera;};
set_center(const Vec3 & _center)2110 			void set_center(const Vec3& _center)
2111 			{	center = _center;};
set_dimensions(const coord_t _width,const coord_t _height,const angle_t _zoom)2112 			void set_dimensions(const coord_t _width, const coord_t _height, const angle_t _zoom)
2113 			{	width = _width; height = _height; zoom = _zoom; temp_sprite_scalar = sprite_scalar * _height / _zoom;};
set_sprite_scalar(const coord_t _scalar)2114 			void set_sprite_scalar(const coord_t _scalar)
2115 			{	sprite_scalar = _scalar; temp_sprite_scalar = _scalar * height;};
2116 			void draw();
2117 			void idle();
2118 			void add_light(GLenum light_id);
2119 			void start_draw();
2120 			void end_draw();
2121 			Uint32 get_texture(const TextureEnum type) const;
2122 			void set_particle_texture_combiner();
2123 			void set_shape_texture_combiner(const float alpha_scale);
2124 
2125 #if __cplusplus >= 201103L
2126 			std::unique_ptr<el::HardwareBuffer> index_buffer;
2127 #else
2128 			std::auto_ptr<el::HardwareBuffer> index_buffer;
2129 #endif
2130 			Uint32 texture_atlas;
2131 			Uint32 texture_burn;
2132 			int max_particles;
2133 			Uint64 max_usec_per_particle_move;
2134 			coord_t max_point_size;
2135 			coord_t max_allowable_point_size;
2136 			Vec3 camera;
2137 			Vec3 center;
2138 			coord_t width;
2139 			coord_t height;
2140 			angle_t zoom;
2141 			Uint64 time_diff;
2142 			float framerate;
2143 			float max_fps;
2144 			light_t lighting_scalar;
2145 			light_t light_estimate;
2146 			bool use_lights;
2147 			std::vector< std::pair<Particle*, light_t> > light_particles;
2148 			unsigned int LOD_1_threshold;
2149 			unsigned int LOD_2_threshold;
2150 			unsigned int LOD_3_threshold;
2151 			unsigned int LOD_4_threshold;
2152 			unsigned int LOD_5_threshold;
2153 			unsigned int LOD_6_threshold;
2154 			unsigned int LOD_7_threshold;
2155 			unsigned int LOD_8_threshold;
2156 			unsigned int LOD_9_threshold;
2157 			float LOD_1_time_threshold;
2158 			float LOD_2_time_threshold;
2159 			float LOD_3_time_threshold;
2160 			float LOD_4_time_threshold;
2161 			float LOD_5_time_threshold;
2162 			float LOD_6_time_threshold;
2163 			float LOD_7_time_threshold;
2164 			float LOD_8_time_threshold;
2165 			float LOD_9_time_threshold;
2166 			float LOD_10_time_threshold;
2167 			int allowable_particles_to_add;
2168 			bool draw_shapes;
2169 			Uint16 last_forced_LOD;
2170 			coord_t billboard_scalar;
2171 			coord_t sprite_scalar;
2172 			coord_t temp_sprite_scalar;
2173 			Vec3 corner_offset1;
2174 			Vec3 corner_offset2;
2175 			std::vector<Effect*> effects;
2176 			std::vector<Particle*> particles;
2177 			std::vector<GLenum> lights;
2178 		};
2179 
2180 		extern bool ec_error_status;
get_error_status()2181 		inline bool get_error_status()
2182 		{	return ec_error_status;};
2183 
2184 		/*!
2185 		 \brief A class to simplify logging of output.
2186 		 */
2187 		class LoggerBuf : public std::streambuf
2188 		{
2189 			public:
LoggerBuf()2190 			LoggerBuf()
2191 			{	temp_log = "";};
~LoggerBuf()2192 			virtual ~LoggerBuf()
2193 			{};
2194 
xsputn(const char_type * str,std::streamsize n)2195 			std::streamsize xsputn(const char_type* str, std::streamsize n)
2196 			{
2197 				std::string new_str(str, n);
2198 				temp_log += new_str;
2199 
2200 				std::cout << new_str;
2201 
2202 				const std::streamsize ret = static_cast<std::streamsize>(new_str.length());
2203 				return ret;
2204 			};
2205 
overflow(int_type ch)2206 			int_type overflow(int_type ch)
2207 			{
2208 				if ((char)ch == '\n')
2209 				{
2210 					logs.push_back(temp_log + "\n");
2211 					temp_log = "";
2212 					std::cout << std::endl;
2213 				}
2214 				return ch;
2215 			}
2216 
2217 			public:
2218 			std::string temp_log;
2219 			std::vector<std::string> logs;
2220 		};
2221 
2222 		class Logger : public std::ostream
2223 		{
2224 			public:
Logger()2225 			Logger() : std::ios(0), std::ostream(new LoggerBuf())
2226 			{};
~Logger()2227 			~Logger()
2228 			{	delete rdbuf();};
2229 
log_text(const std::string & message)2230 			void log_text(const std::string& message)
2231 			{	((LoggerBuf*)rdbuf())->logs.push_back(message);};
log_warning(const std::string & message)2232 			void log_warning(const std::string& message)
2233 			{	log_text("WARNING: " + message + "\n");};
log_error(const std::string & message)2234 			void log_error(const std::string& message)
2235 			{	log_text("ERROR: " + message + "\n"); ec_error_status = true;};
fetch()2236 			std::vector<std::string> fetch()
2237 			{	const std::vector<std::string> ret(((LoggerBuf*)rdbuf())->logs); ((LoggerBuf*)rdbuf())->logs.clear(); return ret;};
2238 		};
2239 
2240 		extern Logger logger;
2241 
2242 		///////////////////////////////////////////////////////////////////////////////
2243 
2244 	} // End namespace ec
2245 
2246 #endif	// defined EYE_CANDY_H
2247