dynamic particles: made the attachment an entry in the particle properties

stable-5.4
David Dormagen 2013-10-18 12:33:31 +02:00
parent 32968f2735
commit 0707458264
4 changed files with 117 additions and 47 deletions

View File

@ -133,12 +133,17 @@
<row> <row>
<col>CollisionVertex</col> <col>CollisionVertex</col>
<col>0 to 1000</col> <col>0 to 1000</col>
<col>The offset of the particle's hit point relative to its width. When set, the particle will collide with the landscape. If unsure, something around 750 should be fine.</col> <col>The offset of the particle's hit point relative to its width. When set, the particle will collide with the landscape. 0 means the particle will collide with its center.</col>
</row> </row>
<row> <row>
<col>OnCollision</col> <col>OnCollision</col>
<col><funclink>PC_Die</funclink>, <funclink>PC_Bounce</funclink></col> <col><funclink>PC_Die</funclink>, <funclink>PC_Bounce</funclink></col>
<col>Defines what happens when the particle collides with the landscape.</col> <col>Defines what happens when the particle collides with the landscape.</col>
</row>
<row>
<col>Attach</col>
<col>bit mask</col>
<col>Defines the attachment of the particles to the calling object. Can be a combination of ATTACH_Front, ATTACH_Back, and ATTACH_MoveRelative. For example <code>ATTACH_Front | ATTACH_MoveRelative</code></col>
</row> </row>
</table> </table>
</text> </text>

View File

@ -1533,7 +1533,7 @@ static bool FnCreateParticle(C4PropList * _this, C4String *szName, long iX, long
return true; return true;
} }
static bool FnCreateParticleEx(C4PropList * _this, C4String *name, long x, long y, C4Value speedX, C4Value speedY, C4Value lifetime, C4PropList *properties, int amount, int attachment) static bool FnCreateParticleEx(C4PropList * _this, C4String *name, long x, long y, C4Value speedX, C4Value speedY, C4Value lifetime, C4PropList *properties, int amount)
{ {
// safety // safety
C4Object *obj = Object(_this); C4Object *obj = Object(_this);
@ -1541,12 +1541,6 @@ static bool FnCreateParticleEx(C4PropList * _this, C4String *name, long x, long
#ifndef USE_CONSOLE #ifndef USE_CONSOLE
if (amount <= 0) amount = 1; if (amount <= 0) amount = 1;
// local offset
if (obj)
{
x += obj->GetX();
y += obj->GetY();
}
// get particle // get particle
C4ParticleDef *pDef=::Particles.GetDef(FnStringPar(name)); C4ParticleDef *pDef=::Particles.GetDef(FnStringPar(name));
if (!pDef) return false; if (!pDef) return false;
@ -1556,7 +1550,7 @@ static bool FnCreateParticleEx(C4PropList * _this, C4String *name, long x, long
valueSpeedY.Set(speedY); valueSpeedY.Set(speedY);
valueLifetime.Set(lifetime); valueLifetime.Set(lifetime);
// create // create
::DynamicParticles.Create(pDef, (float)x, (float)y, valueSpeedX, valueSpeedY, valueLifetime, properties, amount, obj ? (attachment == 1 ? obj->DynamicBackParticles : obj->DynamicFrontParticles) : NULL, obj); ::DynamicParticles.Create(pDef, (float)x, (float)y, valueSpeedX, valueSpeedY, valueLifetime, properties, amount, obj);
#endif #endif
// success, even if not created // success, even if not created
return true; return true;
@ -2684,6 +2678,10 @@ C4ScriptConstDef C4ScriptGameConstMap[]=
{ "PLRZOOM_LimitMin" ,C4V_Int, PLRZOOM_LimitMin }, { "PLRZOOM_LimitMin" ,C4V_Int, PLRZOOM_LimitMin },
{ "PLRZOOM_LimitMax" ,C4V_Int, PLRZOOM_LimitMax }, { "PLRZOOM_LimitMax" ,C4V_Int, PLRZOOM_LimitMax },
{ "ATTACH_Front" ,C4V_Int, C4ATTACH_Front },
{ "ATTACH_Back" ,C4V_Int, C4ATTACH_Back },
{ "ATTACH_MoveRelative" ,C4V_Int, C4ATTACH_MoveRelative },
{ NULL, C4V_Nil, 0} { NULL, C4V_Nil, 0}
}; };

View File

@ -43,28 +43,28 @@ void C4DynamicParticle::DrawingData::SetPosition(float x, float y, float size, f
if (rotation == 0.f) if (rotation == 0.f)
{ {
vertices[0].x = x - sizeX; vertices[0].x = x - sizeX + offsetX;
vertices[0].y = y + sizeY; vertices[0].y = y + sizeY + offsetY;
vertices[1].x = x - sizeX; vertices[1].x = x - sizeX + offsetX;
vertices[1].y = y - sizeY; vertices[1].y = y - sizeY + offsetY;
vertices[2].x = x + sizeX; vertices[2].x = x + sizeX + offsetX;
vertices[2].y = y + sizeY; vertices[2].y = y + sizeY + offsetY;
vertices[3].x = x + sizeX; vertices[3].x = x + sizeX + offsetX;
vertices[3].y = y - sizeY; vertices[3].y = y - sizeY + offsetY;
} }
else else
{ {
float sine = sinf(rotation); float sine = sinf(rotation);
float cosine = cosf(rotation); float cosine = cosf(rotation);
vertices[0].x = x + ((-sizeX) * cosine - (+sizeY) * sine); vertices[0].x = x + ((-sizeX) * cosine - (+sizeY) * sine) + offsetX;
vertices[0].y = y + ((-sizeX) * sine + (+sizeY) * cosine); vertices[0].y = y + ((-sizeX) * sine + (+sizeY) * cosine) + offsetY;
vertices[1].x = x + ((-sizeX) * cosine - (-sizeY) * sine); vertices[1].x = x + ((-sizeX) * cosine - (-sizeY) * sine) + offsetX;
vertices[1].y = y + ((-sizeX) * sine + (-sizeY) * cosine); vertices[1].y = y + ((-sizeX) * sine + (-sizeY) * cosine) + offsetY;
vertices[2].x = x + ((+sizeX) * cosine - (+sizeY) * sine); vertices[2].x = x + ((+sizeX) * cosine - (+sizeY) * sine) + offsetX;
vertices[2].y = y + ((+sizeX) * sine + (+sizeY) * cosine); vertices[2].y = y + ((+sizeX) * sine + (+sizeY) * cosine) + offsetY;
vertices[3].x = x + ((+sizeX) * cosine - (-sizeY) * sine); vertices[3].x = x + ((+sizeX) * cosine - (-sizeY) * sine) + offsetX;
vertices[3].y = y + ((+sizeX) * sine + (-sizeY) * cosine); vertices[3].y = y + ((+sizeX) * sine + (-sizeY) * cosine) + offsetY;
} }
} }
@ -598,6 +598,11 @@ void C4DynamicParticleProperties::Set(C4PropList *dataSource)
// needs to be constant // needs to be constant
blitMode = (uint32_t) property.getInt(); blitMode = (uint32_t) property.getInt();
} }
else if(&Strings.P[P_Attach] == key)
{
// needs to be constant
attachment = (uint32_t) property.getInt();
}
else if(&Strings.P[P_Phase] == key) else if(&Strings.P[P_Phase] == key)
{ {
phase.Set(property); phase.Set(property);
@ -770,6 +775,17 @@ void C4DynamicParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj)
C4TexRef *textureRef = (*sourceDefinition->Gfx.GetFace().ppTex); C4TexRef *textureRef = (*sourceDefinition->Gfx.GetFace().ppTex);
assert(textureRef != 0 && "Particle definition had no texture assigned."); assert(textureRef != 0 && "Particle definition had no texture assigned.");
// use a relative offset?
bool resetMatrix(false);
if ((attachment & C4ATTACH_MoveRelative) && (obj != 0))
{
resetMatrix = true;
glPushMatrix();
glTranslatef((float)obj->GetX(), (float)obj->GetY(), 0.0f);
}
glBlendFunc(GL_SRC_ALPHA, (blitMode & C4GFXBLIT_ADDITIVE) ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, (blitMode & C4GFXBLIT_ADDITIVE) ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -780,11 +796,14 @@ void C4DynamicParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj)
glTexCoordPointer(2, GL_FLOAT, stride, &(vertexCoordinates[0].u)); glTexCoordPointer(2, GL_FLOAT, stride, &(vertexCoordinates[0].u));
glColorPointer(4, GL_FLOAT, stride, &(vertexCoordinates[0].r)); glColorPointer(4, GL_FLOAT, stride, &(vertexCoordinates[0].r));
glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) (5 * particleCount), GL_UNSIGNED_INT, ::DynamicParticles.GetPrimitiveRestartArray()); glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) (5 * particleCount), GL_UNSIGNED_INT, ::DynamicParticles.GetPrimitiveRestartArray());
if (resetMatrix)
glPopMatrix();
} }
bool C4DynamicParticleChunk::IsOfType(C4ParticleDef *def, uint32_t _blitMode) const bool C4DynamicParticleChunk::IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t _attachment) const
{ {
return def == sourceDefinition && blitMode == _blitMode; return def == sourceDefinition && blitMode == _blitMode && attachment == _attachment;
} }
C4DynamicParticle *C4DynamicParticleChunk::AddNewParticle() C4DynamicParticle *C4DynamicParticleChunk::AddNewParticle()
@ -888,19 +907,19 @@ void C4DynamicParticleList::Clear()
accessMutex.Leave(); accessMutex.Leave();
} }
C4DynamicParticle *C4DynamicParticleList::AddNewParticle(C4ParticleDef *def, uint32_t blitMode) C4DynamicParticle *C4DynamicParticleList::AddNewParticle(C4ParticleDef *def, uint32_t blitMode, uint32_t attachment)
{ {
accessMutex.Enter(); accessMutex.Enter();
// if not cached, find correct chunk in list // if not cached, find correct chunk in list
C4DynamicParticleChunk *chunk = 0; C4DynamicParticleChunk *chunk = 0;
if (lastAccessedChunk && lastAccessedChunk->IsOfType(def, blitMode)) if (lastAccessedChunk && lastAccessedChunk->IsOfType(def, blitMode, attachment))
chunk = lastAccessedChunk; chunk = lastAccessedChunk;
else else
{ {
for (std::list<C4DynamicParticleChunk>::iterator iter = particleChunks.begin(); iter != particleChunks.end(); ++iter) for (std::list<C4DynamicParticleChunk>::iterator iter = particleChunks.begin(); iter != particleChunks.end(); ++iter)
{ {
if (!iter->IsOfType(def, blitMode)) continue; if (!iter->IsOfType(def, blitMode, attachment)) continue;
chunk = &(*iter); chunk = &(*iter);
break; break;
} }
@ -913,6 +932,7 @@ C4DynamicParticle *C4DynamicParticleList::AddNewParticle(C4ParticleDef *def, uin
chunk = &particleChunks.back(); chunk = &particleChunks.back();
chunk->sourceDefinition = def; chunk->sourceDefinition = def;
chunk->blitMode = blitMode; chunk->blitMode = blitMode;
chunk->attachment = attachment;
} }
assert(chunk && "No suitable particle chunk could be found or created."); assert(chunk && "No suitable particle chunk could be found or created.");
@ -997,20 +1017,12 @@ void C4DynamicParticleSystem::ReleaseParticleList(C4DynamicParticleList *first,
} }
#ifndef USE_CONSOLE #ifndef USE_CONSOLE
void C4DynamicParticleSystem::Create(C4ParticleDef *of_def, float x, float y, C4DynamicParticleValueProvider &speedX, C4DynamicParticleValueProvider &speedY, C4DynamicParticleValueProvider &lifetime, C4PropList *properties, int amount, C4DynamicParticleList *pxList, C4Object *object) void C4DynamicParticleSystem::Create(C4ParticleDef *of_def, float x, float y, C4DynamicParticleValueProvider &speedX, C4DynamicParticleValueProvider &speedY, C4DynamicParticleValueProvider &lifetime, C4PropList *properties, int amount, C4Object *object)
{ {
// todo: check amount etc // todo: check amount etc
if (!pxList) C4DynamicParticleList * pxList(globalParticles);
{
pxList = globalParticles;
}
if (pxList == globalParticles && globalParticles == 0)
{
globalParticles = GetNewParticleList();
pxList = globalParticles;
}
// initialize the particle properties // initialize the particle properties
// this is done here, because it would also be the right place to implement caching // this is done here, because it would also be the right place to implement caching
@ -1020,6 +1032,36 @@ void C4DynamicParticleSystem::Create(C4ParticleDef *of_def, float x, float y, C4
speedX.Floatify(10.f); speedX.Floatify(10.f);
speedY.Floatify(10.f); speedY.Floatify(10.f);
// 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();
// figure out particle list to use
if (particleProperties.attachment & C4ATTACH_Front) pxList = object->DynamicFrontParticles;
else if (particleProperties.attachment & C4ATTACH_Back) pxList = object->DynamicBackParticles;
if (particleProperties.attachment & C4ATTACH_MoveRelative)
{
drawingOffsetX = -xoff;
drawingOffsetY = -yoff;
}
}
// sanity for global particles - might have to be created first
if (pxList == globalParticles && globalParticles == 0)
{
globalParticles = GetNewParticleList();
pxList = globalParticles;
}
while (amount--) while (amount--)
{ {
if (speedX.IsRandom()) speedX.RollRandom(); if (speedX.IsRandom()) speedX.RollRandom();
@ -1027,7 +1069,7 @@ void C4DynamicParticleSystem::Create(C4ParticleDef *of_def, float x, float y, C4
if (lifetime.IsRandom()) lifetime.RollRandom(); if (lifetime.IsRandom()) lifetime.RollRandom();
// create a particle in the fitting chunk // create a particle in the fitting chunk
C4DynamicParticle *particle = pxList->AddNewParticle(of_def, particleProperties.blitMode); C4DynamicParticle *particle = pxList->AddNewParticle(of_def, particleProperties.blitMode, particleProperties.attachment);
// initialize some more properties // initialize some more properties
particle->properties = particleProperties; particle->properties = particleProperties;
@ -1039,7 +1081,8 @@ void C4DynamicParticleSystem::Create(C4ParticleDef *of_def, float x, float y, C4
particle->currentSpeedX = speedX.GetValue(particle); particle->currentSpeedX = speedX.GetValue(particle);
particle->currentSpeedY = speedY.GetValue(particle); particle->currentSpeedY = speedY.GetValue(particle);
particle->drawingData.aspect = of_def->Aspect; particle->drawingData.aspect = of_def->Aspect;
particle->SetPosition(x, y); particle->drawingData.SetOffset(drawingOffsetX, drawingOffsetY);
particle->SetPosition(x + xoff, y + yoff);
particle->drawingData.SetColor(particle->properties.colorR.GetValue(particle), particle->properties.colorG.GetValue(particle), particle->properties.colorB.GetValue(particle), particle->properties.colorAlpha.GetValue(particle)); particle->drawingData.SetColor(particle->properties.colorR.GetValue(particle), particle->properties.colorG.GetValue(particle), particle->properties.colorB.GetValue(particle), particle->properties.colorAlpha.GetValue(particle));
} }
} }

View File

@ -33,6 +33,14 @@ enum C4ParticleValueProviderID
C4PV_Speed, C4PV_Speed,
}; };
enum C4ParticleAttachmentPropertyID
{
C4ATTACH_None = 0,
C4ATTACH_Front = 1,
C4ATTACH_Back = 2,
C4ATTACH_MoveRelative = 4
};
enum C4ParticleCollisionFuncID enum C4ParticleCollisionFuncID
{ {
C4PC_Die, C4PC_Die,
@ -148,6 +156,8 @@ public:
uint32_t blitMode; uint32_t blitMode;
uint32_t attachment;
C4DynamicParticleProperties(); C4DynamicParticleProperties();
@ -190,6 +200,14 @@ public:
float sizeX, sizeY; float sizeX, sizeY;
float aspect; float aspect;
float offsetX, offsetY;
void SetOffset(float x, float y)
{
offsetX = x;
offsetY = y;
}
void SetPointer(Vertex *startingVertex, bool initial = false) void SetPointer(Vertex *startingVertex, bool initial = false)
{ {
vertices = startingVertex; vertices = startingVertex;
@ -221,7 +239,7 @@ public:
void SetPosition(float x, float y, float size, float rotation = 0.f, float stretch = 1.f); void SetPosition(float x, float y, float size, float rotation = 0.f, float stretch = 1.f);
void SetPhase(int phase, C4ParticleDef *sourceDef); void SetPhase(int phase, C4ParticleDef *sourceDef);
DrawingData() : currentStretch(1.f), originalSize(0.0001f), aspect(1.f) DrawingData() : currentStretch(1.f), originalSize(0.0001f), aspect(1.f), offsetX(0.f), offsetY(0.f)
{ {
} }
@ -260,7 +278,11 @@ class C4DynamicParticleChunk
{ {
private: private:
C4ParticleDef *sourceDefinition; C4ParticleDef *sourceDefinition;
uint32_t blitMode; uint32_t blitMode;
// whether the particles are translated according to the object's position
uint32_t attachment;
std::vector<C4DynamicParticle*> particles; std::vector<C4DynamicParticle*> particles;
std::vector<C4DynamicParticle::DrawingData::Vertex> vertexCoordinates; std::vector<C4DynamicParticle::DrawingData::Vertex> vertexCoordinates;
@ -269,7 +291,7 @@ private:
// delete the particle at indexTo. If possible, replace it with the particle at indexFrom to keep the particles tighly packed // 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); void DeleteAndReplaceParticle(size_t indexToReplace, size_t indexFrom);
public: public:
C4DynamicParticleChunk() : sourceDefinition(0), blitMode(0), particleCount(0) C4DynamicParticleChunk() : sourceDefinition(0), blitMode(0), attachment(C4ATTACH_None), particleCount(0)
{ {
} }
@ -281,7 +303,7 @@ public:
void Clear(); void Clear();
bool Exec(C4Object *obj, float timeDelta); bool Exec(C4Object *obj, float timeDelta);
void Draw(C4TargetFacet cgo, C4Object *obj); void Draw(C4TargetFacet cgo, C4Object *obj);
bool IsOfType(C4ParticleDef *def, uint32_t _blitMode) const; bool IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t attachment) const;
C4DynamicParticle *AddNewParticle(); C4DynamicParticle *AddNewParticle();
@ -301,17 +323,19 @@ private:
// for making sure that the list is not drawn and calculated at the same time // for making sure that the list is not drawn and calculated at the same time
CStdCSec accessMutex; CStdCSec accessMutex;
public: public:
C4DynamicParticleList(C4Object *obj = 0) : targetObject(obj), lastAccessedChunk(0) C4DynamicParticleList(C4Object *obj = 0) : targetObject(obj), lastAccessedChunk(0)
{ {
} }
// deletes all the particles // deletes all the particles
void Clear(); void Clear();
void Exec(float timeDelta = 1.f); void Exec(float timeDelta = 1.f);
void Draw(C4TargetFacet cgo, C4Object *obj); void Draw(C4TargetFacet cgo, C4Object *obj);
C4DynamicParticle *AddNewParticle(C4ParticleDef *def, uint32_t blitMode); C4DynamicParticle *AddNewParticle(C4ParticleDef *def, uint32_t blitMode, uint32_t attachment);
}; };
#endif #endif
@ -376,7 +400,7 @@ public:
void PreparePrimitiveRestartIndices(uint32_t forSize); void PreparePrimitiveRestartIndices(uint32_t forSize);
void *GetPrimitiveRestartArray() { return (void*)&primitiveRestartIndices[0]; } void *GetPrimitiveRestartArray() { return (void*)&primitiveRestartIndices[0]; }
void Create(C4ParticleDef *of_def, float x, float y, C4DynamicParticleValueProvider &speedX, C4DynamicParticleValueProvider &speedY, C4DynamicParticleValueProvider &lifetime, C4PropList *properties, int amount = 1, C4DynamicParticleList *pxList=NULL, C4Object *object=NULL); void Create(C4ParticleDef *of_def, float x, float y, C4DynamicParticleValueProvider &speedX, C4DynamicParticleValueProvider &speedY, C4DynamicParticleValueProvider &lifetime, C4PropList *properties, int amount = 1, C4Object *object=NULL);
#endif #endif
}; };