Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**** SG3D_ParticleSystem.h ****/
- #pragma once
- #include "SG3D_Utils.h"
- struct SGParticleSystem
- {
- struct Op
- {
- UINT8 type;
- UINT8 argc;
- INT8 args[ 6 ];
- };
- typedef TPrimArray< Op > OpVec;
- SGParticleSystem() : MaxParticles( 0 ), Color( NULL ), Position( NULL ), Velocity( NULL ),
- Accel( NULL ), Size( NULL ), Angle( NULL ), TimeLeft( NULL ),
- Time( 0 ), Next( 0 ), RestartTime( 0 ), Count( 0 )
- { int i = 4; while( i > 0 ) Tmp[ --i ] = NULL; }
- ~SGParticleSystem(){ SetMaxParticles( 0 ); }
- UBOOL Load( const UNICHAR* file );
- UBOOL Parse( TString& code );
- // particle info
- TString Material;
- mVec2 SpawnCount;
- mVec2 RestartInfo;
- TArray< mVecSIMD > Consts;
- OpVec CreateOps;
- OpVec TickOps;
- // particle data
- INT32 MaxParticles;
- mVecSIMD* Color; // 4
- mVecSIMD* Position; // 3
- mVecSIMD* Velocity; // 3
- mVecSIMD* Accel; // 3
- mVecSIMD* Size; // 1
- mVecSIMD* Angle; // 1
- mVecSIMD* TimeLeft; // 1
- mVecSIMD* Tmp[ 4 ]; // 4
- void SetMaxParticles( UINT32 maxparts );
- // simulation
- FLOAT Time;
- INT32 Next;
- FLOAT RestartTime;
- UINT32 Count;
- void AddParticles( INT32 n );
- void Tick( FLOAT delta );
- void ExecOps( const OpVec& ops, UINT32 from, UINT32 to );
- };
- /**** SG3D_ParticleSystem.cpp ****/
- #include "SGFileIO.h"
- #include "SG3D_ParticleSystem.h"
- struct PSExpArg
- {
- mVecSIMD* data;
- UINT32 stride;
- };
- #define PSOP__NULL 0
- #define PSOP_ADD 1
- #define PSOP_SUB 2
- #define PSOP_MUL 3
- #define PSOP_DIV 4
- #define PSOP_MOD 5
- #define PSOP_POW 6
- #define PSOP_SET 7
- #define PSOP_SETRAND 8
- #define PSOP_CLAMP 9
- #define PSOP__END 10
- UINT8 PS_GetOpId( const TString& op )
- {
- const CHAR* names[] =
- {
- "<>",
- "ADD", "SUB", "MUL", "DIV", "MOD", "POW",
- "SET", "SETRAND",
- "CLAMP",
- };
- UINT8 arrsize = sizeof( names ) / sizeof( names[0] );
- for( UINT8 i = 1; i < arrsize; ++i )
- {
- if( op == names[ i ] )
- return i;
- }
- return 0;
- }
- UINT8 PS_GetOpArgs( UINT8 op )
- {
- if( op <= PSOP__NULL || op >= PSOP__END )
- return 0;
- static const UINT8 opargs[] =
- {
- // null
- 0,
- // add, sub, mul, div, mod, pow
- 3, 3, 3, 3, 3, 3,
- // set, setrand
- 2, 3,
- // clamp
- 4,
- };
- return opargs[ op ];
- }
- #define PS_OP( name ) PS_Op_##name
- #define PS_DEFINE_OP1( name, op ) \
- void PS_Op_##name( PSExpArg* args, UINT32 from, UINT32 to ){ \
- mVecSIMD* out = args[ 0 ].data + args[ 0 ].stride * from; \
- mVecSIMD* arg1 = args[ 1 ].data + args[ 1 ].stride * from; \
- mVecSIMD* out_end = out + ( to + 1 - from ); \
- while( out < out_end ) { \
- op; \
- out += args[ 0 ].stride; \
- arg1 += args[ 1 ].stride; \
- } \
- }
- #define PS_DEFINE_OP2( name, op ) \
- void PS_Op_##name( PSExpArg* args, UINT32 from, UINT32 to ){ \
- mVecSIMD* out = args[ 0 ].data + args[ 0 ].stride * from; \
- mVecSIMD* arg1 = args[ 1 ].data + args[ 1 ].stride * from; \
- mVecSIMD* arg2 = args[ 2 ].data + args[ 2 ].stride * from; \
- mVecSIMD* out_end = out + ( to + 1 - from ); \
- while( out < out_end ) { \
- op; \
- out += args[ 0 ].stride; \
- arg1 += args[ 1 ].stride; \
- arg2 += args[ 2 ].stride; \
- } \
- }
- #define PS_DEFINE_OP3( name, op ) \
- void PS_Op_##name( PSExpArg* args, UINT32 from, UINT32 to ){ \
- mVecSIMD* out = args[ 0 ].data + args[ 0 ].stride * from; \
- mVecSIMD* arg1 = args[ 1 ].data + args[ 1 ].stride * from; \
- mVecSIMD* arg2 = args[ 2 ].data + args[ 2 ].stride * from; \
- mVecSIMD* arg3 = args[ 3 ].data + args[ 3 ].stride * from; \
- mVecSIMD* out_end = out + ( to + 1 - from ); \
- while( out < out_end ) { \
- op; \
- out += args[ 0 ].stride; \
- arg1 += args[ 1 ].stride; \
- arg2 += args[ 2 ].stride; \
- arg3 += args[ 3 ].stride; \
- } \
- }
- PS_DEFINE_OP2( ADD, *out = *arg1 + *arg2 )
- PS_DEFINE_OP2( SUB, *out = *arg1 - *arg2 )
- PS_DEFINE_OP2( MUL, *out = *arg1 * *arg2 )
- PS_DEFINE_OP2( DIV, *out = *arg1 / *arg2 )
- PS_DEFINE_OP1( SET, *out = *arg1 )
- PS_DEFINE_OP2( SETRAND, *out = *arg1 + mVecSIMD( M_RandF() * 2 - 1 ) * *arg2 )
- PS_DEFINE_OP3( CLAMP, *out = arg1->Max( *arg2 ).Min( *arg3 ) )
- void PS_CallOp( UINT8 op, PSExpArg* args, UINT32 from, UINT32 to )
- {
- switch( op )
- {
- #define CASE( op ) case PSOP_##op: PS_OP( op )( args, from, to ); break
- CASE( ADD );
- CASE( SUB );
- CASE( MUL );
- CASE( DIV );
- CASE( SET );
- CASE( SETRAND );
- CASE( CLAMP );
- #undef CASE
- }
- }
- UBOOL SGParticleSystem::Load( const UNICHAR* file )
- {
- TString code;
- if( !ReadTextFileToString( file, code ) )
- return FALSE;
- return Parse( code );
- }
- UBOOL SGParticleSystem::Parse( TString& code )
- {
- INT32 at = 0;
- Material.Clear();
- RestartInfo.Set( 0, 0 );
- Consts.Clear();
- CreateOps.Clear();
- TickOps.Clear();
- // prealloc special consts
- Consts.PushBack( mVecSIMD( 0.0f ) ); // 0 - Delta Time
- UINT8 cnsbase = Consts.Size();
- F_SkipWhitespaces( code, at );
- while( at < (INT32) code.Size() )
- {
- TString key = F_ReadString( code, at, &FStr_Token );
- if( key == "MATERIAL" ) Material = F_ReadQString( code, at );
- else if( key == "SPAWN" ) SpawnCount = F_ReadVec2( code, at );
- else if( key == "RESTART" ) RestartInfo = F_ReadVec2( code, at );
- else if( key == "MAX" ) SetMaxParticles( F_ReadInteger( code, at ) );
- else if( key == "CREATE" || key == "TICK" )
- {
- OpVec* ops = key == "CREATE" ? &CreateOps : &TickOps;
- F_SkipLine( code, at );
- INT32 at2 = at;
- do
- {
- F_SkipLine( code, at2 );
- TString line = code.TrimRet( at, at2 - at );
- line.TrimSpaces();
- // parse line
- if( line.Size() )
- {
- INT32 x = 0;
- TString op = F_ReadString( line, x, &FStr_Token );
- if( op == "END" )
- break;
- Op rop;
- rop.argc = 0;
- rop.type = PS_GetOpId( op );
- if( !rop.type )
- {
- E_Notify( NOTIFY_WARN, "[PS] parsing error: invalid op name \"%s\"", op.CStr() );
- return FALSE;
- }
- F_SkipWhitespaces( line, x );
- while( rop.argc < 6 && x < (INT32) line.Size() )
- {
- if( gCharIsDigit( line[ x ] ) )
- {
- if( rop.argc == 0 )
- {
- E_Notify( NOTIFY_WARN, "[PS] parsing error: argument 0 (output) can't be a constant" );
- return FALSE;
- }
- mVec4 c( F_ReadFloat( line, x, FALSE ) );
- if( F_CheckChar( line, x, ';' ) )
- {
- c.w = c.y = F_ReadFloat( line, x, FALSE );
- if( F_CheckChar( line, x, ';' ) )
- {
- c.z = F_ReadFloat( line, x, FALSE );
- c.w = 0;
- if( F_CheckChar( line, x, ';' ) )
- {
- c.w = F_ReadFloat( line, x, FALSE );
- }
- }
- }
- mVecSIMD csimd( c );
- UINT8 cnst = cnsbase;
- while( cnst < (UINT8) Consts.Size() )
- {
- if( Consts[ cnst ] == csimd )
- break;
- else
- cnst++;
- }
- if( cnst == Consts.Size() )
- {
- if( cnst == 127 )
- {
- E_Notify( NOTIFY_WARN, "[PS] parsing error: out of const space" );
- return FALSE;
- }
- Consts.PushBack( csimd );
- }
- rop.args[ rop.argc ] = cnst;
- }
- else
- {
- UINT8 arg = 0;
- TString str = F_ReadString( line, x, &FStr_Token );
- if( str == "color" ) arg = -1;
- else if( str == "pos" ) arg = -2;
- else if( str == "vel" ) arg = -3;
- else if( str == "accel" ) arg = -4;
- else if( str == "size" ) arg = -5;
- else if( str == "angle" ) arg = -6;
- else if( str == "time" ) arg = -7;
- else if( str == "tmp0" ) arg = -8;
- else if( str == "tmp1" ) arg = -9;
- else if( str == "tmp2" ) arg = -10;
- else if( str == "tmp3" ) arg = -11;
- // special constants
- else if( str == "DT" ) arg = 0;
- // problems
- else if( str == "" )
- break;
- else
- {
- E_Notify( NOTIFY_WARN, "[PS] parsing error: invalid argument name \"%s\"", str.CStr() );
- return FALSE;
- }
- rop.args[ rop.argc ] = arg;
- }
- rop.argc++;
- F_SkipWhitespaces( line, x );
- }
- UINT8 expargs = PS_GetOpArgs( rop.type );
- if( expargs != rop.argc )
- {
- E_Notify( NOTIFY_WARN, "[PS] parsing error: op \"%s\" - expected %d args, got %d", op.CStr(), (INT32) expargs, (INT32) rop.argc );
- return FALSE;
- }
- ops->PushBack( rop );
- }
- at = at2;
- }
- while( at2 < (INT32) code.Size() );
- }
- else if( key == "DRAW" )
- {
- F_SkipLine( code, at );
- }
- F_SkipWhitespaces( code, at );
- }
- return TRUE;
- }
- void SGParticleSystem::SetMaxParticles( UINT32 maxparts )
- {
- if( MaxParticles == maxparts )
- return;
- if( Color ) delete [] Color;
- if( Position ) delete [] Position;
- if( Velocity ) delete [] Velocity;
- if( Accel ) delete [] Accel;
- if( Size ) delete [] Size;
- if( Angle ) delete [] Angle;
- if( TimeLeft ) delete [] TimeLeft;
- if( Tmp[ 0 ] ) delete [] Tmp[ 0 ];
- if( Tmp[ 1 ] ) delete [] Tmp[ 1 ];
- if( Tmp[ 2 ] ) delete [] Tmp[ 2 ];
- if( Tmp[ 3 ] ) delete [] Tmp[ 3 ];
- MaxParticles = maxparts;
- if( maxparts )
- {
- Color = new mVecSIMD[ maxparts ];
- Position = new mVecSIMD[ maxparts ];
- Velocity = new mVecSIMD[ maxparts ];
- Accel = new mVecSIMD[ maxparts ];
- Size = new mVecSIMD[ maxparts ];
- Angle = new mVecSIMD[ maxparts ];
- TimeLeft = new mVecSIMD[ maxparts ];
- gMemSet( TimeLeft, 0, maxparts * sizeof( mVecSIMD ) );
- Tmp[ 0 ] = new mVecSIMD[ maxparts ];
- Tmp[ 1 ] = new mVecSIMD[ maxparts ];
- Tmp[ 2 ] = new mVecSIMD[ maxparts ];
- Tmp[ 3 ] = new mVecSIMD[ maxparts ];
- }
- Next = 0;
- }
- void SGParticleSystem::AddParticles( INT32 n )
- {
- Count = ( Count + n ) % MaxParticles;
- while( n > 0 )
- {
- UINT32 amt = MIN( n, MaxParticles - Next );
- ExecOps( CreateOps, Next, Next + amt - 1 );
- Next = ( Next + amt ) % MaxParticles;
- n -= amt;
- }
- }
- void SGParticleSystem::Tick( FLOAT delta )
- {
- if( delta < FLT_EPSILON || !MaxParticles )
- return;
- // tick
- Consts[ 0 ] = mVecSIMD( delta );
- if( Count )
- ExecOps( TickOps, 0, Count - 1 );
- // resort and recount
- for( UINT32 i = 0; i < Count; ++i )
- {
- if( TimeLeft[ i ].Get( 0 ) <= 0 )
- {
- Count--;
- if( Count > i )
- {
- Color[ i ] = Color[ Count ];
- Position[ i ] = Position[ Count ];
- Velocity[ i ] = Velocity[ Count ];
- Accel[ i ] = Accel[ Count ];
- Size[ i ] = Size[ Count ];
- Angle[ i ] = Angle[ Count ];
- TimeLeft[ i ] = TimeLeft[ Count ];
- Tmp[ 0 ][ i ] = Tmp[ 0 ][ Count ];
- Tmp[ 1 ][ i ] = Tmp[ 1 ][ Count ];
- Tmp[ 2 ][ i ] = Tmp[ 2 ][ Count ];
- Tmp[ 3 ][ i ] = Tmp[ 3 ][ Count ];
- }
- i--;
- }
- }
- Next = ( Count ) % MaxParticles;
- // restart
- if( Time + delta > RestartTime && Time <= RestartTime )
- {
- Time = FLT_EPSILON;
- RestartTime = RestartInfo.x + ( M_RandF() - 0.5f ) * 2 * RestartInfo.y;
- INT32 cnt = SpawnCount.x + ( M_RandF() - 0.5f ) * 2 * SpawnCount.y;
- AddParticles( cnt );
- }
- else
- Time += delta;
- }
- void SGParticleSystem::ExecOps( const OpVec& ops, UINT32 from, UINT32 to )
- {
- PSExpArg args[ 6 ];
- for( UINT32 o = 0; o < ops.Size(); ++o )
- {
- Op& O = ops[ o ];
- for( UINT8 i = 0; i < O.argc; ++i )
- {
- PSExpArg& A = args[ i ];
- if( O.args[ i ] >= 0 )
- {
- A.data = &Consts[ O.args[ i ] ];
- A.stride = 0;
- }
- else
- {
- switch( O.args[ i ] )
- {
- case -1: A.data = Color; break;
- case -2: A.data = Position; break;
- case -3: A.data = Velocity; break;
- case -4: A.data = Accel; break;
- case -5: A.data = Size; break;
- case -6: A.data = Angle; break;
- case -7: A.data = TimeLeft; break;
- case -8: A.data = Tmp[ 0 ]; break;
- case -9: A.data = Tmp[ 1 ]; break;
- case -10: A.data = Tmp[ 2 ]; break;
- case -11: A.data = Tmp[ 3 ]; break;
- }
- A.stride = 1;
- }
- }
- PS_CallOp( O.type, args, from, to );
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement