daily pastebin goal
22%
SHARE
TWEET

SGScript - particle system

snake5 May 12th, 2014 308 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
Top