Particle.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. For Android OpenGL ES 2.0 'D._sampler_address' is forced to 'GL_CLAMP_TO_EDGE' because particles don't need 'GL_REPEAT',
  6. and according to GLES2.0 docs https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml
  7. "Similarly, if the width or height of a texture image are not powers of two and either the GL_TEXTURE_MIN_FILTER
  8. is set to one of the functions that requires mipmaps or the GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T is not set
  9. to GL_CLAMP_TO_EDGE, then the texture image unit will return (R, G, B, A) = (0, 0, 0, 1)."
  10. This means that if tex is not pow2 size, then it will return (0,0,0,1) color when GL_REPEAT is used.
  11. Afterwards we're restoring to 'GL_REPEAT' because particles are always drawn after 'D.sampler3D' which sets 'GL_REPEAT'.
  12. iOS also operates on GLES2.0 however its GPU's don't need this.
  13. /******************************************************************************/
  14. #define CC4_PRTC CC4('P','R','T','C')
  15. #if 0
  16. #define TEX_ZERO HalfZero
  17. #define TEX_ONE HalfOne
  18. #define SET_TEX(t, x, y) t.set(x, y)
  19. #else
  20. #define TEX_ZERO 0
  21. #define TEX_ONE 255
  22. #define SET_TEX(t, x, y) t.set(x, y, 0, 0)
  23. #endif
  24. /******************************************************************************/
  25. Cache<Particles> ParticlesCache("Particles");
  26. static Color (*ColorFunc)(C Color &color, Flt s)=ColorAlpha; // pointer to function
  27. static Bool SoftParticles()
  28. {
  29. return D.particlesSoft() && Renderer.canReadDepth1S(); // we want soft and it's available
  30. }
  31. /******************************************************************************/
  32. static Flt OpacityDefault(Flt s) {return Sqrt((s<0.1f) ? s/0.1f : 1-(s-0.1f)/0.9f);}
  33. static Flt OpacitySin (Flt s) {return Sin (s*PI );}
  34. /******************************************************************************/
  35. static Flt (*GetOpacityFunc(Bool smooth)) (Flt s) // function which returns function, "GetOpacityFunc(Bool smooth)" -> "Flt f(Flt s)"
  36. {
  37. return smooth ? OpacitySin : OpacityDefault;
  38. }
  39. static Flt (*GetOpacityFunc(C Particles &particles)) (Flt s) // function which returns function, "GetOpacityFunc(C Particles &particles)" -> "Flt f(Flt s)"
  40. {
  41. return particles.opacity_func ? particles.opacity_func : GetOpacityFunc(particles.smooth_fade);
  42. }
  43. /******************************************************************************/
  44. // MAIN
  45. /******************************************************************************/
  46. Bool DrawParticleBegin(C Image &image, Byte glow, Bool motion_affects_alpha)
  47. {
  48. Renderer.wantDepthRead(); // !! call before 'SoftParticles' !!
  49. Shader *shader;
  50. Bool soft=SoftParticles();
  51. switch(Renderer())
  52. {
  53. default : return false;
  54. case RM_BLEND : shader=Sh.h_Particle[false][soft][0][motion_affects_alpha]; ColorFunc=ColorAlpha; D.alpha(ALPHA_BLEND_FACTOR); D.alphaFactor(Color(0, 0, 0, glow)); Renderer._has_glow|=(glow!=0); break;
  55. case RM_PALETTE :
  56. case RM_PALETTE1: shader=Sh.h_Particle[true ][soft][0][motion_affects_alpha]; ColorFunc=ColorMul ; D.alpha(ALPHA_ADD ); break;
  57. }
  58. SetOneMatrix ( );
  59. D .depthWrite( false );
  60. VI.image (&image );
  61. VI.shader ( shader);
  62. VI.setFirst ( VI_3D_BILB, VI_QUAD_IND);
  63. #if DX9 || GL // DX10+ should support all sizes
  64. Sh.h_ColSize->set(image._part.xy);
  65. #endif
  66. #if GL_ES && ANDROID // check the comments at the top why this is called
  67. D._sampler_address=GL_CLAMP_TO_EDGE;
  68. #endif
  69. MaterialClear(); // because of alpha factor
  70. return true;
  71. }
  72. void DrawParticleAdd(C Color &color, Flt opacity, Flt radius, Flt angle, C Vec &pos, C Vec &vel)
  73. {
  74. if(Vtx3DBilb *v=(Vtx3DBilb*)VI.addVtx(4))
  75. {
  76. v[0].pos =v[1].pos =v[2].pos =v[3].pos =pos;
  77. v[0].vel_angle=v[1].vel_angle=v[2].vel_angle=v[3].vel_angle.set(vel.x, vel.y, vel.z, GPU_HALF_SUPPORTED ? AngleFull(angle) : angle);
  78. v[0].color =v[1].color =v[2].color =v[3].color =ColorFunc(color, opacity);
  79. v[0].size =v[1].size =v[2].size =v[3].size =radius;
  80. SET_TEX(v[0].tex, TEX_ZERO, TEX_ONE );
  81. SET_TEX(v[1].tex, TEX_ONE , TEX_ONE );
  82. SET_TEX(v[2].tex, TEX_ONE , TEX_ZERO);
  83. SET_TEX(v[3].tex, TEX_ZERO, TEX_ZERO);
  84. }
  85. }
  86. void DrawParticleEnd()
  87. {
  88. VI.end();
  89. #if GL_ES && ANDROID
  90. D._sampler_address=GL_REPEAT;
  91. #endif
  92. }
  93. /******************************************************************************/
  94. Bool DrawAnimatedParticleBegin(C Image &image, Byte glow, Bool motion_affects_alpha, Int x_frames, Int y_frames)
  95. {
  96. Renderer.wantDepthRead(); // !! call before 'SoftParticles' !!
  97. Shader *shader;
  98. Bool soft=SoftParticles();
  99. switch(Renderer())
  100. {
  101. default : return false;
  102. case RM_BLEND : shader=Sh.h_Particle[false][soft][1+D.particlesSmoothAnim()][motion_affects_alpha]; ColorFunc=ColorAlpha; D.alpha(ALPHA_BLEND_FACTOR); D.alphaFactor(Color(0, 0, 0, glow)); Renderer._has_glow|=(glow!=0); break;
  103. case RM_PALETTE :
  104. case RM_PALETTE1: shader=Sh.h_Particle[true ][soft][1+D.particlesSmoothAnim()][motion_affects_alpha]; ColorFunc=ColorMul ; D.alpha(ALPHA_ADD ); break;
  105. }
  106. SetOneMatrix ( );
  107. D .depthWrite( false );
  108. VI.image (&image );
  109. VI.shader ( shader);
  110. VI.setFirst ( VI_3D_BILB_ANIM, VI_QUAD_IND);
  111. Sh.h_ParticleFrames->set(VecI2(x_frames, y_frames));
  112. #if DX9 || GL // DX10+ should support all sizes
  113. Sh.h_ColSize->set(image._part.xy);
  114. #endif
  115. #if GL_ES && ANDROID // check the comments at the top why this is called
  116. D._sampler_address=GL_CLAMP_TO_EDGE;
  117. #endif
  118. MaterialClear(); // because of alpha factor
  119. return true;
  120. }
  121. void DrawAnimatedParticleAdd(C Color &color, Flt opacity, Flt radius, Flt angle, C Vec &pos, C Vec &vel, Flt frame)
  122. {
  123. if(Vtx3DBilbAnim *v=(Vtx3DBilbAnim*)VI.addVtx(4))
  124. {
  125. v[0].pos =v[1].pos =v[2].pos =v[3].pos =pos;
  126. v[0].vel_angle=v[1].vel_angle=v[2].vel_angle=v[3].vel_angle.set(vel.x, vel.y, vel.z, GPU_HALF_SUPPORTED ? AngleFull(angle) : angle);
  127. v[0].color =v[1].color =v[2].color =v[3].color =ColorFunc(color, opacity);
  128. v[0].size =v[1].size =v[2].size =v[3].size =radius;
  129. v[0].frame =v[1].frame =v[2].frame =v[3].frame =frame;
  130. SET_TEX(v[0].tex, TEX_ZERO, TEX_ONE );
  131. SET_TEX(v[1].tex, TEX_ONE , TEX_ONE );
  132. SET_TEX(v[2].tex, TEX_ONE , TEX_ZERO);
  133. SET_TEX(v[3].tex, TEX_ZERO, TEX_ZERO);
  134. }
  135. }
  136. void DrawAnimatedParticleEnd()
  137. {
  138. VI.end();
  139. #if GL_ES && ANDROID
  140. D._sampler_address=GL_REPEAT;
  141. #endif
  142. }
  143. /******************************************************************************/
  144. Flt ParticleOpacity(Flt particle_life, Flt particle_life_max, Bool particles_smooth_fade)
  145. {
  146. if(particle_life_max>EPS)return GetOpacityFunc(particles_smooth_fade)(particle_life/particle_life_max);
  147. return 0;
  148. }
  149. /******************************************************************************/
  150. // PARTICLES
  151. /******************************************************************************/
  152. void Particles::zero()
  153. {
  154. reborn =true;
  155. smooth_fade =false;
  156. motion_affects_alpha=true;
  157. glow =0;
  158. color .set(255, 255, 255, 255);
  159. _palette =false;
  160. _palette_index=0;
  161. _render_mode =RM_BLEND;
  162. radius =0.05f;
  163. radius_random=0;
  164. radius_growth=1;
  165. offset_range =0;
  166. offset_speed =1;
  167. life =1;
  168. life_random =0;
  169. glue =0;
  170. damping =0;
  171. ang_vel =0;
  172. vel_random =0;
  173. vel_constant=0;
  174. accel =0;
  175. emitter_life_max =0;
  176. emitter_life =0;
  177. _fade =1;
  178. fade_in =1;
  179. fade_out =1;
  180. radius_scale_base=1;
  181. radius_scale_time=0;
  182. matrix.identity();
  183. _matrix_prev.zero(); // zero so that the nearest 'update' will not use 'glue'
  184. inside_shape=true;
  185. shape =Ball(1);
  186. _src_type =PARTICLE_NONE;
  187. _src_ptr =null;
  188. _src_elms =0;
  189. _src_help =null;
  190. image_x_frames=1;
  191. image_y_frames=1;
  192. image_speed =1;
  193. hard_depth_offset=0;
  194. opacity_func=null;
  195. }
  196. Particles& Particles::del()
  197. {
  198. p .del();
  199. palette_image=null;
  200. image =null;
  201. _src_mesh =null;
  202. Free(_src_help);
  203. zero(); return T;
  204. }
  205. Particles::Particles() {zero();}
  206. Particles& Particles::create(C ImagePtr &image, C Color &color, Int elms, Flt radius, Flt life)
  207. {
  208. del();
  209. p.setNum(elms);
  210. T.color =color;
  211. T.radius =radius;
  212. T.life =life;
  213. _src_type =PARTICLE_STATIC_SHAPE;
  214. T.image =image;
  215. setRenderMode();
  216. return T;
  217. }
  218. Particles& Particles::create(C Particles &src)
  219. {
  220. if(this!=&src)
  221. {
  222. del();
  223. reborn =src.reborn;
  224. smooth_fade =src.smooth_fade;
  225. motion_affects_alpha=src.motion_affects_alpha;
  226. glow =src.glow;
  227. color =src.color;
  228. radius =src.radius;
  229. radius_random=src.radius_random;
  230. radius_growth=src.radius_growth;
  231. offset_range =src.offset_range;
  232. offset_speed =src.offset_speed;
  233. life =src.life;
  234. life_random =src.life_random;
  235. glue =src.glue;
  236. damping =src.damping;
  237. ang_vel =src.ang_vel;
  238. vel_random =src.vel_random;
  239. vel_constant=src.vel_constant;
  240. accel =src.accel;
  241. emitter_life_max =src.emitter_life_max;
  242. emitter_life =src.emitter_life;
  243. fade_in =src.fade_in;
  244. fade_out =src.fade_out;
  245. radius_scale_base=src.radius_scale_base;
  246. radius_scale_time=src.radius_scale_time;
  247. matrix =src.matrix;
  248. shape =src.shape;
  249. inside_shape=src.inside_shape;
  250. image =src.image;
  251. image_x_frames=src.image_x_frames;
  252. image_y_frames=src.image_y_frames;
  253. image_speed =src.image_speed;
  254. palette_image=src.palette_image;
  255. _palette =src._palette;
  256. _palette_index=src._palette_index;
  257. _fade =src._fade;
  258. _render_mode =src._render_mode;
  259. _src_type =src._src_type;
  260. _src_ptr =src._src_ptr;
  261. _src_mesh =src._src_mesh;
  262. hard_depth_offset=src.hard_depth_offset;
  263. opacity_func =src.opacity_func;
  264. CopyN(Alloc(_src_help, src._src_elms), src._src_help, _src_elms=src._src_elms);
  265. p=src.p;
  266. }
  267. return T;
  268. }
  269. /******************************************************************************/
  270. Particles& Particles::source(C Shape &static_shape)
  271. {
  272. T._src_type=PARTICLE_STATIC_SHAPE;
  273. T._src_ptr =null;
  274. T._src_mesh=null;
  275. T. shape =static_shape;
  276. return T;
  277. }
  278. Particles& Particles::source(C Shape *dynamic_shape)
  279. {
  280. T._src_type=PARTICLE_DYNAMIC_SHAPE;
  281. T._src_ptr =dynamic_shape;
  282. T._src_mesh=null;
  283. T._src_elms=1;
  284. return T;
  285. }
  286. Particles& Particles::source(C Shape *dynamic_shapes, Int shapes)
  287. {
  288. T._src_type=PARTICLE_DYNAMIC_SHAPES;
  289. T._src_ptr =dynamic_shapes;
  290. T._src_mesh=null;
  291. T._src_elms=Max(shapes, 0);
  292. return T;
  293. }
  294. Particles& Particles::source(C OrientP *dynamic_point)
  295. {
  296. T._src_type=PARTICLE_DYNAMIC_ORIENTP;
  297. T._src_ptr =dynamic_point;
  298. T._src_mesh=null;
  299. T._src_elms=1;
  300. return T;
  301. }
  302. Particles& Particles::source(C AnimatedSkeleton *dynamic_skeleton, Bool ragdoll_bones_only)
  303. {
  304. T._src_type=PARTICLE_DYNAMIC_SKELETON;
  305. T._src_ptr =dynamic_skeleton;
  306. T._src_mesh=null;
  307. T._src_elms=0;
  308. Free(_src_help);
  309. if(ragdoll_bones_only)if(C Skeleton *skel=dynamic_skeleton->skeleton())
  310. {
  311. REPA(skel->bones)if(skel->bones[i].flag&BONE_RAGDOLL) _src_elms++;
  312. Alloc(_src_help, _src_elms); _src_elms=0; REPA(skel->bones)if(skel->bones[i].flag&BONE_RAGDOLL)_src_help[_src_elms++]=i;
  313. }
  314. return T;
  315. }
  316. Particles& Particles::source(C MeshPtr &dynamic_mesh)
  317. {
  318. T._src_type=PARTICLE_DYNAMIC_MESH;
  319. T._src_ptr =null;
  320. T._src_mesh=dynamic_mesh;
  321. T._src_elms=1;
  322. return T;
  323. }
  324. Particles& Particles::source(C MeshPtr &dynamic_mesh, C AnimatedSkeleton *dynamic_skeleton)
  325. {
  326. T._src_type=PARTICLE_DYNAMIC_MESH_SKELETON;
  327. T._src_ptr =dynamic_skeleton;
  328. T._src_mesh=dynamic_mesh;
  329. T._src_elms=1;
  330. return T;
  331. }
  332. /******************************************************************************/
  333. Flt Particles::opacity(Vec *pos)C
  334. {
  335. Vec vec =0;
  336. Flt opacity=0;
  337. Flt (*func)(Flt s)=GetOpacityFunc(T);
  338. REPA(p)
  339. {
  340. C Particle &p=T.p[i];
  341. if(p.life_max>EPS)
  342. {
  343. Flt o=func(p.life/p.life_max);
  344. vec +=o*p.pos;
  345. opacity+=o;
  346. }
  347. }
  348. if(opacity)
  349. {
  350. vec /=opacity;
  351. opacity/=p.elms();
  352. }
  353. if(pos)*pos=vec;
  354. return opacity;
  355. }
  356. /******************************************************************************/
  357. RENDER_MODE Particles:: renderMode()C {return D.colorPaletteAllow() ? _render_mode : RM_BLEND;}
  358. Particles& Particles::setRenderMode()
  359. {
  360. _render_mode=(palette() ? (paletteIndex() ? RM_PALETTE1 : RM_PALETTE) : RM_BLEND);
  361. return T;
  362. }
  363. Particles& Particles::palette (Bool palette ) {T._palette = palette ; return setRenderMode();}
  364. Particles& Particles::paletteIndex(Byte palette_index) {T._palette_index=Sat(palette_index); return setRenderMode();}
  365. /******************************************************************************/
  366. void Particles::reset(Int i)
  367. {
  368. if(InRange(i, p))
  369. {
  370. Particle &p=T.p[i];
  371. if(reborn)
  372. {
  373. switch(_src_type)
  374. {
  375. case PARTICLE_STATIC_SHAPE : p.pos=( Random( shape , inside_shape)*matrix ); break;
  376. case PARTICLE_DYNAMIC_SHAPE : p.pos=((_src_ptr ) ? Random(((Shape *)_src_ptr)[ 0], inside_shape) : matrix.pos); break;
  377. case PARTICLE_DYNAMIC_SHAPES : p.pos=((_src_ptr && _src_elms) ? Random(((Shape *)_src_ptr)[Random(_src_elms)], inside_shape) : matrix.pos); break;
  378. case PARTICLE_DYNAMIC_ORIENTP: p.pos=((_src_ptr ) ? ((OrientP*)_src_ptr)->pos : matrix.pos); break;
  379. case PARTICLE_DYNAMIC_SKELETON:
  380. {
  381. if(C AnimatedSkeleton *anim_skel=(AnimatedSkeleton*)_src_ptr)
  382. if(C Skeleton * skel=anim_skel->skeleton())
  383. if( Int bones=anim_skel->minBones())
  384. {
  385. Int bone=((_src_help && _src_elms) ? Min(bones, _src_help[Random(_src_elms)]) : Random(bones));
  386. C SkelBone &skel_bone=skel->bones[bone];
  387. p.pos = skel_bone.pos+skel_bone.dir*Random.f(skel_bone.length);
  388. p.pos*= anim_skel->bones[bone]._matrix;
  389. break;
  390. }
  391. p.pos=matrix.pos;
  392. }break;
  393. case PARTICLE_DYNAMIC_MESH : p.pos=(_src_mesh ? Random(*_src_mesh )*matrix : matrix.pos); break;
  394. case PARTICLE_DYNAMIC_MESH_SKELETON: p.pos=(_src_mesh ? Random(*_src_mesh, (AnimatedSkeleton*)_src_ptr) : matrix.pos); break;
  395. default: p.pos=matrix.pos; break;
  396. }
  397. p.palette_y =(palette_image ? Random(palette_image->h()) : 0);
  398. p.image_index=Random (image_x_frames*image_y_frames);
  399. p.vel =Random (Ball(vel_random))+vel_constant;
  400. p.ang_vel =Random.f(-ang_vel, ang_vel);
  401. p.radius =T.radius*ScaleFactor(Random.f(-radius_random, radius_random));
  402. Flt life =T.life *ScaleFactor(Random.f(- life_random, life_random));
  403. if(p.life_max>EPS && life)p.life=Frac (p.life-p.life_max, life); // if particle was created before, then set some initial life depending on what it has already
  404. else p.life=Random.f(life ); // if we're creating particle for the first time, then set random initial life so it won't look like all particles created in the same time
  405. p.life_max=life;
  406. }else
  407. {
  408. p.life=p.life_max=0; // if it shouldn't reborn then set life already as dead
  409. }
  410. }
  411. }
  412. Particles& Particles::reset()
  413. {
  414. REPA(p)reset(i);
  415. return T;
  416. }
  417. Particles& Particles::resetFull()
  418. {
  419. Bool temp=reborn; reborn=true;
  420. emitter_life=0;
  421. reset();
  422. reborn=temp;
  423. return T;
  424. }
  425. /******************************************************************************/
  426. Bool Particles::update(Flt dt)
  427. {
  428. if(_src_type)
  429. {
  430. // life/death/fade
  431. if(emitter_life_max>0)
  432. {
  433. emitter_life+=dt;
  434. if(emitter_life>=emitter_life_max)
  435. {
  436. _fade=0;
  437. return false;
  438. }
  439. Flt left=emitter_life_max-emitter_life;
  440. if(emitter_life<fade_in )_fade=emitter_life/fade_in ;else // fade in
  441. if(left <fade_out)_fade=left /fade_out;else // fade out
  442. _fade= 1; // middle
  443. }else
  444. {
  445. _fade=1;
  446. }
  447. // matrix
  448. Matrix matrix;
  449. switch(_src_type)
  450. {
  451. default : matrix.identity() ; break;
  452. case PARTICLE_STATIC_SHAPE : matrix=shape.asMatrix()*T.matrix; break;
  453. case PARTICLE_DYNAMIC_MESH : matrix= T.matrix; break;
  454. case PARTICLE_DYNAMIC_SHAPE : if(_src_ptr )matrix=((Shape *)_src_ptr)[ 0].asMatrix();else matrix.identity(); break;
  455. case PARTICLE_DYNAMIC_SHAPES : if(_src_ptr && _src_elms)matrix=((Shape *)_src_ptr)[Random(_src_elms)].asMatrix();else matrix.identity(); break;
  456. case PARTICLE_DYNAMIC_ORIENTP : if(_src_ptr )matrix=*(OrientP *)_src_ptr ;else matrix.identity(); break;
  457. case PARTICLE_DYNAMIC_SKELETON : if(_src_ptr )matrix=((AnimatedSkeleton*)_src_ptr)->matrix() ;else matrix.identity(); break;
  458. case PARTICLE_DYNAMIC_MESH_SKELETON: if(_src_ptr )matrix=((AnimatedSkeleton*)_src_ptr)->matrix() ;else matrix.identity(); break;
  459. }
  460. // single particles
  461. Bool glue =(T.glue>EPS && _matrix_prev.x.any()),
  462. glue_full=(T.glue>=1-EPS);
  463. Matrix glue_transform; if(glue)GetTransform(glue_transform, _matrix_prev, matrix);
  464. Flt glue_mul, glue_add,
  465. damping=Pow(1-T.damping , dt),
  466. growth =Pow( T.radius_growth, dt);
  467. Vec accel = T.accel * dt ;
  468. if(glue && !glue_full)
  469. {
  470. glue_mul=Abs(T.glue-0.5f)*-2+1; // 1-Abs(T.glue-0.5f)/0.5f
  471. glue_add=Sat(T.glue*2-1 ) ;
  472. }
  473. REPA(p)
  474. {
  475. Particle &p=T.p[i];
  476. p.life+=dt;
  477. if(p.life>p.life_max)reset(i);else
  478. {
  479. if(glue)
  480. {
  481. if(glue_full)p.pos*=glue_transform;
  482. else p.pos =Lerp(p.pos, p.pos*glue_transform, Sqr(1-p.life/p.life_max)*glue_mul+glue_add);
  483. }
  484. p.vel *=damping;
  485. p.vel +=accel ;
  486. p.pos +=p.vel*dt;
  487. p.radius*=growth;
  488. }
  489. }
  490. _matrix_prev=matrix;
  491. return true;
  492. }
  493. return false;
  494. }
  495. /******************************************************************************/
  496. void Particles::draw(Flt opacity)C
  497. {
  498. if(_src_type && image && Renderer()==renderMode())
  499. {
  500. opacity*=_fade;
  501. if(opacity>0)
  502. {
  503. Bool initialized =false,
  504. depth_offset=(!SoftParticles() && hard_depth_offset>0),
  505. offset =(offset_range>EPS);
  506. Flt offset_time =Time.time()*offset_speed,
  507. offset_time2=offset_time*0.7f,
  508. radius_scale=radiusScale();
  509. Flt (*func)(Flt s)=GetOpacityFunc(T);
  510. Randomizer random(UIDZero);
  511. Image *render_color_palette=null;
  512. Int render_color_palette_w1, palette_image_w1;
  513. if(Renderer()!=_render_mode) // if particle is palette based, however we're rendering it in blend mode, then we need to palettize each single particle
  514. {
  515. render_color_palette=&D._color_palette_soft[paletteIndex()];
  516. if(render_color_palette->h()<4)return;
  517. render_color_palette_w1=render_color_palette->w()-1;
  518. }
  519. if(palette_image)palette_image_w1=palette_image->w()-1;
  520. if(image_x_frames>1 || image_y_frames>1) // animated particles
  521. {
  522. Bool animate=(image_speed>0);
  523. REPA(p)
  524. {
  525. C Particle &p=T.p[i]; if(p.life_max>EPS)
  526. {
  527. Flt life =p.life/p.life_max,
  528. radius=p.radius*radius_scale;
  529. Vec pos =p.pos;
  530. if(offset)pos +=random(Ball(offset_range), false)*Sin(random.f(PI2)+offset_time )
  531. +random(Ball(offset_range), false)*Sin(random.f(PI2)+offset_time2);
  532. Color color=T.color;
  533. if(palette_image)
  534. {
  535. Int x=RoundPos(life*palette_image_w1);
  536. color=ColorMul(color, palette_image->color(x, p.palette_y));
  537. }
  538. if(render_color_palette)
  539. {
  540. Int x=render_color_palette_w1-RoundPos(life*render_color_palette_w1);
  541. VecI4 p=0;
  542. REP(4)if(Byte c=color.c[i])
  543. {
  544. C VecB4 &col=render_color_palette->pixB4(x, i);
  545. p.x+=col.x*c;
  546. p.y+=col.y*c;
  547. p.z+=col.z*c;
  548. p.w+= c;
  549. }
  550. if(!p.w)continue;
  551. color.r=p.x/p.w;
  552. color.g=p.y/p.w;
  553. color.b=p.z/p.w;
  554. color.a=255;
  555. }
  556. if(!initialized){initialized=true; DrawAnimatedParticleBegin(*image, glow, motion_affects_alpha, image_x_frames, image_y_frames);}
  557. if(depth_offset)pos-=CamMatrix.z*(radius*hard_depth_offset);
  558. DrawAnimatedParticleAdd(color, opacity*func(life), radius, p.ang_vel*p.life, pos, p.vel, animate ? p.life*image_speed : p.image_index);
  559. }
  560. }
  561. }
  562. else // non-animated particles
  563. {
  564. REPA(p)
  565. {
  566. C Particle &p=T.p[i]; if(p.life_max>EPS)
  567. {
  568. Flt life =p.life/p.life_max,
  569. radius=p.radius*radius_scale;
  570. Vec pos =p.pos;
  571. if(offset)pos +=random(Ball(offset_range), false)*Sin(random.f(PI2)+offset_time )
  572. +random(Ball(offset_range), false)*Sin(random.f(PI2)+offset_time2);
  573. Color color=T.color;
  574. if(palette_image)
  575. {
  576. Int x=RoundPos(life*palette_image_w1);
  577. color=ColorMul(color, palette_image->color(x, p.palette_y));
  578. }
  579. if(render_color_palette)
  580. {
  581. Int x=render_color_palette_w1-RoundPos(life*render_color_palette_w1);
  582. VecI4 p=0;
  583. REP(4)if(Byte c=color.c[i])
  584. {
  585. C VecB4 &col=render_color_palette->pixB4(x, i);
  586. p.x+=col.x*c;
  587. p.y+=col.y*c;
  588. p.z+=col.z*c;
  589. p.w+= c;
  590. }
  591. if(!p.w)continue;
  592. color.r=p.x/p.w;
  593. color.g=p.y/p.w;
  594. color.b=p.z/p.w;
  595. color.a=255;
  596. }
  597. if(!initialized){initialized=true; DrawParticleBegin(*image, glow, motion_affects_alpha);}
  598. if(depth_offset)pos-=CamMatrix.z*(radius*hard_depth_offset);
  599. DrawParticleAdd(color, opacity*func(life), radius, p.ang_vel*p.life, pos, p.vel);
  600. }
  601. }
  602. }
  603. if(initialized)DrawParticleEnd();
  604. }
  605. }
  606. }
  607. /******************************************************************************/
  608. Bool Particles::save(File &f, Bool include_particles, CChar *path)C
  609. {
  610. f.putUInt (CC4_PRTC);
  611. f.cmpUIntV(is() ? 1 : 0); // version (0 is reserved for empty)
  612. if(is())
  613. {
  614. f<<reborn<<motion_affects_alpha<<_palette<<_palette_index<<inside_shape<<glow<<color<<smooth_fade<<_src_type
  615. <<radius<<radius_random<<radius_growth<<offset_range<<offset_speed<<life<<life_random<<glue<<damping<<ang_vel<<vel_random<<vel_constant<<accel<<matrix
  616. <<emitter_life_max<<emitter_life<<_fade<<fade_in<<fade_out
  617. <<radius_scale_base<<radius_scale_time<<image_x_frames<<image_y_frames<<image_speed<<hard_depth_offset;
  618. f.putBool(include_particles); if(include_particles){if(!p.saveRaw(f))return false;}else f.cmpUIntV(p.elms()); // particles
  619. f.putAsset(image.id()).putAsset(palette_image.id()); // image names
  620. if(!shape.save(f))return false; // shape
  621. }
  622. return f.ok();
  623. }
  624. /******************************************************************************/
  625. Bool Particles::load(File &f, CChar *path)
  626. {
  627. del(); if(f.getUInt()==CC4_PRTC)switch(f.decUIntV())
  628. {
  629. case 1:
  630. {
  631. f>>reborn>>motion_affects_alpha>>_palette>>_palette_index>>inside_shape>>glow>>color>>smooth_fade>>_src_type
  632. >>radius>>radius_random>>radius_growth>>offset_range>>offset_speed>>life>>life_random>>glue>>damping>>ang_vel>>vel_random>>vel_constant>>accel>>matrix
  633. >>emitter_life_max>>emitter_life>>_fade>>fade_in>>fade_out
  634. >>radius_scale_base>>radius_scale_time>>image_x_frames>>image_y_frames>>image_speed>>hard_depth_offset;
  635. if(f.getBool()){if(!p.loadRaw(f))goto error;}else p.setNumZero(f.decUIntV()); // particles
  636. image .require(f.getAssetID(), path); // image name
  637. palette_image.require(f.getAssetID(), path); // image name
  638. if(!shape.load(f))goto error; // shape
  639. setRenderMode();
  640. if(f.ok())return true;
  641. }break;
  642. case 0:
  643. {
  644. if(f.ok())return true; // 0 is empty
  645. }break;
  646. }
  647. error:
  648. del(); return false;
  649. }
  650. /******************************************************************************/
  651. Bool Particles::save(C Str &name, Bool include_particles)C
  652. {
  653. File f; if(f.writeTry(name)){if(save(f, include_particles, _GetPath(name)) && f.flush())return true; f.del(); FDelFile(name);}
  654. return false;
  655. }
  656. /******************************************************************************/
  657. Bool Particles::load(C Str &name)
  658. {
  659. File f; if(f.readTry(name))return load(f, _GetPath(name));
  660. del(); return false;
  661. }
  662. /******************************************************************************/
  663. // RAW PARTICLES
  664. /******************************************************************************/
  665. void RawParticles::zero()
  666. {
  667. motion_affects_alpha=true;
  668. glow =0;
  669. _palette =false;
  670. _palette_index=0;
  671. _particles =_max_particles=0;
  672. _render_mode =RM_BLEND;
  673. }
  674. RawParticles::RawParticles() {zero();}
  675. /******************************************************************************/
  676. RawParticles& RawParticles::del()
  677. {
  678. image.clear();
  679. _vb .del ();
  680. _ib .del ();
  681. zero(); return T;
  682. }
  683. RawParticles& RawParticles::create(C ImagePtr &image)
  684. {
  685. del();
  686. T.image=image;
  687. return setRenderMode();
  688. }
  689. /******************************************************************************/
  690. RawParticles& RawParticles::set(C Particle *particle, Int particles)
  691. {
  692. MAX(particles, 0);
  693. if( particles>_max_particles) // re-create buffers
  694. {
  695. _max_particles=particles;
  696. _vb.createNum(SIZE(Vtx3DBilb), particles*4, false); // non-dynamic was always faster (DX9: 140/15 fps, DX10+: 96/15 fps, GL: 96/84 fps, GL ES: ?)
  697. // indexes
  698. if(_vb.vtxs()<=0x10000)_ib.del();else // use 'IndBuf16384Quads'
  699. { // custom Index Buffer needed
  700. if(!_ib.create(particles*(2*3), _vb.vtxs()<=0x10000, false))return del();
  701. if(Ptr index=_ib.lock(LOCK_WRITE))
  702. {
  703. Int p=0;
  704. if(_ib.bit16()){U16 *ind=(U16*)index; REP(particles){ind[0]=p; ind[1]=p+1; ind[2]=p+3; ind[3]=p+3; ind[4]=p+1; ind[5]=p+2; p+=4; ind+=6;}}
  705. else {U32 *ind=(U32*)index; REP(particles){ind[0]=p; ind[1]=p+1; ind[2]=p+3; ind[3]=p+3; ind[4]=p+1; ind[5]=p+2; p+=4; ind+=6;}}
  706. _ib.unlock();
  707. }else return del();
  708. }
  709. }
  710. // vertexes
  711. _particles=particles;
  712. if(Vtx3DBilb *v=(Vtx3DBilb*)_vb.lock(LOCK_WRITE))
  713. {
  714. FREP(particles)
  715. {
  716. C Particle &p=particle[i];
  717. v[0].pos =v[1].pos =v[2].pos =v[3].pos =p.pos;
  718. v[0].vel_angle=v[1].vel_angle=v[2].vel_angle=v[3].vel_angle.set(p.vel.x, p.vel.y, p.vel.z, GPU_HALF_SUPPORTED ? AngleFull(p.angle) : p.angle);
  719. v[0].color =v[1].color =v[2].color =v[3].color =p.color;
  720. v[0].size =v[1].size =v[2].size =v[3].size =p.radius;
  721. SET_TEX(v[0].tex, TEX_ZERO, TEX_ONE );
  722. SET_TEX(v[1].tex, TEX_ONE , TEX_ONE );
  723. SET_TEX(v[2].tex, TEX_ONE , TEX_ZERO);
  724. SET_TEX(v[3].tex, TEX_ZERO, TEX_ZERO);
  725. v+=4;
  726. }
  727. _vb.unlock();
  728. }else return del();
  729. return T;
  730. }
  731. /******************************************************************************/
  732. RENDER_MODE RawParticles:: renderMode()C {return D.colorPaletteAllow() ? _render_mode : RM_BLEND;}
  733. RawParticles& RawParticles::setRenderMode()
  734. {
  735. _render_mode=(palette() ? (paletteIndex() ? RM_PALETTE1 : RM_PALETTE) : RM_BLEND);
  736. return T;
  737. }
  738. /******************************************************************************/
  739. RawParticles& RawParticles::palette (Bool palette) {T._palette =palette; return setRenderMode();}
  740. RawParticles& RawParticles::paletteIndex(Byte index ) {T._palette_index=index ; return setRenderMode();}
  741. /******************************************************************************/
  742. void RawParticles::draw()C
  743. {
  744. if(image && _particles && Renderer()==renderMode())
  745. {
  746. Renderer.wantDepthRead(); // !! call before 'SoftParticles' !!
  747. Shader *shader;
  748. Bool soft=SoftParticles();
  749. switch(Renderer())
  750. {
  751. default : return;
  752. case RM_BLEND : shader=Sh.h_Particle[false][soft][0][motion_affects_alpha]; D.alpha(ALPHA_BLEND_FACTOR); D.alphaFactor(Color(0, 0, 0, glow)); Renderer._has_glow|=(glow!=0); break;
  753. case RM_PALETTE :
  754. case RM_PALETTE1: shader=Sh.h_Particle[true ][soft][0][motion_affects_alpha]; D.alpha(ALPHA_ADD ); break;
  755. }
  756. SetOneMatrix ( );
  757. D .depthWrite(false);
  758. D .depth (true );
  759. D .cull (false);
  760. Sh.h_ImageCol[0]->set(image());
  761. #if DX9 || GL // DX10+ should support all sizes
  762. Sh.h_ColSize->set(image->_part.xy);
  763. #endif
  764. #if GL_ES && ANDROID // check the comments at the top why this is called
  765. D._sampler_address=GL_CLAMP_TO_EDGE;
  766. #endif
  767. // set
  768. C IndBuf &ib=(T._ib.is() ? T._ib : IndBuf16384Quads);
  769. SetDefaultVAO(); _vb.set(); ib.set(); D.vf(VI._vf3D_bilb.vf); // OpenGL requires setting 1)VAO 2)VB+IB 3)VF
  770. // draw
  771. shader->begin();
  772. #if DX9
  773. D3D->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, _vb._vtx_num, 0, _particles*2);
  774. #elif DX11
  775. D3DC->DrawIndexed(_particles*(2*3), 0, 0);
  776. #elif GL
  777. glDrawElements(GL_TRIANGLES, _particles*(2*3), ib.bit16() ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, null);
  778. #endif
  779. ShaderEnd();
  780. #if GL_ES && ANDROID
  781. D._sampler_address=GL_REPEAT;
  782. #endif
  783. // we've changed textures and set alpha factor so we need to clear material
  784. MaterialClear();
  785. }
  786. }
  787. /******************************************************************************/
  788. // MAIN
  789. /******************************************************************************/
  790. void ShutParticles()
  791. {
  792. ParticlesCache.del();
  793. }
  794. /******************************************************************************/
  795. }
  796. /******************************************************************************/