ddnet/src/game/client/components/particles.cpp

279 lines
7.8 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2010-05-29 07:25:38 +00:00
#include <base/math.h>
#include <engine/demo.h>
#include <engine/graphics.h>
2009-10-27 14:38:53 +00:00
#include "particles.h"
2010-05-29 07:25:38 +00:00
#include <game/client/render.h>
#include <game/gamecore.h>
#include <game/generated/client_data.h>
2010-05-29 07:25:38 +00:00
CParticles::CParticles()
{
2010-05-29 07:25:38 +00:00
OnReset();
m_RenderTrail.m_pParts = this;
m_RenderExplosions.m_pParts = this;
m_RenderGeneral.m_pParts = this;
}
2010-05-29 07:25:38 +00:00
void CParticles::OnReset()
{
// reset particles
for(int i = 0; i < MAX_PARTICLES; i++)
{
m_aParticles[i].m_PrevPart = i - 1;
m_aParticles[i].m_NextPart = i + 1;
}
2010-05-29 07:25:38 +00:00
m_aParticles[0].m_PrevPart = 0;
m_aParticles[MAX_PARTICLES - 1].m_NextPart = -1;
2010-05-29 07:25:38 +00:00
m_FirstFree = 0;
2020-10-26 14:14:07 +00:00
for(int &FirstPart : m_aFirstPart)
FirstPart = -1;
}
void CParticles::Add(int Group, CParticle *pPart, float TimePassed)
{
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
if(pInfo->m_Paused)
return;
}
2012-01-09 23:49:31 +00:00
else
{
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED)
2012-01-09 23:49:31 +00:00
return;
}
if(m_FirstFree == -1)
return;
// remove from the free list
2010-05-29 07:25:38 +00:00
int Id = m_FirstFree;
m_FirstFree = m_aParticles[Id].m_NextPart;
if(m_FirstFree != -1)
m_aParticles[m_FirstFree].m_PrevPart = -1;
// copy data
2010-05-29 07:25:38 +00:00
m_aParticles[Id] = *pPart;
// insert to the group list
2010-05-29 07:25:38 +00:00
m_aParticles[Id].m_PrevPart = -1;
m_aParticles[Id].m_NextPart = m_aFirstPart[Group];
if(m_aFirstPart[Group] != -1)
m_aParticles[m_aFirstPart[Group]].m_PrevPart = Id;
m_aFirstPart[Group] = Id;
// set some parameters
m_aParticles[Id].m_Life = TimePassed;
}
2010-05-29 07:25:38 +00:00
void CParticles::Update(float TimePassed)
{
2018-08-24 03:56:33 +00:00
if(TimePassed <= 0.0f)
return;
2010-05-29 07:25:38 +00:00
static float FrictionFraction = 0;
FrictionFraction += TimePassed;
2018-02-04 15:00:47 +00:00
if(FrictionFraction > 2.0f) // safety messure
2010-05-29 07:25:38 +00:00
FrictionFraction = 0;
2010-05-29 07:25:38 +00:00
int FrictionCount = 0;
while(FrictionFraction > 0.05f)
{
2010-05-29 07:25:38 +00:00
FrictionCount++;
FrictionFraction -= 0.05f;
}
2020-10-26 14:14:07 +00:00
for(int &FirstPart : m_aFirstPart)
{
2020-10-26 14:14:07 +00:00
int i = FirstPart;
while(i != -1)
{
2010-05-29 07:25:38 +00:00
int Next = m_aParticles[i].m_NextPart;
//m_aParticles[i].vel += flow_get(m_aParticles[i].pos)*time_passed * m_aParticles[i].flow_affected;
m_aParticles[i].m_Vel.y += m_aParticles[i].m_Gravity * TimePassed;
2010-05-29 07:25:38 +00:00
for(int f = 0; f < FrictionCount; f++) // apply friction
m_aParticles[i].m_Vel *= m_aParticles[i].m_Friction;
// move the point
vec2 Vel = m_aParticles[i].m_Vel * TimePassed;
Collision()->MovePoint(&m_aParticles[i].m_Pos, &Vel, 0.1f + 0.9f * frandom(), NULL);
m_aParticles[i].m_Vel = Vel * (1.0f / TimePassed);
2010-05-29 07:25:38 +00:00
m_aParticles[i].m_Life += TimePassed;
m_aParticles[i].m_Rot += TimePassed * m_aParticles[i].m_Rotspeed;
// check particle death
2010-05-29 07:25:38 +00:00
if(m_aParticles[i].m_Life > m_aParticles[i].m_LifeSpan)
{
// remove it from the group list
2010-05-29 07:25:38 +00:00
if(m_aParticles[i].m_PrevPart != -1)
m_aParticles[m_aParticles[i].m_PrevPart].m_NextPart = m_aParticles[i].m_NextPart;
else
2020-10-26 14:14:07 +00:00
FirstPart = m_aParticles[i].m_NextPart;
2010-05-29 07:25:38 +00:00
if(m_aParticles[i].m_NextPart != -1)
m_aParticles[m_aParticles[i].m_NextPart].m_PrevPart = m_aParticles[i].m_PrevPart;
// insert to the free list
2010-05-29 07:25:38 +00:00
if(m_FirstFree != -1)
m_aParticles[m_FirstFree].m_PrevPart = i;
m_aParticles[i].m_PrevPart = -1;
m_aParticles[i].m_NextPart = m_FirstFree;
m_FirstFree = i;
}
2010-05-29 07:25:38 +00:00
i = Next;
}
}
}
2010-05-29 07:25:38 +00:00
void CParticles::OnRender()
{
if(Client()->State() < IClient::STATE_ONLINE)
return;
set_new_tick();
2010-05-29 07:25:38 +00:00
static int64 LastTime = 0;
int64 t = time();
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
if(!pInfo->m_Paused)
Update((float)((t - LastTime) / (double)time_freq()) * pInfo->m_Speed);
}
else
2012-01-09 23:49:31 +00:00
{
if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED))
Update((float)((t - LastTime) / (double)time_freq()));
2012-01-09 23:49:31 +00:00
}
2010-05-29 07:25:38 +00:00
LastTime = t;
}
void CParticles::OnInit()
{
Graphics()->QuadsSetRotation(0);
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
m_ParticleQuadContainerIndex = Graphics()->CreateQuadContainer();
for(int i = 0; i <= (SPRITE_PART9 - SPRITE_PART_SLICE); ++i)
{
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->QuadContainerAddSprite(m_ParticleQuadContainerIndex, 1.f);
}
}
2010-05-29 07:25:38 +00:00
void CParticles::RenderGroup(int Group)
{
// don't use the buffer methods here, else the old renderer gets many draw calls
if(Graphics()->IsQuadContainerBufferingEnabled())
{
int i = m_aFirstPart[Group];
static IGraphics::SRenderSpriteInfo s_aParticleRenderInfo[MAX_PARTICLES];
int CurParticleRenderCount = 0;
// batching makes sense for stuff like ninja particles
float LastColor[4];
int LastQuadOffset = 0;
if(i != -1)
{
LastColor[0] = m_aParticles[i].m_Color.r;
LastColor[1] = m_aParticles[i].m_Color.g;
LastColor[2] = m_aParticles[i].m_Color.b;
LastColor[3] = m_aParticles[i].m_Color.a;
Graphics()->SetColor(
m_aParticles[i].m_Color.r,
m_aParticles[i].m_Color.g,
m_aParticles[i].m_Color.b,
m_aParticles[i].m_Color.a);
LastQuadOffset = m_aParticles[i].m_Spr;
}
while(i != -1)
{
int QuadOffset = m_aParticles[i].m_Spr;
float a = m_aParticles[i].m_Life / m_aParticles[i].m_LifeSpan;
vec2 p = m_aParticles[i].m_Pos;
float Size = mix(m_aParticles[i].m_StartSize, m_aParticles[i].m_EndSize, a);
if(LastColor[0] != m_aParticles[i].m_Color.r || LastColor[1] != m_aParticles[i].m_Color.g || LastColor[2] != m_aParticles[i].m_Color.b || LastColor[3] != m_aParticles[i].m_Color.a || LastQuadOffset != QuadOffset)
{
Graphics()->TextureSet(GameClient()->m_ParticlesSkin.m_SpriteParticles[LastQuadOffset - SPRITE_PART_SLICE]);
Graphics()->RenderQuadContainerAsSpriteMultiple(m_ParticleQuadContainerIndex, LastQuadOffset, CurParticleRenderCount, s_aParticleRenderInfo);
CurParticleRenderCount = 0;
LastQuadOffset = QuadOffset;
Graphics()->SetColor(
m_aParticles[i].m_Color.r,
m_aParticles[i].m_Color.g,
m_aParticles[i].m_Color.b,
m_aParticles[i].m_Color.a);
LastColor[0] = m_aParticles[i].m_Color.r;
LastColor[1] = m_aParticles[i].m_Color.g;
LastColor[2] = m_aParticles[i].m_Color.b;
LastColor[3] = m_aParticles[i].m_Color.a;
}
s_aParticleRenderInfo[CurParticleRenderCount].m_Pos[0] = p.x;
s_aParticleRenderInfo[CurParticleRenderCount].m_Pos[1] = p.y;
s_aParticleRenderInfo[CurParticleRenderCount].m_Scale = Size;
s_aParticleRenderInfo[CurParticleRenderCount].m_Rotation = m_aParticles[i].m_Rot;
++CurParticleRenderCount;
i = m_aParticles[i].m_NextPart;
}
Graphics()->TextureSet(GameClient()->m_ParticlesSkin.m_SpriteParticles[LastQuadOffset - SPRITE_PART_SLICE]);
Graphics()->RenderQuadContainerAsSpriteMultiple(m_ParticleQuadContainerIndex, LastQuadOffset, CurParticleRenderCount, s_aParticleRenderInfo);
}
else
{
int i = m_aFirstPart[Group];
Graphics()->BlendNormal();
Graphics()->WrapClamp();
while(i != -1)
{
Graphics()->TextureSet(GameClient()->m_ParticlesSkin.m_SpriteParticles[m_aParticles[i].m_Spr - SPRITE_PART_SLICE]);
Graphics()->QuadsBegin();
float a = m_aParticles[i].m_Life / m_aParticles[i].m_LifeSpan;
vec2 p = m_aParticles[i].m_Pos;
float Size = mix(m_aParticles[i].m_StartSize, m_aParticles[i].m_EndSize, a);
Graphics()->QuadsSetRotation(m_aParticles[i].m_Rot);
Graphics()->SetColor(
m_aParticles[i].m_Color.r,
m_aParticles[i].m_Color.g,
m_aParticles[i].m_Color.b,
m_aParticles[i].m_Color.a); // pow(a, 0.75f) *
IGraphics::CQuadItem QuadItem(p.x, p.y, Size, Size);
Graphics()->QuadsDraw(&QuadItem, 1);
i = m_aParticles[i].m_NextPart;
Graphics()->QuadsEnd();
}
Graphics()->WrapNormal();
Graphics()->BlendNormal();
}
}