snake5

SGScript - particle system

May 12th, 2014
432
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* PARTICLE SYSTEM
  2.  
  3. Specification format:
  4. {
  5.     looptime
  6.     looptime_rand
  7.     emitters =
  8.     [
  9.         {
  10.             spawncount
  11.             spawncount_rand
  12.             tempslots
  13.             maxparticles
  14.             spawn_code =
  15.             [
  16.                 "RANDBOX; position; vec2(-5,-5); vec2(5,5)"
  17.                 "DIRDVG; velocity; vec2(1,0); 1; randf() * 10"
  18.             ]
  19.             tick_code =
  20.             [
  21.                 "MAD; velocity; vec2(0,1); dt"
  22.             ]
  23.         }
  24.     ]
  25. }
  26.  
  27. */
  28.  
  29.  
  30. global ParticleSystem =
  31. {
  32.     ReqArgCounts =
  33.     {
  34.         SET = 2,      // assign
  35.         RANDBOX = 3,  // randbox
  36.         RANDEXT = 3,  // randext
  37.        
  38.         ADDA = 2,     // add_assign
  39.         SUBA = 2,     // sub_assign
  40.         MULA = 2,     // mul_assign
  41.         DIVA = 2,     // div_assign
  42.         ADD = 3,      // add
  43.         SUB = 3,      // sub
  44.         MUL = 3,      // mul
  45.         DIV = 3,      // div
  46.         MAD = 3,      // multiply_add_assign
  47.         LERPTO = 3,   // lerp_to
  48.        
  49.         DBG = 1,      // - (custom code)
  50.     },
  51. };
  52.  
  53. // PUBLIC
  54.  
  55. function ParticleSystem.create( psspec )
  56. {
  57.     PS =
  58.     {
  59.         // spec
  60.         emitters = [],
  61.         looptime = if( @psspec.looptime !== null, toreal( psspec.looptime ), null ),
  62.         looptime_rand = toreal( @psspec.looptime_rand ),
  63.         // state
  64.         playing = false,
  65.         time = 0.0,
  66.         curlooptime = null,
  67.         renderbuf = SS_CreateRenderBuffer(),
  68.         // settings
  69.         offset = vec2(0,0),
  70.         xdir = vec2(1,0),
  71.         emitpos = vec2(0,0),
  72.         emitdir = vec2(1,0),
  73.     };
  74.     foreach( em : psspec.emitters )
  75.     {
  76.         cem = this._compileEmitter( em );
  77.         PS.emitters.push( cem );
  78.     }
  79.     return class( PS, ParticleSystem );
  80. }
  81.  
  82. function ParticleSystem.tick( delta )
  83. {
  84.     if( this.playing )
  85.     {
  86.         this.time += delta;
  87.         if( this.curlooptime !== null && this.time >= this.curlooptime )
  88.         {
  89.             this.time -= this.curlooptime;
  90.             this._spawn();
  91.             this._regenLoopTime();
  92.         }
  93.     }
  94.     foreach( em : this.emitters )
  95.         em.tick( this, delta );
  96. }
  97.  
  98. function ParticleSystem.draw()
  99. {
  100.     rb = this.renderbuf;
  101.     foreach( em : this.emitters )
  102.     {
  103.         vcnt = 0;
  104.         rb.begin();
  105.         for( i = 0; i < em.maxparticles; ++i )
  106.         {
  107.             readpos = i * 4;
  108.             if( em.timeleft[ readpos ] <= 0 )
  109.                 continue;
  110.             pos = vec2( em.position[ readpos ], em.position[ readpos + 1 ] );
  111.             vel = vec2( em.velocity[ readpos ], em.velocity[ readpos + 1 ] );
  112.             halfsize = em.size[ readpos ] * 0.5;
  113.             angle = em.angle[ readpos ] / M_PI * 180;
  114.             xaxis = vec2( cos( angle ), sin( angle ) ) * halfsize;
  115.             yaxis = xaxis.perp;
  116.             col_r = em.color[ readpos+0 ];
  117.             col_g = em.color[ readpos+1 ];
  118.             col_b = em.color[ readpos+2 ];
  119.             col_a = em.color[ readpos+3 ];
  120.            
  121.             p1 = pos - xaxis - yaxis;
  122.             p2 = pos + xaxis - yaxis;
  123.             p3 = pos + xaxis + yaxis;
  124.             p4 = pos - xaxis + yaxis;
  125.            
  126.             rb.f( p1.x, p1.y, 0, 0 ).cf2b( col_r, col_g, col_b, col_a );
  127.             rb.f( p2.x, p2.y, 1, 0 ).cf2b( col_r, col_g, col_b, col_a );
  128.             rb.f( p3.x, p3.y, 1, 1 ).cf2b( col_r, col_g, col_b, col_a );
  129.            
  130.             rb.f( p3.x, p3.y, 1, 1 ).cf2b( col_r, col_g, col_b, col_a );
  131.             rb.f( p4.x, p4.y, 0, 1 ).cf2b( col_r, col_g, col_b, col_a );
  132.             rb.f( p1.x, p1.y, 0, 0 ).cf2b( col_r, col_g, col_b, col_a );
  133.            
  134.             vcnt += 6;
  135.         }
  136.         if( em.rendermode == "additive" )
  137.             SS_SetBlending( SS_BLENDOP_ADD, SS_BLEND_SRCALPHA, SS_BLEND_ONE );
  138.         else
  139.             SS_SetBlending( SS_BLENDOP_ADD, SS_BLEND_SRCALPHA, SS_BLEND_INVSRCALPHA );
  140.         rb.draw( em.texture, g_VD_P2T2CC4, 0, vcnt, SS_PT_TRIANGLES );
  141.     }
  142.     SS_SetBlending( SS_BLENDOP_ADD, SS_BLEND_SRCALPHA, SS_BLEND_INVSRCALPHA );
  143. }
  144.  
  145. function ParticleSystem.play()
  146. {
  147.     this.playing = true;
  148.     this._regenLoopTime();
  149.     this._spawn();
  150. }
  151.  
  152. function ParticleSystem.stop()
  153. {
  154.     this.playing = false;
  155. }
  156.  
  157.  
  158.  
  159. // PRIVATE
  160.  
  161. function ParticleSystem._regenLoopTime()
  162. {
  163.     if( this.looptime !== null )
  164.     {
  165.         this.curlooptime = this.looptime + randf() * this.looptime_rand;
  166.     }
  167. }
  168.  
  169. function ParticleSystem._spawn()
  170. {
  171.     foreach( em : this.emitters )
  172.     {
  173.         count = toint( em.spawncount + em.spawncount_rand * randf() );
  174.         em.spawn( this, count );
  175.     }
  176. }
  177.  
  178. function ParticleSystem._compileEmitter( emitspec )
  179. {
  180.     EM =
  181.     {
  182.         // data
  183.         keys = ["position","size","color","angle","timeleft","velocity"],
  184.         spawncount = toreal( @emitspec.spawncount ),
  185.         spawncount_rand = toreal( @emitspec.spawncount_rand ),
  186.         maxparticles = toint( @emitspec.maxparticles ),
  187.         texture = if( @emitspec.texture, SS_CreateTexture( emitspec.texture ), null ),
  188.         rendermode = @emitspec.rendermode,
  189.         // state
  190.         lastparticle = -1,
  191.     };
  192.     if( EM.maxparticles < 1 )
  193.         EM.maxparticles = 100;
  194.    
  195.     tempslots = toint( @emitspec.tempslots );
  196.    
  197.     for( i = 0; i < tempslots; ++i )
  198.         EM.keys.push( "tmp" $ i );
  199.     foreach( key : EM.keys )
  200.         EM.(key) = floatarray_buffer( 4 * EM.maxparticles );
  201.    
  202.     EM.spawn = ParticleSystem._compileEmitterCode( EM, "spawn", emitspec.spawn_code );
  203.     EM.tick = ParticleSystem._compileEmitterCode( EM, "tick", emitspec.tick_code );
  204.     return EM;
  205. }
  206.  
  207. function ParticleSystem._compileEmitterCode( EM, funcname, codelines )
  208. {
  209.     args = "";
  210.     if( funcname == "tick" ) args = "PS, delta";
  211.     else if( funcname == "spawn" ) args = "PS, count";
  212.    
  213.     // INTRO
  214.     code = "pse = {}; function pse." $ funcname $ "(" $ args $ "){\n";
  215.     if( funcname == "spawn" )
  216.     {
  217.         foreach( key : EM.keys )
  218.             code $= key $ " = floatarray_buffer( 4 * count );\n";
  219.     }
  220.     else if( funcname == "tick" )
  221.     {
  222.         foreach( key : EM.keys )
  223.             code $= key $ " = this." $ key $ ";\n";
  224.     }
  225.    
  226.     // PROGRAMMABLE OPS
  227.     code $= "/// BEGIN CODE\n";
  228.     foreach( line : codelines )
  229.     {
  230.         items = array_filter( array_process( string_explode( line, ";" ), function(x){ return string_trim(x); } ) );
  231.         if( !items )
  232.             continue;
  233.         op = string_toupper( items.shift() );
  234.        
  235.         reqargs = @this.ReqArgCounts[ op ];
  236.         if( reqargs === null )
  237.             return ERROR( "op '" $ op $ "' was not found" );
  238.         if( items.size != reqargs )
  239.             return ERROR( "'" $ op $ "' requires " $ reqargs $ " arguments, " $ items.size $ " given" );
  240.        
  241.         if( op == "SET" ) code $= items[0] $ ".assign(" $ items[1] $ ");";
  242.         else if( op == "RANDBOX" ) code $= items[0] $ ".randbox(" $ items[1] $ "," $ items[2] $ ");";
  243.         else if( op == "RANDEXT" ) code $= items[0] $ ".randext(" $ items[1] $ "," $ items[2] $ ");";
  244.        
  245.         else if( op == "ADDA" ) code $= items[0] $ ".add_assign(" $ items[1] $ ");";
  246.         else if( op == "SUBA" ) code $= items[0] $ ".sub_assign(" $ items[1] $ ");";
  247.         else if( op == "MULA" ) code $= items[0] $ ".mul_assign(" $ items[1] $ ");";
  248.         else if( op == "DIVA" ) code $= items[0] $ ".div_assign(" $ items[1] $ ");";
  249.         else if( op == "ADD" ) code $= items[0] $ ".add(" $ items[1] $ "," $ items[2] $ ");";
  250.         else if( op == "SUB" ) code $= items[0] $ ".sub(" $ items[1] $ "," $ items[2] $ ");";
  251.         else if( op == "MUL" ) code $= items[0] $ ".mul(" $ items[1] $ "," $ items[2] $ ");";
  252.         else if( op == "DIV" ) code $= items[0] $ ".div(" $ items[1] $ "," $ items[2] $ ");";
  253.         else if( op == "MAD" ) code $= items[0] $ ".multiply_add_assign(" $ items[1] $ "," $ items[2] $ ");";
  254.         else if( op == "LERPTO" ) code $= items[0] $ ".lerp_to(" $ items[1] $ "," $ items[2] $ ");";
  255.        
  256.         else if( op == "DBG" ) code $= items[0] $ ";";
  257.        
  258.         code $= "\n";
  259.     }
  260.     code $= "/// END CODE\n";
  261.    
  262.     // OUTRO
  263.     if( funcname == "spawn" )
  264.     {
  265.         code $= "for( i = 0; i < count; ++i ){"
  266.              $  "\n\tthis.lastparticle++;"
  267.              $  "\n\tif( this.lastparticle >= this.maxparticles ) this.lastparticle = 0;"
  268.              $  "\n\tfor( j = 0; j < 4; ++j ){"
  269.              $  "\n\t\treadpos = i * 4 + j;"
  270.              $  "\n\t\twritepos = this.lastparticle * 4 + j;";
  271.         foreach( key : EM.keys )
  272.         {
  273.             code $= "\n\t\tthis." $ key $ "[writepos] = " $ key $ "[readpos];";
  274.         }
  275.         code $= "\n\t}";
  276.         code $= "\n}\n";
  277.     }
  278.     else if( funcname == "tick" )
  279.     {
  280.     }
  281.     code $= "}\nreturn pse." $ funcname $ ";";
  282.    
  283. //  printlns("=========",code,"-------");
  284.     return eval( code );
  285. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×