forked from Mirrors/openclonk
528 lines
14 KiB
C++
528 lines
14 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2013-2016, 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.
|
|
*/
|
|
|
|
#include "graphics/C4FacetEx.h"
|
|
#include "lib/C4Random.h"
|
|
|
|
#include "platform/StdScheduler.h"
|
|
|
|
#include <pcg/pcg_random.hpp>
|
|
|
|
#ifndef INC_C4Particles
|
|
#define INC_C4Particles
|
|
|
|
enum C4ParticleValueProviderID
|
|
{
|
|
C4PV_Const,
|
|
C4PV_Linear,
|
|
C4PV_Random,
|
|
C4PV_KeyFrames,
|
|
C4PV_Sin,
|
|
C4PV_Cos,
|
|
C4PV_Direction,
|
|
C4PV_Step,
|
|
C4PV_Speed,
|
|
C4PV_Wind,
|
|
C4PV_Gravity,
|
|
};
|
|
|
|
enum C4ParticleAttachmentPropertyID
|
|
{
|
|
C4ATTACH_None = 0,
|
|
C4ATTACH_Front = 1,
|
|
C4ATTACH_Back = 2,
|
|
C4ATTACH_MoveRelative = 4
|
|
};
|
|
|
|
enum C4ParticleCollisionFuncID
|
|
{
|
|
C4PC_Die,
|
|
C4PC_Bounce,
|
|
C4PC_Stop,
|
|
};
|
|
|
|
class C4ParticleDefCore;
|
|
class C4ParticleDef;
|
|
class C4ParticleList;
|
|
class C4ParticleChunk;
|
|
class C4Particle;
|
|
class C4ParticleProperties;
|
|
class C4ParticleValueProvider;
|
|
|
|
// core for particle defs
|
|
class C4ParticleDefCore
|
|
{
|
|
public:
|
|
StdStrBuf Name; // name
|
|
C4Rect GfxFace; // rect for graphics
|
|
|
|
C4ParticleDefCore(); // ctor
|
|
void CompileFunc(StdCompiler * compiler);
|
|
|
|
bool Compile(char *particle_source, const char *name); // compile from def file
|
|
};
|
|
|
|
// one particle definition
|
|
class C4ParticleDef : public C4ParticleDefCore
|
|
{
|
|
public:
|
|
C4ParticleDef *previous, *next; // linked list members
|
|
|
|
StdStrBuf Filename; // path to group this particle was loaded from (for EM reloading)
|
|
|
|
C4FacetSurface Gfx; // graphics
|
|
int32_t Length; // number of phases in gfx
|
|
int32_t PhasesX; // number of phases per line in gfx
|
|
float Aspect; // height:width
|
|
|
|
C4ParticleDef(); // ctor
|
|
~C4ParticleDef(); // dtor
|
|
|
|
void Clear(); // free mem associated with this class
|
|
|
|
bool Load(C4Group &group); // load particle from group; assume file to be accessed already
|
|
bool Reload(); // reload particle from stored position
|
|
};
|
|
|
|
typedef float (C4ParticleValueProvider::*C4ParticleValueProviderFunction) (C4Particle*);
|
|
typedef bool (C4ParticleProperties::*C4ParticleCollisionCallback) (C4Particle*);
|
|
|
|
#ifndef USE_CONSOLE
|
|
// the value providers are used to change the attributes of a particle over the lifetime
|
|
class C4ParticleValueProvider
|
|
{
|
|
private:
|
|
float startValue, endValue;
|
|
|
|
// used by Random
|
|
float currentValue;
|
|
|
|
union
|
|
{
|
|
int rerollInterval; // for Random
|
|
float delay; // for Step
|
|
float speedFactor; // for Speed & Wind & Gravity
|
|
float parameterValue; // for Sin
|
|
};
|
|
|
|
union
|
|
{
|
|
int alreadyRolled; // for Random
|
|
int smoothing; // for KeyFrames
|
|
float maxValue; // for Step & Sin
|
|
};
|
|
|
|
pcg32 rng; // for Random
|
|
|
|
size_t keyFrameCount;
|
|
std::vector<float> keyFrames;
|
|
|
|
C4ParticleValueProviderFunction valueFunction;
|
|
bool isConstant;
|
|
|
|
std::vector<C4ParticleValueProvider*> childrenValueProviders;
|
|
|
|
union
|
|
{
|
|
float C4ParticleValueProvider::*floatValueToChange;
|
|
int C4ParticleValueProvider::*intValueToChange;
|
|
size_t keyFrameIndex;
|
|
};
|
|
enum
|
|
{
|
|
VAL_TYPE_INT,
|
|
VAL_TYPE_FLOAT,
|
|
VAL_TYPE_KEYFRAMES,
|
|
};
|
|
int typeOfValueToChange;
|
|
|
|
public:
|
|
bool IsConstant() const { return isConstant; }
|
|
bool IsRandom() const { return valueFunction == &C4ParticleValueProvider::Random; }
|
|
C4ParticleValueProvider() :
|
|
startValue(0.f), endValue(0.f), currentValue(0.f), rerollInterval(0), smoothing(0), keyFrameCount(0), valueFunction(0), isConstant(true), floatValueToChange(0), typeOfValueToChange(VAL_TYPE_FLOAT)
|
|
{ }
|
|
~C4ParticleValueProvider()
|
|
{
|
|
for (std::vector<C4ParticleValueProvider*>::iterator iter = childrenValueProviders.begin(); iter != childrenValueProviders.end(); ++iter)
|
|
delete *iter;
|
|
}
|
|
C4ParticleValueProvider(const C4ParticleValueProvider &other) { *this = other; }
|
|
C4ParticleValueProvider & operator= (const C4ParticleValueProvider &other);
|
|
|
|
// divides by denominator
|
|
void Floatify(float denominator);
|
|
|
|
void Set(const C4Value &value);
|
|
void Set(const C4ValueArray &fromArray);
|
|
void Set(float to); // constant
|
|
float GetValue(C4Particle *forParticle);
|
|
|
|
private:
|
|
void UpdatePointerValue(C4Particle *particle, C4ParticleValueProvider *parent);
|
|
void UpdateChildren(C4Particle *particle);
|
|
void FloatifyParameterValue(float C4ParticleValueProvider::*value, float denominator, size_t keyFrameIndex = 0);
|
|
void SetParameterValue(int type, const C4Value &value, float C4ParticleValueProvider::*floatVal, int C4ParticleValueProvider::*intVal = 0, size_t keyFrameIndex = 0);
|
|
|
|
void SetType(C4ParticleValueProviderID what = C4PV_Const);
|
|
float Linear(C4Particle *forParticle);
|
|
float Const(C4Particle *forParticle);
|
|
float Random(C4Particle *forParticle);
|
|
float KeyFrames(C4Particle *forParticle);
|
|
float Sin(C4Particle *forParticle);
|
|
float Cos(C4Particle *forParticle);
|
|
float Direction(C4Particle *forParticle);
|
|
float Step(C4Particle *forParticle);
|
|
float Speed(C4Particle *forParticle);
|
|
float Wind(C4Particle *forParticle);
|
|
float Gravity(C4Particle *forParticle);
|
|
};
|
|
|
|
// the properties are part of every particle and contain certain changeable attributes
|
|
class C4ParticleProperties
|
|
{
|
|
public:
|
|
bool hasConstantColor;
|
|
bool hasCollisionVertex;
|
|
|
|
C4ParticleValueProvider size, stretch;
|
|
C4ParticleValueProvider forceX, forceY;
|
|
C4ParticleValueProvider speedDampingX, speedDampingY;
|
|
C4ParticleValueProvider colorR, colorG, colorB, colorAlpha;
|
|
C4ParticleValueProvider rotation;
|
|
C4ParticleValueProvider phase;
|
|
C4ParticleValueProvider collisionVertex, collisionDensity;
|
|
|
|
float bouncyness;
|
|
C4ParticleCollisionCallback collisionCallback;
|
|
void SetCollisionFunc(const C4Value &source);
|
|
|
|
uint32_t blitMode;
|
|
|
|
uint32_t attachment;
|
|
|
|
C4ParticleProperties();
|
|
|
|
|
|
void Set(C4PropList *dataSource);
|
|
// divides ints in certain properties by 1000f and in the color properties by 255f
|
|
void Floatify();
|
|
|
|
|
|
bool CollisionDie(C4Particle *forParticle) { return false; }
|
|
bool CollisionBounce(C4Particle *forParticle);
|
|
bool CollisionStop(C4Particle *forParticle);
|
|
};
|
|
|
|
// one single particle
|
|
class C4Particle
|
|
{
|
|
public:
|
|
|
|
struct DrawingData
|
|
{
|
|
static const int vertexCountPerParticle;
|
|
|
|
struct Vertex
|
|
{
|
|
float x;
|
|
float y;
|
|
|
|
float u;
|
|
float v;
|
|
|
|
float r;
|
|
float g;
|
|
float b;
|
|
float alpha;
|
|
};
|
|
Vertex *vertices;
|
|
|
|
int phase;
|
|
|
|
float currentStretch;
|
|
float originalSize;
|
|
float sizeX, sizeY;
|
|
float aspect;
|
|
|
|
float offsetX, offsetY;
|
|
|
|
void SetOffset(float x, float y)
|
|
{
|
|
offsetX = x;
|
|
offsetY = y;
|
|
}
|
|
|
|
void SetPointer(Vertex *startingVertex, bool initial = false)
|
|
{
|
|
vertices = startingVertex;
|
|
|
|
if (initial)
|
|
{
|
|
vertices[0].u = 0.f; vertices[0].v = 1.f;
|
|
vertices[1].u = 0.f; vertices[1].v = 0.f;
|
|
vertices[2].u = 1.f; vertices[2].v = 1.f;
|
|
vertices[3].u = 1.f; vertices[3].v = 0.f;
|
|
|
|
SetColor(1.f, 1.f, 1.f, 1.f);
|
|
|
|
phase = -1;
|
|
}
|
|
}
|
|
|
|
void SetColor(float r, float g, float b, float a = 1.0f)
|
|
{
|
|
for (int vertex = 0; vertex < 4; ++vertex)
|
|
{
|
|
vertices[vertex].r = r;
|
|
vertices[vertex].g = g;
|
|
vertices[vertex].b = b;
|
|
vertices[vertex].alpha = a;
|
|
}
|
|
}
|
|
|
|
void SetPosition(float x, float y, float size, float rotation = 0.f, float stretch = 1.f);
|
|
void SetPhase(int phase, C4ParticleDef *sourceDef);
|
|
|
|
DrawingData() : currentStretch(1.f), originalSize(0.0001f), aspect(1.f), offsetX(0.f), offsetY(0.f)
|
|
{
|
|
}
|
|
|
|
} drawingData;
|
|
protected:
|
|
float currentSpeedX, currentSpeedY;
|
|
float positionX, positionY;
|
|
float lifetime, startingLifetime;
|
|
|
|
C4ParticleProperties properties;
|
|
|
|
public:
|
|
float GetAge() const { return startingLifetime - lifetime; }
|
|
float GetLifetime() const { return lifetime; }
|
|
float GetRelativeAge() const { return (startingLifetime != 0.f) ? (1.0f - (lifetime / startingLifetime)) : 0.f; }
|
|
|
|
void Init();
|
|
C4Particle() { Init(); }
|
|
|
|
void SetPosition(float x, float y)
|
|
{
|
|
positionX = x;
|
|
positionY = y;
|
|
drawingData.SetPosition(positionX, positionY, properties.size.GetValue(this), properties.rotation.GetValue(this));
|
|
}
|
|
|
|
bool Exec(C4Object *obj, float timeDelta, C4ParticleDef *sourceDef);
|
|
|
|
friend class C4ParticleProperties;
|
|
friend class C4ParticleValueProvider;
|
|
friend class C4ParticleChunk;
|
|
friend class C4ParticleSystem;
|
|
};
|
|
|
|
// a chunk contains all of the single particles that can be drawn with one draw call (~"have certain similar attributes")
|
|
class C4ParticleChunk
|
|
{
|
|
private:
|
|
C4ParticleDef *sourceDefinition;
|
|
|
|
uint32_t blitMode;
|
|
|
|
// whether the particles are translated according to the object's position
|
|
uint32_t attachment;
|
|
|
|
std::vector<C4Particle*> particles;
|
|
std::vector<C4Particle::DrawingData::Vertex> vertexCoordinates;
|
|
size_t particleCount;
|
|
|
|
// OpenGL optimizations
|
|
GLuint drawingDataVertexBufferObject;
|
|
unsigned int drawingDataVertexArraysObject;
|
|
void ClearBufferObjects();
|
|
|
|
// delete the particle at indexTo. If possible, replace it with the particle at indexFrom to keep the particles tighly packed
|
|
void DeleteAndReplaceParticle(size_t indexToReplace, size_t indexFrom);
|
|
|
|
public:
|
|
C4ParticleChunk() : sourceDefinition(0), blitMode(0), attachment(C4ATTACH_None), particleCount(0), drawingDataVertexBufferObject(0), drawingDataVertexArraysObject(0)
|
|
{
|
|
|
|
}
|
|
// this is noncopyable to make sure that the OpenGL buffers are never freed multiple times
|
|
C4ParticleChunk(const C4ParticleChunk&) = delete;
|
|
C4ParticleChunk& operator=(const C4ParticleChunk&) = delete;
|
|
~C4ParticleChunk()
|
|
{
|
|
Clear();
|
|
}
|
|
// removes all particles
|
|
void Clear();
|
|
bool Exec(C4Object *obj, float timeDelta);
|
|
void Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call, int texUnit, const StdProjectionMatrix& modelview);
|
|
bool IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t attachment) const;
|
|
bool IsEmpty() const { return !particleCount; }
|
|
|
|
// before adding a particle, you should ReserveSpace for it
|
|
C4Particle *AddNewParticle();
|
|
// sets up internal data structures to be large enough for the passed amount of ADDITIONAL particles
|
|
void ReserveSpace(uint32_t forAmount);
|
|
|
|
friend class C4ParticleList;
|
|
};
|
|
|
|
// this class must not be copied, because deleting the contained CStdCSec twice would be fatal
|
|
// a particle list belongs to a game-world entity (objects or global particles) and contains the chunks associated with that entity
|
|
class C4ParticleList
|
|
{
|
|
private:
|
|
std::list<C4ParticleChunk*> particleChunks;
|
|
|
|
C4Object *targetObject;
|
|
|
|
// caching..
|
|
C4ParticleChunk *lastAccessedChunk;
|
|
|
|
// for making sure that the list is not drawn and calculated at the same time
|
|
CStdCSec accessMutex;
|
|
|
|
public:
|
|
C4ParticleList(C4Object *obj = 0) : targetObject(obj), lastAccessedChunk(0)
|
|
{
|
|
|
|
}
|
|
// non-copyable
|
|
C4ParticleList(const C4ParticleList&) = delete;
|
|
C4ParticleList& operator=(const C4ParticleList&) = delete;
|
|
|
|
~C4ParticleList() { Clear(); }
|
|
|
|
// this enables third-parties to lock the particle list (for example because a particle in the list is modified from outside)
|
|
void Lock() { accessMutex.Enter(); }
|
|
void Unlock() { accessMutex.Leave(); }
|
|
|
|
// deletes all the particles
|
|
void Clear();
|
|
|
|
void Exec(float timeDelta = 1.f);
|
|
void Draw(C4TargetFacet cgo, C4Object *obj);
|
|
C4ParticleChunk *GetFittingParticleChunk(C4ParticleDef *def, uint32_t blitMode, uint32_t attachment, bool alreadyLocked);
|
|
C4Particle *AddNewParticle(C4ParticleDef *def, uint32_t blitMode, uint32_t attachment, bool alreadyLocked, int remaining = 0);
|
|
};
|
|
#endif
|
|
|
|
// cares for the management of particle definitions
|
|
class C4ParticleSystemDefinitionList
|
|
{
|
|
private:
|
|
// pointers to the last and first element of linked list of particle definitions
|
|
C4ParticleDef *first, *last;
|
|
public:
|
|
C4ParticleSystemDefinitionList() : first(0), last(0) {}
|
|
void Clear();
|
|
C4ParticleDef *GetDef(const char *name, C4ParticleDef *exclude=0);
|
|
|
|
friend class C4ParticleDef;
|
|
};
|
|
|
|
// the global particle system interface class
|
|
class C4ParticleSystem
|
|
{
|
|
#ifndef USE_CONSOLE
|
|
class CalculationThread : public StdThread
|
|
{
|
|
protected:
|
|
virtual void Execute();
|
|
public:
|
|
CalculationThread() { StdThread::Start(); }
|
|
};
|
|
friend class CalculationThread;
|
|
|
|
private:
|
|
// contains an array with indices for vertices, separated by a primitive restart index
|
|
GLuint ibo;
|
|
size_t ibo_size;
|
|
std::list<C4ParticleList> particleLists;
|
|
void PreparePrimitiveRestartIndices(uint32_t forSize);
|
|
|
|
CStdCSec particleListAccessMutex;
|
|
CStdEvent frameCounterAdvancedEvent;
|
|
CalculationThread calculationThread;
|
|
|
|
int currentSimulationTime; // in game time
|
|
|
|
// calculates the physics in all of the existing particle lists
|
|
void ExecuteCalculation();
|
|
|
|
C4ParticleList *globalParticles;
|
|
#endif
|
|
|
|
public:
|
|
#ifndef USE_CONSOLE
|
|
C4ParticleSystem();
|
|
~C4ParticleSystem();
|
|
#endif
|
|
// called to allow the particle system the simulation of another step
|
|
void CalculateNextStep()
|
|
{
|
|
#ifndef USE_CONSOLE
|
|
frameCounterAdvancedEvent.Set();
|
|
#endif
|
|
}
|
|
// resets the internal state of the particle system and unloads all definitions
|
|
void Clear();
|
|
void DrawGlobalParticles(C4TargetFacet cgo)
|
|
{
|
|
#ifndef USE_CONSOLE
|
|
if (globalParticles) globalParticles->Draw(cgo, 0);
|
|
#endif
|
|
}
|
|
|
|
C4ParticleList *GetGlobalParticles()
|
|
{
|
|
#ifndef USE_CONSOLE
|
|
return globalParticles;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
C4ParticleList *GetNewParticleList(C4Object *forTarget = 0);
|
|
// releases up to 2 lists
|
|
void ReleaseParticleList(C4ParticleList *first, C4ParticleList *second = 0);
|
|
|
|
// interface for particle definitions
|
|
C4ParticleSystemDefinitionList definitions;
|
|
|
|
#ifndef USE_CONSOLE
|
|
// Returns the IBO ID that contains the PRI data.
|
|
// This makes sure that the IBO contains enough indices for at least 'forParticleAmount' particles.
|
|
GLuint GetIBO(size_t forParticleAmount);
|
|
|
|
// creates a new particle
|
|
void Create(C4ParticleDef *of_def, C4ParticleValueProvider &x, C4ParticleValueProvider &y, C4ParticleValueProvider &speedX, C4ParticleValueProvider &speedY, C4ParticleValueProvider &lifetime, C4PropList *properties, int amount = 1, C4Object *object=NULL);
|
|
|
|
#endif
|
|
|
|
// removes all of the existing particles (used f.e. for scenario section loading)
|
|
void ClearAllParticles();
|
|
|
|
friend class C4ParticleList;
|
|
|
|
|
|
};
|
|
|
|
extern C4ParticleSystem Particles;
|
|
|
|
#endif
|