Advertisement
snake5

SG3D Particle System

Dec 22nd, 2012
231
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.74 KB | None | 0 0
  1. /**** SG3D_ParticleSystem.h ****/
  2.  
  3. #pragma once
  4.  
  5. #include "SG3D_Utils.h"
  6.  
  7.  
  8. struct SGParticleSystem
  9. {
  10.     struct Op
  11.     {
  12.         UINT8 type;
  13.         UINT8 argc;
  14.         INT8 args[ 6 ];
  15.     };
  16.     typedef TPrimArray< Op > OpVec;
  17.  
  18.     SGParticleSystem() : MaxParticles( 0 ), Color( NULL ), Position( NULL ), Velocity( NULL ),
  19.         Accel( NULL ), Size( NULL ), Angle( NULL ), TimeLeft( NULL ),
  20.         Time( 0 ), Next( 0 ), RestartTime( 0 ), Count( 0 )
  21.     { int i = 4; while( i > 0 ) Tmp[ --i ] = NULL; }
  22.     ~SGParticleSystem(){ SetMaxParticles( 0 ); }
  23.  
  24.     UBOOL Load( const UNICHAR* file );
  25.     UBOOL Parse( TString& code );
  26.  
  27.     // particle info
  28.     TString Material;
  29.     mVec2 SpawnCount;
  30.     mVec2 RestartInfo;
  31.     TArray< mVecSIMD > Consts;
  32.     OpVec CreateOps;
  33.     OpVec TickOps;
  34.  
  35.     // particle data
  36.     INT32 MaxParticles;
  37.     mVecSIMD* Color;    // 4
  38.     mVecSIMD* Position; // 3
  39.     mVecSIMD* Velocity; // 3
  40.     mVecSIMD* Accel;    // 3
  41.     mVecSIMD* Size;     // 1
  42.     mVecSIMD* Angle;    // 1
  43.     mVecSIMD* TimeLeft; // 1
  44.     mVecSIMD* Tmp[ 4 ]; // 4
  45.  
  46.     void SetMaxParticles( UINT32 maxparts );
  47.  
  48.     // simulation
  49.     FLOAT Time;
  50.     INT32 Next;
  51.     FLOAT RestartTime;
  52.     UINT32 Count;
  53.  
  54.     void AddParticles( INT32 n );
  55.     void Tick( FLOAT delta );
  56.     void ExecOps( const OpVec& ops, UINT32 from, UINT32 to );
  57. };
  58.  
  59. /**** SG3D_ParticleSystem.cpp ****/
  60.  
  61.  
  62. #include "SGFileIO.h"
  63.  
  64. #include "SG3D_ParticleSystem.h"
  65.  
  66.  
  67. struct PSExpArg
  68. {
  69.     mVecSIMD* data;
  70.     UINT32 stride;
  71. };
  72.  
  73. #define PSOP__NULL      0
  74. #define PSOP_ADD        1
  75. #define PSOP_SUB        2
  76. #define PSOP_MUL        3
  77. #define PSOP_DIV        4
  78. #define PSOP_MOD        5
  79. #define PSOP_POW        6
  80. #define PSOP_SET        7
  81. #define PSOP_SETRAND    8
  82. #define PSOP_CLAMP      9
  83. #define PSOP__END       10
  84.  
  85. UINT8 PS_GetOpId( const TString& op )
  86. {
  87.     const CHAR* names[] =
  88.     {
  89.         "<>",
  90.         "ADD", "SUB", "MUL", "DIV", "MOD", "POW",
  91.         "SET", "SETRAND",
  92.         "CLAMP",
  93.     };
  94.     UINT8 arrsize = sizeof( names ) / sizeof( names[0] );
  95.  
  96.     for( UINT8 i = 1; i < arrsize; ++i )
  97.     {
  98.         if( op == names[ i ] )
  99.             return i;
  100.     }
  101.     return 0;
  102. }
  103.  
  104. UINT8 PS_GetOpArgs( UINT8 op )
  105. {
  106.     if( op <= PSOP__NULL || op >= PSOP__END )
  107.         return 0;
  108.  
  109.     static const UINT8 opargs[] =
  110.     {
  111.         // null
  112.         0,
  113.         // add, sub, mul, div, mod, pow
  114.         3, 3, 3, 3, 3, 3,
  115.         // set, setrand
  116.         2, 3,
  117.         // clamp
  118.         4,
  119.     };
  120.  
  121.     return opargs[ op ];
  122. }
  123.  
  124. #define PS_OP( name ) PS_Op_##name
  125. #define PS_DEFINE_OP1( name, op ) \
  126.     void PS_Op_##name( PSExpArg* args, UINT32 from, UINT32 to ){ \
  127.         mVecSIMD* out = args[ 0 ].data + args[ 0 ].stride * from; \
  128.         mVecSIMD* arg1 = args[ 1 ].data + args[ 1 ].stride * from; \
  129.         mVecSIMD* out_end = out + ( to + 1 - from ); \
  130.         while( out < out_end ) { \
  131.             op; \
  132.             out += args[ 0 ].stride; \
  133.             arg1 += args[ 1 ].stride; \
  134.         } \
  135.     }
  136. #define PS_DEFINE_OP2( name, op ) \
  137.     void PS_Op_##name( PSExpArg* args, UINT32 from, UINT32 to ){ \
  138.         mVecSIMD* out = args[ 0 ].data + args[ 0 ].stride * from; \
  139.         mVecSIMD* arg1 = args[ 1 ].data + args[ 1 ].stride * from; \
  140.         mVecSIMD* arg2 = args[ 2 ].data + args[ 2 ].stride * from; \
  141.         mVecSIMD* out_end = out + ( to + 1 - from ); \
  142.         while( out < out_end ) { \
  143.             op; \
  144.             out += args[ 0 ].stride; \
  145.             arg1 += args[ 1 ].stride; \
  146.             arg2 += args[ 2 ].stride; \
  147.         } \
  148.     }
  149. #define PS_DEFINE_OP3( name, op ) \
  150.     void PS_Op_##name( PSExpArg* args, UINT32 from, UINT32 to ){ \
  151.         mVecSIMD* out = args[ 0 ].data + args[ 0 ].stride * from; \
  152.         mVecSIMD* arg1 = args[ 1 ].data + args[ 1 ].stride * from; \
  153.         mVecSIMD* arg2 = args[ 2 ].data + args[ 2 ].stride * from; \
  154.         mVecSIMD* arg3 = args[ 3 ].data + args[ 3 ].stride * from; \
  155.         mVecSIMD* out_end = out + ( to + 1 - from ); \
  156.         while( out < out_end ) { \
  157.             op; \
  158.             out += args[ 0 ].stride; \
  159.             arg1 += args[ 1 ].stride; \
  160.             arg2 += args[ 2 ].stride; \
  161.             arg3 += args[ 3 ].stride; \
  162.         } \
  163.     }
  164.  
  165.  
  166. PS_DEFINE_OP2( ADD, *out = *arg1 + *arg2 )
  167. PS_DEFINE_OP2( SUB, *out = *arg1 - *arg2 )
  168. PS_DEFINE_OP2( MUL, *out = *arg1 * *arg2 )
  169. PS_DEFINE_OP2( DIV, *out = *arg1 / *arg2 )
  170.  
  171. PS_DEFINE_OP1( SET, *out = *arg1 )
  172. PS_DEFINE_OP2( SETRAND, *out = *arg1 + mVecSIMD( M_RandF() * 2 - 1 ) * *arg2 )
  173.  
  174. PS_DEFINE_OP3( CLAMP, *out = arg1->Max( *arg2 ).Min( *arg3 ) )
  175.        
  176.  
  177. void PS_CallOp( UINT8 op, PSExpArg* args, UINT32 from, UINT32 to )
  178. {
  179.     switch( op )
  180.     {
  181. #define CASE( op ) case PSOP_##op: PS_OP( op )( args, from, to ); break
  182.     CASE( ADD );
  183.     CASE( SUB );
  184.     CASE( MUL );
  185.     CASE( DIV );
  186.  
  187.     CASE( SET );
  188.     CASE( SETRAND );
  189.     CASE( CLAMP );
  190. #undef CASE
  191.     }
  192. }
  193.  
  194.  
  195. UBOOL SGParticleSystem::Load( const UNICHAR* file )
  196. {
  197.     TString code;
  198.     if( !ReadTextFileToString( file, code ) )
  199.         return FALSE;
  200.  
  201.     return Parse( code );
  202. }
  203.  
  204. UBOOL SGParticleSystem::Parse( TString& code )
  205. {
  206.     INT32 at = 0;
  207.  
  208.     Material.Clear();
  209.     RestartInfo.Set( 0, 0 );
  210.     Consts.Clear();
  211.     CreateOps.Clear();
  212.     TickOps.Clear();
  213.  
  214.     // prealloc special consts
  215.     Consts.PushBack( mVecSIMD( 0.0f ) );    // 0 - Delta Time
  216.     UINT8 cnsbase = Consts.Size();
  217.    
  218.     F_SkipWhitespaces( code, at );
  219.     while( at < (INT32) code.Size() )
  220.     {
  221.         TString key = F_ReadString( code, at, &FStr_Token );
  222.         if( key == "MATERIAL" )     Material = F_ReadQString( code, at );
  223.         else if( key == "SPAWN" )   SpawnCount = F_ReadVec2( code, at );
  224.         else if( key == "RESTART" ) RestartInfo = F_ReadVec2( code, at );
  225.         else if( key == "MAX" )     SetMaxParticles( F_ReadInteger( code, at ) );
  226.         else if( key == "CREATE" || key == "TICK" )
  227.         {
  228.             OpVec* ops = key == "CREATE" ? &CreateOps : &TickOps;
  229.             F_SkipLine( code, at );
  230.             INT32 at2 = at;
  231.             do
  232.             {
  233.                 F_SkipLine( code, at2 );
  234.                 TString line = code.TrimRet( at, at2 - at );
  235.                 line.TrimSpaces();
  236.  
  237.                 // parse line
  238.                 if( line.Size() )
  239.                 {
  240.                     INT32 x = 0;
  241.                     TString op = F_ReadString( line, x, &FStr_Token );
  242.                     if( op == "END" )
  243.                         break;
  244.  
  245.                     Op rop;
  246.                     rop.argc = 0;
  247.                     rop.type = PS_GetOpId( op );
  248.                     if( !rop.type )
  249.                     {
  250.                         E_Notify( NOTIFY_WARN, "[PS] parsing error: invalid op name \"%s\"", op.CStr() );
  251.                         return FALSE;
  252.                     }
  253.                    
  254.                     F_SkipWhitespaces( line, x );
  255.                     while( rop.argc < 6 && x < (INT32) line.Size() )
  256.                     {
  257.                         if( gCharIsDigit( line[ x ] ) )
  258.                         {
  259.                             if( rop.argc == 0 )
  260.                             {
  261.                                 E_Notify( NOTIFY_WARN, "[PS] parsing error: argument 0 (output) can't be a constant" );
  262.                                 return FALSE;
  263.                             }
  264.  
  265.                             mVec4 c( F_ReadFloat( line, x, FALSE ) );
  266.                             if( F_CheckChar( line, x, ';' ) )
  267.                             {
  268.                                 c.w = c.y = F_ReadFloat( line, x, FALSE );
  269.                                 if( F_CheckChar( line, x, ';' ) )
  270.                                 {
  271.                                     c.z = F_ReadFloat( line, x, FALSE );
  272.                                     c.w = 0;
  273.                                     if( F_CheckChar( line, x, ';' ) )
  274.                                     {
  275.                                         c.w = F_ReadFloat( line, x, FALSE );
  276.                                     }
  277.                                 }
  278.                             }
  279.  
  280.                             mVecSIMD csimd( c );
  281.  
  282.                             UINT8 cnst = cnsbase;
  283.                             while( cnst < (UINT8) Consts.Size() )
  284.                             {
  285.                                 if( Consts[ cnst ] == csimd )
  286.                                     break;
  287.                                 else
  288.                                     cnst++;
  289.                             }
  290.                             if( cnst == Consts.Size() )
  291.                             {
  292.                                 if( cnst == 127 )
  293.                                 {
  294.                                     E_Notify( NOTIFY_WARN, "[PS] parsing error: out of const space" );
  295.                                     return FALSE;
  296.                                 }
  297.                                 Consts.PushBack( csimd );
  298.                             }
  299.                             rop.args[ rop.argc ] = cnst;
  300.                         }
  301.                         else
  302.                         {
  303.                             UINT8 arg = 0;
  304.                             TString str = F_ReadString( line, x, &FStr_Token );
  305.                             if( str == "color" )        arg = -1;
  306.                             else if( str == "pos" )     arg = -2;
  307.                             else if( str == "vel" )     arg = -3;
  308.                             else if( str == "accel" )   arg = -4;
  309.                             else if( str == "size" )    arg = -5;
  310.                             else if( str == "angle" )   arg = -6;
  311.                             else if( str == "time" )    arg = -7;
  312.                             else if( str == "tmp0" )    arg = -8;
  313.                             else if( str == "tmp1" )    arg = -9;
  314.                             else if( str == "tmp2" )    arg = -10;
  315.                             else if( str == "tmp3" )    arg = -11;
  316.                             // special constants
  317.                             else if( str == "DT" )      arg = 0;
  318.                             // problems
  319.                             else if( str == "" )
  320.                                 break;
  321.                             else
  322.                             {
  323.                                 E_Notify( NOTIFY_WARN, "[PS] parsing error: invalid argument name \"%s\"", str.CStr() );
  324.                                 return FALSE;
  325.                             }
  326.  
  327.                             rop.args[ rop.argc ] = arg;
  328.                         }
  329.  
  330.                         rop.argc++;
  331.  
  332.                         F_SkipWhitespaces( line, x );
  333.                     }
  334.  
  335.                     UINT8 expargs = PS_GetOpArgs( rop.type );
  336.                     if( expargs != rop.argc )
  337.                     {
  338.                         E_Notify( NOTIFY_WARN, "[PS] parsing error: op \"%s\" - expected %d args, got %d", op.CStr(), (INT32) expargs, (INT32) rop.argc );
  339.                         return FALSE;
  340.                     }
  341.  
  342.                     ops->PushBack( rop );
  343.                 }
  344.  
  345.                 at = at2;
  346.             }
  347.             while( at2 < (INT32) code.Size() );
  348.         }
  349.         else if( key == "DRAW" )
  350.         {
  351.             F_SkipLine( code, at );
  352.         }
  353.         F_SkipWhitespaces( code, at );
  354.     }
  355.  
  356.     return TRUE;
  357. }
  358.  
  359. void SGParticleSystem::SetMaxParticles( UINT32 maxparts )
  360. {
  361.     if( MaxParticles == maxparts )
  362.         return;
  363.  
  364.     if( Color )     delete [] Color;
  365.     if( Position )  delete [] Position;
  366.     if( Velocity )  delete [] Velocity;
  367.     if( Accel )     delete [] Accel;
  368.     if( Size )      delete [] Size;
  369.     if( Angle )     delete [] Angle;
  370.     if( TimeLeft )  delete [] TimeLeft;
  371.     if( Tmp[ 0 ] )  delete [] Tmp[ 0 ];
  372.     if( Tmp[ 1 ] )  delete [] Tmp[ 1 ];
  373.     if( Tmp[ 2 ] )  delete [] Tmp[ 2 ];
  374.     if( Tmp[ 3 ] )  delete [] Tmp[ 3 ];
  375.  
  376.     MaxParticles = maxparts;
  377.     if( maxparts )
  378.     {
  379.         Color = new mVecSIMD[ maxparts ];
  380.         Position = new mVecSIMD[ maxparts ];
  381.         Velocity = new mVecSIMD[ maxparts ];
  382.         Accel = new mVecSIMD[ maxparts ];
  383.         Size = new mVecSIMD[ maxparts ];
  384.         Angle = new mVecSIMD[ maxparts ];
  385.         TimeLeft = new mVecSIMD[ maxparts ];
  386.         gMemSet( TimeLeft, 0, maxparts * sizeof( mVecSIMD ) );
  387.         Tmp[ 0 ] = new mVecSIMD[ maxparts ];
  388.         Tmp[ 1 ] = new mVecSIMD[ maxparts ];
  389.         Tmp[ 2 ] = new mVecSIMD[ maxparts ];
  390.         Tmp[ 3 ] = new mVecSIMD[ maxparts ];
  391.     }
  392.  
  393.     Next = 0;
  394. }
  395.  
  396. void SGParticleSystem::AddParticles( INT32 n )
  397. {
  398.     Count = ( Count + n ) % MaxParticles;
  399.     while( n > 0 )
  400.     {
  401.         UINT32 amt = MIN( n, MaxParticles - Next );
  402.         ExecOps( CreateOps, Next, Next + amt - 1 );
  403.         Next = ( Next + amt ) % MaxParticles;
  404.         n -= amt;
  405.     }
  406. }
  407.  
  408. void SGParticleSystem::Tick( FLOAT delta )
  409. {
  410.     if( delta < FLT_EPSILON || !MaxParticles )
  411.         return;
  412.  
  413.     // tick
  414.     Consts[ 0 ] = mVecSIMD( delta );
  415.     if( Count )
  416.         ExecOps( TickOps, 0, Count - 1 );
  417.  
  418.     // resort and recount
  419.     for( UINT32 i = 0; i < Count; ++i )
  420.     {
  421.         if( TimeLeft[ i ].Get( 0 ) <= 0 )
  422.         {
  423.             Count--;
  424.             if( Count > i )
  425.             {
  426.                 Color[ i ] = Color[ Count ];
  427.                 Position[ i ] = Position[ Count ];
  428.                 Velocity[ i ] = Velocity[ Count ];
  429.                 Accel[ i ] = Accel[ Count ];
  430.                 Size[ i ] = Size[ Count ];
  431.                 Angle[ i ] = Angle[ Count ];
  432.                 TimeLeft[ i ] = TimeLeft[ Count ];
  433.                 Tmp[ 0 ][ i ] = Tmp[ 0 ][ Count ];
  434.                 Tmp[ 1 ][ i ] = Tmp[ 1 ][ Count ];
  435.                 Tmp[ 2 ][ i ] = Tmp[ 2 ][ Count ];
  436.                 Tmp[ 3 ][ i ] = Tmp[ 3 ][ Count ];
  437.             }
  438.             i--;
  439.         }
  440.     }
  441.     Next = ( Count ) % MaxParticles;
  442.  
  443.     // restart
  444.     if( Time + delta > RestartTime && Time <= RestartTime )
  445.     {
  446.         Time = FLT_EPSILON;
  447.         RestartTime = RestartInfo.x + ( M_RandF() - 0.5f ) * 2 * RestartInfo.y;
  448.         INT32 cnt = SpawnCount.x + ( M_RandF() - 0.5f ) * 2 * SpawnCount.y;
  449.         AddParticles( cnt );
  450.     }
  451.     else
  452.         Time += delta;
  453. }
  454.  
  455. void SGParticleSystem::ExecOps( const OpVec& ops, UINT32 from, UINT32 to )
  456. {
  457.     PSExpArg args[ 6 ];
  458.     for( UINT32 o = 0; o < ops.Size(); ++o )
  459.     {
  460.         Op& O = ops[ o ];
  461.         for( UINT8 i = 0; i < O.argc; ++i )
  462.         {
  463.             PSExpArg& A = args[ i ];
  464.             if( O.args[ i ] >= 0 )
  465.             {
  466.                 A.data = &Consts[ O.args[ i ] ];
  467.                 A.stride = 0;
  468.             }
  469.             else
  470.             {
  471.                 switch( O.args[ i ] )
  472.                 {
  473.                 case -1: A.data = Color; break;
  474.                 case -2: A.data = Position; break;
  475.                 case -3: A.data = Velocity; break;
  476.                 case -4: A.data = Accel; break;
  477.                 case -5: A.data = Size; break;
  478.                 case -6: A.data = Angle; break;
  479.                 case -7: A.data = TimeLeft; break;
  480.                 case -8: A.data = Tmp[ 0 ]; break;
  481.                 case -9: A.data = Tmp[ 1 ]; break;
  482.                 case -10: A.data = Tmp[ 2 ]; break;
  483.                 case -11: A.data = Tmp[ 3 ]; break;
  484.                 }
  485.                 A.stride = 1;
  486.             }
  487.         }
  488.         PS_CallOp( O.type, args, from, to );
  489.     }
  490. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement