WoW Model Viewer
Your premiere tool for viewing, equipping and animating World of Warcraft models.
Loading...
Searching...
No Matches
particle.cpp
Go to the documentation of this file.
1#include "Bone.h"
2#include "particle.h"
3#include "GlobalSettings.h"
4#include "WoWModel.h"
5#include "Logger.h"
6
7#include "glm/gtc/type_ptr.hpp"
8#include <cmath>
9
10constexpr float Epsilon = 1e-6f; // Add this near the top of the file
11
12enum
13{
14 MAX_PARTICLES = 10000
15};
16
18
19template <class T>
20T lifeRamp(float life, float mid, const T& a, const T& b, const T& c)
21{
22 if (life <= mid)
23 return interpolate<T>(life / mid, a, b);
24 else
25 return interpolate<T>((life - mid) / (1.0f - mid), b, c);
26}
27
28void ParticleSystem::init(GameFile* f, M2ParticleDef& mta, std::vector<uint32>& globals)
29{
30 flags = mta.flags;
34 else
35 doNotTrail = false;
36 speed.init(mta.EmissionSpeed, f, globals);
37 variation.init(mta.SpeedVariation, f, globals);
38 spread.init(mta.VerticalRange, f, globals);
39 lat.init(mta.HorizontalRange, f, globals);
40 gravity.init(mta.Gravity, f, globals);
41 lifespan.init(mta.Lifespan, f, globals);
42 rate.init(mta.EmissionRate, f, globals);
43 areal.init(mta.EmissionAreaLength, f, globals);
44 areaw.init(mta.EmissionAreaWidth, f, globals);
45 // deacceleration.init (mta.Gravity2, f, globals);
46 enabled.init(mta.EnabledIn, f, globals);
48
49 glm::vec3 colors2[3];
50 memcpy(colors2, f->getBuffer() + mta.p.colors.ofsKeys, sizeof(glm::vec3) * 3);
51
52 for (int i = 0; i < 3; i++)
53 {
54 const float opacity = *reinterpret_cast<short*>(f->getBuffer() + mta.p.opacity.ofsKeys + i * 2);
55 colors[i] = glm::vec4(colors2[i].x / 255.0f, colors2[i].y / 255.0f,
56 colors2[i].z / 255.0f, opacity / 32767.0f);
57 sizes[i] = (*reinterpret_cast<float*>(f->getBuffer() + mta.p.sizes.ofsKeys + i * sizeof(glm::vec2))) * mta.p.scales[i];
58 }
59 mid = 0.5; // mid can't be 0 or 1, TODO, Alfred
60
61 slowdown = mta.p.slowdown;
62 rotation = mta.p.rotation;
63 pos = mta.pos;
64 tpos = mta.pos;
66 blend = mta.blend;
67 rows = mta.rows;
68 if (rows == 0)
69 rows = 1;
70 cols = mta.cols;
71 if (cols == 0)
72 cols = 1;
74 //order = mta.s2;
76
77 parent = &(model->bones[mta.bone]);
78
79 // transform = flags & 1024;
80
81 if (multitexture)
82 {
83 // when multitexture is flagged, three textures are packed into the one uint16
84 // (5 bits each, plus one bit left over):
85 texture = model->getGLTexture(mta.texture & 0x1f);
86 texture2 = model->getGLTexture((mta.texture >> 5) & 0x1f);
87 texture3 = model->getGLTexture((mta.texture >> 10) & 0x1f);
88 }
89
91
92 // diagnosis test info
94
95 manim = mtime = 0;
96 rem = 0;
97
98 emitter = nullptr;
99 switch (EmitterType)
100 {
102 emitter = new PlaneParticleEmitter(this);
103 break;
105 emitter = new SphereParticleEmitter(this);
106 break;
107 case MODELPARTICLE_EMITTER_SPLINE: // Spline?
108 default:
109 LOG_ERROR << "Unknown Emitter:" << EmitterType;
110 break;
111 }
112
113 tofs = frand();
114
115 // init tiles, slice the texture
116 for (int i = 0; i < rows * cols; i++)
117 {
118 TexCoordSet tc;
119 initTile(tc.tc, i);
120 tiles.push_back(tc);
121 }
122}
123
124void ParticleSystem::initTile(glm::vec2* tc, int num)
125{
126 glm::vec2 otc[4];
127 glm::vec2 a, b;
128 const int x = num % cols;
129 const int y = num / cols;
130 a.x = x * (1.0f / cols);
131 b.x = (x + 1) * (1.0f / cols);
132 a.y = y * (1.0f / rows);
133 b.y = (y + 1) * (1.0f / rows);
134
135 otc[0] = a;
136 otc[2] = b;
137 otc[1].x = b.x;
138 otc[1].y = a.y;
139 otc[3].x = a.x;
140 otc[3].y = b.y;
141
142 for (size_t i = 0; i < 4; i++)
143 {
144 tc[(i + 4 - order) & 3] = otc[i];
145 }
146}
147
149{
150 glm::vec4 colVals[3];
151
153 {
154 int id = particleColID - 11;
155 colVals[0] = particleColorReplacements[id][0];
156 colVals[1] = particleColorReplacements[id][1];
157 colVals[2] = particleColorReplacements[id][2];
158 // need to add the particle alphas:
159 colVals[0][3] = colors[0][3];
160 colVals[1][3] = colors[1][3];
161 colVals[2][3] = colors[2][3];
162 }
163 else
164 {
165 colVals[0] = colors[0];
166 colVals[1] = colors[1];
167 colVals[2] = colors[2];
168 }
169 if (fabs(dt) < Epsilon)
170 {
171 // Animation is stopped, so no new/moved particles. Just update particle colour
172 // in case it changed (if someone selects a new skin while model stopped):
173 for (auto& p : particles)
174 {
175 float rlife = p.life / p.maxlife;
176 p.color = lifeRamp<glm::vec4>(rlife, mid, colVals[0], colVals[1], colVals[2]);
177 }
178 return;
179 }
180
181 dt = abs(dt); // dt can be negative if animation slider is used, but that would break particle anims
182
183 size_t l_manim = manim;
184 if (GLOBALSETTINGS.bZeroParticle)
185 l_manim = 0;
186 float grav = gravity.getValue(l_manim, mtime);
187 // float deaccel = deacceleration.getValue(l_manim, mtime);
188
189 // spawn new particles
190 if (emitter)
191 {
192 float frate = rate.getValue(l_manim, mtime);
193 float flife = lifespan.getValue(l_manim, mtime);
194
195 float ftospawn;
196 if (fabs(flife) > Epsilon)
197 ftospawn = (dt * frate / flife) + rem;
198 else
199 ftospawn = rem;
200 if (ftospawn < 1.0f)
201 {
202 rem = ftospawn;
203 if (rem < 0)
204 rem = 0;
205 }
206 else
207 {
208 unsigned int tospawn = static_cast<int>(ftospawn);
209
210 if ((tospawn + particles.size()) > MAX_PARTICLES)
211 // Error check to prevent the program from trying to load insane amounts of particles.
212 tospawn = (unsigned int)(MAX_PARTICLES - particles.size());
213
214 rem = ftospawn - static_cast<float>(tospawn);
215
216 float w = areal.getValue(l_manim, mtime) * 0.5f;
217 float l = areaw.getValue(l_manim, mtime) * 0.5f;
218 float spd = speed.getValue(l_manim, mtime);
219 float var = variation.getValue(l_manim, mtime);
220 float spr = spread.getValue(l_manim, mtime);
221 float spr2 = lat.getValue(l_manim, mtime);
222 bool en = true;
223 if (enabled.uses(manim))
224 en = enabled.getValue(manim, mtime) != 0;
225
226 //rem = 0;
227 if (en)
228 {
229 for (size_t i = 0; i < tospawn; i++)
230 {
231 Particle p = emitter->newParticle(manim, mtime, w, l, spd, var, spr, spr2);
232 // sanity check:
233 if (particles.size() < MAX_PARTICLES)
234 // No need to check this every loop iteration. Already checked above.
235 particles.push_back(p);
236 }
237 }
238 }
239 }
240
241 float mspeed = 1.0f;
242
243 glm::mat4 m = parent->mat;
244
245 for (ParticleList::iterator it = particles.begin(); it != particles.end();)
246 {
247 Particle& p = *it;
248 // p.speed += p.down * grav * dt - p.dir * deaccel * dt;
249 p.speed += p.down * grav * dt;
250
251 if (slowdown > 0)
252 mspeed = expf(-1.0f * slowdown * p.life);
253 p.pos += p.speed * mspeed * dt;
254 p.tpos = glm::vec3(m * glm::vec4(p.pos, 1.0f));
255 p.life += dt;
256 float rlife = p.life / p.maxlife;
257 // calculate size and color based on lifetime
258 p.size = lifeRamp<float>(rlife, mid, sizes[0], sizes[1], sizes[2]);
259 p.color = lifeRamp<glm::vec4>(rlife, mid, colVals[0], colVals[1], colVals[2]);
260
261 // kill off old particles
262 if (rlife >= 1.0f)
263 particles.erase(it++);
264 else
265 ++it;
266 }
267}
268
269void ParticleSystem::setup(size_t anim, size_t time)
270{
271 manim = anim;
272 mtime = time;
273
274 /*
275 if (transform) {
276 // transform every particle by the parent trans matrix - apparently this isn't needed
277 Matrix m = parent->mat;
278 for (ParticleList::iterator it = particles.begin(); it != particles.end(); ++it) {
279 it->tpos = m * it->pos;
280 }
281 } else {
282 for (ParticleList::iterator it = particles.begin(); it != particles.end(); ++it) {
283 it->tpos = it->pos;
284 }
285 }
286 */
287}
288
290{
291 // ALPHA BLENDING
292 // blend mode
293 if (blend < 0)
294 blend = 0;
295 else if (blend > 7)
296 blend = 2;
297 switch (blend)
298 {
299 case BM_OPAQUE: // 0
300 glDisable(GL_BLEND);
301 glDisable(GL_ALPHA_TEST);
302 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
303 break;
304 case BM_TRANSPARENT: // 1
305 glDisable(GL_BLEND);
306 glEnable(GL_ALPHA_TEST);
307 glBlendFunc(GL_ONE, GL_ZERO);
308 break;
309 case BM_ALPHA_BLEND: // 2
310 glEnable(GL_BLEND);
311 glDisable(GL_ALPHA_TEST);
312 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
313 break;
314 case BM_ADDITIVE: // 3
315 glEnable(GL_BLEND);
316 glDisable(GL_ALPHA_TEST);
317 glBlendFunc(GL_SRC_COLOR, GL_ONE);
318 break;
319 case BM_ADDITIVE_ALPHA: // 4
320 glEnable(GL_BLEND);
321 glDisable(GL_ALPHA_TEST);
322 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
323 break;
324 case BM_MODULATE: // 5
325 glEnable(GL_BLEND);
326 glDisable(GL_ALPHA_TEST);
327 glBlendFunc(GL_DST_COLOR, GL_ZERO);
328 break;
329 case BM_MODULATEX2: // 6
330 glEnable(GL_BLEND);
331 glDisable(GL_ALPHA_TEST);
332 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
333 break;
334 case BM_7: // 7, new in WoD
335 glEnable(GL_BLEND);
336 glDisable(GL_ALPHA_TEST);
337 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
338 break;
339 default:
340 LOG_ERROR << "Unknown blendmode:" << blend;
341 glEnable(GL_BLEND);
342 glDisable(GL_ALPHA_TEST);
343 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
344 }
345
346 if (!multitexture)
347 {
348 glActiveTextureARB(GL_TEXTURE0_ARB);
349 glEnable(GL_TEXTURE_2D);
350 glBindTexture(GL_TEXTURE_2D, texture);
351 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
352 }
353 else
354 {
355 glActiveTextureARB(GL_TEXTURE0_ARB);
356 glEnable(GL_TEXTURE_2D);
357 glBindTexture(GL_TEXTURE_2D, texture);
358 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
359 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
360 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
361 glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 4.0);
362 glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 4.0);
363 if (texture2)
364 {
365 glActiveTextureARB(GL_TEXTURE1_ARB);
366 glEnable(GL_TEXTURE_2D);
367 glBindTexture(GL_TEXTURE_2D, texture2);
368 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
369 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
370 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
371 glActiveTextureARB(GL_TEXTURE0_ARB);
372 }
373 if (texture3)
374 {
375 glActiveTextureARB(GL_TEXTURE2_ARB);
376 glEnable(GL_TEXTURE_2D);
377 glBindTexture(GL_TEXTURE_2D, texture3);
378 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
379 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
380 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
381 glActiveTextureARB(GL_TEXTURE0_ARB);
382 }
383 }
384 glm::vec3 vRight(1, 0, 0);
385 glm::vec3 vUp(0, 0, 1);
386
387 // position stuff
388 const float f = 1; //0.707106781f; // sqrt(2)/2
389 glm::vec3 bv0 = glm::vec3(-f, 0, +f);
390 glm::vec3 bv1 = glm::vec3(+f, 0, +f);
391
392 if (billboard)
393 {
394 float modelview[16];
395 glGetFloatv(GL_MODELVIEW_MATRIX, modelview);
396
397 vRight = glm::vec3(modelview[0], modelview[4], modelview[8]);
398 vUp = glm::vec3(modelview[1], modelview[5], modelview[9]); // Spherical billboarding
399 //vUp = glm::vec3(0,1,0); // Cylindrical billboarding
400 }
401
402 /*
403 * type:
404 * 0 "normal" particle
405 * 1 large quad from the particle's origin to its position (used in Moonwell water effects)
406 * 2 seems to be the same as 0 (found some in the Deeprun Tram blinky-lights-sign thing)
407 */
408
409 glm::vec3 vert1, vert2, vert3, vert4, Pos;
410 float size;
411
412 glBegin(GL_QUADS);
413
414 for (auto& particle : particles)
415 {
416 if (tiles.size() - 1 < particle.tile) // Alfred, 2009.08.07, error prevent
417 break;
418 glColor4fv(glm::value_ptr(particle.color));
419 size = particle.size;
420 if (doNotTrail)
421 Pos = particle.tpos;
422 else
423 Pos = particle.pos;
424 if (ParticleType == 0 || ParticleType > 1)
425 {
426 // TODO: figure out type 2 (deeprun tram subway sign)
427 // - doesn't seem to be any different from 0 -_- regular particles
428 if (billboard)
429 {
430 vert1 = Pos - (vRight + vUp) * size;
431 vert2 = Pos + (vRight - vUp) * size;
432 vert3 = Pos + (vRight + vUp) * size;
433 vert4 = Pos - (vRight - vUp) * size;
434 }
435 else
436 {
437 vert1 = Pos + particle.corners[0] * size;
438 vert2 = Pos + particle.corners[1] * size;
439 vert3 = Pos + particle.corners[2] * size;
440 vert4 = Pos + particle.corners[3] * size;
441 }
442 }
443 else if (ParticleType == 1)
444 {
445 vert1 = Pos + bv0 * size;
446 vert2 = Pos + bv1 * size;
447 vert3 = particle.origin + bv1 * size;
448 vert4 = particle.origin + bv0 * size;
449 }
450
451 glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, glm::value_ptr(tiles[particle.tile].tc[0]));
452 if (texture2)
453 glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, glm::value_ptr(tiles[particle.tile].tc[0]));
454 if (texture3)
455 glMultiTexCoord2fvARB(GL_TEXTURE2_ARB, glm::value_ptr(tiles[particle.tile].tc[0]));
456 glVertex3fv(glm::value_ptr(vert1));
457
458 glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, glm::value_ptr(tiles[particle.tile].tc[1]));
459 if (texture2)
460 glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, glm::value_ptr(tiles[particle.tile].tc[1]));
461 if (texture3)
462 glMultiTexCoord2fvARB(GL_TEXTURE2_ARB, glm::value_ptr(tiles[particle.tile].tc[1]));
463 glVertex3fv(glm::value_ptr(vert2));
464
465 glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, glm::value_ptr(tiles[particle.tile].tc[2]));
466 if (texture2)
467 glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, glm::value_ptr(tiles[particle.tile].tc[2]));
468 if (texture3)
469 glMultiTexCoord2fvARB(GL_TEXTURE2_ARB, glm::value_ptr(tiles[particle.tile].tc[2]));
470 glVertex3fv(glm::value_ptr(vert3));
471
472 glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, glm::value_ptr(tiles[particle.tile].tc[3]));
473 if (texture2)
474 glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, glm::value_ptr(tiles[particle.tile].tc[3]));
475 if (texture3)
476 glMultiTexCoord2fvARB(GL_TEXTURE2_ARB, glm::value_ptr(tiles[particle.tile].tc[3]));
477 glVertex3fv(glm::value_ptr(vert4));
478 }
479 glEnd();
480
481 glActiveTextureARB(GL_TEXTURE0_ARB);
482 glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 1.0);
483 glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1.0);
484 glDisable(GL_TEXTURE_2D);
485 if (texture2)
486 {
487 glActiveTextureARB(GL_TEXTURE1_ARB);
488 glDisable(GL_TEXTURE_2D);
489 glActiveTextureARB(GL_TEXTURE0_ARB);
490 }
491 if (texture3)
492 {
493 glActiveTextureARB(GL_TEXTURE2_ARB);
494 glDisable(GL_TEXTURE_2D);
495 glActiveTextureARB(GL_TEXTURE0_ARB);
496 }
497 glActiveTextureARB(GL_TEXTURE0_ARB);
498}
499
500//Generates the rotation matrix based on spread
501static glm::mat4 SpreadMat;
502
503void CalcSpreadMatrix(float Spread1, float Spread2, float w, float l)
504{
505 int i;
506 float a[2]{}, c[2]{}, s[2]{};
507 glm::mat4 Temp(1.0f);
508
509 SpreadMat = glm::mat4(1.0f);
510
511 a[0] = randfloat(-Spread1, Spread1) / 2.0f;
512 a[1] = randfloat(-Spread2, Spread2) / 2.0f;
513
514 /*
515 SpreadMat.m[0][0]*=l;
516 SpreadMat.m[1][1]*=l;
517 SpreadMat.m[2][2]*=w;
518 */
519
520 for (i = 0; i < 2; i++)
521 {
522 c[i] = cos(a[i]);
523 s[i] = sin(a[i]);
524 }
525 Temp[1][1] = c[0];
526 Temp[2][1] = s[0];
527 Temp[2][2] = c[0];
528 Temp[1][2] = -s[0];
529
530 SpreadMat = SpreadMat * Temp;
531
532 Temp = glm::mat4(1.0f);
533 Temp[0][0] = c[1]; //-V525
534 Temp[1][0] = s[1];
535 Temp[1][1] = c[1];
536 Temp[0][1] = -s[1];
537
538 SpreadMat = SpreadMat * Temp;
539
540 const float Size = abs(c[0]) * l + abs(s[0]) * w;
541 for (i = 0; i < 3; i++)
542 for (int j = 0; j < 3; j++)
543 SpreadMat[i][j] *= Size;
544}
545
546Particle PlaneParticleEmitter::newParticle(size_t anim, size_t time, float w, float l, float spd, float var, float spr, float spr2)
547{
548 Particle p;
549
550 //Spread Calculation
551 glm::mat4 mrot;
552
553 CalcSpreadMatrix(spr, spr, 1.0f, 1.0f);
554 mrot = sys->parent->mrot * SpreadMat;
555
556 if (sys->flags == 1041)
557 {
558 // Trans Halo
559 //p.pos = (sys->pos + glm::vec3(randfloat(-l, l), randfloat(-w, w), 0));
560 const float t = randfloat(0.0f, 2 * glm::pi<float>());
561 p.pos = glm::vec3(0.0f, sys->pos.y + 0.15f, sys->pos.z) + glm::vec3(cos(t) / 8, sin(t) / 8, 0.0f);
562 // Need to manually correct for the halo - why?
563
564 // var isn't being used, which is set to 1.0f, whats the importance of this?
565 // why does this set of values differ from other particles
566
567 glm::vec3 dir(0.0f, 0.0f, 1.0f);
568 p.dir = dir;
569
570 p.speed = glm::normalize(dir) * spd * randfloat(0, var);
571 }
572 else if (sys->flags == 25 && sys->parent->parent < 1)
573 {
574 // Weapon Flame
575 p.pos = sys->parent->pivot * (sys->pos + glm::vec3(randfloat(-l, l), randfloat(-w, w), randfloat(-l, l)));
576 glm::vec3 dir = glm::vec3(mrot * glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
577 p.dir = glm::normalize(dir);
578 //glm::vec3 dir = sys->model->bones[sys->parent->parent].mrot * sys->parent->mrot * glm::vec3(0.0f, 1.0f, 0.0f);
579 //p.speed = dir.normalize() * spd;
580 }
581 else if (sys->flags == 25 && sys->parent->parent > 0)
582 {
583 // Weapon with built-in Flame (Avenger lightsaber!)
584 p.pos = (sys->pos + glm::vec3(randfloat(-l, l), randfloat(-w, w), randfloat(-l, l)));
585 glm::vec3 dir = glm::vec3(sys->parent->mat[1][0], sys->parent->mat[1][1], sys->parent->mat[1][2]) * glm::vec3(
586 0.0f, 0.0f, 1.0f);
587 p.speed = glm::normalize(dir) * spd * randfloat(0, var * 2);
588 }
589 else if (sys->flags == 17 && sys->parent->parent < 1)
590 {
591 // Weapon Glow
592 p.pos = sys->parent->pivot * (sys->pos + glm::vec3(randfloat(-l, l), randfloat(-w, w), randfloat(-l, l)));
593 glm::vec3 dir = glm::vec3(mrot * glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
594 p.dir = glm::normalize(dir);
595 }
596 else
597 {
598 p.pos = sys->pos + glm::vec3(randfloat(-l, l), randfloat(-w, w), 0.0f);
599
600 //glm::vec3 dir = mrot * glm::vec3(0,1,0);
601 glm::vec3 dir = glm::vec3(sys->parent->mrot * glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
602
603 p.dir = dir; //.normalize();
604 p.down = glm::vec3(0.0f, 0.0f, -1.0f);
605 p.speed = glm::normalize(dir) * spd * (1.0f + randfloat(-var, var));
606 }
607
608 if (!sys->billboard)
609 {
610 p.corners[0] = glm::vec3(mrot * glm::vec4(-1, 0, +1, +1));
611 p.corners[1] = glm::vec3(mrot * glm::vec4(+1, 0, +1, +1));
612 p.corners[2] = glm::vec3(mrot * glm::vec4(+1, 0, -1, +1));
613 p.corners[3] = glm::vec3(mrot * glm::vec4(-1, 0, -1, +1));
614 }
615 p.tpos = glm::vec3(sys->parent->mat * glm::vec4(p.pos, 1.0f));
616 p.life = 0;
617 size_t l_anim = anim;
618 if (GLOBALSETTINGS.bZeroParticle)
619 l_anim = 0;
620 p.maxlife = sys->lifespan.getValue(l_anim, time);
621 if (fabs(p.maxlife) < Epsilon)
622 p.maxlife = 1;
623
624 p.origin = p.tpos;
625 if (!sys->doNotTrail)
626 p.pos = p.tpos;
627
628 p.tile = randint(0, sys->rows * sys->cols - 1);
629 return p;
630}
631
632Particle SphereParticleEmitter::newParticle(size_t anim, size_t time, float w, float l, float spd, float var, float spr, float spr2)
633{
634 Particle p;
635 glm::vec3 dir;
636 float radius;
637
638 radius = randfloat(0, 1);
639
640 // Old method
641 //float t = randfloat(0,2*PI);
642
643 // New
644 // Spread should never be zero for sphere particles ?
645 float t = 0;
646 if (fabs(spr) < Epsilon)
647 t = randfloat(-glm::pi<float>(), glm::pi<float>());
648else
649 t = randfloat(-spr, spr);
650
651 //Spread Calculation
652 glm::mat4 mrot;
653
654 CalcSpreadMatrix(spr * 2, spr2 * 2, w, l);
655 mrot = sys->parent->mrot * SpreadMat;
656
657 // New
658 // Length should never technically be zero ?
659 //if (l==0)
660 // l = w;
661
662 // New method
663 // glm::vec3 bdir(w*cosf(t), 0.0f, l*sinf(t));
664 // --
665
666 // TODO: fix sphere emitters to work properly
667 /*
668 // Old Method
669 //glm::vec3 bdir(l*cosf(t), 0, w*sinf(t));
670 //glm::vec3 bdir(0, w*cosf(t), l*sinf(t));
671
672
673 float theta_range = sys->spread.getValue(anim, time);
674 float theta = -0.5f* theta_range + randfloat(0, theta_range);
675 glm::vec3 bdir(0, l*cosf(theta), w*sinf(theta));
676
677 float phi_range = sys->lat.getValue(anim, time);
678 float phi = randfloat(0, phi_range);
679 rotate(0,0, &bdir.z, &bdir.x, phi);
680 */
681 if (sys->flags == 57 || sys->flags == 313)
682 {
683 // Faith Halo
684 glm::vec3 bdir(w * cosf(t) * 1.6, l * sinf(t) * 1.6, 0.0f);
685
686 p.pos = sys->pos + bdir;
687 p.tpos = glm::vec3(sys->parent->mat * glm::vec4(p.pos, 1.0f));
688
689 if (glm::length(bdir) == 0)
690 p.speed = glm::vec3(0, 0, 0);
691 else
692 {
693 dir = glm::vec3(sys->parent->mrot * glm::vec4(glm::normalize(dir), 1.0f)); //mrot * glm::vec3(0, 1.0f,0);
694 p.speed = glm::normalize(dir) * spd * (1.0f + randfloat(-var, var)); // ?
695 }
696 }
697 else
698 {
699 glm::vec3 bdir;
700 float temp;
701
702 bdir = glm::vec3(mrot * glm::vec4(0, 0, 1, 1)) * radius;
703 temp = bdir.z;
704 bdir.z = bdir.y;
705 bdir.y = temp;
706
707 p.pos = sys->pos + bdir;
708 p.tpos = glm::vec3(sys->parent->mat * glm::vec4(p.pos, 1.0f));
709
710
711 if (glm::length(bdir) == 0 && (sys->flags & 0x100) != 0x100)
712 {
713 p.speed = glm::vec3(0, 0, 0);
714 dir = glm::vec3(sys->parent->mrot * glm::vec4(0, 0, 1, 1));
715 }
716 else
717 {
718 if (sys->flags & 0x100)
719 dir = glm::vec3(sys->parent->mrot * glm::vec4(0, 0, 1, 1));
720 else
721 dir = glm::normalize(bdir);
722
723 p.speed = glm::normalize(dir) * spd * (1.0f + randfloat(-var, var)); // ?
724 }
725 }
726
727 p.dir = glm::normalize(dir); //mrot * glm::vec3(0, 1.0f,0);
728 p.down = glm::vec3(0, 0, 1.0f);
729
730 p.life = 0;
731 size_t l_anim = anim;
732 if (GLOBALSETTINGS.bZeroParticle)
733 l_anim = 0;
734 p.maxlife = sys->lifespan.getValue(l_anim, time);
735 if (fabs(p.maxlife) < Epsilon)
736 p.maxlife = 1;
737
738 p.origin = p.tpos;
739 if (!sys->doNotTrail)
740 p.pos = p.tpos;
741
742 p.tile = randint(0, sys->rows * sys->cols - 1);
743 return p;
744}
745
746void RibbonEmitter::init(GameFile* f, ModelRibbonEmitterDef& mta, std::vector<uint32>& globals)
747{
748 color.init(mta.color, f, globals);
749 opacity.init(mta.opacity, f, globals);
750 above.init(mta.above, f, globals);
751 below.init(mta.below, f, globals);
752
753 parent = &(model->bones[mta.bone]);
754 const int* texlist = reinterpret_cast<int*>(f->getBuffer() + mta.ofsTextures);
755 // just use the first texture for now; most models I've checked only had one
756 texture = model->getGLTexture(texlist[0]);
757
758 tpos = pos = mta.pos;
759
760 // TODO: figure out actual correct way to calculate length
761 // in BFD, res is 60 and len is 0.6, the trails are very short (too long here)
762 // in CoT, res and len are like 10 but the trails are supposed to be much longer (too short here)
763 numsegs = static_cast<int>(mta.res);
764 seglen = mta.length;
765 length = mta.res * seglen;
766
767 // create first segment
768 RibbonSegment rs;
769 rs.pos = tpos;
770 rs.len = 0;
771 segs.push_back(rs);
772}
773
774void RibbonEmitter::setup(size_t anim, size_t time)
775{
776 const glm::vec3 ntpos = glm::vec3(parent->mat * glm::vec4(pos, 1.0f));
777 glm::vec3 ntup = glm::vec3(parent->mat * (glm::vec4(pos, 1.f) + glm::vec4(0, 0, 1, 1)));
778 ntup -= ntpos;
779 ntup = glm::normalize(ntup);
780 const float dlen = (ntpos - tpos).length();
781
782 manim = anim;
783 mtime = time;
784
785 // move first segment
786 RibbonSegment& first = *segs.begin();
787 if (first.len > seglen)
788 {
789 // add new segment
790 first.back = glm::normalize(tpos - ntpos);
791 first.len0 = first.len;
792 RibbonSegment newseg;
793 newseg.pos = ntpos;
794 newseg.up = ntup;
795 newseg.len = dlen;
796 segs.push_front(newseg);
797 }
798 else
799 {
800 first.up = ntup;
801 first.pos = ntpos;
802 first.len += dlen;
803 }
804
805 // kill stuff from the end
806 float l = 0;
807 bool erasemode = false;
808 for (std::list<RibbonSegment>::iterator it = segs.begin(); it != segs.end();)
809 {
810 if (!erasemode)
811 {
812 l += it->len;
813 if (l > length)
814 {
815 it->len = l - length;
816 erasemode = true;
817 }
818 ++it;
819 }
820 else
821 {
822 segs.erase(it++);
823 }
824 }
825
826 tpos = ntpos;
827 tcolor = glm::vec4(color.getValue(anim, time), opacity.getValue(anim, time));
828
829 tabove = above.getValue(anim, time);
830 tbelow = below.getValue(anim, time);
831}
832
834{
835 /*
836 // placeholders
837 glDisable(GL_TEXTURE_2D);
838 glDisable(GL_LIGHTING);
839 glColor4f(1,1,1,1);
840 glBegin(GL_TRIANGLES);
841 glVertex3fv(tpos);
842 glVertex3fv(tpos + glm::vec3(1,1,0));
843 glVertex3fv(tpos + glm::vec3(-1,1,0));
844 glEnd();
845 glEnable(GL_TEXTURE_2D);
846 glEnable(GL_LIGHTING);
847 */
848
849 glEnable(GL_TEXTURE_2D);
850 glBindTexture(GL_TEXTURE_2D, texture);
851 glEnable(GL_BLEND);
852 glDisable(GL_LIGHTING);
853 glDisable(GL_ALPHA_TEST);
854 glDisable(GL_CULL_FACE);
855 glDepthMask(GL_FALSE);
856 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
857 glColor4fv(glm::value_ptr(tcolor));
858
859 glBegin(GL_QUAD_STRIP);
860 std::list<RibbonSegment>::iterator it = segs.begin();
861 float l = 0;
862 for (; it != segs.end(); ++it)
863 {
864 const float u = l / length;
865
866 glTexCoord2f(u, 0);
867 glVertex3fv(glm::value_ptr(it->pos + tabove * it->up));
868 glTexCoord2f(u, 1);
869 glVertex3fv(glm::value_ptr(it->pos - tbelow * it->up));
870
871 l += it->len;
872 }
873
874 if (segs.size() > 1)
875 {
876 // last segment...?
877 --it;
878 glTexCoord2f(1, 0);
879 glVertex3fv(glm::value_ptr(it->pos + tabove * it->up + (it->len / it->len0) * it->back));
880 glTexCoord2f(1, 1);
881 glVertex3fv(glm::value_ptr(it->pos - tbelow * it->up + (it->len / it->len0) * it->back));
882 }
883 glEnd();
884
885 glColor4f(1, 1, 1, 1);
886 glEnable(GL_LIGHTING);
887 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
888 glDepthMask(GL_TRUE);
889}
#define GLOBALSETTINGS
#define LOG_ERROR
Definition Logger.h:11
float randfloat(float lower, float upper)
Definition animated.cpp:10
float frand()
Definition animated.cpp:5
int randint(int lower, int upper)
Definition animated.cpp:15
T getValue(ssize_t anim, size_t time)
Definition animated.h:178
void init(AnimationBlock &b, GameFile *f, std::vector< uint32 > &gs)
Definition animated.h:264
bool uses(ssize_t anim) const
Definition animated.h:171
int16 parent
Parent bone index (-1 if root).
Definition Bone.h:27
glm::mat4 mat
Computed transformation matrix.
Definition Bone.h:30
glm::mat4 mrot
Computed rotation-only matrix.
Definition Bone.h:31
glm::vec3 pivot
Definition Bone.h:26
Abstract base class representing a file within the game data archive.
Definition GameFile.h:12
unsigned char * getBuffer() const
Pointer to the start of the internal buffer.
Definition GameFile.cpp:144
ParticleSystem * sys
Owning particle system.
Definition particle.h:33
virtual Particle newParticle(size_t anim, size_t time, float w, float l, float spd, float var, float spr, float spr2)=0
Create a new particle with the given emission parameters.
friend class SphereParticleEmitter
Definition particle.h:143
GLuint texture
Definition particle.h:80
float rotation
Definition particle.h:78
ParticleEmitter * emitter
Definition particle.h:81
Bone * parent
Definition particle.h:94
size_t manim
Definition particle.h:84
void setup(size_t anim, size_t time)
Definition particle.cpp:269
friend class PlaneParticleEmitter
Definition particle.h:142
Animated< float > speed
Definition particle.h:101
int16 EmitterType
Definition particle.h:93
bool replaceParticleColors
Definition particle.h:107
glm::vec3 pos
Definition particle.h:79
void init(GameFile *f, M2ParticleDef &mta, std::vector< uint32 > &globals)
Definition particle.cpp:28
bool multitexture
Definition particle.h:105
Animated< float > areal
Definition particle.h:101
float slowdown
Definition particle.h:78
std::vector< particleColorSet > particleColorReplacements
Definition particle.h:113
GLuint texture3
Definition particle.h:80
Animated< float > rate
Definition particle.h:101
ParticleList particles
Definition particle.h:82
glm::vec3 tpos
Definition particle.h:79
void update(float dt)
Definition particle.cpp:148
glm::vec4 colors[3]
Definition particle.h:103
Animated< float > spread
Definition particle.h:101
Animated< float > lat
Definition particle.h:101
std::vector< TexCoordSet > tiles
Definition particle.h:86
void initTile(glm::vec2 *tc, int num)
Definition particle.cpp:124
GLuint texture2
Definition particle.h:80
size_t mtime
Definition particle.h:84
Animated< float > gravity
Definition particle.h:101
Animated< uint16 > enabled
Definition particle.h:100
WoWModel * model
Definition particle.h:98
static bool useDoNotTrail
Definition particle.h:182
Animated< float > lifespan
Definition particle.h:101
Animated< float > areaw
Definition particle.h:101
float sizes[3]
Definition particle.h:104
Animated< float > variation
Definition particle.h:101
Particle newParticle(size_t anim, size_t time, float w, float l, float spd, float var, float spr, float spr2)
Create a new particle with the given emission parameters.
Definition particle.cpp:546
Animated< glm::vec3 > color
Definition particle.h:195
WoWModel * model
Definition particle.h:216
Animated< float > below
Definition particle.h:197
glm::vec3 pos
Definition particle.h:201
size_t mtime
Definition particle.h:203
glm::vec4 tcolor
Definition particle.h:208
size_t manim
Definition particle.h:203
void setup(size_t anim, size_t time)
Definition particle.cpp:774
Bone * parent
Definition particle.h:199
glm::vec3 tpos
Definition particle.h:207
AnimatedShort opacity
Definition particle.h:196
GLuint texture
Definition particle.h:211
std::list< RibbonSegment > segs
Definition particle.h:213
Animated< float > above
Definition particle.h:197
void init(GameFile *f, ModelRibbonEmitterDef &mta, std::vector< uint32 > &globals)
Definition particle.cpp:746
Particle newParticle(size_t anim, size_t time, float w, float l, float spd, float var, float spr, float spr2)
Create a new particle with the given emission parameters.
Definition particle.cpp:632
GLuint getGLTexture(uint16 tex) const
std::vector< Bone > bones
Definition WoWModel.h:181
@ MODELPARTICLE_FLAGS_MULTITEXTURE
@ MODELPARTICLE_FLAGS_DONOTTRAIL
@ MODELPARTICLE_EMITTER_SPLINE
@ MODELPARTICLE_FLAGS_DONOTBILLBOARD
@ MODELPARTICLE_EMITTER_PLANE
@ MODELPARTICLE_EMITTER_SPHERE
static glm::mat4 SpreadMat
Definition particle.cpp:501
void CalcSpreadMatrix(float Spread1, float Spread2, float w, float l)
Definition particle.cpp:503
constexpr float Epsilon
Definition particle.cpp:10
T lifeRamp(float life, float mid, const T &a, const T &b, const T &c)
Definition particle.cpp:20
@ MAX_PARTICLES
Definition particle.cpp:14
On-disk particle emitter definition in an M2 file.
glm::vec3 pos
ModelParticleParams p
AnimationBlock SpeedVariation
AnimationBlock HorizontalRange
AnimationBlock VerticalRange
AnimationBlock EnabledIn
uint16 ParticleColorIndex
AnimationBlock Gravity
int16 TextureTileRotation
AnimationBlock EmissionRate
AnimationBlock Lifespan
AnimationBlock EmissionAreaWidth
AnimationBlock EmissionSpeed
AnimationBlock EmissionAreaLength
FakeAnimationBlock opacity
FakeAnimationBlock sizes
FakeAnimationBlock colors
AnimationBlock above
AnimationBlock opacity
AnimationBlock below
AnimationBlock color
A single particle instance in a particle system.
Definition particle.h:18
glm::vec3 dir
Position, velocity, gravity, origin, and direction.
Definition particle.h:19
float life
Definition particle.h:22
glm::vec3 origin
Definition particle.h:19
glm::vec3 corners[4]
Billboard corner positions.
Definition particle.h:20
float maxlife
Current size, remaining life, and maximum lifespan.
Definition particle.h:22
float size
Definition particle.h:22
size_t tile
Current texture tile index.
Definition particle.h:23
glm::vec3 speed
Definition particle.h:19
glm::vec3 pos
Definition particle.h:19
glm::vec3 tpos
Transformed position.
Definition particle.h:21
glm::vec4 color
Current RGBA colour.
Definition particle.h:24
glm::vec3 down
Definition particle.h:19
A single segment in a ribbon trail.
Definition particle.h:187
glm::vec3 up
Definition particle.h:188
float len0
Current length and initial length.
Definition particle.h:189
glm::vec3 pos
Definition particle.h:188
glm::vec3 back
Segment position, up vector, and back vector.
Definition particle.h:188
A set of 4 texture coordinates for a particle tile.
Definition particle.h:71
glm::vec2 tc[4]
Texture coordinates for the four corners.
Definition particle.h:72
@ BM_ADDITIVE
Definition wow_enums.h:289
@ BM_ADDITIVE_ALPHA
Definition wow_enums.h:290
@ BM_ALPHA_BLEND
Definition wow_enums.h:288
@ BM_OPAQUE
Definition wow_enums.h:286
@ BM_7
Definition wow_enums.h:293
@ BM_TRANSPARENT
Definition wow_enums.h:287
@ BM_MODULATEX2
Definition wow_enums.h:292
@ BM_MODULATE
Definition wow_enums.h:291