the dynamic particle system now runs the calculations in a different thread

added PV_KeyFrames
stable-5.4
David Dormagen 2013-09-21 17:22:46 +02:00
parent 40b66d1c76
commit 8025e7c2b0
7 changed files with 273 additions and 36 deletions

View File

@ -740,7 +740,6 @@ bool C4Game::Execute() // Returns true if the game is over
EXEC_S_DR( pGlobalEffects->Execute(NULL); , GEStats , "GEEx\0");
EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx")
EXEC_S_DR( Particles.GlobalParticles.Exec(); , PartStat , "ParEx")
EXEC_S_DR( DynamicParticles.globalParticles.Exec(); , DynPartStat , "DParEx")
EXEC_S_DR( MassMover.Execute(); , MassMoverStat , "MMvEx")
EXEC_S_DR( Weather.Execute(); , WeatherStat , "WtrEx")
EXEC_S_DR( Landscape.Execute(); , LandscapeStat , "LdsEx")

View File

@ -262,7 +262,7 @@ void C4Viewport::Draw(C4TargetFacet &cgo0, bool fDrawOverlay)
// draw global dynamic particles
C4ST_STARTNEW(PartStat, "C4Viewport::Draw: Dynamic Particles")
::DynamicParticles.globalParticles.Draw(cgo, NULL);
::DynamicParticles.DrawGlobalParticles(cgo);
C4ST_STOP(PartStat)
// Draw PathFinder

View File

