snake5

SGScript - particle system

May 12th, 2014
396
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