2013-09-21 14:48:28 +00:00
/*
2013-12-17 20:01:09 +00:00
* OpenClonk , http : //www.openclonk.org
*
* Copyright ( c ) 2013 , The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
*
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
*
* To redistribute this file separately , substitute the full license texts
* for the above references .
*/
2013-09-21 14:48:28 +00:00
# include <C4Include.h>
2013-12-17 20:40:40 +00:00
# include <C4Particles.h>
2013-09-21 14:48:28 +00:00
2013-12-17 17:09:14 +00:00
// headers for particle loading
# include <C4Log.h>
# include <C4Components.h>
# include <C4Config.h>
2013-10-18 16:52:04 +00:00
# ifndef USE_CONSOLE
2013-12-17 17:09:14 +00:00
// headers for particle excution
2015-03-22 08:52:34 +00:00
# include <C4Application.h>
2013-10-07 22:08:15 +00:00
# include <C4AulDefFunc.h>
2013-09-21 14:48:28 +00:00
# include <C4Value.h>
# include <C4ValueArray.h>
2015-09-12 08:53:23 +00:00
# include <C4Material.h>
2013-09-21 14:48:28 +00:00
# include <C4MeshAnimation.h>
# include <C4DrawGL.h>
# include <C4Random.h>
2013-09-27 13:27:53 +00:00
# include <C4Landscape.h>
2015-03-22 08:52:34 +00:00
# include <C4Weather.h>
2013-10-18 16:52:04 +00:00
# endif
2013-09-21 14:48:28 +00:00
2013-12-17 17:09:14 +00:00
void C4ParticleDefCore : : CompileFunc ( StdCompiler * pComp )
{
pComp - > Value ( mkNamingAdapt ( toC4CStrBuf ( Name ) , " Name " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( GfxFace , " Face " ) ) ;
}
C4ParticleDefCore : : C4ParticleDefCore ( )
{
GfxFace . Default ( ) ;
}
bool C4ParticleDefCore : : Compile ( char * particle_source , const char * name )
{
return CompileFromBuf_LogWarn < StdCompilerINIRead > ( mkNamingAdapt ( * this , " Particle " ) ,
StdStrBuf ( particle_source ) , name ) ;
}
C4ParticleDef : : C4ParticleDef ( ) : C4ParticleDefCore ( )
{
// zero fields
Gfx . Default ( ) ;
// link into list
2013-12-17 20:40:40 +00:00
if ( ! Particles . definitions . first )
2013-12-17 17:09:14 +00:00
{
previous = NULL ;
2013-12-17 20:40:40 +00:00
Particles . definitions . first = this ;
2013-12-17 17:09:14 +00:00
}
else
{
2013-12-17 20:40:40 +00:00
previous = Particles . definitions . last ;
2013-12-17 17:09:14 +00:00
previous - > next = this ;
}
2013-12-17 20:40:40 +00:00
Particles . definitions . last = this ;
2013-12-17 17:09:14 +00:00
next = 0 ;
}
C4ParticleDef : : ~ C4ParticleDef ( )
{
// clear
Clear ( ) ;
// unlink from list
2013-12-17 20:40:40 +00:00
if ( previous ) previous - > next = next ; else Particles . definitions . first = next ;
if ( next ) next - > previous = previous ; else Particles . definitions . last = previous ;
2013-12-17 17:09:14 +00:00
}
void C4ParticleDef : : Clear ( )
{
Name . Clear ( ) ;
}
bool C4ParticleDef : : Load ( C4Group & group )
{
// store file
Filename . Copy ( group . GetFullName ( ) ) ;
// load
char * particle_source ;
if ( group . LoadEntry ( C4CFN_ParticleCore , & particle_source , NULL , 1 ) )
{
if ( ! Compile ( particle_source , Filename . getData ( ) ) )
{
DebugLogF ( " invalid particle def at '%s' " , group . GetFullName ( ) . getData ( ) ) ;
delete [ ] particle_source ; return false ;
}
delete [ ] particle_source ;
// load graphics
2015-09-19 01:10:39 +00:00
if ( ! Gfx . Load ( group , C4CFN_DefGraphics , C4FCT_Full , C4FCT_Full , false , C4SF_MipMap ) )
2013-12-17 17:09:14 +00:00
{
DebugLogF ( " particle %s has no valid graphics defined " , Name . getData ( ) ) ;
return false ;
}
// set facet, if assigned - otherwise, assume full surface
if ( GfxFace . Wdt ) Gfx . Set ( Gfx . Surface , GfxFace . x , GfxFace . y , GfxFace . Wdt , GfxFace . Hgt ) ;
// set phase num
int32_t Q ; Gfx . GetPhaseNum ( PhasesX , Q ) ;
Length = PhasesX * Q ;
if ( ! Length )
{
DebugLogF ( " invalid facet for particle '%s' " , Name . getData ( ) ) ;
return false ;
}
// calc aspect
Aspect = ( float ) Gfx . Hgt / Gfx . Wdt ;
// particle overloading
C4ParticleDef * def_overload ;
2013-12-17 20:40:40 +00:00
if ( ( def_overload = Particles . definitions . GetDef ( Name . getData ( ) , this ) ) )
2013-12-17 17:09:14 +00:00
{
if ( Config . Graphics . VerboseObjectLoading > = 1 )
{ char ostr [ 250 ] ; sprintf ( ostr , LoadResStr ( " IDS_PRC_DEFOVERLOAD " ) , def_overload - > Name . getData ( ) , " <particle> " ) ; Log ( ostr ) ; }
delete def_overload ;
}
// success
return true ;
}
return false ;
}
bool C4ParticleDef : : Reload ( )
{
// no file?
if ( ! Filename [ 0 ] ) return false ;
// open group
C4Group group ;
if ( ! group . Open ( Filename . getData ( ) ) ) return false ;
// reset class
Clear ( ) ;
// load
return Load ( group ) ;
}
2013-10-18 16:52:04 +00:00
# ifndef USE_CONSOLE
2013-12-17 20:40:40 +00:00
const int C4Particle : : DrawingData : : vertexCountPerParticle ( 4 ) ;
2013-09-21 14:48:28 +00:00
2013-12-17 20:40:40 +00:00
void C4Particle : : DrawingData : : SetPosition ( float x , float y , float size , float rotation , float stretch )
2013-09-24 18:36:49 +00:00
{
2013-09-27 11:28:38 +00:00
if ( size ! = originalSize | | stretch ! = currentStretch )
{
currentStretch = stretch ;
2013-10-07 22:08:15 +00:00
originalSize = std : : max ( size , 0.0001f ) ; // a size of zero results in undefined behavior
2013-09-27 11:28:38 +00:00
sizeX = originalSize / aspect ;
sizeY = originalSize * currentStretch ;
}
2013-09-24 18:36:49 +00:00
if ( rotation = = 0.f )
{
2013-10-18 10:33:31 +00:00
vertices [ 0 ] . x = x - sizeX + offsetX ;
vertices [ 0 ] . y = y + sizeY + offsetY ;
vertices [ 1 ] . x = x - sizeX + offsetX ;
vertices [ 1 ] . y = y - sizeY + offsetY ;
vertices [ 2 ] . x = x + sizeX + offsetX ;
vertices [ 2 ] . y = y + sizeY + offsetY ;
vertices [ 3 ] . x = x + sizeX + offsetX ;
vertices [ 3 ] . y = y - sizeY + offsetY ;
2013-09-24 18:36:49 +00:00
}
else
{
2013-09-25 11:30:06 +00:00
float sine = sinf ( rotation ) ;
float cosine = cosf ( rotation ) ;
2013-09-24 18:36:49 +00:00
2013-10-18 10:33:31 +00:00
vertices [ 0 ] . x = x + ( ( - sizeX ) * cosine - ( + sizeY ) * sine ) + offsetX ;
vertices [ 0 ] . y = y + ( ( - sizeX ) * sine + ( + sizeY ) * cosine ) + offsetY ;
vertices [ 1 ] . x = x + ( ( - sizeX ) * cosine - ( - sizeY ) * sine ) + offsetX ;
vertices [ 1 ] . y = y + ( ( - sizeX ) * sine + ( - sizeY ) * cosine ) + offsetY ;
vertices [ 2 ] . x = x + ( ( + sizeX ) * cosine - ( + sizeY ) * sine ) + offsetX ;
vertices [ 2 ] . y = y + ( ( + sizeX ) * sine + ( + sizeY ) * cosine ) + offsetY ;
vertices [ 3 ] . x = x + ( ( + sizeX ) * cosine - ( - sizeY ) * sine ) + offsetX ;
vertices [ 3 ] . y = y + ( ( + sizeX ) * sine + ( - sizeY ) * cosine ) + offsetY ;
2013-09-24 18:36:49 +00:00
}
}
2013-09-21 14:48:28 +00:00
2013-12-17 20:40:40 +00:00
void C4Particle : : DrawingData : : SetPhase ( int phase , C4ParticleDef * sourceDef )
2013-09-25 11:30:06 +00:00
{
this - > phase = phase ;
phase = phase % sourceDef - > Length ;
int offsetY = phase / sourceDef - > PhasesX ;
int offsetX = phase % sourceDef - > PhasesX ;
float wdt = 1.0f / ( float ) sourceDef - > PhasesX ;
int numOfLines = sourceDef - > Length / sourceDef - > PhasesX ;
float hgt = 1.0f / ( float ) numOfLines ;
float x = wdt * ( float ) offsetX ;
float y = hgt * ( float ) offsetY ;
float xr = x + wdt ;
float yr = y + hgt ;
vertices [ 0 ] . u = x ; vertices [ 0 ] . v = yr ;
vertices [ 1 ] . u = x ; vertices [ 1 ] . v = y ;
vertices [ 2 ] . u = xr ; vertices [ 2 ] . v = yr ;
vertices [ 3 ] . u = xr ; vertices [ 3 ] . v = y ;
}
2013-12-17 20:40:40 +00:00
C4ParticleValueProvider & C4ParticleValueProvider : : operator = ( const C4ParticleValueProvider & other )
2013-09-24 19:15:24 +00:00
{
startValue = other . startValue ;
endValue = other . endValue ;
currentValue = other . currentValue ;
rerollInterval = other . rerollInterval ;
smoothing = other . smoothing ;
valueFunction = other . valueFunction ;
isConstant = other . isConstant ;
2013-10-07 11:36:51 +00:00
keyFrameCount = other . keyFrameCount ;
2013-09-24 19:15:24 +00:00
2013-11-30 00:11:06 +00:00
if ( keyFrameCount > 0 )
{
keyFrames . reserve ( 2 * keyFrameCount ) ;
keyFrames . assign ( other . keyFrames . begin ( ) , other . keyFrames . end ( ) ) ;
}
2013-10-07 11:36:51 +00:00
2013-10-07 16:10:32 +00:00
typeOfValueToChange = other . typeOfValueToChange ;
2013-10-08 23:49:10 +00:00
switch ( typeOfValueToChange )
{
case VAL_TYPE_FLOAT :
floatValueToChange = other . floatValueToChange ;
break ;
case VAL_TYPE_INT :
intValueToChange = other . intValueToChange ;
break ;
case VAL_TYPE_KEYFRAMES :
keyFrameIndex = other . keyFrameIndex ;
break ;
default :
2013-12-17 20:40:40 +00:00
assert ( false & & " Trying to copy C4ParticleValueProvider with invalid value type " ) ;
2013-10-08 23:49:10 +00:00
break ;
}
2013-10-07 16:10:32 +00:00
2013-10-07 11:36:51 +00:00
// copy the other's children, too
2013-12-17 20:40:40 +00:00
for ( std : : vector < C4ParticleValueProvider * > : : const_iterator iter = other . childrenValueProviders . begin ( ) ; iter ! = other . childrenValueProviders . end ( ) ; + + iter )
2013-10-07 11:36:51 +00:00
{
2013-12-17 20:40:40 +00:00
childrenValueProviders . push_back ( new C4ParticleValueProvider ( * * iter ) ) ; // custom copy constructor usage
2013-10-07 11:36:51 +00:00
}
2013-09-24 19:15:24 +00:00
return ( * this ) ;
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : SetParameterValue ( int type , const C4Value & value , float C4ParticleValueProvider : : * floatVal , int C4ParticleValueProvider : : * intVal , size_t keyFrameIndex )
2013-10-07 11:36:51 +00:00
{
// just an atomic data type
if ( value . GetType ( ) = = C4V_Int )
{
2013-10-07 16:10:32 +00:00
if ( type = = VAL_TYPE_FLOAT )
this - > * floatVal = ( float ) value . getInt ( ) ;
else if ( type = = VAL_TYPE_INT )
this - > * intVal = value . getInt ( ) ;
else if ( type = = VAL_TYPE_KEYFRAMES )
this - > keyFrames [ keyFrameIndex ] = ( float ) value . getInt ( ) ;
2013-10-07 11:36:51 +00:00
}
else if ( value . GetType ( ) = = C4V_Array )
{
// might be another value provider!
2013-12-17 20:40:40 +00:00
C4ParticleValueProvider * child = new C4ParticleValueProvider ( ) ;
2013-10-07 11:36:51 +00:00
childrenValueProviders . push_back ( child ) ;
2013-10-08 23:28:58 +00:00
child - > Set ( * value . getArray ( ) ) ;
2013-10-07 16:10:32 +00:00
child - > typeOfValueToChange = type ;
2013-10-07 11:36:51 +00:00
2013-10-07 16:10:32 +00:00
if ( type = = VAL_TYPE_FLOAT )
2013-10-07 11:36:51 +00:00
{
2013-10-07 16:10:32 +00:00
child - > floatValueToChange = floatVal ;
2013-10-07 11:36:51 +00:00
}
2013-10-07 16:10:32 +00:00
else if ( type = = VAL_TYPE_INT )
{
child - > intValueToChange = intVal ;
}
else if ( type = = VAL_TYPE_KEYFRAMES )
{
child - > keyFrameIndex = keyFrameIndex ;
}
2013-10-07 11:36:51 +00:00
}
else // invalid
{
2013-10-07 16:10:32 +00:00
if ( type = = VAL_TYPE_FLOAT )
this - > * floatVal = 0.f ;
else if ( type = = VAL_TYPE_INT )
this - > * intVal = 0 ;
else if ( type = = VAL_TYPE_KEYFRAMES )
this - > keyFrames [ keyFrameIndex ] = 0.f ;
2013-10-07 11:36:51 +00:00
}
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : UpdatePointerValue ( C4Particle * particle , C4ParticleValueProvider * parent )
2013-10-07 11:36:51 +00:00
{
2013-10-07 16:10:32 +00:00
switch ( typeOfValueToChange )
{
case VAL_TYPE_FLOAT :
parent - > * floatValueToChange = GetValue ( particle ) ;
break ;
case VAL_TYPE_INT :
parent - > * intValueToChange = ( int ) GetValue ( particle ) ;
break ;
case VAL_TYPE_KEYFRAMES :
parent - > keyFrames [ keyFrameIndex ] = GetValue ( particle ) ;
break ;
default :
assert ( false ) ;
}
2013-10-07 11:36:51 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : UpdateChildren ( C4Particle * particle )
2013-10-07 11:36:51 +00:00
{
2013-12-17 20:40:40 +00:00
for ( std : : vector < C4ParticleValueProvider * > : : iterator iter = childrenValueProviders . begin ( ) ; iter ! = childrenValueProviders . end ( ) ; + + iter )
2013-10-07 11:36:51 +00:00
{
2013-10-07 22:08:15 +00:00
( * iter ) - > UpdatePointerValue ( particle , this ) ;
2013-10-07 11:36:51 +00:00
}
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : FloatifyParameterValue ( float C4ParticleValueProvider : : * value , float denominator , size_t keyFrameIndex )
2013-10-07 11:36:51 +00:00
{
2013-10-08 23:28:58 +00:00
if ( value = = 0 )
2013-10-07 16:10:32 +00:00
this - > keyFrames [ keyFrameIndex ] / = denominator ;
else
this - > * value / = denominator ;
2013-10-07 11:36:51 +00:00
2013-12-17 20:40:40 +00:00
for ( std : : vector < C4ParticleValueProvider * > : : iterator iter = childrenValueProviders . begin ( ) ; iter ! = childrenValueProviders . end ( ) ; + + iter )
2013-10-07 11:36:51 +00:00
{
2013-12-17 20:40:40 +00:00
C4ParticleValueProvider * child = * iter ;
2013-10-08 23:28:58 +00:00
if ( value = = 0 )
2013-10-07 11:36:51 +00:00
{
2013-10-07 22:08:15 +00:00
if ( child - > typeOfValueToChange = = VAL_TYPE_KEYFRAMES & & child - > keyFrameIndex = = keyFrameIndex )
child - > Floatify ( denominator ) ;
}
else
{
if ( child - > floatValueToChange = = value )
child - > Floatify ( denominator ) ;
2013-10-07 11:36:51 +00:00
}
}
2013-10-07 22:08:15 +00:00
2013-10-07 11:36:51 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : Floatify ( float denominator )
2013-09-21 14:48:28 +00:00
{
2013-12-17 20:40:40 +00:00
assert ( denominator ! = 0.f & & " Trying to floatify C4ParticleValueProvider with denominator of 0 " ) ;
2013-09-25 11:30:06 +00:00
2013-12-17 20:40:40 +00:00
if ( valueFunction = = & C4ParticleValueProvider : : Direction )
2013-09-25 11:30:06 +00:00
{
2013-12-17 20:40:40 +00:00
FloatifyParameterValue ( & C4ParticleValueProvider : : startValue , 1000.f ) ;
2013-09-25 11:30:06 +00:00
return ;
}
2013-12-17 20:40:40 +00:00
FloatifyParameterValue ( & C4ParticleValueProvider : : startValue , denominator ) ;
FloatifyParameterValue ( & C4ParticleValueProvider : : endValue , denominator ) ;
FloatifyParameterValue ( & C4ParticleValueProvider : : currentValue , denominator ) ;
2013-09-21 14:48:28 +00:00
2013-09-21 15:22:46 +00:00
// special treatment for keyframes
2013-12-17 20:40:40 +00:00
if ( valueFunction = = & C4ParticleValueProvider : : KeyFrames )
2013-09-21 15:22:46 +00:00
{
2013-10-08 23:28:58 +00:00
for ( size_t i = 0 ; i < keyFrameCount ; + + i )
2013-09-21 15:22:46 +00:00
{
2013-10-07 16:10:32 +00:00
FloatifyParameterValue ( 0 , 1000.f , 2 * i ) ; // even numbers are the time values
FloatifyParameterValue ( 0 , denominator , 2 * i + 1 ) ; // odd numbers are the actual values
2013-09-21 15:22:46 +00:00
}
}
2013-12-17 20:40:40 +00:00
else if ( valueFunction = = & C4ParticleValueProvider : : Speed | | valueFunction = = & C4ParticleValueProvider : : Wind | | valueFunction = = & C4ParticleValueProvider : : Gravity )
2013-10-07 11:36:51 +00:00
{
2013-12-17 20:40:40 +00:00
FloatifyParameterValue ( & C4ParticleValueProvider : : speedFactor , 1000.0f ) ;
2013-10-07 11:36:51 +00:00
}
2014-02-18 19:37:43 +00:00
else if ( valueFunction = = & C4ParticleValueProvider : : Step )
{
FloatifyParameterValue ( & C4ParticleValueProvider : : maxValue , denominator ) ;
}
2014-06-29 23:00:15 +00:00
else if ( valueFunction = = & C4ParticleValueProvider : : Sin )
{
FloatifyParameterValue ( & C4ParticleValueProvider : : parameterValue , 1.0f ) ;
FloatifyParameterValue ( & C4ParticleValueProvider : : maxValue , denominator ) ;
}
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : RollRandom ( )
2013-09-21 14:48:28 +00:00
{
2013-10-10 15:19:20 +00:00
float range = endValue - startValue ;
float rnd = ( float ) ( rand ( ) ) / ( float ) ( RAND_MAX ) ;
currentValue = startValue + rnd * range ;
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : GetValue ( C4Particle * forParticle )
2013-09-21 14:48:28 +00:00
{
2013-10-07 11:36:51 +00:00
UpdateChildren ( forParticle ) ;
2013-09-21 14:48:28 +00:00
return ( this - > * valueFunction ) ( forParticle ) ;
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : Linear ( C4Particle * forParticle )
2013-09-21 14:48:28 +00:00
{
return startValue + ( endValue - startValue ) * forParticle - > GetRelativeAge ( ) ;
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : Const ( C4Particle * forParticle )
2013-09-21 14:48:28 +00:00
{
return startValue ;
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : Random ( C4Particle * forParticle )
2013-09-21 14:48:28 +00:00
{
2013-10-07 11:36:51 +00:00
if ( ( rerollInterval ! = 0 & & ( ( int ) forParticle - > GetAge ( ) % rerollInterval = = 0 ) ) | | alreadyRolled = = 0 )
{
alreadyRolled = 1 ;
2013-09-21 14:48:28 +00:00
RollRandom ( ) ;
2013-10-07 11:36:51 +00:00
}
2013-09-21 14:48:28 +00:00
return currentValue ;
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : Direction ( C4Particle * forParticle )
2013-09-24 18:36:49 +00:00
{
float distX = forParticle - > currentSpeedX ;
float distY = forParticle - > currentSpeedY ;
2013-09-25 11:30:06 +00:00
if ( distX = = 0.f ) return distY > 0.f ? M_PI : 0.f ;
if ( distY = = 0.f ) return distX < 0.f ? 3.0f * M_PI_2 : M_PI_2 ;
2013-09-24 18:36:49 +00:00
2013-09-25 11:30:06 +00:00
return startValue * ( atan2 ( distY , distX ) + ( float ) M_PI_2 ) ;
2013-09-24 18:36:49 +00:00
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : Step ( C4Particle * forParticle )
2013-09-24 18:36:49 +00:00
{
2014-02-18 19:37:43 +00:00
float value = currentValue + startValue * forParticle - > GetAge ( ) / delay ;
if ( maxValue ! = 0.0f & & value > maxValue ) value = maxValue ;
return value ;
2013-09-24 18:36:49 +00:00
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : KeyFrames ( C4Particle * forParticle )
2013-09-21 15:22:46 +00:00
{
float age = forParticle - > GetRelativeAge ( ) ;
2013-10-07 18:56:16 +00:00
// todo, implement smoothing
//if (smoothing == 0) // linear
2013-09-21 15:22:46 +00:00
{
2013-10-08 23:28:58 +00:00
for ( size_t i = 0 ; i < keyFrameCount ; + + i )
2013-09-21 15:22:46 +00:00
{
if ( age > keyFrames [ i * 2 ] ) continue ;
assert ( i > = 1 ) ;
float x1 = keyFrames [ ( i - 1 ) * 2 ] ;
float x2 = keyFrames [ i * 2 ] ;
float y1 = keyFrames [ ( i - 1 ) * 2 + 1 ] ;
float y2 = keyFrames [ i * 2 + 1 ] ;
float position = ( age - x1 ) / ( x2 - x1 ) ;
float totalRange = ( y2 - y1 ) ;
2013-09-24 18:36:49 +00:00
2013-09-21 15:22:46 +00:00
float value = position * totalRange + y1 ;
return value ;
}
}
return startValue ;
}
2014-06-29 23:00:15 +00:00
float C4ParticleValueProvider : : Sin ( C4Particle * forParticle )
{
return sin ( parameterValue * M_PI / 180.0f ) * maxValue + startValue ;
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : Speed ( C4Particle * forParticle )
2013-09-27 11:28:38 +00:00
{
float distX = forParticle - > currentSpeedX ;
float distY = forParticle - > currentSpeedY ;
float speed = sqrtf ( ( distX * distX ) + ( distY * distY ) ) ;
return startValue + speedFactor * speed ;
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : Wind ( C4Particle * forParticle )
2013-10-19 12:50:43 +00:00
{
2013-10-22 14:21:31 +00:00
return startValue + ( 0.01f * speedFactor * : : Weather . GetWind ( ( int ) forParticle - > positionX , ( int ) forParticle - > positionY ) ) ;
2013-10-19 12:50:43 +00:00
}
2013-12-17 20:40:40 +00:00
float C4ParticleValueProvider : : Gravity ( C4Particle * forParticle )
2013-10-19 12:50:43 +00:00
{
return startValue + ( speedFactor * : : Landscape . Gravity ) ;
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : SetType ( C4ParticleValueProviderID what )
2013-09-21 14:48:28 +00:00
{
switch ( what )
{
case C4PV_Const :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Const ;
2013-09-21 14:48:28 +00:00
break ;
case C4PV_Linear :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Linear ;
2013-09-21 14:48:28 +00:00
break ;
case C4PV_Random :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Random ;
2013-09-21 15:22:46 +00:00
break ;
2013-09-24 18:36:49 +00:00
case C4PV_Direction :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Direction ;
2013-09-24 18:36:49 +00:00
break ;
case C4PV_Step :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Step ;
2013-09-24 18:36:49 +00:00
break ;
2013-09-21 15:22:46 +00:00
case C4PV_KeyFrames :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : KeyFrames ;
2013-09-21 15:22:46 +00:00
break ;
2014-06-29 23:00:15 +00:00
case C4PV_Sin :
valueFunction = & C4ParticleValueProvider : : Sin ;
break ;
2013-09-27 11:28:38 +00:00
case C4PV_Speed :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Speed ;
2013-09-27 11:28:38 +00:00
break ;
2013-10-19 12:50:43 +00:00
case C4PV_Wind :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Wind ;
2013-10-19 12:50:43 +00:00
break ;
case C4PV_Gravity :
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Gravity ;
2013-10-19 12:50:43 +00:00
break ;
2013-09-21 14:48:28 +00:00
default :
2013-12-17 20:40:40 +00:00
assert ( false & & " Invalid C4ParticleValueProvider ID passed " ) ;
2013-09-21 14:48:28 +00:00
} ;
if ( what ! = C4PV_Const )
{
isConstant = false ;
}
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : Set ( const C4ValueArray & fromArray )
2013-09-21 14:48:28 +00:00
{
startValue = endValue = 1.0f ;
2013-12-17 20:40:40 +00:00
valueFunction = & C4ParticleValueProvider : : Const ;
2013-10-08 23:28:58 +00:00
size_t arraySize = ( size_t ) fromArray . GetSize ( ) ;
2013-09-21 14:48:28 +00:00
if ( arraySize < 2 ) return ;
2013-10-08 23:28:58 +00:00
int type = fromArray [ 0 ] . getInt ( ) ;
2013-09-21 14:48:28 +00:00
switch ( type )
{
case C4PV_Const :
if ( arraySize > = 2 )
2013-10-07 11:36:51 +00:00
{
SetType ( C4PV_Const ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : startValue ) ;
2013-10-07 11:36:51 +00:00
}
2013-09-21 14:48:28 +00:00
break ;
case C4PV_Linear :
if ( arraySize > = 3 )
2013-09-21 15:22:46 +00:00
{
2013-10-07 11:36:51 +00:00
SetType ( C4PV_Linear ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : startValue ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 2 ] , & C4ParticleValueProvider : : endValue ) ;
2013-09-21 15:22:46 +00:00
}
2013-09-21 14:48:28 +00:00
break ;
case C4PV_Random :
2013-09-25 11:30:06 +00:00
if ( arraySize > = 3 )
2013-09-21 14:48:28 +00:00
{
2013-10-07 11:36:51 +00:00
SetType ( C4PV_Random ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : startValue ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 2 ] , & C4ParticleValueProvider : : endValue ) ;
2013-09-25 11:30:06 +00:00
if ( arraySize > = 4 )
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_INT , fromArray [ 3 ] , 0 , & C4ParticleValueProvider : : rerollInterval ) ;
2013-10-07 11:36:51 +00:00
alreadyRolled = 0 ;
2013-09-21 15:22:46 +00:00
}
2013-09-24 18:36:49 +00:00
break ;
case C4PV_Direction :
if ( arraySize > = 2 )
{
2013-10-07 11:36:51 +00:00
SetType ( C4PV_Direction ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : startValue ) ;
2013-09-24 18:36:49 +00:00
}
break ;
case C4PV_Step :
2014-02-18 19:37:43 +00:00
if ( arraySize > = 4 )
2013-09-24 18:36:49 +00:00
{
2013-10-07 11:36:51 +00:00
SetType ( C4PV_Step ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : startValue ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 2 ] , & C4ParticleValueProvider : : currentValue ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 3 ] , & C4ParticleValueProvider : : delay ) ;
2014-02-18 19:37:43 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 4 ] , & C4ParticleValueProvider : : maxValue ) ;
2013-09-25 11:30:06 +00:00
if ( delay = = 0.f ) delay = 1.f ;
2013-09-24 18:36:49 +00:00
}
break ;
2013-09-21 15:22:46 +00:00
case C4PV_KeyFrames :
if ( arraySize > = 5 )
{
2013-10-07 11:36:51 +00:00
SetType ( C4PV_KeyFrames ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_INT , fromArray [ 1 ] , 0 , & C4ParticleValueProvider : : smoothing ) ;
2013-10-08 23:28:58 +00:00
keyFrames . resize ( arraySize + 4 - 1 ) ; // 2*2 additional information floats at the beginning and ending, offset the first array item, though
2013-09-21 15:22:46 +00:00
keyFrameCount = 0 ;
2013-10-08 23:28:58 +00:00
const size_t startingOffset = 2 ;
size_t i = startingOffset ;
2013-09-21 15:22:46 +00:00
for ( ; i < arraySize ; + + i )
{
2013-10-08 23:28:58 +00:00
SetParameterValue ( VAL_TYPE_KEYFRAMES , fromArray [ ( int32_t ) i ] , 0 , 0 , 2 + i - startingOffset ) ;
2013-09-21 15:22:46 +00:00
}
keyFrameCount = ( i - startingOffset ) / 2 + 2 ;
2013-10-07 11:36:51 +00:00
startValue = keyFrames [ 2 + 1 ] ;
endValue = keyFrames [ 2 * keyFrameCount - 1 ] ;
2013-09-24 18:36:49 +00:00
2013-09-21 15:22:46 +00:00
// add two points for easier interpolation at beginning and ending
keyFrames [ 0 ] = - 500.f ;
keyFrames [ 1 ] = keyFrames [ 2 + 1 ] ;
keyFrames [ 2 * keyFrameCount - 2 ] = 1500.f ;
keyFrames [ 2 * keyFrameCount - 1 ] = keyFrames [ keyFrameCount - 1 - 2 ] ;
2013-09-21 14:48:28 +00:00
}
2013-09-24 18:36:49 +00:00
break ;
2014-06-29 23:00:15 +00:00
case C4PV_Sin :
if ( arraySize > = 3 )
{
SetType ( C4PV_Sin ) ; // Sin(parameterValue) * maxValue + startValue
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : parameterValue ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 2 ] , & C4ParticleValueProvider : : maxValue ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 3 ] , & C4ParticleValueProvider : : startValue ) ;
}
break ;
2013-09-27 11:28:38 +00:00
case C4PV_Speed :
if ( arraySize > = 3 )
{
2013-10-07 11:36:51 +00:00
SetType ( C4PV_Speed ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : speedFactor ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 2 ] , & C4ParticleValueProvider : : startValue ) ;
2013-09-27 11:28:38 +00:00
}
break ;
2013-10-19 12:50:43 +00:00
case C4PV_Wind :
if ( arraySize > = 3 )
{
SetType ( C4PV_Wind ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : speedFactor ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 2 ] , & C4ParticleValueProvider : : startValue ) ;
2013-10-19 12:50:43 +00:00
}
break ;
case C4PV_Gravity :
if ( arraySize > = 3 )
{
SetType ( C4PV_Gravity ) ;
2013-12-17 20:40:40 +00:00
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 1 ] , & C4ParticleValueProvider : : speedFactor ) ;
SetParameterValue ( VAL_TYPE_FLOAT , fromArray [ 2 ] , & C4ParticleValueProvider : : startValue ) ;
2013-10-19 12:50:43 +00:00
}
break ;
2013-09-21 14:48:28 +00:00
default :
2015-12-07 14:15:49 +00:00
throw C4AulExecError ( " invalid particle value provider supplied " ) ;
2013-09-21 14:48:28 +00:00
break ;
}
}
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : Set ( const C4Value & value )
2013-09-27 11:28:38 +00:00
{
2013-10-07 22:08:15 +00:00
C4ValueArray * valueArray = value . getArray ( ) ;
2013-09-27 11:28:38 +00:00
2013-10-08 23:28:58 +00:00
if ( valueArray ! = 0 )
Set ( * valueArray ) ;
else
2013-10-18 16:52:04 +00:00
Set ( ( float ) value . getInt ( ) ) ;
2013-10-07 11:36:51 +00:00
}
2013-09-27 11:28:38 +00:00
2013-12-17 20:40:40 +00:00
void C4ParticleValueProvider : : Set ( float to )
2013-10-07 11:36:51 +00:00
{
SetType ( C4PV_Const ) ;
startValue = endValue = to ;
2013-09-27 11:28:38 +00:00
}
2013-12-17 20:40:40 +00:00
C4ParticleProperties : : C4ParticleProperties ( )
2013-09-21 14:48:28 +00:00
{
2013-09-24 19:15:24 +00:00
blitMode = 0 ;
2013-10-22 14:21:31 +00:00
attachment = C4ATTACH_None ;
2013-09-24 19:15:24 +00:00
hasConstantColor = false ;
2013-10-01 16:15:55 +00:00
hasCollisionVertex = false ;
2013-09-27 14:06:25 +00:00
collisionCallback = 0 ;
bouncyness = 0.f ;
2013-09-24 19:15:24 +00:00
2013-09-21 14:48:28 +00:00
// all values in pre-floatified range (f.e. 0..255 instead of 0..1)
2015-09-12 08:53:23 +00:00
collisionDensity . Set ( static_cast < float > ( C4M_Solid ) ) ;
2013-09-27 13:27:53 +00:00
collisionVertex . Set ( 0.f ) ;
2013-09-21 14:48:28 +00:00
size . Set ( 8.f ) ;
2013-09-28 14:34:51 +00:00
stretch . Set ( 1000.f ) ;
2013-09-21 14:48:28 +00:00
forceX . Set ( 0.f ) ;
forceY . Set ( 0.f ) ;
speedDampingX . Set ( 1000.f ) ;
speedDampingY . Set ( 1000.f ) ;
colorR . Set ( 255.f ) ;
colorG . Set ( 255.f ) ;
colorB . Set ( 255.f ) ;
colorAlpha . Set ( 255.f ) ;
2013-09-24 18:36:49 +00:00
rotation . Set ( 0.f ) ;
2013-09-25 11:30:06 +00:00
phase . Set ( 0.f ) ;
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleProperties : : Floatify ( )
2013-09-21 14:48:28 +00:00
{
2013-09-27 14:06:25 +00:00
bouncyness / = 1000.f ;
2015-09-12 08:53:23 +00:00
collisionDensity . Floatify ( 1.f ) ;
2013-09-27 13:27:53 +00:00
collisionVertex . Floatify ( 1000.f ) ;
2013-09-27 11:28:38 +00:00
size . Floatify ( 2.f ) ;
stretch . Floatify ( 1000.f ) ;
2013-09-25 11:30:06 +00:00
forceX . Floatify ( 100.f ) ;
forceY . Floatify ( 100.f ) ;
2013-09-21 14:48:28 +00:00
speedDampingX . Floatify ( 1000.f ) ;
speedDampingY . Floatify ( 1000.f ) ;
colorR . Floatify ( 255.f ) ;
colorG . Floatify ( 255.f ) ;
colorB . Floatify ( 255.f ) ;
colorAlpha . Floatify ( 255.f ) ;
2013-09-25 11:30:06 +00:00
rotation . Floatify ( 180.0f / ( float ) M_PI ) ;
phase . Floatify ( 1.f ) ;
2013-09-21 14:48:28 +00:00
hasConstantColor = colorR . IsConstant ( ) & & colorG . IsConstant ( ) & & colorB . IsConstant ( ) & & colorAlpha . IsConstant ( ) ;
}
2013-12-17 20:40:40 +00:00
void C4ParticleProperties : : Set ( C4PropList * dataSource )
2013-09-21 14:48:28 +00:00
{
if ( ! dataSource ) return ;
2013-12-07 18:41:34 +00:00
C4PropList : : Iterator iter = dataSource - > begin ( ) , end = dataSource - > end ( ) ;
2013-09-21 14:48:28 +00:00
2013-12-07 18:41:34 +00:00
for ( ; iter ! = end ; + + iter )
2013-09-21 14:48:28 +00:00
{
2013-12-07 18:41:34 +00:00
const C4Property * p = * iter ;
C4String * key = p - > Key ;
2013-09-21 14:48:28 +00:00
assert ( key & & " PropList returns non-string as key " ) ;
2013-12-07 18:41:34 +00:00
const C4Value & property = p - > Value ;
2013-09-21 14:48:28 +00:00
if ( & Strings . P [ P_R ] = = key )
{
colorR . Set ( property ) ;
}
else if ( & Strings . P [ P_G ] = = key )
{
colorG . Set ( property ) ;
}
else if ( & Strings . P [ P_B ] = = key )
{
colorB . Set ( property ) ;
}
else if ( & Strings . P [ P_Alpha ] = = key )
{
colorAlpha . Set ( property ) ;
}
else if ( & Strings . P [ P_ForceX ] = = key )
{
forceX . Set ( property ) ;
}
else if ( & Strings . P [ P_ForceY ] = = key )
{
forceY . Set ( property ) ;
}
else if ( & Strings . P [ P_DampingX ] = = key )
{
speedDampingX . Set ( property ) ;
}
else if ( & Strings . P [ P_DampingY ] = = key )
{
speedDampingY . Set ( property ) ;
}
else if ( & Strings . P [ P_Size ] = = key )
{
size . Set ( property ) ;
}
2013-09-27 11:28:38 +00:00
else if ( & Strings . P [ P_Stretch ] = = key )
{
stretch . Set ( property ) ;
}
2013-09-24 18:36:49 +00:00
else if ( & Strings . P [ P_Rotation ] = = key )
{
rotation . Set ( property ) ;
}
2013-09-24 19:15:24 +00:00
else if ( & Strings . P [ P_BlitMode ] = = key )
{
// needs to be constant
2013-10-08 23:28:58 +00:00
blitMode = ( uint32_t ) property . getInt ( ) ;
2013-09-24 19:15:24 +00:00
}
2013-10-18 10:33:31 +00:00
else if ( & Strings . P [ P_Attach ] = = key )
{
// needs to be constant
attachment = ( uint32_t ) property . getInt ( ) ;
}
2013-09-25 11:30:06 +00:00
else if ( & Strings . P [ P_Phase ] = = key )
{
phase . Set ( property ) ;
}
2013-09-27 13:27:53 +00:00
else if ( & Strings . P [ P_CollisionVertex ] = = key )
{
collisionVertex . Set ( property ) ;
2013-10-07 22:08:15 +00:00
if ( property . GetType ( ) ! = C4V_Nil )
hasCollisionVertex = true ;
2013-09-27 13:27:53 +00:00
}
2015-09-12 08:53:23 +00:00
else if ( & Strings . P [ P_CollisionDensity ] = = key )
{
collisionDensity . Set ( property ) ;
}
2013-09-27 14:06:25 +00:00
else if ( & Strings . P [ P_OnCollision ] = = key )
{
SetCollisionFunc ( property ) ;
}
}
2013-10-08 23:28:58 +00:00
2013-09-27 14:06:25 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleProperties : : SetCollisionFunc ( const C4Value & source )
2013-09-27 14:06:25 +00:00
{
C4ValueArray * valueArray ;
if ( ! ( valueArray = source . getArray ( ) ) ) return ;
int arraySize = valueArray - > GetSize ( ) ;
if ( arraySize < 1 ) return ;
int type = ( * valueArray ) [ 0 ] . getInt ( ) ;
switch ( type )
{
case C4PC_Die :
2013-12-17 20:40:40 +00:00
collisionCallback = & C4ParticleProperties : : CollisionDie ;
2013-09-27 14:06:25 +00:00
break ;
case C4PC_Bounce :
2013-12-17 20:40:40 +00:00
collisionCallback = & C4ParticleProperties : : CollisionBounce ;
2013-09-27 14:06:25 +00:00
bouncyness = 1.f ;
if ( arraySize > = 2 )
bouncyness = ( ( float ) ( * valueArray ) [ 1 ] . getInt ( ) ) ;
break ;
2013-10-19 12:50:43 +00:00
case C4PC_Stop :
2013-12-17 20:40:40 +00:00
collisionCallback = & C4ParticleProperties : : CollisionStop ;
2013-10-19 12:50:43 +00:00
break ;
2013-09-27 14:06:25 +00:00
default :
assert ( false ) ;
break ;
2013-09-21 14:48:28 +00:00
}
}
2013-12-17 20:40:40 +00:00
bool C4ParticleProperties : : CollisionBounce ( C4Particle * forParticle )
2013-09-27 14:06:25 +00:00
{
forParticle - > currentSpeedX = - forParticle - > currentSpeedX * bouncyness ;
forParticle - > currentSpeedY = - forParticle - > currentSpeedY * bouncyness ;
return true ;
}
2013-12-17 20:40:40 +00:00
bool C4ParticleProperties : : CollisionStop ( C4Particle * forParticle )
2013-10-19 12:50:43 +00:00
{
forParticle - > currentSpeedX = 0.f ;
forParticle - > currentSpeedY = 0.f ;
return true ;
}
2013-12-17 20:40:40 +00:00
void C4Particle : : Init ( )
2013-09-21 14:48:28 +00:00
{
2013-10-07 22:08:15 +00:00
currentSpeedX = currentSpeedY = 0.f ;
2013-09-21 14:48:28 +00:00
positionX = positionY = 0.f ;
lifetime = startingLifetime = 5.f * 38.f ;
}
2013-12-17 20:40:40 +00:00
bool C4Particle : : Exec ( C4Object * obj , float timeDelta , C4ParticleDef * sourceDef )
2013-09-21 14:48:28 +00:00
{
// die of old age? :<
2013-09-21 15:22:46 +00:00
lifetime - = timeDelta ;
2013-10-22 14:21:31 +00:00
// check only if we had a maximum lifetime to begin with (for permanent particles)
if ( startingLifetime > 0.f )
{
if ( lifetime < = 0.f ) return false ;
}
2013-09-21 14:48:28 +00:00
// movement
float currentForceX = properties . forceX . GetValue ( this ) ;
float currentForceY = properties . forceY . GetValue ( this ) ;
2013-09-21 15:22:46 +00:00
2013-09-21 14:48:28 +00:00
currentSpeedX + = currentForceX ;
currentSpeedY + = currentForceY ;
if ( currentSpeedX ! = 0.f | | currentSpeedY ! = 0.f )
{
float currentDampingX = properties . speedDampingX . GetValue ( this ) ;
float currentDampingY = properties . speedDampingY . GetValue ( this ) ;
2013-09-27 13:27:53 +00:00
float size = properties . size . GetValue ( this ) ;
2013-09-21 14:48:28 +00:00
currentSpeedX * = currentDampingX ;
currentSpeedY * = currentDampingY ;
2013-09-27 13:27:53 +00:00
// move & collision check
// note: accessing Landscape.GetDensity here is not protected by locks
// it is assumed that the particle system is cleaned up before, f.e., the landscape memory is freed
2013-10-01 16:15:55 +00:00
bool collided = false ;
if ( properties . hasCollisionVertex )
2013-09-27 13:27:53 +00:00
{
2013-10-01 16:15:55 +00:00
float collisionPoint = properties . collisionVertex . GetValue ( this ) ;
float size_x = ( currentSpeedX > 0.f ? size : - size ) * 0.5f * collisionPoint ;
float size_y = ( currentSpeedY > 0.f ? size : - size ) * 0.5f * collisionPoint ;
2015-09-12 08:53:23 +00:00
float density = static_cast < float > ( GBackDensity ( positionX + size_x + timeDelta * currentSpeedX , positionY + size_y + timeDelta * currentSpeedY ) ) ;
if ( density + 0.5f > = properties . collisionDensity . GetValue ( this ) ) // Small offset against floating point insanities.
2013-10-01 16:15:55 +00:00
{
// exec collision func
if ( properties . collisionCallback ! = 0 & & ! ( properties . * properties . collisionCallback ) ( this ) ) return false ;
collided = true ;
}
2013-09-27 13:27:53 +00:00
}
2013-10-01 16:15:55 +00:00
if ( ! collided )
2013-09-27 13:27:53 +00:00
{
positionX + = timeDelta * currentSpeedX ;
positionY + = timeDelta * currentSpeedY ;
}
drawingData . SetPosition ( positionX , positionY , size , properties . rotation . GetValue ( this ) , properties . stretch . GetValue ( this ) ) ;
2013-09-21 15:22:46 +00:00
2013-09-21 14:48:28 +00:00
}
2013-09-27 11:28:38 +00:00
else if ( ! properties . size . IsConstant ( ) | | ! properties . rotation . IsConstant ( ) | | ! properties . stretch . IsConstant ( ) )
2013-09-21 14:48:28 +00:00
{
2013-09-27 11:28:38 +00:00
drawingData . SetPosition ( positionX , positionY , properties . size . GetValue ( this ) , properties . rotation . GetValue ( this ) , properties . stretch . GetValue ( this ) ) ;
2013-09-21 14:48:28 +00:00
}
// adjust color
if ( ! properties . hasConstantColor )
{
drawingData . SetColor ( properties . colorR . GetValue ( this ) , properties . colorG . GetValue ( this ) , properties . colorB . GetValue ( this ) , properties . colorAlpha . GetValue ( this ) ) ;
}
2013-09-25 11:30:06 +00:00
int currentPhase = ( int ) ( properties . phase . GetValue ( this ) + 0.5f ) ;
if ( currentPhase ! = drawingData . phase )
drawingData . SetPhase ( currentPhase , sourceDef ) ;
2013-09-21 14:48:28 +00:00
return true ;
}
2013-12-17 20:40:40 +00:00
void C4ParticleChunk : : Clear ( )
2013-09-21 14:48:28 +00:00
{
2013-10-08 23:28:58 +00:00
for ( size_t i = 0 ; i < particleCount ; + + i )
2013-09-21 14:48:28 +00:00
{
delete particles [ i ] ;
}
particleCount = 0 ;
particles . clear ( ) ;
vertexCoordinates . clear ( ) ;
2013-12-18 16:50:30 +00:00
if ( ! Particles . useBufferObjectWorkaround )
ClearBufferObjects ( ) ;
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleChunk : : DeleteAndReplaceParticle ( size_t indexToReplace , size_t indexFrom )
2013-09-21 14:48:28 +00:00
{
2013-12-17 20:40:40 +00:00
C4Particle * oldParticle = particles [ indexToReplace ] ;
2013-09-21 15:22:46 +00:00
2013-10-08 23:28:58 +00:00
// try to replace the soon-to-be empty slot in the array
if ( indexFrom ! = indexToReplace ) // false when "replacing" the last one
2013-09-21 14:48:28 +00:00
{
2013-12-17 20:40:40 +00:00
std : : copy ( & vertexCoordinates [ indexFrom * C4Particle : : DrawingData : : vertexCountPerParticle ] , & vertexCoordinates [ indexFrom * C4Particle : : DrawingData : : vertexCountPerParticle ] + C4Particle : : DrawingData : : vertexCountPerParticle , & vertexCoordinates [ indexToReplace * C4Particle : : DrawingData : : vertexCountPerParticle ] ) ;
2013-10-08 23:28:58 +00:00
particles [ indexToReplace ] = particles [ indexFrom ] ;
2013-12-17 20:40:40 +00:00
particles [ indexToReplace ] - > drawingData . SetPointer ( & vertexCoordinates [ indexToReplace * C4Particle : : DrawingData : : vertexCountPerParticle ] ) ;
2013-09-21 14:48:28 +00:00
}
delete oldParticle ;
}
2013-12-17 20:40:40 +00:00
bool C4ParticleChunk : : Exec ( C4Object * obj , float timeDelta )
2013-09-21 14:48:28 +00:00
{
2013-10-08 23:28:58 +00:00
for ( size_t i = 0 ; i < particleCount ; + + i )
2013-09-21 14:48:28 +00:00
{
2013-09-25 11:30:06 +00:00
if ( ! particles [ i ] - > Exec ( obj , timeDelta , sourceDefinition ) )
2013-09-21 14:48:28 +00:00
{
2013-10-08 23:28:58 +00:00
DeleteAndReplaceParticle ( i , particleCount - 1 ) ;
2013-09-21 14:48:28 +00:00
- - particleCount ;
}
}
return particleCount > 0 ;
}
2014-01-11 14:44:40 +00:00
# if defined(__APPLE__)
# undef glGenVertexArrays
# undef glBindVertexArray
# undef glDeleteVertexArrays
# define glGenVertexArrays glGenVertexArraysAPPLE
# define glBindVertexArray glBindVertexArrayAPPLE
# define glDeleteVertexArrays glDeleteVertexArraysAPPLE
# endif
2015-12-20 23:46:28 +00:00
void C4ParticleChunk : : Draw ( C4TargetFacet cgo , C4Object * obj , C4ShaderCall & call , int texUnit , const StdProjectionMatrix & modelview )
2013-09-21 14:48:28 +00:00
{
if ( particleCount = = 0 ) return ;
2013-12-17 20:40:40 +00:00
const int stride = sizeof ( C4Particle : : DrawingData : : Vertex ) ;
2013-09-21 14:48:28 +00:00
assert ( sourceDefinition & & " No source definition assigned to particle chunk. " ) ;
2015-01-18 21:09:47 +00:00
C4TexRef * textureRef = & sourceDefinition - > Gfx . GetFace ( ) . textures [ 0 ] ;
2013-09-21 14:48:28 +00:00
assert ( textureRef ! = 0 & & " Particle definition had no texture assigned. " ) ;
2013-10-18 10:33:31 +00:00
// use a relative offset?
2015-12-20 06:37:36 +00:00
// (note the normal matrix is unaffected by this)
2013-10-18 10:33:31 +00:00
if ( ( attachment & C4ATTACH_MoveRelative ) & & ( obj ! = 0 ) )
{
2015-12-20 23:46:28 +00:00
StdProjectionMatrix new_modelview ( modelview ) ;
2015-12-20 06:37:36 +00:00
Translate ( new_modelview , fixtof ( obj - > GetFixedX ( ) ) , fixtof ( obj - > GetFixedY ( ) ) , 0.0f ) ;
call . SetUniformMatrix4x4 ( C4SSU_ModelViewMatrix , new_modelview ) ;
}
else
{
call . SetUniformMatrix4x4 ( C4SSU_ModelViewMatrix , modelview ) ;
2013-10-18 10:33:31 +00:00
}
2015-01-17 10:13:08 +00:00
// enable additive blending for particles with that blit mode
glBlendFunc ( GL_SRC_ALPHA , ( blitMode & C4GFXBLIT_ADDITIVE ) ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA ) ;
2014-11-25 16:40:10 +00:00
glActiveTexture ( texUnit ) ;
2013-09-21 14:48:28 +00:00
glBindTexture ( GL_TEXTURE_2D , textureRef - > texName ) ;
2013-12-18 16:50:30 +00:00
if ( ! Particles . useBufferObjectWorkaround )
{
// generate the buffer as necessary
if ( drawingDataVertexBufferObject = = 0 )
{
// clear up old data
ClearBufferObjects ( ) ;
// generate new buffer objects
glGenBuffers ( 1 , & drawingDataVertexBufferObject ) ;
assert ( drawingDataVertexBufferObject ! = 0 & & " Could not generate OpenGL buffer object. " ) ;
// generate new vertex arrays object
glGenVertexArrays ( 1 , & drawingDataVertexArraysObject ) ;
assert ( drawingDataVertexArraysObject ! = 0 & & " Could not generate OpenGL vertex arrays object. " ) ;
// set up the vertex array structure once
glBindVertexArray ( drawingDataVertexArraysObject ) ;
glBindBuffer ( GL_ARRAY_BUFFER , drawingDataVertexBufferObject ) ;
2015-03-25 22:07:50 +00:00
# ifdef GL_KHR_debug
2015-03-25 20:54:31 +00:00
if ( glObjectLabel )
2015-03-25 22:07:50 +00:00
{
glObjectLabel ( GL_VERTEX_ARRAY , drawingDataVertexArraysObject , - 1 , " <particles>/VAO " ) ;
2015-03-25 20:54:31 +00:00
glObjectLabel ( GL_BUFFER , drawingDataVertexBufferObject , - 1 , " <particles>/VBO " ) ;
2015-03-25 22:07:50 +00:00
}
# endif
2015-03-25 20:54:31 +00:00
2013-12-18 16:50:30 +00:00
glEnableClientState ( GL_VERTEX_ARRAY ) ;
glEnableClientState ( GL_COLOR_ARRAY ) ;
glEnableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
glVertexPointer ( 2 , GL_FLOAT , stride , reinterpret_cast < GLvoid * > ( offsetof ( C4Particle : : DrawingData : : Vertex , x ) ) ) ;
glTexCoordPointer ( 2 , GL_FLOAT , stride , reinterpret_cast < GLvoid * > ( offsetof ( C4Particle : : DrawingData : : Vertex , u ) ) ) ;
glColorPointer ( 4 , GL_FLOAT , stride , reinterpret_cast < GLvoid * > ( offsetof ( C4Particle : : DrawingData : : Vertex , r ) ) ) ;
glBindVertexArray ( 0 ) ;
}
assert ( ( drawingDataVertexArraysObject ! = 0 ) & & " No vertex arrays object has been created yet. " ) ;
assert ( ( drawingDataVertexBufferObject ! = 0 ) & & " No buffer object has been created yet. " ) ;
// bind the VBO and push the new vertex data
// this has to be done before binding the vertex arrays object
glBindBuffer ( GL_ARRAY_BUFFER , drawingDataVertexBufferObject ) ;
glBufferData ( GL_ARRAY_BUFFER , 4 * sizeof ( C4Particle : : DrawingData : : Vertex ) * particleCount , & vertexCoordinates [ 0 ] , GL_DYNAMIC_DRAW ) ;
// bind VAO and set correct state
glBindVertexArray ( drawingDataVertexArraysObject ) ;
}
else
{
glVertexPointer ( 2 , GL_FLOAT , stride , & ( vertexCoordinates [ 0 ] . x ) ) ;
glTexCoordPointer ( 2 , GL_FLOAT , stride , & ( vertexCoordinates [ 0 ] . u ) ) ;
glColorPointer ( 4 , GL_FLOAT , stride , & ( vertexCoordinates [ 0 ] . r ) ) ;
}
2013-10-18 10:33:31 +00:00
2013-12-17 20:40:40 +00:00
if ( ! Particles . usePrimitiveRestartIndexWorkaround )
2013-11-29 21:42:04 +00:00
{
2013-12-17 20:40:40 +00:00
glDrawElements ( GL_TRIANGLE_STRIP , static_cast < GLsizei > ( 5 * particleCount ) , GL_UNSIGNED_INT , : : Particles . GetPrimitiveRestartArray ( ) ) ;
2013-11-29 21:42:04 +00:00
}
else
{
2013-12-17 20:40:40 +00:00
glMultiDrawElements ( GL_TRIANGLE_STRIP , : : Particles . GetMultiDrawElementsCountArray ( ) , GL_UNSIGNED_INT , const_cast < const GLvoid * * > ( : : Particles . GetMultiDrawElementsIndexArray ( ) ) , static_cast < GLsizei > ( particleCount ) ) ;
2013-11-29 21:42:04 +00:00
}
2013-12-18 16:50:30 +00:00
// reset buffer data
if ( ! Particles . useBufferObjectWorkaround )
{
glBindVertexArray ( 0 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
}
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
bool C4ParticleChunk : : IsOfType ( C4ParticleDef * def , uint32_t _blitMode , uint32_t _attachment ) const
2013-09-21 14:48:28 +00:00
{
2013-10-18 10:33:31 +00:00
return def = = sourceDefinition & & blitMode = = _blitMode & & attachment = = _attachment ;
2013-09-21 14:48:28 +00:00
}
2013-12-18 16:50:30 +00:00
void C4ParticleChunk : : ClearBufferObjects ( )
{
if ( drawingDataVertexBufferObject ! = 0 ) // the value 0 as a buffer index is reserved and will never be returned by glGenBuffers
glDeleteBuffers ( 1 , & drawingDataVertexBufferObject ) ;
if ( drawingDataVertexArraysObject ! = 0 )
glDeleteVertexArrays ( 1 , & drawingDataVertexArraysObject ) ;
drawingDataVertexArraysObject = 0 ;
drawingDataVertexBufferObject = 0 ;
}
2013-12-17 20:40:40 +00:00
void C4ParticleChunk : : ReserveSpace ( uint32_t forAmount )
2013-12-07 18:41:34 +00:00
{
uint32_t newSize = static_cast < uint32_t > ( particleCount ) + forAmount + 1 ;
2013-12-17 20:40:40 +00:00
: : Particles . PreparePrimitiveRestartIndices ( newSize ) ;
2013-12-07 18:41:34 +00:00
if ( particles . capacity ( ) < newSize )
particles . reserve ( std : : max < uint32_t > ( newSize , particles . capacity ( ) * 2 ) ) ;
// resizing the points vector is relatively costly, hopefully we only do it rarely
2013-12-17 20:40:40 +00:00
while ( vertexCoordinates . capacity ( ) < = newSize * C4Particle : : DrawingData : : vertexCountPerParticle )
2013-12-07 18:41:34 +00:00
{
2013-12-17 20:40:40 +00:00
vertexCoordinates . reserve ( std : : max < uint32_t > ( C4Particle : : DrawingData : : vertexCountPerParticle * newSize , vertexCoordinates . capacity ( ) * 2 ) ) ;
2013-12-07 18:41:34 +00:00
// update all existing particles' pointers..
for ( size_t i = 0 ; i < particleCount ; + + i )
2013-12-17 20:40:40 +00:00
particles [ i ] - > drawingData . SetPointer ( & vertexCoordinates [ i * C4Particle : : DrawingData : : vertexCountPerParticle ] ) ;
2013-12-07 18:41:34 +00:00
}
}
2013-12-17 20:40:40 +00:00
C4Particle * C4ParticleChunk : : AddNewParticle ( )
2013-09-21 14:48:28 +00:00
{
2013-10-08 23:28:58 +00:00
size_t currentIndex = particleCount + + ;
2013-09-21 14:48:28 +00:00
if ( currentIndex < particles . size ( ) )
{
2013-12-17 20:40:40 +00:00
particles [ currentIndex ] = new C4Particle ( ) ;
2013-09-21 14:48:28 +00:00
}
else
{
2013-12-17 20:40:40 +00:00
particles . push_back ( new C4Particle ( ) ) ;
vertexCoordinates . resize ( vertexCoordinates . size ( ) + C4Particle : : DrawingData : : vertexCountPerParticle ) ;
2013-09-21 14:48:28 +00:00
}
2013-12-07 18:41:34 +00:00
2013-12-17 20:40:40 +00:00
C4Particle * newParticle = particles [ currentIndex ] ;
newParticle - > drawingData . SetPointer ( & vertexCoordinates [ currentIndex * C4Particle : : DrawingData : : vertexCountPerParticle ] , true ) ;
2013-09-21 14:48:28 +00:00
return newParticle ;
}
2013-12-17 20:40:40 +00:00
void C4ParticleList : : Exec ( float timeDelta )
2013-09-21 14:48:28 +00:00
{
2013-09-21 15:22:46 +00:00
if ( particleChunks . empty ( ) ) return ;
accessMutex . Enter ( ) ;
2014-07-20 17:20:28 +00:00
for ( std : : list < C4ParticleChunk * > : : iterator iter = particleChunks . begin ( ) ; iter ! = particleChunks . end ( ) ; + + iter )
2013-09-21 14:48:28 +00:00
{
2013-12-22 16:23:47 +00:00
C4ParticleChunk * chunk = * iter ;
2014-07-20 17:20:28 +00:00
chunk - > Exec ( targetObject , timeDelta ) ;
2013-09-21 14:48:28 +00:00
}
2013-09-21 15:22:46 +00:00
accessMutex . Leave ( ) ;
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleList : : Draw ( C4TargetFacet cgo , C4Object * obj )
2013-09-21 14:48:28 +00:00
{
if ( particleChunks . empty ( ) ) return ;
pDraw - > DeactivateBlitModulation ( ) ;
pDraw - > ResetBlitMode ( ) ;
2013-09-24 19:15:24 +00:00
2013-12-17 20:40:40 +00:00
if ( ! Particles . usePrimitiveRestartIndexWorkaround )
2013-11-29 21:42:04 +00:00
{
glPrimitiveRestartIndex ( 0xffffffff ) ;
glEnable ( GL_PRIMITIVE_RESTART ) ;
}
2013-09-21 14:48:28 +00:00
2014-11-25 16:40:10 +00:00
// enable shader
C4ShaderCall call ( pGL - > GetSpriteShader ( true , false , false ) ) ;
// apply zoom and upload shader uniforms
2015-12-20 23:46:28 +00:00
StdProjectionMatrix modelview = StdProjectionMatrix : : Identity ( ) ;
2015-12-20 06:37:36 +00:00
pGL - > SetupMultiBlt ( call , NULL , 0 , 0 , 0 , 0 , & modelview ) ;
// go to correct output position (note the normal matrix is unaffected
// by this)
Translate ( modelview , cgo . X - cgo . TargetX , cgo . Y - cgo . TargetY , 0.0f ) ;
2014-11-25 16:40:10 +00:00
// allocate texture unit for particle texture, and remember allocated
// texture unit. Will be used for each particle chunk to bind
// their texture to this unit.
2015-09-17 00:52:34 +00:00
const GLint texUnit = call . AllocTexUnit ( C4SSU_BaseTex ) ;
2014-11-25 16:40:10 +00:00
// Texture coordinates are always associated to texture unit 0, since
// there is only one set of texture coordinates
glClientActiveTexture ( GL_TEXTURE0 ) ;
2013-09-21 14:48:28 +00:00
2013-12-18 16:50:30 +00:00
if ( Particles . useBufferObjectWorkaround )
{
glEnableClientState ( GL_VERTEX_ARRAY ) ;
glEnableClientState ( GL_COLOR_ARRAY ) ;
glEnableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
}
2013-09-21 14:48:28 +00:00
2013-09-21 15:22:46 +00:00
accessMutex . Enter ( ) ;
2014-07-20 17:20:28 +00:00
for ( std : : list < C4ParticleChunk * > : : iterator iter = particleChunks . begin ( ) ; iter ! = particleChunks . end ( ) ; )
2013-09-21 14:48:28 +00:00
{
2014-07-20 17:20:28 +00:00
if ( ( * iter ) - > IsEmpty ( ) )
{
delete * iter ;
iter = particleChunks . erase ( iter ) ;
lastAccessedChunk = 0 ;
}
else
{
2015-12-20 06:37:36 +00:00
( * iter ) - > Draw ( cgo , obj , call , texUnit , modelview ) ;
2014-07-20 17:20:28 +00:00
+ + iter ;
}
2013-09-21 14:48:28 +00:00
}
2013-09-21 15:22:46 +00:00
accessMutex . Leave ( ) ;
2013-12-18 16:50:30 +00:00
if ( Particles . useBufferObjectWorkaround )
{
glDisableClientState ( GL_VERTEX_ARRAY ) ;
glDisableClientState ( GL_COLOR_ARRAY ) ;
glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
}
2013-09-21 14:48:28 +00:00
2013-12-17 20:40:40 +00:00
if ( ! Particles . usePrimitiveRestartIndexWorkaround )
2013-11-29 21:42:04 +00:00
{
glDisable ( GL_PRIMITIVE_RESTART ) ;
}
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleList : : Clear ( )
2013-09-25 11:30:06 +00:00
{
accessMutex . Enter ( ) ;
2013-12-22 16:23:47 +00:00
for ( std : : list < C4ParticleChunk * > : : iterator iter = particleChunks . begin ( ) ; iter ! = particleChunks . end ( ) ; + + iter )
delete * iter ;
2013-09-25 11:30:06 +00:00
particleChunks . clear ( ) ;
2013-12-22 16:23:47 +00:00
2013-12-28 20:30:45 +00:00
if ( targetObject )
{
if ( this = = targetObject - > FrontParticles ) targetObject - > FrontParticles = NULL ;
else if ( this = = targetObject - > BackParticles ) targetObject - > BackParticles = NULL ;
}
else
if ( this = = : : Particles . globalParticles ) : : Particles . globalParticles = NULL ;
2013-09-25 11:30:06 +00:00
accessMutex . Leave ( ) ;
}
2013-12-17 20:40:40 +00:00
C4ParticleChunk * C4ParticleList : : GetFittingParticleChunk ( C4ParticleDef * def , uint32_t blitMode , uint32_t attachment , bool alreadyLocked )
2013-09-21 14:48:28 +00:00
{
2013-12-02 19:05:45 +00:00
if ( ! alreadyLocked )
accessMutex . Enter ( ) ;
2013-09-21 15:22:46 +00:00
2013-09-21 14:48:28 +00:00
// if not cached, find correct chunk in list
2013-12-17 20:40:40 +00:00
C4ParticleChunk * chunk = 0 ;
2013-10-18 10:33:31 +00:00
if ( lastAccessedChunk & & lastAccessedChunk - > IsOfType ( def , blitMode , attachment ) )
2013-09-21 14:48:28 +00:00
chunk = lastAccessedChunk ;
else
{
2013-12-22 16:23:47 +00:00
for ( std : : list < C4ParticleChunk * > : : iterator iter = particleChunks . begin ( ) ; iter ! = particleChunks . end ( ) ; + + iter )
2013-09-21 14:48:28 +00:00
{
2013-12-22 16:23:47 +00:00
C4ParticleChunk * current = * iter ;
if ( ! current - > IsOfType ( def , blitMode , attachment ) ) continue ;
chunk = current ;
2013-09-21 14:48:28 +00:00
break ;
}
}
// add new chunk?
if ( ! chunk )
{
2013-12-22 16:23:47 +00:00
particleChunks . push_back ( new C4ParticleChunk ( ) ) ;
chunk = particleChunks . back ( ) ;
2013-09-21 14:48:28 +00:00
chunk - > sourceDefinition = def ;
2013-09-24 19:15:24 +00:00
chunk - > blitMode = blitMode ;
2013-10-18 10:33:31 +00:00
chunk - > attachment = attachment ;
2013-09-21 14:48:28 +00:00
}
assert ( chunk & & " No suitable particle chunk could be found or created. " ) ;
lastAccessedChunk = chunk ;
2013-09-21 15:22:46 +00:00
2013-12-02 19:05:45 +00:00
if ( ! alreadyLocked )
accessMutex . Leave ( ) ;
2013-12-07 18:41:34 +00:00
return chunk ;
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleSystem : : CalculationThread : : Execute ( )
2013-09-21 15:22:46 +00:00
{
2013-12-17 20:40:40 +00:00
Particles . ExecuteCalculation ( ) ;
2013-09-21 15:22:46 +00:00
}
2013-12-17 20:40:40 +00:00
C4ParticleSystem : : C4ParticleSystem ( ) : frameCounterAdvancedEvent ( false )
2013-11-29 21:42:04 +00:00
{
currentSimulationTime = 0 ;
globalParticles = 0 ;
usePrimitiveRestartIndexWorkaround = false ;
2013-12-18 16:50:30 +00:00
useBufferObjectWorkaround = false ;
2013-11-29 21:42:04 +00:00
}
2013-12-17 20:40:40 +00:00
C4ParticleSystem : : ~ C4ParticleSystem ( )
2013-10-11 22:04:33 +00:00
{
2013-12-17 17:09:14 +00:00
Clear ( ) ;
2013-10-11 22:34:21 +00:00
calculationThread . SignalStop ( ) ;
CalculateNextStep ( ) ;
2013-11-29 21:42:04 +00:00
for ( std : : vector < uint32_t * > : : iterator iter = multiDrawElementsIndexArray . begin ( ) ; iter ! = multiDrawElementsIndexArray . end ( ) ; + + iter )
2014-12-06 20:38:32 +00:00
delete [ ] ( * iter ) ;
2013-11-29 21:42:04 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleSystem : : DoInit ( )
2013-11-29 21:42:04 +00:00
{
// we use features that are only supported from 3.1 upwards. Check whether the graphics card supports that and - if not - use workarounds
2013-11-30 12:43:18 +00:00
if ( ! GLEW_VERSION_3_1 | | ( glPrimitiveRestartIndex = = 0 ) )
2013-11-29 21:42:04 +00:00
{
usePrimitiveRestartIndexWorkaround = true ;
2013-12-02 18:39:29 +00:00
LogSilent ( " WARNING (particle system): Your graphics card does not support glPrimitiveRestartIndex - a (slower) fallback will be used! " ) ;
2013-11-29 21:42:04 +00:00
}
2013-12-18 16:50:30 +00:00
assert ( glGenBuffers ! = 0 & & " Your graphics card does not seem to support buffer objects. " ) ;
useBufferObjectWorkaround = false ;
2015-03-22 08:52:34 +00:00
2015-03-26 14:22:54 +00:00
# ifndef USE_WIN32_WINDOWS
2015-03-22 08:52:34 +00:00
// Every window in developers' mode has an own OpenGL context at the moment. Certain objects are not shared between contexts.
// In that case we can just use the slower workaround without VBAs and VBOs to allow the developer to view particles in every viewport.
// The best solution would obviously be to make all windows use a single OpenGL context. This has to be considered as a workaround.
if ( Application . isEditor )
useBufferObjectWorkaround = true ;
2015-03-26 14:22:54 +00:00
# endif
2013-10-11 22:04:33 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleSystem : : ExecuteCalculation ( )
2013-09-21 15:22:46 +00:00
{
2013-10-07 21:37:01 +00:00
frameCounterAdvancedEvent . WaitFor ( INFINITE ) ;
2013-10-08 23:28:58 +00:00
frameCounterAdvancedEvent . Reset ( ) ;
2013-10-07 21:37:01 +00:00
2013-09-21 15:22:46 +00:00
int gameTime = Game . FrameCounter ;
if ( currentSimulationTime < gameTime )
{
float timeDelta = 1.f ;
if ( currentSimulationTime ! = 0 )
timeDelta = ( float ) ( gameTime - currentSimulationTime ) ;
currentSimulationTime = gameTime ;
particleListAccessMutex . Enter ( ) ;
2013-12-17 20:40:40 +00:00
for ( std : : list < C4ParticleList > : : iterator iter = particleLists . begin ( ) ; iter ! = particleLists . end ( ) ; + + iter )
2013-09-21 15:22:46 +00:00
{
iter - > Exec ( timeDelta ) ;
}
particleListAccessMutex . Leave ( ) ;
}
}
2014-03-31 14:30:40 +00:00
# else // ifdef USE_CONSOLE
void C4ParticleSystem : : DoInit ( ) { }
2013-10-18 16:52:04 +00:00
# endif
2013-09-21 15:22:46 +00:00
2013-12-17 20:40:40 +00:00
C4ParticleList * C4ParticleSystem : : GetNewParticleList ( C4Object * forObject )
2013-09-21 15:22:46 +00:00
{
2013-10-18 16:52:04 +00:00
# ifdef USE_CONSOLE
return 0 ;
# else
2013-12-17 20:40:40 +00:00
C4ParticleList * newList = 0 ;
2013-09-21 15:22:46 +00:00
particleListAccessMutex . Enter ( ) ;
2013-10-08 23:28:58 +00:00
particleLists . emplace_back ( forObject ) ;
2013-09-21 15:22:46 +00:00
newList = & particleLists . back ( ) ;
particleListAccessMutex . Leave ( ) ;
return newList ;
2013-10-18 16:52:04 +00:00
# endif
2013-09-21 15:22:46 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleSystem : : ReleaseParticleList ( C4ParticleList * first , C4ParticleList * second )
2013-09-21 15:22:46 +00:00
{
2013-10-18 16:52:04 +00:00
# ifndef USE_CONSOLE
2013-09-21 15:22:46 +00:00
particleListAccessMutex . Enter ( ) ;
2013-12-17 20:40:40 +00:00
for ( std : : list < C4ParticleList > : : iterator iter = particleLists . begin ( ) ; iter ! = particleLists . end ( ) ; )
2013-09-21 15:22:46 +00:00
{
2013-12-17 20:40:40 +00:00
C4ParticleList * list = & ( * iter ) ;
2013-09-21 15:22:46 +00:00
if ( list = = first | | list = = second )
{
iter = particleLists . erase ( iter ) ;
}
else
{
+ + iter ;
}
}
particleListAccessMutex . Leave ( ) ;
2013-10-18 16:52:04 +00:00
# endif
2013-09-21 15:22:46 +00:00
}
2013-10-18 16:52:04 +00:00
# ifndef USE_CONSOLE
2013-12-17 20:40:40 +00:00
void C4ParticleSystem : : Create ( C4ParticleDef * of_def , C4ParticleValueProvider & x , C4ParticleValueProvider & y , C4ParticleValueProvider & speedX , C4ParticleValueProvider & speedY , C4ParticleValueProvider & lifetime , C4PropList * properties , int amount , C4Object * object )
2013-09-21 14:48:28 +00:00
{
// todo: check amount etc
2013-12-22 17:51:05 +00:00
C4ParticleList * pxList ( 0 ) ;
2013-09-21 15:22:46 +00:00
2013-09-21 14:48:28 +00:00
2013-10-08 23:28:58 +00:00
// initialize the particle properties
// this is done here, because it would also be the right place to implement caching
2013-12-17 20:40:40 +00:00
C4ParticleProperties particleProperties ;
2013-09-24 19:15:24 +00:00
particleProperties . Set ( properties ) ;
2013-10-10 15:19:20 +00:00
speedX . Floatify ( 10.f ) ;
speedY . Floatify ( 10.f ) ;
2013-10-08 23:28:58 +00:00
2013-10-18 10:33:31 +00:00
// position offset that will be added to the particle
float xoff ( 0.f ) , yoff ( 0.f ) ;
// offset only for the drawing position - this is needed so that particles relative to an object work correctly
float drawingOffsetX ( 0.f ) , drawingOffsetY ( 0.f ) ;
if ( object ! = 0 )
{
// for all types of particles add object's offset (mainly for collision etc.)
xoff = object - > GetX ( ) ;
yoff = object - > GetY ( ) ;
if ( particleProperties . attachment & C4ATTACH_MoveRelative )
{
drawingOffsetX = - xoff ;
drawingOffsetY = - yoff ;
2013-10-22 14:21:31 +00:00
// move relative implies that the particle needs to be in the object's particle list (back OR front)
// just select the front particles here - will be overwritten below if necessary
2013-12-22 17:51:05 +00:00
if ( ! ( particleProperties . attachment & C4ATTACH_Front ) & & ! ( particleProperties . attachment & C4ATTACH_Back ) )
particleProperties . attachment | = C4ATTACH_Front ;
2013-10-18 10:33:31 +00:00
}
2013-10-22 14:21:31 +00:00
// figure out particle list to use
2013-12-22 17:51:05 +00:00
if ( particleProperties . attachment & C4ATTACH_Front )
{
if ( ! object - > FrontParticles ) object - > FrontParticles = GetNewParticleList ( object ) ;
pxList = object - > FrontParticles ;
}
else if ( particleProperties . attachment & C4ATTACH_Back )
{
if ( ! object - > BackParticles ) object - > BackParticles = GetNewParticleList ( object ) ;
pxList = object - > BackParticles ;
}
2013-10-18 10:33:31 +00:00
}
2013-12-22 17:51:05 +00:00
// no assigned list implies that we are going to use the global particles
if ( ! pxList )
2013-10-18 10:33:31 +00:00
{
2013-12-22 17:51:05 +00:00
if ( ! globalParticles ) globalParticles = GetNewParticleList ( ) ;
2013-10-18 10:33:31 +00:00
pxList = globalParticles ;
}
2013-12-02 19:05:45 +00:00
// It is necessary to lock the particle list, because we will have it create a particle first that we are going to modify.
// Inbetween creation of the particle and modification, the particle list's calculations should not be executed
// (this could f.e. lead to the particle being removed before it was fully instantiated).
pxList - > Lock ( ) ;
2013-12-07 18:41:34 +00:00
// retrieve the fitting chunk for the particle (note that we tell the particle list, we already locked it)
2013-12-17 20:40:40 +00:00
C4ParticleChunk * chunk = pxList - > GetFittingParticleChunk ( of_def , particleProperties . blitMode , particleProperties . attachment , true ) ;
2013-12-07 18:41:34 +00:00
// set up chunk to be able to contain enough particles
chunk - > ReserveSpace ( static_cast < uint32_t > ( amount ) ) ;
2013-10-10 15:19:20 +00:00
while ( amount - - )
{
2013-10-22 14:21:31 +00:00
if ( x . IsRandom ( ) ) x . RollRandom ( ) ;
if ( y . IsRandom ( ) ) y . RollRandom ( ) ;
2013-10-10 15:19:20 +00:00
if ( speedX . IsRandom ( ) ) speedX . RollRandom ( ) ;
if ( speedY . IsRandom ( ) ) speedY . RollRandom ( ) ;
if ( lifetime . IsRandom ( ) ) lifetime . RollRandom ( ) ;
2013-12-02 19:05:45 +00:00
// create a particle in the fitting chunk (note that we tell the particle list, we already locked it)
2013-12-17 20:40:40 +00:00
C4Particle * particle = chunk - > AddNewParticle ( ) ;
2013-10-10 15:19:20 +00:00
// initialize some more properties
particle - > properties = particleProperties ;
// this will adjust the initial values of the (possibly cached) particle properties
particle - > properties . Floatify ( ) ;
// setup some more non-property attributes of the particle
2013-11-26 20:59:14 +00:00
float lifetime_value = lifetime . GetValue ( particle ) ;
if ( lifetime_value < 0.0f ) lifetime_value = 0.0f ; // negative values not allowed (would crash later); using a value of 0 is most likely visible to the scripter
particle - > lifetime = particle - > startingLifetime = lifetime_value ;
2013-10-10 15:19:20 +00:00
particle - > currentSpeedX = speedX . GetValue ( particle ) ;
particle - > currentSpeedY = speedY . GetValue ( particle ) ;
particle - > drawingData . aspect = of_def - > Aspect ;
2013-10-18 10:33:31 +00:00
particle - > drawingData . SetOffset ( drawingOffsetX , drawingOffsetY ) ;
2013-10-22 14:21:31 +00:00
particle - > SetPosition ( x . GetValue ( particle ) + xoff , y . GetValue ( particle ) + yoff ) ;
2013-10-10 15:19:20 +00:00
particle - > drawingData . SetColor ( particle - > properties . colorR . GetValue ( particle ) , particle - > properties . colorG . GetValue ( particle ) , particle - > properties . colorB . GetValue ( particle ) , particle - > properties . colorAlpha . GetValue ( particle ) ) ;
2013-11-04 12:02:35 +00:00
particle - > drawingData . SetPhase ( ( int ) ( particle - > properties . phase . GetValue ( particle ) + 0.5f ) , of_def ) ;
2013-10-10 15:19:20 +00:00
}
2013-12-02 19:05:45 +00:00
pxList - > Unlock ( ) ;
2013-09-21 14:48:28 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleSystem : : PreparePrimitiveRestartIndices ( uint32_t forAmount )
2013-09-21 14:48:28 +00:00
{
2013-11-29 21:42:04 +00:00
if ( ! usePrimitiveRestartIndexWorkaround )
2013-09-21 14:48:28 +00:00
{
2013-11-29 21:42:04 +00:00
// prepare array with indices, separated by special primitive restart index
const uint32_t PRI = 0xffffffff ;
size_t neededAmount = 5 * forAmount ;
2013-09-21 14:48:28 +00:00
2013-11-29 21:42:04 +00:00
if ( primitiveRestartIndices . size ( ) < neededAmount )
2013-09-21 14:48:28 +00:00
{
2013-11-29 21:42:04 +00:00
uint32_t oldValue = 0 ;
2013-09-21 14:48:28 +00:00
2013-11-29 21:42:04 +00:00
if ( primitiveRestartIndices . size ( ) > 2 )
{
oldValue = primitiveRestartIndices [ primitiveRestartIndices . size ( ) - 1 ] ;
if ( oldValue = = PRI )
oldValue = primitiveRestartIndices [ primitiveRestartIndices . size ( ) - 2 ] ;
+ + oldValue ;
}
size_t oldSize = primitiveRestartIndices . size ( ) ;
primitiveRestartIndices . resize ( neededAmount ) ;
for ( size_t i = oldSize ; i < neededAmount ; + + i )
2013-09-21 14:48:28 +00:00
{
2013-11-29 21:42:04 +00:00
if ( ( ( i + 1 ) % 5 = = 0 ) & & ( i ! = 0 ) )
{
primitiveRestartIndices [ i ] = PRI ;
}
else
{
primitiveRestartIndices [ i ] = oldValue + + ;
}
2013-09-21 14:48:28 +00:00
}
2013-11-29 21:42:04 +00:00
}
}
else
{
// prepare arrays for glMultiDrawElements
if ( multiDrawElementsCountArray . size ( ) < = forAmount )
{
multiDrawElementsCountArray . resize ( forAmount , 4 ) ;
}
if ( multiDrawElementsIndexArray . size ( ) < = forAmount )
{
uint32_t oldSize = multiDrawElementsIndexArray . size ( ) ;
multiDrawElementsIndexArray . resize ( forAmount ) ;
for ( ; oldSize < forAmount ; + + oldSize )
2013-09-21 14:48:28 +00:00
{
2013-11-29 21:42:04 +00:00
multiDrawElementsIndexArray [ oldSize ] = new uint32_t [ 4 ] ;
for ( uint32_t i = 0 ; i < 4 ; + + i )
multiDrawElementsIndexArray [ oldSize ] [ i ] = 4 * oldSize + i ;
2013-09-21 14:48:28 +00:00
}
}
}
}
2013-10-18 16:52:04 +00:00
# endif
2013-09-21 14:48:28 +00:00
2013-12-17 20:40:40 +00:00
void C4ParticleSystem : : Clear ( )
2013-09-21 15:22:46 +00:00
{
2013-10-18 16:52:04 +00:00
# ifndef USE_CONSOLE
2013-09-21 15:22:46 +00:00
currentSimulationTime = 0 ;
2013-12-17 17:09:14 +00:00
ClearAllParticles ( ) ;
# endif
// clear definitions even in console mode
definitions . Clear ( ) ;
}
2013-09-21 15:22:46 +00:00
2013-12-17 20:40:40 +00:00
void C4ParticleSystem : : ClearAllParticles ( )
2013-12-17 17:09:14 +00:00
{
2014-03-31 14:30:40 +00:00
# ifndef USE_CONSOLE
2013-09-21 15:22:46 +00:00
particleListAccessMutex . Enter ( ) ;
particleLists . clear ( ) ;
particleListAccessMutex . Leave ( ) ;
2014-03-31 14:30:40 +00:00
# endif
2013-12-17 17:09:14 +00:00
}
2013-12-17 20:40:40 +00:00
C4ParticleDef * C4ParticleSystemDefinitionList : : GetDef ( const char * name , C4ParticleDef * exclude )
2013-12-17 17:09:14 +00:00
{
# ifndef USE_CONSOLE
// seek list
for ( C4ParticleDef * def = first ; def ! = 0 ; def = def - > next )
if ( def ! = exclude & & def - > Name = = name )
return def ;
2013-10-18 16:52:04 +00:00
# endif
2013-12-17 17:09:14 +00:00
// nothing found
return 0 ;
2013-09-21 15:22:46 +00:00
}
2013-12-17 20:40:40 +00:00
void C4ParticleSystemDefinitionList : : Clear ( )
2013-12-17 17:09:14 +00:00
{
// the particle definitions update the list in their destructor
while ( first )
delete first ;
}
2013-12-17 20:40:40 +00:00
C4ParticleSystem Particles ;