ParticleSystem.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143
  1. /**
  2. * Copyright (c) 2006-2020 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. //LOVE
  21. #include "common/config.h"
  22. #include "ParticleSystem.h"
  23. #include "Graphics.h"
  24. #include "common/math.h"
  25. #include "modules/math/RandomGenerator.h"
  26. // STD
  27. #include <algorithm>
  28. #include <cmath>
  29. #include <cstdlib>
  30. namespace love
  31. {
  32. namespace graphics
  33. {
  34. namespace
  35. {
  36. love::math::RandomGenerator rng;
  37. float calculate_variation(float inner, float outer, float var)
  38. {
  39. float low = inner - (outer/2.0f)*var;
  40. float high = inner + (outer/2.0f)*var;
  41. float r = (float) rng.random();
  42. return low*(1-r)+high*r;
  43. }
  44. } // anonymous namespace
  45. love::Type ParticleSystem::type("ParticleSystem", &Drawable::type);
  46. ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
  47. : pMem(nullptr)
  48. , pFree(nullptr)
  49. , pHead(nullptr)
  50. , pTail(nullptr)
  51. , texture(texture)
  52. , active(true)
  53. , insertMode(INSERT_MODE_TOP)
  54. , maxParticles(0)
  55. , activeParticles(0)
  56. , emissionRate(0)
  57. , emitCounter(0)
  58. , emissionAreaDistribution(DISTRIBUTION_NONE)
  59. , emissionAreaAngle(0)
  60. , directionRelativeToEmissionCenter(false)
  61. , lifetime(-1)
  62. , life(0)
  63. , particleLifeMin(0)
  64. , particleLifeMax(0)
  65. , direction(0)
  66. , spread(0)
  67. , speedMin(0)
  68. , speedMax(0)
  69. , linearAccelerationMin(0, 0)
  70. , linearAccelerationMax(0, 0)
  71. , radialAccelerationMin(0)
  72. , radialAccelerationMax(0)
  73. , tangentialAccelerationMin(0)
  74. , tangentialAccelerationMax(0)
  75. , linearDampingMin(0.0f)
  76. , linearDampingMax(0.0f)
  77. , sizeVariation(0)
  78. , rotationMin(0)
  79. , rotationMax(0)
  80. , spinStart(0)
  81. , spinEnd(0)
  82. , spinVariation(0)
  83. , offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
  84. , defaultOffset(true)
  85. , relativeRotation(false)
  86. , vertexAttributes(CommonFormat::XYf_STf_RGBAub, 0)
  87. , buffer(nullptr)
  88. {
  89. if (size == 0 || size > MAX_PARTICLES)
  90. throw love::Exception("Invalid ParticleSystem size.");
  91. if (texture->getTextureType() != TEXTURE_2D)
  92. throw love::Exception("Only 2D textures can be used with ParticleSystems.");
  93. sizes.push_back(1.0f);
  94. colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
  95. setBufferSize(size);
  96. }
  97. ParticleSystem::ParticleSystem(const ParticleSystem &p)
  98. : pMem(nullptr)
  99. , pFree(nullptr)
  100. , pHead(nullptr)
  101. , pTail(nullptr)
  102. , texture(p.texture)
  103. , active(p.active)
  104. , insertMode(p.insertMode)
  105. , maxParticles(p.maxParticles)
  106. , activeParticles(0)
  107. , emissionRate(p.emissionRate)
  108. , emitCounter(0.0f)
  109. , position(p.position)
  110. , prevPosition(p.prevPosition)
  111. , emissionAreaDistribution(p.emissionAreaDistribution)
  112. , emissionArea(p.emissionArea)
  113. , emissionAreaAngle(p.emissionAreaAngle)
  114. , directionRelativeToEmissionCenter(p.directionRelativeToEmissionCenter)
  115. , lifetime(p.lifetime)
  116. , life(p.lifetime) // Initialize with the maximum life time.
  117. , particleLifeMin(p.particleLifeMin)
  118. , particleLifeMax(p.particleLifeMax)
  119. , direction(p.direction)
  120. , spread(p.spread)
  121. , speedMin(p.speedMin)
  122. , speedMax(p.speedMax)
  123. , linearAccelerationMin(p.linearAccelerationMin)
  124. , linearAccelerationMax(p.linearAccelerationMax)
  125. , radialAccelerationMin(p.radialAccelerationMin)
  126. , radialAccelerationMax(p.radialAccelerationMax)
  127. , tangentialAccelerationMin(p.tangentialAccelerationMin)
  128. , tangentialAccelerationMax(p.tangentialAccelerationMax)
  129. , linearDampingMin(p.linearDampingMin)
  130. , linearDampingMax(p.linearDampingMax)
  131. , sizes(p.sizes)
  132. , sizeVariation(p.sizeVariation)
  133. , rotationMin(p.rotationMin)
  134. , rotationMax(p.rotationMax)
  135. , spinStart(p.spinStart)
  136. , spinEnd(p.spinEnd)
  137. , spinVariation(p.spinVariation)
  138. , offset(p.offset)
  139. , defaultOffset(p.defaultOffset)
  140. , colors(p.colors)
  141. , quads(p.quads)
  142. , relativeRotation(p.relativeRotation)
  143. , vertexAttributes(p.vertexAttributes)
  144. , buffer(nullptr)
  145. {
  146. setBufferSize(maxParticles);
  147. }
  148. ParticleSystem::~ParticleSystem()
  149. {
  150. deleteBuffers();
  151. }
  152. ParticleSystem *ParticleSystem::clone()
  153. {
  154. return new ParticleSystem(*this);
  155. }
  156. void ParticleSystem::resetOffset()
  157. {
  158. if (quads.empty())
  159. offset = love::Vector2(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f);
  160. else
  161. {
  162. Quad::Viewport v = quads[0]->getViewport();
  163. offset = love::Vector2(v.x*0.5f, v.y*0.5f);
  164. }
  165. }
  166. void ParticleSystem::createBuffers(size_t size)
  167. {
  168. try
  169. {
  170. pFree = pMem = new Particle[size];
  171. maxParticles = (uint32) size;
  172. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  173. size_t bytes = sizeof(Vertex) * size * 4;
  174. Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STREAM);
  175. buffer = gfx->newBuffer(settings, nullptr, bytes);
  176. }
  177. catch (std::bad_alloc &)
  178. {
  179. deleteBuffers();
  180. throw love::Exception("Out of memory");
  181. }
  182. }
  183. void ParticleSystem::deleteBuffers()
  184. {
  185. delete[] pMem;
  186. if (buffer)
  187. buffer->release();
  188. pMem = nullptr;
  189. buffer = nullptr;
  190. maxParticles = 0;
  191. activeParticles = 0;
  192. }
  193. void ParticleSystem::setBufferSize(uint32 size)
  194. {
  195. if (size == 0 || size > MAX_PARTICLES)
  196. throw love::Exception("Invalid buffer size");
  197. deleteBuffers();
  198. createBuffers(size);
  199. reset();
  200. }
  201. uint32 ParticleSystem::getBufferSize() const
  202. {
  203. return maxParticles;
  204. }
  205. void ParticleSystem::addParticle(float t)
  206. {
  207. if (isFull())
  208. return;
  209. // Gets a free particle and updates the allocation pointer.
  210. Particle *p = pFree++;
  211. initParticle(p, t);
  212. switch (insertMode)
  213. {
  214. default:
  215. case INSERT_MODE_TOP:
  216. insertTop(p);
  217. break;
  218. case INSERT_MODE_BOTTOM:
  219. insertBottom(p);
  220. break;
  221. case INSERT_MODE_RANDOM:
  222. insertRandom(p);
  223. break;
  224. }
  225. activeParticles++;
  226. }
  227. void ParticleSystem::initParticle(Particle *p, float t)
  228. {
  229. float min,max;
  230. // Linearly interpolate between the previous and current emitter position.
  231. love::Vector2 pos = prevPosition + (position - prevPosition) * t;
  232. min = particleLifeMin;
  233. max = particleLifeMax;
  234. if (min == max)
  235. p->life = min;
  236. else
  237. p->life = (float) rng.random(min, max);
  238. p->lifetime = p->life;
  239. p->position = pos;
  240. min = direction - spread/2.0f;
  241. max = direction + spread/2.0f;
  242. float dir = (float) rng.random(min, max);
  243. // In this switch statement, variables 'rand_y', 'min', and 'max'
  244. // are sometimes reused as data stores for performance reasons
  245. float rand_x, rand_y;
  246. float c, s;
  247. switch (emissionAreaDistribution)
  248. {
  249. case DISTRIBUTION_UNIFORM:
  250. c = cosf(emissionAreaAngle); s = sinf(emissionAreaAngle);
  251. rand_x = (float) rng.random(-emissionArea.x, emissionArea.x);
  252. rand_y = (float) rng.random(-emissionArea.y, emissionArea.y);
  253. p->position.x += c * rand_x - s * rand_y;
  254. p->position.y += s * rand_x + c * rand_y;
  255. break;
  256. case DISTRIBUTION_NORMAL:
  257. c = cosf(emissionAreaAngle); s = sinf(emissionAreaAngle);
  258. rand_x = (float) rng.randomNormal(emissionArea.x);
  259. rand_y = (float) rng.randomNormal(emissionArea.y);
  260. p->position.x += c * rand_x - s * rand_y;
  261. p->position.y += s * rand_x + c * rand_y;
  262. break;
  263. case DISTRIBUTION_ELLIPSE:
  264. c = cosf(emissionAreaAngle); s = sinf(emissionAreaAngle);
  265. rand_x = (float) rng.random(-1, 1);
  266. rand_y = (float) rng.random(-1, 1);
  267. min = emissionArea.x * (rand_x * sqrt(1 - 0.5f*pow(rand_y, 2)));
  268. max = emissionArea.y * (rand_y * sqrt(1 - 0.5f*pow(rand_x, 2)));
  269. p->position.x += c * min - s * max;
  270. p->position.y += s * min + c * max;
  271. break;
  272. case DISTRIBUTION_BORDER_ELLIPSE:
  273. c = cosf(emissionAreaAngle); s = sinf(emissionAreaAngle);
  274. rand_x = (float) rng.random(0, LOVE_M_PI * 2);
  275. min = cosf(rand_x) * emissionArea.x;
  276. max = sinf(rand_x) * emissionArea.y;
  277. p->position.x += c * min - s * max;
  278. p->position.y += s * min + c * max;
  279. break;
  280. case DISTRIBUTION_BORDER_RECTANGLE:
  281. c = cosf(emissionAreaAngle); s = sinf(emissionAreaAngle);
  282. rand_x = (float) rng.random((emissionArea.x + emissionArea.y) * -2, (emissionArea.x + emissionArea.y) * 2);
  283. rand_y = emissionArea.y * 2;
  284. if (rand_x < -rand_y)
  285. {
  286. min = rand_x + rand_y + emissionArea.x;
  287. p->position.x += c * min - s * -emissionArea.y;
  288. p->position.y += s * min + c * -emissionArea.y;
  289. }
  290. else if (rand_x < 0)
  291. {
  292. max = rand_x + emissionArea.y;
  293. p->position.x += c * -emissionArea.x - s * max;
  294. p->position.y += s * -emissionArea.x + c * max;
  295. }
  296. else if (rand_x < rand_y)
  297. {
  298. max = rand_x - emissionArea.y;
  299. p->position.x += c * emissionArea.x - s * max;
  300. p->position.y += s * emissionArea.x + c * max;
  301. }
  302. else
  303. {
  304. min = rand_x - rand_y - emissionArea.x;
  305. p->position.x += c * min - s * emissionArea.y;
  306. p->position.y += s * min + c * emissionArea.y;
  307. }
  308. break;
  309. case DISTRIBUTION_NONE:
  310. default:
  311. break;
  312. }
  313. // Determine if the origin of each particle is the center of the area
  314. if (directionRelativeToEmissionCenter)
  315. dir += atan2(p->position.y - pos.y, p->position.x - pos.x);
  316. p->origin = pos;
  317. min = speedMin;
  318. max = speedMax;
  319. float speed = (float) rng.random(min, max);
  320. p->velocity = love::Vector2(cosf(dir), sinf(dir)) * speed;
  321. p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x);
  322. p->linearAcceleration.y = (float) rng.random(linearAccelerationMin.y, linearAccelerationMax.y);
  323. min = radialAccelerationMin;
  324. max = radialAccelerationMax;
  325. p->radialAcceleration = (float) rng.random(min, max);
  326. min = tangentialAccelerationMin;
  327. max = tangentialAccelerationMax;
  328. p->tangentialAcceleration = (float) rng.random(min, max);
  329. min = linearDampingMin;
  330. max = linearDampingMax;
  331. p->linearDamping = (float) rng.random(min, max);
  332. p->sizeOffset = (float) rng.random(sizeVariation); // time offset for size change
  333. p->sizeIntervalSize = (1.0f - (float) rng.random(sizeVariation)) - p->sizeOffset;
  334. p->size = sizes[(size_t)(p->sizeOffset - .5f) * (sizes.size() - 1)];
  335. min = rotationMin;
  336. max = rotationMax;
  337. p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
  338. p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
  339. p->rotation = (float) rng.random(min, max);
  340. p->angle = p->rotation;
  341. if (relativeRotation)
  342. p->angle += atan2f(p->velocity.y, p->velocity.x);
  343. p->color = colors[0];
  344. p->quadIndex = 0;
  345. }
  346. void ParticleSystem::insertTop(Particle *p)
  347. {
  348. if (pHead == nullptr)
  349. {
  350. pHead = p;
  351. p->prev = nullptr;
  352. }
  353. else
  354. {
  355. pTail->next = p;
  356. p->prev = pTail;
  357. }
  358. p->next = nullptr;
  359. pTail = p;
  360. }
  361. void ParticleSystem::insertBottom(Particle *p)
  362. {
  363. if (pTail == nullptr)
  364. {
  365. pTail = p;
  366. p->next = nullptr;
  367. }
  368. else
  369. {
  370. pHead->prev = p;
  371. p->next = pHead;
  372. }
  373. p->prev = nullptr;
  374. pHead = p;
  375. }
  376. void ParticleSystem::insertRandom(Particle *p)
  377. {
  378. // Nonuniform, but 64-bit is so large nobody will notice. Hopefully.
  379. uint64 pos = rng.rand() % ((int64) activeParticles + 1);
  380. // Special case where the particle gets inserted before the head.
  381. if (pos == activeParticles)
  382. {
  383. Particle *pA = pHead;
  384. if (pA)
  385. pA->prev = p;
  386. p->prev = nullptr;
  387. p->next = pA;
  388. pHead = p;
  389. return;
  390. }
  391. // Inserts the particle after the randomly selected particle.
  392. Particle *pA = pMem + pos;
  393. Particle *pB = pA->next;
  394. pA->next = p;
  395. if (pB)
  396. pB->prev = p;
  397. else
  398. pTail = p;
  399. p->prev = pA;
  400. p->next = pB;
  401. }
  402. ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p)
  403. {
  404. // The linked list is updated in this function and old pointers may be
  405. // invalidated. The returned pointer will inform the caller of the new
  406. // pointer to the next particle.
  407. Particle *pNext = nullptr;
  408. // Removes the particle from the linked list.
  409. if (p->prev)
  410. p->prev->next = p->next;
  411. else
  412. pHead = p->next;
  413. if (p->next)
  414. {
  415. p->next->prev = p->prev;
  416. pNext = p->next;
  417. }
  418. else
  419. pTail = p->prev;
  420. // The (in memory) last particle can now be moved into the free slot.
  421. // It will skip the moving if it happens to be the removed particle.
  422. pFree--;
  423. if (p != pFree)
  424. {
  425. *p = *pFree;
  426. if (pNext == pFree)
  427. pNext = p;
  428. if (p->prev)
  429. p->prev->next = p;
  430. else
  431. pHead = p;
  432. if (p->next)
  433. p->next->prev = p;
  434. else
  435. pTail = p;
  436. }
  437. activeParticles--;
  438. return pNext;
  439. }
  440. void ParticleSystem::setTexture(Texture *tex)
  441. {
  442. if (texture->getTextureType() != TEXTURE_2D)
  443. throw love::Exception("Only 2D textures can be used with ParticleSystems.");
  444. texture.set(tex);
  445. if (defaultOffset)
  446. resetOffset();
  447. }
  448. Texture *ParticleSystem::getTexture() const
  449. {
  450. return texture.get();
  451. }
  452. void ParticleSystem::setInsertMode(InsertMode mode)
  453. {
  454. insertMode = mode;
  455. }
  456. ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
  457. {
  458. return insertMode;
  459. }
  460. void ParticleSystem::setEmissionRate(float rate)
  461. {
  462. if (rate < 0.0f)
  463. throw love::Exception("Invalid emission rate");
  464. emissionRate = rate;
  465. // Prevent an explosion when dramatically increasing the rate
  466. emitCounter = std::min(emitCounter, 1.0f/rate);
  467. }
  468. float ParticleSystem::getEmissionRate() const
  469. {
  470. return emissionRate;
  471. }
  472. void ParticleSystem::setEmitterLifetime(float life)
  473. {
  474. this->life = lifetime = life;
  475. }
  476. float ParticleSystem::getEmitterLifetime() const
  477. {
  478. return lifetime;
  479. }
  480. void ParticleSystem::setParticleLifetime(float min, float max)
  481. {
  482. particleLifeMin = min;
  483. if (max == 0)
  484. particleLifeMax = min;
  485. else
  486. particleLifeMax = max;
  487. }
  488. void ParticleSystem::getParticleLifetime(float &min, float &max) const
  489. {
  490. min = particleLifeMin;
  491. max = particleLifeMax;
  492. }
  493. void ParticleSystem::setPosition(float x, float y)
  494. {
  495. position = love::Vector2(x, y);
  496. prevPosition = position;
  497. }
  498. const love::Vector2 &ParticleSystem::getPosition() const
  499. {
  500. return position;
  501. }
  502. void ParticleSystem::moveTo(float x, float y)
  503. {
  504. position = love::Vector2(x, y);
  505. }
  506. void ParticleSystem::setEmissionArea(AreaSpreadDistribution distribution, float x, float y, float angle, bool directionRelativeToCenter)
  507. {
  508. emissionArea = love::Vector2(x, y);
  509. emissionAreaDistribution = distribution;
  510. emissionAreaAngle = angle;
  511. directionRelativeToEmissionCenter = directionRelativeToCenter;
  512. }
  513. ParticleSystem::AreaSpreadDistribution ParticleSystem::getEmissionArea(love::Vector2 &params, float &angle, bool &directionRelativeToCenter) const
  514. {
  515. params = emissionArea;
  516. angle = emissionAreaAngle;
  517. directionRelativeToCenter = directionRelativeToEmissionCenter;
  518. return emissionAreaDistribution;
  519. }
  520. void ParticleSystem::setDirection(float direction)
  521. {
  522. this->direction = direction;
  523. }
  524. float ParticleSystem::getDirection() const
  525. {
  526. return direction;
  527. }
  528. void ParticleSystem::setSpread(float spread)
  529. {
  530. this->spread = spread;
  531. }
  532. float ParticleSystem::getSpread() const
  533. {
  534. return spread;
  535. }
  536. void ParticleSystem::setSpeed(float speed)
  537. {
  538. speedMin = speedMax = speed;
  539. }
  540. void ParticleSystem::setSpeed(float min, float max)
  541. {
  542. speedMin = min;
  543. speedMax = max;
  544. }
  545. void ParticleSystem::getSpeed(float &min, float &max) const
  546. {
  547. min = speedMin;
  548. max = speedMax;
  549. }
  550. void ParticleSystem::setLinearAcceleration(float x, float y)
  551. {
  552. linearAccelerationMin.x = linearAccelerationMax.x = x;
  553. linearAccelerationMin.y = linearAccelerationMax.y = y;
  554. }
  555. void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, float ymax)
  556. {
  557. linearAccelerationMin = love::Vector2(xmin, ymin);
  558. linearAccelerationMax = love::Vector2(xmax, ymax);
  559. }
  560. void ParticleSystem::getLinearAcceleration(love::Vector2 &min, love::Vector2 &max) const
  561. {
  562. min = linearAccelerationMin;
  563. max = linearAccelerationMax;
  564. }
  565. void ParticleSystem::setRadialAcceleration(float acceleration)
  566. {
  567. radialAccelerationMin = radialAccelerationMax = acceleration;
  568. }
  569. void ParticleSystem::setRadialAcceleration(float min, float max)
  570. {
  571. radialAccelerationMin = min;
  572. radialAccelerationMax = max;
  573. }
  574. void ParticleSystem::getRadialAcceleration(float &min, float &max) const
  575. {
  576. min = radialAccelerationMin;
  577. max = radialAccelerationMax;
  578. }
  579. void ParticleSystem::setTangentialAcceleration(float acceleration)
  580. {
  581. tangentialAccelerationMin = tangentialAccelerationMax = acceleration;
  582. }
  583. void ParticleSystem::setTangentialAcceleration(float min, float max)
  584. {
  585. tangentialAccelerationMin = min;
  586. tangentialAccelerationMax = max;
  587. }
  588. void ParticleSystem::getTangentialAcceleration(float &min, float &max) const
  589. {
  590. min = tangentialAccelerationMin;
  591. max = tangentialAccelerationMax;
  592. }
  593. void ParticleSystem::setLinearDamping(float min, float max)
  594. {
  595. linearDampingMin = min;
  596. linearDampingMax = max;
  597. }
  598. void ParticleSystem::getLinearDamping(float &min, float &max) const
  599. {
  600. min = linearDampingMin;
  601. max = linearDampingMax;
  602. }
  603. void ParticleSystem::setSize(float size)
  604. {
  605. sizes.resize(1);
  606. sizes[0] = size;
  607. }
  608. void ParticleSystem::setSizes(const std::vector<float> &newSizes)
  609. {
  610. sizes = newSizes;
  611. }
  612. const std::vector<float> &ParticleSystem::getSizes() const
  613. {
  614. return sizes;
  615. }
  616. void ParticleSystem::setSizeVariation(float variation)
  617. {
  618. sizeVariation = variation;
  619. }
  620. float ParticleSystem::getSizeVariation() const
  621. {
  622. return sizeVariation;
  623. }
  624. void ParticleSystem::setRotation(float rotation)
  625. {
  626. rotationMin = rotationMax = rotation;
  627. }
  628. void ParticleSystem::setRotation(float min, float max)
  629. {
  630. rotationMin = min;
  631. rotationMax = max;
  632. }
  633. void ParticleSystem::getRotation(float &min, float &max) const
  634. {
  635. min = rotationMin;
  636. max = rotationMax;
  637. }
  638. void ParticleSystem::setSpin(float spin)
  639. {
  640. spinStart = spin;
  641. spinEnd = spin;
  642. }
  643. void ParticleSystem::setSpin(float start, float end)
  644. {
  645. spinStart = start;
  646. spinEnd = end;
  647. }
  648. void ParticleSystem::getSpin(float &start, float &end) const
  649. {
  650. start = spinStart;
  651. end = spinEnd;
  652. }
  653. void ParticleSystem::setSpinVariation(float variation)
  654. {
  655. spinVariation = variation;
  656. }
  657. float ParticleSystem::getSpinVariation() const
  658. {
  659. return spinVariation;
  660. }
  661. void ParticleSystem::setOffset(float x, float y)
  662. {
  663. offset = love::Vector2(x, y);
  664. defaultOffset = false;
  665. }
  666. love::Vector2 ParticleSystem::getOffset() const
  667. {
  668. return offset;
  669. }
  670. void ParticleSystem::setColor(const std::vector<Colorf> &newColors)
  671. {
  672. colors = newColors;
  673. // We don't support colors outside of [0,1] when drawing the ParticleSystem.
  674. for (auto &c : colors)
  675. {
  676. c.r = std::min(std::max(c.r, 0.0f), 1.0f);
  677. c.g = std::min(std::max(c.g, 0.0f), 1.0f);
  678. c.b = std::min(std::max(c.b, 0.0f), 1.0f);
  679. c.a = std::min(std::max(c.a, 0.0f), 1.0f);
  680. }
  681. }
  682. std::vector<Colorf> ParticleSystem::getColor() const
  683. {
  684. return colors;
  685. }
  686. void ParticleSystem::setQuads(const std::vector<Quad *> &newQuads)
  687. {
  688. std::vector<StrongRef<Quad>> quadlist;
  689. quadlist.reserve(newQuads.size());
  690. for (Quad *q : newQuads)
  691. quadlist.push_back(q);
  692. quads = quadlist;
  693. if (defaultOffset)
  694. resetOffset();
  695. }
  696. void ParticleSystem::setQuads()
  697. {
  698. quads.clear();
  699. }
  700. std::vector<Quad *> ParticleSystem::getQuads() const
  701. {
  702. std::vector<Quad *> quadlist;
  703. quadlist.reserve(quads.size());
  704. for (const StrongRef<Quad> &q : quads)
  705. quadlist.push_back(q.get());
  706. return quadlist;
  707. }
  708. void ParticleSystem::setRelativeRotation(bool enable)
  709. {
  710. relativeRotation = enable;
  711. }
  712. bool ParticleSystem::hasRelativeRotation() const
  713. {
  714. return relativeRotation;
  715. }
  716. uint32 ParticleSystem::getCount() const
  717. {
  718. return activeParticles;
  719. }
  720. void ParticleSystem::start()
  721. {
  722. active = true;
  723. }
  724. void ParticleSystem::stop()
  725. {
  726. active = false;
  727. life = lifetime;
  728. emitCounter = 0;
  729. }
  730. void ParticleSystem::pause()
  731. {
  732. active = false;
  733. }
  734. void ParticleSystem::reset()
  735. {
  736. if (pMem == nullptr)
  737. return;
  738. pFree = pMem;
  739. pHead = nullptr;
  740. pTail = nullptr;
  741. activeParticles = 0;
  742. life = lifetime;
  743. emitCounter = 0;
  744. }
  745. void ParticleSystem::emit(uint32 num)
  746. {
  747. if (!active)
  748. return;
  749. num = std::min(num, maxParticles - activeParticles);
  750. while (num--)
  751. addParticle(1.0f);
  752. }
  753. bool ParticleSystem::isActive() const
  754. {
  755. return active;
  756. }
  757. bool ParticleSystem::isPaused() const
  758. {
  759. return !active && life < lifetime;
  760. }
  761. bool ParticleSystem::isStopped() const
  762. {
  763. return !active && life >= lifetime;
  764. }
  765. bool ParticleSystem::isEmpty() const
  766. {
  767. return activeParticles == 0;
  768. }
  769. bool ParticleSystem::isFull() const
  770. {
  771. return activeParticles == maxParticles;
  772. }
  773. void ParticleSystem::update(float dt)
  774. {
  775. if (pMem == nullptr || dt == 0.0f)
  776. return;
  777. // Traverse all particles and update.
  778. Particle *p = pHead;
  779. while (p)
  780. {
  781. // Decrease lifespan.
  782. p->life -= dt;
  783. if (p->life <= 0)
  784. p = removeParticle(p);
  785. else
  786. {
  787. // Temp variables.
  788. love::Vector2 radial, tangential;
  789. love::Vector2 ppos = p->position;
  790. // Get vector from particle center to particle.
  791. radial = ppos - p->origin;
  792. radial.normalize();
  793. tangential = radial;
  794. // Resize radial acceleration.
  795. radial *= p->radialAcceleration;
  796. // Calculate tangential acceleration.
  797. {
  798. float a = tangential.x;
  799. tangential.x = -tangential.y;
  800. tangential.y = a;
  801. }
  802. // Resize tangential.
  803. tangential *= p->tangentialAcceleration;
  804. // Update velocity.
  805. p->velocity += (radial + tangential + p->linearAcceleration) * dt;
  806. // Apply damping.
  807. p->velocity *= 1.0f / (1.0f + p->linearDamping * dt);
  808. // Modify position.
  809. ppos += p->velocity * dt;
  810. p->position = ppos;
  811. const float t = 1.0f - p->life / p->lifetime;
  812. // Rotate.
  813. p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt;
  814. p->angle = p->rotation;
  815. if (relativeRotation)
  816. p->angle += atan2f(p->velocity.y, p->velocity.x);
  817. // Change size according to given intervals:
  818. // i = 0 1 2 3 n-1
  819. // |-------|-------|------|--- ... ---|
  820. // t = 0 1/(n-1) 3/(n-1) 1
  821. //
  822. // `s' is the interpolation variable scaled to the current
  823. // interval width, e.g. if n = 5 and t = 0.3, then the current
  824. // indices are 1,2 and s = 0.3 - 0.25 = 0.05
  825. float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation
  826. s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size()
  827. size_t i = (size_t)s;
  828. size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f)
  829. s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1
  830. p->size = sizes[i] * (1.0f - s) + sizes[k] * s;
  831. // Update color according to given intervals (as above)
  832. s = t * (float)(colors.size() - 1);
  833. i = (size_t)s;
  834. k = (i == colors.size() - 1) ? i : i + 1;
  835. s -= (float)i; // 0 <= s <= 1
  836. p->color = colors[i] * (1.0f - s) + colors[k] * s;
  837. // Update the quad index.
  838. k = quads.size();
  839. if (k > 0)
  840. {
  841. s = t * (float) k; // [0:numquads-1] (clamped below)
  842. i = (s > 0.0f) ? (size_t) s : 0;
  843. p->quadIndex = (int) ((i < k) ? i : k - 1);
  844. }
  845. // Next particle.
  846. p = p->next;
  847. }
  848. }
  849. // Make some more particles.
  850. if (active)
  851. {
  852. float rate = 1.0f / emissionRate; // the amount of time between each particle emit
  853. emitCounter += dt;
  854. float total = emitCounter - rate;
  855. while (emitCounter > rate)
  856. {
  857. addParticle(1.0f - (emitCounter - rate) / total);
  858. emitCounter -= rate;
  859. }
  860. life -= dt;
  861. if (lifetime != -1 && life < 0)
  862. stop();
  863. }
  864. prevPosition = position;
  865. }
  866. void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
  867. {
  868. uint32 pCount = getCount();
  869. if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || buffer == nullptr)
  870. return;
  871. gfx->flushStreamDraws();
  872. if (Shader::isDefaultActive())
  873. Shader::attachDefault(Shader::STANDARD_DEFAULT);
  874. if (Shader::current && texture.get())
  875. Shader::current->checkMainTexture(texture);
  876. const Vector2 *positions = texture->getQuad()->getVertexPositions();
  877. const Vector2 *texcoords = texture->getQuad()->getVertexTexCoords();
  878. Vertex *pVerts = (Vertex *) buffer->map();
  879. Particle *p = pHead;
  880. bool useQuads = !quads.empty();
  881. Matrix3 t;
  882. // set the vertex data for each particle (transformation, texcoords, color)
  883. while (p)
  884. {
  885. if (useQuads)
  886. {
  887. positions = quads[p->quadIndex]->getVertexPositions();
  888. texcoords = quads[p->quadIndex]->getVertexTexCoords();
  889. }
  890. // particle vertices are image vertices transformed by particle info
  891. t.setTransformation(p->position.x, p->position.y, p->angle, p->size, p->size, offset.x, offset.y, 0.0f, 0.0f);
  892. t.transformXY(pVerts, positions, 4);
  893. // Particle colors are stored as floats (0-1) but vertex colors are
  894. // unsigned bytes (0-255).
  895. Color32 c = toColor32(p->color);
  896. // set the texture coordinate and color data for particle vertices
  897. for (int v = 0; v < 4; v++)
  898. {
  899. pVerts[v].s = texcoords[v].x;
  900. pVerts[v].t = texcoords[v].y;
  901. pVerts[v].color = c;
  902. }
  903. pVerts += 4;
  904. p = p->next;
  905. }
  906. buffer->unmap();
  907. Graphics::TempTransform transform(gfx, m);
  908. BufferBindings vertexbuffers;
  909. vertexbuffers.set(0, buffer, 0);
  910. gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, texture);
  911. }
  912. bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
  913. {
  914. return distributions.find(in, out);
  915. }
  916. bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out)
  917. {
  918. return distributions.find(in, out);
  919. }
  920. std::vector<std::string> ParticleSystem::getConstants(AreaSpreadDistribution)
  921. {
  922. return distributions.getNames();
  923. }
  924. bool ParticleSystem::getConstant(const char *in, InsertMode &out)
  925. {
  926. return insertModes.find(in, out);
  927. }
  928. bool ParticleSystem::getConstant(InsertMode in, const char *&out)
  929. {
  930. return insertModes.find(in, out);
  931. }
  932. std::vector<std::string> ParticleSystem::getConstants(InsertMode)
  933. {
  934. return insertModes.getNames();
  935. }
  936. StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM>::Entry ParticleSystem::distributionsEntries[] =
  937. {
  938. { "none", DISTRIBUTION_NONE },
  939. { "uniform", DISTRIBUTION_UNIFORM },
  940. { "normal", DISTRIBUTION_NORMAL },
  941. { "ellipse", DISTRIBUTION_ELLIPSE },
  942. { "borderellipse", DISTRIBUTION_BORDER_ELLIPSE },
  943. { "borderrectangle", DISTRIBUTION_BORDER_RECTANGLE }
  944. };
  945. StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM> ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries));
  946. StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM>::Entry ParticleSystem::insertModesEntries[] =
  947. {
  948. { "top", INSERT_MODE_TOP },
  949. { "bottom", INSERT_MODE_BOTTOM },
  950. { "random", INSERT_MODE_RANDOM },
  951. };
  952. StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM> ParticleSystem::insertModes(ParticleSystem::insertModesEntries, sizeof(ParticleSystem::insertModesEntries));
  953. } // graphics
  954. } // love