Implement animated particles that aren't tied to the global animation timer

This commit is contained in:
Ricardo Luís Vaz Silva 2024-01-08 01:30:51 -03:00 committed by Rachael Alexanderson
parent 9dd6460fe6
commit 7eab519795
8 changed files with 228 additions and 116 deletions

View file

@ -930,6 +930,109 @@ void FAnimDef::SetSwitchTime (uint64_t mstime)
} }
} }
static void AdvanceFrame(uint16_t &frame, uint8_t &AnimType, const FAnimDef &anim)
{
switch (AnimType)
{
default:
case FAnimDef::ANIM_Forward:
frame = (frame + 1) % anim.NumFrames;
break;
case FAnimDef::ANIM_Backward:
if (frame == 0)
{
frame = anim.NumFrames - 1;
}
else
{
frame--;
}
break;
case FAnimDef::ANIM_Random:
// select a random frame other than the current one
if (anim.NumFrames > 1)
{
uint16_t rndFrame = (uint16_t)pr_animatepictures(anim.NumFrames - 1);
if(rndFrame == frame) rndFrame++;
frame = rndFrame % anim.NumFrames;
}
break;
case FAnimDef::ANIM_OscillateUp:
frame = frame + 1;
assert(frame < anim.NumFrames);
if (frame == anim.NumFrames - 1)
{
AnimType = FAnimDef::ANIM_OscillateDown;
}
break;
case FAnimDef::ANIM_OscillateDown:
frame = frame - 1;
if (frame == 0)
{
AnimType = FAnimDef::ANIM_OscillateUp;
}
break;
}
}
constexpr double msPerTic = 1'000.0 / TICRATE;
bool FTextureAnimator::InitStandaloneAnimation(FStandaloneAnimation &animInfo, FTextureID tex, uint32_t curTic)
{
FAnimDef * anim;
animInfo.ok = false;
for(int i = 0; i < mAnimations.Size(); i++)
{
if(mAnimations[i].BasePic == tex)
{
animInfo.ok = true;
animInfo.AnimIndex = i;
anim = &mAnimations[i];
}
}
if(!animInfo.ok) return false;
animInfo.CurFrame = 0;
animInfo.SwitchTic = curTic;
animInfo.AnimType = (anim->AnimType == FAnimDef::ANIM_OscillateDown) ? FAnimDef::ANIM_OscillateUp : anim->AnimType;
uint32_t time = anim->Frames[0].SpeedMin;
if(anim->Frames[0].SpeedRange != 0)
{
time += pr_animatepictures(anim->Frames[0].SpeedRange);
}
animInfo.SwitchTic += time / msPerTic;
return true;
}
FTextureID FTextureAnimator::UpdateStandaloneAnimation(FStandaloneAnimation &animInfo, double curTic)
{
if(!animInfo.ok) return nullptr;
auto &anim = mAnimations[animInfo.AnimIndex];
if(animInfo.SwitchTic <= curTic)
{
uint16_t frame = animInfo.CurFrame;
uint16_t speedframe = anim.bDiscrete ? frame : 0;
while(animInfo.SwitchTic <= curTic)
{
AdvanceFrame(frame, animInfo.AnimType, anim);
if(anim.bDiscrete) speedframe = frame;
uint32_t time = anim.Frames[speedframe].SpeedMin;
if(anim.Frames[speedframe].SpeedRange != 0)
{
time += pr_animatepictures(anim.Frames[speedframe].SpeedRange);
}
animInfo.SwitchTic += time / msPerTic;
}
animInfo.CurFrame = frame;
}
return anim.bDiscrete ? anim.Frames[animInfo.CurFrame].FramePic : (anim.BasePic + animInfo.CurFrame);
}
//========================================================================== //==========================================================================
// //
@ -955,50 +1058,7 @@ void FTextureAnimator::UpdateAnimations (uint64_t mstime)
{ // Multiple frames may have passed since the last time calling { // Multiple frames may have passed since the last time calling
// R_UpdateAnimations, so be sure to loop through them all. // R_UpdateAnimations, so be sure to loop through them all.
switch (anim->AnimType) AdvanceFrame(anim->CurFrame, anim->AnimType, *anim);
{
default:
case FAnimDef::ANIM_Forward:
anim->CurFrame = (anim->CurFrame + 1) % anim->NumFrames;
break;
case FAnimDef::ANIM_Backward:
if (anim->CurFrame == 0)
{
anim->CurFrame = anim->NumFrames - 1;
}
else
{
anim->CurFrame -= 1;
}
break;
case FAnimDef::ANIM_Random:
// select a random frame other than the current one
if (anim->NumFrames > 1)
{
uint16_t rndFrame = (uint16_t)pr_animatepictures(anim->NumFrames - 1);
if (rndFrame >= anim->CurFrame) rndFrame++;
anim->CurFrame = rndFrame;
}
break;
case FAnimDef::ANIM_OscillateUp:
anim->CurFrame = anim->CurFrame + 1;
if (anim->CurFrame >= anim->NumFrames - 1)
{
anim->AnimType = FAnimDef::ANIM_OscillateDown;
}
break;
case FAnimDef::ANIM_OscillateDown:
anim->CurFrame = anim->CurFrame - 1;
if (anim->CurFrame == 0)
{
anim->AnimType = FAnimDef::ANIM_OscillateUp;
}
break;
}
anim->SetSwitchTime (mstime); anim->SetSwitchTime (mstime);
} }

View file

@ -6,6 +6,17 @@
#include "tarray.h" #include "tarray.h"
#include "s_soundinternal.h" #include "s_soundinternal.h"
struct FStandaloneAnimation
{
double SwitchTic;
uint32_t AnimIndex;
uint16_t CurFrame;
bool ok = false;
uint8_t AnimType;
};
static_assert(sizeof(FStandaloneAnimation) == sizeof(uint64_t)*2);
struct FAnimDef struct FAnimDef
{ {
struct FAnimFrame struct FAnimFrame
@ -112,6 +123,9 @@ public:
FixAnimations(); FixAnimations();
InitSwitchList(); InitSwitchList();
} }
bool InitStandaloneAnimation(FStandaloneAnimation &animInfo, FTextureID tex, uint32_t curTic);
FTextureID UpdateStandaloneAnimation(FStandaloneAnimation &animInfo, double curTic);
}; };
extern FTextureAnimator TexAnim; extern FTextureAnimator TexAnim;

View file

@ -297,6 +297,11 @@ void P_ThinkParticles (FLevelLocals *Level)
i = particle->tnext; i = particle->tnext;
if (Level->isFrozen() && !(particle->flags &SPF_NOTIMEFREEZE)) if (Level->isFrozen() && !(particle->flags &SPF_NOTIMEFREEZE))
{ {
if(particle->flags & SPF_STANDALONE_ANIMATIONS)
{
particle->animData.SwitchTic++;
}
prev = particle; prev = particle;
continue; continue;
} }
@ -366,14 +371,13 @@ void P_SpawnParticle(FLevelLocals *Level, const DVector3 &pos, const DVector3 &v
if (particle) if (particle)
{ {
particle->Pos = pos; particle->Pos = pos;
particle->Vel = vel; particle->Vel = FVector3(vel);
particle->Acc = accel; particle->Acc = FVector3(accel);
particle->color = ParticleColor(color); particle->color = ParticleColor(color);
particle->alpha = float(startalpha); particle->alpha = float(startalpha);
if (fadestep < 0) particle->fadestep = FADEFROMTTL(lifetime); if (fadestep < 0) particle->fadestep = FADEFROMTTL(lifetime);
else particle->fadestep = float(fadestep); else particle->fadestep = float(fadestep);
particle->ttl = lifetime; particle->ttl = lifetime;
particle->bright = !!(flags & SPF_FULLBRIGHT);
particle->size = size; particle->size = size;
particle->sizestep = sizestep; particle->sizestep = sizestep;
particle->texture = texture; particle->texture = texture;
@ -382,6 +386,10 @@ void P_SpawnParticle(FLevelLocals *Level, const DVector3 &pos, const DVector3 &v
particle->RollVel = rollvel; particle->RollVel = rollvel;
particle->RollAcc = rollacc; particle->RollAcc = rollacc;
particle->flags = flags; particle->flags = flags;
if(flags & SPF_STANDALONE_ANIMATIONS)
{
TexAnim.InitStandaloneAnimation(particle->animData, texture, Level->maptime);
}
} }
} }
@ -432,10 +440,10 @@ static void MakeFountain (AActor *actor, int color1, int color2)
particle->Pos = actor->Vec3Angle(out, an, actor->Height + 1); particle->Pos = actor->Vec3Angle(out, an, actor->Height + 1);
if (out < actor->radius/8) if (out < actor->radius/8)
particle->Vel.Z += 10./3; particle->Vel.Z += 10.f/3;
else else
particle->Vel.Z += 3; particle->Vel.Z += 3;
particle->Acc.Z -= 1./11; particle->Acc.Z -= 1.f/11;
if (M_Random() < 30) { if (M_Random() < 30) {
particle->size = 4; particle->size = 4;
particle->color = color2; particle->color = color2;
@ -474,8 +482,8 @@ void P_RunEffect (AActor *actor, int effects)
speed = (M_Random () - 128) * (1./200); speed = (M_Random () - 128) * (1./200);
particle->Vel.X += speed * an.Cos(); particle->Vel.X += speed * an.Cos();
particle->Vel.Y += speed * an.Sin(); particle->Vel.Y += speed * an.Sin();
particle->Vel.Z -= 1./36; particle->Vel.Z -= 1.f/36;
particle->Acc.Z -= 1./20; particle->Acc.Z -= 1.f/20;
particle->color = yellow; particle->color = yellow;
particle->size = 2; particle->size = 2;
} }
@ -492,8 +500,8 @@ void P_RunEffect (AActor *actor, int effects)
speed = (M_Random () - 128) * (1./200); speed = (M_Random () - 128) * (1./200);
particle->Vel.X += speed * an.Cos(); particle->Vel.X += speed * an.Cos();
particle->Vel.Y += speed * an.Sin(); particle->Vel.Y += speed * an.Sin();
particle->Vel.Z += 1. / 80; particle->Vel.Z += 1.f / 80;
particle->Acc.Z += 1. / 40; particle->Acc.Z += 1.f / 40;
if (M_Random () & 7) if (M_Random () & 7)
particle->color = grey2; particle->color = grey2;
else else
@ -635,7 +643,7 @@ void P_DrawSplash2 (FLevelLocals *Level, int count, const DVector3 &pos, DAngle
p->size = 4; p->size = 4;
p->color = M_Random() & 0x80 ? color1 : color2; p->color = M_Random() & 0x80 ? color1 : color2;
p->Vel.Z = M_Random() * zvel; p->Vel.Z = M_Random() * zvel;
p->Acc.Z = -1 / 22.; p->Acc.Z = -1 / 22.f;
if (kind) if (kind)
{ {
an = angle + DAngle::fromDeg((M_Random() - 128) * (180 / 256.)); an = angle + DAngle::fromDeg((M_Random() - 128) * (180 / 256.));
@ -788,10 +796,13 @@ void P_DrawRailTrail(AActor *source, TArray<SPortalHit> &portalhits, int color1,
p->ttl = spiralduration; p->ttl = spiralduration;
p->fadestep = FADEFROMTTL(spiralduration); p->fadestep = FADEFROMTTL(spiralduration);
p->size = 3; p->size = 3;
p->bright = fullbright; if(fullbright)
{
p->flags |= SPF_FULLBRIGHT;
}
tempvec = DMatrix3x3(trail[segment].dir, deg) * trail[segment].extend; tempvec = DMatrix3x3(trail[segment].dir, deg) * trail[segment].extend;
p->Vel = tempvec * drift / 16.; p->Vel = FVector3(tempvec * drift / 16.);
p->Pos = tempvec + pos; p->Pos = tempvec + pos;
pos += trail[segment].dir * stepsize; pos += trail[segment].dir * stepsize;
deg += DAngle::fromDeg(r_rail_spiralsparsity * 14); deg += DAngle::fromDeg(r_rail_spiralsparsity * 14);
@ -871,7 +882,10 @@ void P_DrawRailTrail(AActor *source, TArray<SPortalHit> &portalhits, int color1,
p->Acc.Z -= 1./4096; p->Acc.Z -= 1./4096;
pos += trail[segment].dir * stepsize; pos += trail[segment].dir * stepsize;
lencount -= stepsize; lencount -= stepsize;
p->bright = fullbright; if(fullbright)
{
p->flags |= SPF_FULLBRIGHT;
}
if (color2 == -1) if (color2 == -1)
{ {
@ -995,7 +1009,8 @@ void P_DisconnectEffect (AActor *actor)
void DVisualThinker::Construct() void DVisualThinker::Construct()
{ {
PT = {}; PT = {};
PT.Pos = PT.Vel = { 0,0,0 }; PT.Pos = { 0,0,0 };
PT.Vel = { 0,0,0 };
Offset = { 0,0 }; Offset = { 0,0 };
Scale = { 1,1 }; Scale = { 1,1 };
PT.Roll = 0.0; PT.Roll = 0.0;

View file

@ -38,6 +38,7 @@
#include "renderstyle.h" #include "renderstyle.h"
#include "dthinker.h" #include "dthinker.h"
#include "palettecontainer.h" #include "palettecontainer.h"
#include "animations.h"
enum enum
{ {
@ -54,35 +55,40 @@ struct FLevelLocals;
enum EParticleFlags enum EParticleFlags
{ {
SPF_FULLBRIGHT = 1, SPF_FULLBRIGHT = 1 << 0,
SPF_RELPOS = 1 << 1, SPF_RELPOS = 1 << 1,
SPF_RELVEL = 1 << 2, SPF_RELVEL = 1 << 2,
SPF_RELACCEL = 1 << 3, SPF_RELACCEL = 1 << 3,
SPF_RELANG = 1 << 4, SPF_RELANG = 1 << 4,
SPF_NOTIMEFREEZE = 1 << 5, SPF_NOTIMEFREEZE = 1 << 5,
SPF_ROLL = 1 << 6, SPF_ROLL = 1 << 6,
SPF_REPLACE = 1 << 7, SPF_REPLACE = 1 << 7,
SPF_NO_XY_BILLBOARD = 1 << 8, SPF_NO_XY_BILLBOARD = 1 << 8,
SPF_STANDALONE_ANIMATIONS = 1 << 9,
}; };
class DVisualThinker; class DVisualThinker;
struct particle_t struct particle_t
{ {
DVector3 Pos; subsector_t* subsector; //+8 = 8
DVector3 Vel; DVector3 Pos; //+24 = 32
DVector3 Acc; FVector3 Vel; //+12 = 44
double size, sizestep; FVector3 Acc; //+12 = 56
float fadestep, alpha; float size, sizestep; //+8 = 64
subsector_t* subsector; float fadestep, alpha; //+8 = 72
int32_t ttl; int32_t ttl; // +4 = 76
int color; int color; //+4 = 80
FTextureID texture; FTextureID texture; // +4 = 84
ERenderStyle style; ERenderStyle style; //+4 = 88
double Roll, RollVel, RollAcc; float Roll, RollVel, RollAcc; //+12 = 100
uint16_t tnext, snext, tprev; uint16_t tnext, snext, tprev; //+6 = 106
bool bright; uint16_t flags; //+2 = 108
uint16_t flags; // uint32_t padding; //+4 = 112
FStandaloneAnimation animData; //+16 = 128
}; };
static_assert(sizeof(particle_t) == 128);
const uint16_t NO_PARTICLE = 0xffff; const uint16_t NO_PARTICLE = 0xffff;
void P_InitParticles(FLevelLocals *); void P_InitParticles(FLevelLocals *);
@ -154,7 +160,7 @@ public:
DVector3 Prev; DVector3 Prev;
DVector2 Scale, DVector2 Scale,
Offset; Offset;
double PrevRoll; float PrevRoll;
int16_t LightLevel; int16_t LightLevel;
FTranslationID Translation; FTranslationID Translation;
sector_t *cursector; sector_t *cursector;

View file

@ -1283,13 +1283,13 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
index = 0; index = 0;
actor = nullptr; actor = nullptr;
this->particle = particle; this->particle = particle;
fullbright = particle->bright; fullbright = particle->flags & SPF_FULLBRIGHT;
if (di->isFullbrightScene()) if (di->isFullbrightScene())
{ {
Colormap.Clear(); Colormap.Clear();
} }
else if (!particle->bright) else if (!(particle->flags & SPF_FULLBRIGHT))
{ {
TArray<lightlist_t> & lightlist=sector->e->XFloor.lightlist; TArray<lightlist_t> & lightlist=sector->e->XFloor.lightlist;
double lightbottom; double lightbottom;
@ -1332,11 +1332,18 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
ThingColor.a = 255; ThingColor.a = 255;
const auto& vp = di->Viewpoint; const auto& vp = di->Viewpoint;
double timefrac = vp.TicFrac;
if (paused || (di->Level->isFrozen() && !(particle->flags & SPF_NOTIMEFREEZE)))
timefrac = 0.;
if (spr) if (spr)
{
AdjustVisualThinker(di, spr, sector); AdjustVisualThinker(di, spr, sector);
}
else else
{ {
bool has_texture = !particle->texture.isNull(); bool has_texture = particle->texture.isValid();
bool custom_animated_texture = has_texture && (particle->flags & SPF_STANDALONE_ANIMATIONS) && particle->animData.ok;
int particle_style = has_texture ? 2 : gl_particles_style; // Treat custom texture the same as smooth particles int particle_style = has_texture ? 2 : gl_particles_style; // Treat custom texture the same as smooth particles
@ -1350,27 +1357,36 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
} }
else if (particle_style == 2) else if (particle_style == 2)
{ {
lump = has_texture ? particle->texture : TexMan.glPart; if(custom_animated_texture)
{
lump = TexAnim.UpdateStandaloneAnimation(particle->animData, di->Level->maptime + timefrac);
}
else if(has_texture)
{
lump = particle->texture;
}
else
{
lump = TexMan.glPart;
}
}
else
{
lump.SetNull();
} }
else lump.SetNull();
if (lump.isValid()) if (lump.isValid())
{ {
translation = NO_TRANSLATION; translation = NO_TRANSLATION;
//auto tex = TexMan.GetGameTexture(lump, false);
ul = 0; ul = vt = 0;
ur = 1; ur = vb = 1;
vt = 0;
vb = 1; texture = TexMan.GetGameTexture(lump, !custom_animated_texture);
texture = TexMan.GetGameTexture(lump, true);
} }
} }
double timefrac = vp.TicFrac;
if (paused || di->Level->isFrozen())
timefrac = 0.;
float xvf = (particle->Vel.X) * timefrac; float xvf = (particle->Vel.X) * timefrac;
float yvf = (particle->Vel.Y) * timefrac; float yvf = (particle->Vel.Y) * timefrac;
float zvf = (particle->Vel.Z) * timefrac; float zvf = (particle->Vel.Z) * timefrac;

View file

@ -223,7 +223,7 @@ namespace swrenderer
vis->floorclip = 0; vis->floorclip = 0;
vis->foggy = foggy; vis->foggy = foggy;
vis->Light.SetColormap(thread, tz, lightlevel, foggy, map, particle->bright != 0, false, false, false, true); vis->Light.SetColormap(thread, tz, lightlevel, foggy, map, particle->flags & SPF_FULLBRIGHT, false, false, false, true);
thread->SpriteList->Push(vis); thread->SpriteList->Push(vis);
} }

View file

@ -701,17 +701,18 @@ enum ECheckBlockFlags
enum EParticleFlags enum EParticleFlags
{ {
SPF_FULLBRIGHT = 1, SPF_FULLBRIGHT = 1 << 0,
SPF_RELPOS = 1 << 1, SPF_RELPOS = 1 << 1,
SPF_RELVEL = 1 << 2, SPF_RELVEL = 1 << 2,
SPF_RELACCEL = 1 << 3, SPF_RELACCEL = 1 << 3,
SPF_RELANG = 1 << 4, SPF_RELANG = 1 << 4,
SPF_NOTIMEFREEZE = 1 << 5, SPF_NOTIMEFREEZE = 1 << 5,
SPF_ROLL = 1 << 6, SPF_ROLL = 1 << 6,
SPF_REPLACE = 1 << 7, SPF_REPLACE = 1 << 7,
SPF_NO_XY_BILLBOARD = 1 << 8, SPF_NO_XY_BILLBOARD = 1 << 8,
SPF_STANDALONE_ANIMATIONS = 1 << 9,
SPF_RELATIVE = SPF_RELPOS|SPF_RELVEL|SPF_RELACCEL|SPF_RELANG SPF_RELATIVE = SPF_RELPOS|SPF_RELVEL|SPF_RELACCEL|SPF_RELANG
}; };
//Flags for A_FaceMovementDirection //Flags for A_FaceMovementDirection

View file

@ -1,13 +1,13 @@
Class VisualThinker : Thinker native Class VisualThinker : Thinker native
{ {
native Vector3 Pos, native Vector3 Pos,
Vel,
Prev; Prev;
native FVector3 Vel;
native Vector2 Scale, native Vector2 Scale,
Offset; Offset;
native double Roll, native float Roll,
PrevRoll; PrevRoll,
native float Alpha; Alpha;
native TextureID Texture; native TextureID Texture;
native TranslationID Translation; native TranslationID Translation;
native uint16 Flags; native uint16 Flags;