@ -1554,7 +1554,7 @@ static bool FnCreateParticleEx(C4PropList * _this, C4String *name, long x, long
valueSpeedY.Set(speedY);
valueSize.Set(size);
// create
::DynamicParticles.Create(pDef, (float)x, (float)y, valueSpeedX, valueSpeedY, valueSize, (float)lifetime, properties, obj ? (attachment == 1 ? &obj->DynamicBackParticles : &obj->DynamicFrontParticles) : NULL, obj);
::DynamicParticles.Create(pDef, (float)x, (float)y, valueSpeedX, valueSpeedY, valueSize, (float)lifetime, properties, obj ? (attachment == 1 ? obj->DynamicBackParticles : obj->DynamicFrontParticles) : NULL, obj);
// success, even if not created
return true;
}
@ -1636,13 +1636,34 @@ static C4ValueArray* FnPV_Linear(C4PropList * _this, long startValue, long endVa
static C4ValueArray* FnPV_Random(C4PropList * _this, long startValue, long endValue, long rerollInterval)
{
C4ValueArray *pArray = new C4ValueArray(4);
pArray->SetItem(0, C4VInt(C4PV_Linear));
pArray->SetItem(0, C4VInt(C4PV_Random));
pArray->SetItem(1, C4VInt(startValue));
pArray->SetItem(2, C4VInt(endValue));
pArray->SetItem(3, C4VInt(rerollInterval));
return pArray;
}
static C4Value FnPV_KeyFrames(C4PropList * _this, C4Value *pars)
{
C4ValueArray *pArray = new C4ValueArray(C4AUL_MAX_Par);
pArray->SetItem(0, C4VInt(C4PV_KeyFrames));
const int offset = 1;
// Read all parameters
int i = 0;
for (; i < C4AUL_MAX_Par; i++)
{
C4Value Data = *(pars++);
// No data given?
if (Data.GetType() == C4V_Nil) break;
pArray->SetItem(offset + i, C4VInt(Data.getInt()));
}
pArray->SetSize(i + offset);
return C4Value(pArray);
}
static bool FnSetSkyParallax(C4PropList * _this, Nillable<long> iMode, Nillable<long> iParX, Nillable<long> iParY, Nillable<long> iXDir, Nillable<long> iYDir, Nillable<long> iX, Nillable<long> iY)
{
// set all parameters that aren't nil
@ -2486,6 +2507,7 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine)
F(PV_Linear);
F(PV_Random);
// F(PV_KeyFrames); added below
AddFunc(pEngine, "IncinerateLandscape", FnIncinerateLandscape);
AddFunc(pEngine, "GetGravity", FnGetGravity);
@ -2626,6 +2648,7 @@ C4ScriptFnDef C4ScriptGameFnMap[]=
{ "Message", 1, C4V_Bool, { C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnMessage },
{ "AddMessage", 1, C4V_Bool, { C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnAddMessage },
{ "EffectCall", 1, C4V_Any, { C4V_Object ,C4V_PropList,C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnEffectCall },
{ "PV_KeyFrames", 1, C4V_Array, { C4V_Int ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnPV_KeyFrames },
{ NULL, 0, C4V_Nil, { C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil}, 0 }
};

View File

@ -33,8 +33,19 @@ void C4DynamicParticleValueProvider::Floatify(float denominator)
startValue /= denominator;
endValue /= denominator;
if (valueFunction == &C4DynamicParticleValueProvider::Random)
RollRandom();
// special treatment for keyframes
if (valueFunction == &C4DynamicParticleValueProvider::KeyFrames)
{
for (int i = 0; i < keyFrameCount; ++i)
{
keyFrames[2 * i] /= 1000.0f; // even numbers are the time values
keyFrames[2 * i + 1] /= denominator; // odd numbers are the actual values
//LogF("KF is %f @ %f", keyFrames[2 * i + 1], keyFrames[2 * i]);
}
}
else
if (valueFunction == &C4DynamicParticleValueProvider::Random)
RollRandom();
}
void C4DynamicParticleValueProvider::RollRandom()
@ -64,6 +75,31 @@ float C4DynamicParticleValueProvider::Random(C4DynamicParticle *forParticle)
return currentValue;
}
float C4DynamicParticleValueProvider::KeyFrames(C4DynamicParticle *forParticle)
{
float age = forParticle->GetRelativeAge();
if (smoothing == 0) // linear
{
for (int i = 0; i < keyFrameCount; ++i)
{
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);
float value = position * totalRange + y1;
return value;
}
}
return startValue;
}
void C4DynamicParticleValueProvider::Set(float _startValue, float _endValue, C4ParticleValueProviderID what)
{
startValue = _startValue;
@ -79,6 +115,10 @@ void C4DynamicParticleValueProvider::Set(float _startValue, float _endValue, C4P
break;
case C4PV_Random:
valueFunction = &C4DynamicParticleValueProvider::Random;
break;
case C4PV_KeyFrames:
valueFunction = &C4DynamicParticleValueProvider::KeyFrames;
break;
default:
assert(false);
};
@ -97,9 +137,9 @@ void C4DynamicParticleValueProvider::Set(const C4Value &value)
Set(valueArray);
return;
}
int32_t valueInt = value.getInt();
Set((float)valueInt, (float)valueInt, C4PV_Const);
}
@ -122,7 +162,9 @@ void C4DynamicParticleValueProvider::Set(C4ValueArray *fromArray)
case C4PV_Linear:
if (arraySize >= 3)
{
Set((float)(*fromArray)[1].getInt(), (float)(*fromArray)[2].getInt(), C4PV_Linear);
}
break;
case C4PV_Random:
if (arraySize >= 4)
@ -130,7 +172,31 @@ void C4DynamicParticleValueProvider::Set(C4ValueArray *fromArray)
Set((float)(*fromArray)[1].getInt(), (float)(*fromArray)[2].getInt(), C4PV_Random);
rerollInterval = (*fromArray)[3].getInt();
RollRandom();
}
case C4PV_KeyFrames:
if (arraySize >= 5)
{
smoothing = (*fromArray)[1].getInt();
keyFrames = (float*) malloc(sizeof(float) * (arraySize + 3)); // 2 additional information floats at the beginning and ending
keyFrameCount = 0;
const int startingOffset = 2;
int i = startingOffset;
for (; i < arraySize; ++i)
{
keyFrames[2 + i - startingOffset] = (float)(*fromArray)[i].getInt();
}
keyFrameCount = (i - startingOffset) / 2 + 2;
Set(keyFrames[2 + 1], keyFrames[2 * keyFrameCount - 1], C4PV_KeyFrames);
// 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];
//for (int i = 0; i < keyFrameCount; ++i)
// LogF("KF is %f @ %d of %d", keyFrames[i * 2 + 1], int(keyFrames[i * 2]), keyFrameCount);
}
default:
break;
@ -154,6 +220,7 @@ C4DynamicParticleProperties::C4DynamicParticleProperties()
void C4DynamicParticleProperties::Floatify()
{
size.Floatify(1.f);
forceX.Floatify(10.f);
forceY.Floatify(10.f);
speedDampingX.Floatify(1000.f);
@ -229,16 +296,16 @@ void C4DynamicParticle::Init()
lifetime = startingLifetime = 5.f * 38.f;
}
bool C4DynamicParticle::Exec(C4Object *obj)
bool C4DynamicParticle::Exec(C4Object *obj, float timeDelta)
{
// die of old age? :<
lifetime -= 1.f;
lifetime -= timeDelta;
if (lifetime <= 0.f) return false;
// movement
float currentForceX = properties.forceX.GetValue(this);
float currentForceY = properties.forceY.GetValue(this);
currentSpeedX += currentForceX;
currentSpeedY += currentForceY;
@ -251,11 +318,11 @@ bool C4DynamicParticle::Exec(C4Object *obj)
currentSpeedY *= currentDampingY;
// todo: collision check
positionX += currentSpeedX;
positionY += currentSpeedY;
positionX += timeDelta * currentSpeedX;
positionY += timeDelta * currentSpeedY;
drawingData.SetPosition(positionX, positionY, properties.size.GetValue(this));
}
else if(!properties.size.IsConstant())
{
@ -284,7 +351,7 @@ void C4DynamicParticleChunk::Clear()
void C4DynamicParticleChunk::ReplaceParticle(int indexTo, int indexFrom)
{
C4DynamicParticle *oldParticle = particles[indexTo];
if (indexFrom != indexTo) // false when "replacing" the last one
{
memcpy(&vertexCoordinates[indexTo * C4DynamicParticle::DrawingData::vertexCountPerParticle], &vertexCoordinates[indexFrom * C4DynamicParticle::DrawingData::vertexCountPerParticle], sizeof(C4DynamicParticle::DrawingData::Vertex) * C4DynamicParticle::DrawingData::vertexCountPerParticle);
@ -295,11 +362,11 @@ void C4DynamicParticleChunk::ReplaceParticle(int indexTo, int indexFrom)
delete oldParticle;
}
bool C4DynamicParticleChunk::Exec(C4Object *obj)
bool C4DynamicParticleChunk::Exec(C4Object *obj, float timeDelta)
{
for (int i = 0; i < particleCount; ++i)
{
if (!particles[i]->Exec(obj))
if (!particles[i]->Exec(obj, timeDelta))
{
ReplaceParticle(i, particleCount - 1);
--particleCount;
@ -359,19 +426,26 @@ C4DynamicParticle *C4DynamicParticleChunk::AddNewParticle()
return newParticle;
}
void C4DynamicParticleList::Exec(C4Object *obj)
void C4DynamicParticleList::Exec(float timeDelta)
{
if (particleChunks.empty()) return;
accessMutex.Enter();
for (std::list<C4DynamicParticleChunk>::iterator iter = particleChunks.begin(); iter != particleChunks.end();)
{
if (iter->Exec(obj))
if (iter->Exec(targetObject, timeDelta))
{
++iter;
}
else
{
iter = particleChunks.erase(iter);
lastAccessedChunk = 0;
}
}
accessMutex.Leave();
}
void C4DynamicParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
@ -384,9 +458,9 @@ void C4DynamicParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
pDraw->DeactivateBlitModulation();
pDraw->ResetBlitMode();
pDraw->SetTexture();
glPrimitiveRestartIndex(0xffffffff);
// apply zoom
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@ -400,11 +474,15 @@ void C4DynamicParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_PRIMITIVE_RESTART);
accessMutex.Enter();
for (std::list<C4DynamicParticleChunk>::iterator iter = particleChunks.begin(); iter != particleChunks.end(); ++iter)
{
iter->Draw(cgo, obj);
}
accessMutex.Leave();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@ -416,6 +494,8 @@ void C4DynamicParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
C4DynamicParticle *C4DynamicParticleList::AddNewParticle(C4ParticleDef *def, bool additive)
{
accessMutex.Enter();
// if not cached, find correct chunk in list
C4DynamicParticleChunk *chunk = 0;
if (lastAccessedChunk && lastAccessedChunk->IsOfType(def))
@ -441,15 +521,84 @@ C4DynamicParticle *C4DynamicParticleList::AddNewParticle(C4ParticleDef *def, boo
assert(chunk && "No suitable particle chunk could be found or created.");
lastAccessedChunk = chunk;
accessMutex.Leave();
return chunk->AddNewParticle();
}
void C4DynamicParticleSystem::CalculationThread::Execute()
{
DynamicParticles.ExecuteCalculation();
}
void C4DynamicParticleSystem::ExecuteCalculation()
{
int gameTime = Game.FrameCounter;
if (currentSimulationTime < gameTime)
{
float timeDelta = 1.f;
if (currentSimulationTime != 0)
timeDelta = (float)(gameTime - currentSimulationTime);
currentSimulationTime = gameTime;
particleListAccessMutex.Enter();
for (std::list<C4DynamicParticleList>::iterator iter = particleLists.begin(); iter != particleLists.end(); ++iter)
{
iter->Exec(timeDelta);
}
particleListAccessMutex.Leave();
}
Sleep(1000 / 38);
}
C4DynamicParticleList *C4DynamicParticleSystem::GetNewParticleList(C4Object *forObject)
{
C4DynamicParticleList *newList = 0;
particleListAccessMutex.Enter();
particleLists.push_back(C4DynamicParticleList(forObject));
newList = &particleLists.back();
particleListAccessMutex.Leave();
return newList;
}
void C4DynamicParticleSystem::ReleaseParticleList(C4DynamicParticleList *first, C4DynamicParticleList *second)
{
particleListAccessMutex.Enter();
for(std::list<C4DynamicParticleList>::iterator iter = particleLists.begin(); iter != particleLists.end();)
{
C4DynamicParticleList *list = &(*iter);
if (list == first || list == second)
{
iter = particleLists.erase(iter);
}
else
{
++iter;
}
}
particleListAccessMutex.Leave();
}
C4DynamicParticle *C4DynamicParticleSystem::Create(C4ParticleDef *of_def, float x, float y, C4DynamicParticleValueProvider speedX, C4DynamicParticleValueProvider speedY, C4DynamicParticleValueProvider size, float lifetime, C4PropList *properties, C4DynamicParticleList *pxList, C4Object *object)
{
// todo: check amount etc
if (!pxList)
pxList = &globalParticles;
{
pxList = globalParticles;
}
if (pxList == globalParticles && globalParticles == 0)
{
globalParticles = GetNewParticleList();
pxList = globalParticles;
}
C4DynamicParticle *particle = pxList->AddNewParticle(of_def, true);
particle->properties.Set(properties);
@ -469,7 +618,7 @@ void C4DynamicParticleSystem::PreparePrimitiveRestartIndices(int forAmount)
{
const uint32_t PRI = 0xffffffff;
int neededAmount = 5 * forAmount;
if (primitiveRestartIndices.size() < neededAmount)
{
uint32_t oldValue = 0;
@ -498,4 +647,13 @@ void C4DynamicParticleSystem::PreparePrimitiveRestartIndices(int forAmount)
}
}
void C4DynamicParticleSystem::Clear()
{
currentSimulationTime = 0;
particleListAccessMutex.Enter();
particleLists.clear();
particleListAccessMutex.Leave();
}
C4DynamicParticleSystem DynamicParticles;

View File

@ -16,6 +16,7 @@
*/
#include <C4Particles.h>
#include <StdScheduler.h>
#ifndef INC_C4DynamicParticles
#define INC_C4DynamicParticles
@ -25,6 +26,7 @@ enum C4ParticleValueProviderID
C4PV_Const,
C4PV_Linear,
C4PV_Random,
C4PV_KeyFrames,
};
class C4DynamicParticleList;
@ -41,14 +43,24 @@ protected:
// used by Random
float currentValue;
int rerollInterval;
union
{
int rerollInterval; // for Random
int keyFrameCount; // for KeyFrames
};
int smoothing;
float *keyFrames;
C4DynamicParticleValueProviderFunction valueFunction;
bool isConstant;
public:
bool IsConstant() { return isConstant; }
C4DynamicParticleValueProvider() : startValue(0.f), endValue(0.f), currentValue(0.f), rerollInterval(0), valueFunction(0), isConstant(true) { }
C4DynamicParticleValueProvider() : startValue(0.f), endValue(0.f), currentValue(0.f), rerollInterval(0), smoothing(0), keyFrames(0), valueFunction(0), isConstant(true) { }
~C4DynamicParticleValueProvider()
{
if (keyFrames != 0) free(keyFrames);
}
void RollRandom();
// divides by denominator
@ -62,6 +74,7 @@ public:
float Linear(C4DynamicParticle *forParticle);
float Const(C4DynamicParticle *forParticle);
float Random(C4DynamicParticle *forParticle);
float KeyFrames(C4DynamicParticle *forParticle);
};
class C4DynamicParticleProperties
@ -164,7 +177,7 @@ public:
drawingData.SetPosition(positionX, positionY, properties.size.GetValue(this));
}
bool Exec(C4Object *obj);
bool Exec(C4Object *obj, float timeDelta);
friend class C4DynamicParticleChunk;
friend class C4DynamicParticleSystem;
@ -192,7 +205,7 @@ public:
}
// removes all particles
void Clear();
bool Exec(C4Object *obj);
bool Exec(C4Object *obj, float timeDelta);
void Draw(C4TargetFacet cgo, C4Object *obj);
bool IsOfType(C4ParticleDef *def);
@ -206,29 +219,65 @@ class C4DynamicParticleList
private:
std::list<C4DynamicParticleChunk> particleChunks;
C4Object *targetObject;
// caching..
C4DynamicParticleChunk *lastAccessedChunk;
// for making sure that the list is not drawn and calculated at the same time
CStdCSec accessMutex;
public:
C4DynamicParticleList() : lastAccessedChunk(0)
C4DynamicParticleList(C4Object *obj = 0) : targetObject(obj), lastAccessedChunk(0)
{
}
void Exec(C4Object *obj = 0);
void Exec(float timeDelta = 1.f);
void Draw(C4TargetFacet cgo, C4Object *obj);
C4DynamicParticle *AddNewParticle(C4ParticleDef *def, bool additive);
};
class C4DynamicParticleSystem
{
class CalculationThread : public StdThread
{
protected:
virtual void Execute();
public:
CalculationThread() { StdThread::Start(); }
};
private:
std::vector<uint32_t> primitiveRestartIndices;
std::list<C4ParticleList> particleLists;
std::list<C4DynamicParticleList> particleLists;
CalculationThread calculationThread;
CStdCSec particleListAccessMutex;
int currentSimulationTime; // in game time
// calculates the physics in all of the existing particle lists
void ExecuteCalculation();
C4DynamicParticleList *globalParticles;
public:
C4DynamicParticleSystem()
{
currentSimulationTime = 0;
globalParticles = 0;
}
void Clear();
void DrawGlobalParticles(C4TargetFacet cgo) { if (globalParticles) globalParticles->Draw(cgo, 0); }
C4DynamicParticleList *GetNewParticleList(C4Object *forTarget = 0);
// releases up to 2 lists
void ReleaseParticleList(C4DynamicParticleList *first, C4DynamicParticleList *second = 0);
void PreparePrimitiveRestartIndices(int forSize);
void *GetPrimitiveRestartArray() { return (void*)&primitiveRestartIndices[0]; }
C4DynamicParticleList globalParticles;
C4DynamicParticle *Create(C4ParticleDef *of_def, float x, float y, C4DynamicParticleValueProvider speedX, C4DynamicParticleValueProvider speedY, C4DynamicParticleValueProvider size, float lifetime, C4PropList *properties, C4DynamicParticleList *pxList=NULL, C4Object *object=NULL);
friend class CalculationThread;
};
extern C4DynamicParticleSystem DynamicParticles;

