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