View File

@ -170,6 +170,8 @@ void C4Action::GetBridgeData(int32_t &riBridgeTime, bool &rfMoveClonk, bool &rfW
C4Object::C4Object()
{
DynamicFrontParticles = DynamicBackParticles = 0;
Default();
}
@ -229,6 +231,11 @@ void C4Object::Default()
pEffects=NULL;
pGfxOverlay=NULL;
iLastAttachMovementFrame=-1;
if (DynamicFrontParticles == 0)
DynamicFrontParticles = DynamicParticles.GetNewParticleList(this);
if (DynamicBackParticles == 0)
DynamicBackParticles = DynamicParticles.GetNewParticleList(this);
}
bool C4Object::Init(C4PropList *pDef, C4Object *pCreator,
@ -1052,8 +1059,6 @@ void C4Object::Execute()
// particles
if (BackParticles) BackParticles.Exec(this);
if (FrontParticles) FrontParticles.Exec(this);
DynamicBackParticles.Exec();
DynamicFrontParticles.Exec();
// effects
if (pEffects)
{
@ -2567,6 +2572,9 @@ void C4Object::ClearInfo(C4ObjectInfo *pInfo)
void C4Object::Clear()
{
DynamicParticles.ReleaseParticleList(DynamicFrontParticles, DynamicBackParticles);
DynamicFrontParticles = DynamicBackParticles = NULL;
if (pEffects) { delete pEffects; pEffects=NULL; }
if (FrontParticles) FrontParticles.Clear();
if (BackParticles) BackParticles.Clear();

View File

@ -178,7 +178,7 @@ public:
StdMeshInstance* pMeshInstance; // Instance for mesh-type objects
C4Effect *pEffects; // linked list of effects
C4ParticleList FrontParticles, BackParticles; // lists of object local particles
C4DynamicParticleList DynamicFrontParticles, DynamicBackParticles; // the same only for the dynamic particle system
C4DynamicParticleList *DynamicFrontParticles, *DynamicBackParticles; // the same only for the dynamic particle system
uint32_t ColorMod; // color by which the object-drawing is modulated
uint32_t BlitMode; // extra blitting flags (like additive, ClrMod2, etc.)