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
// R_UpdateAnimations, so be sure to loop through them all.
switch (anim->AnimType)
{
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;
}
AdvanceFrame(anim->CurFrame, anim->AnimType, *anim);
anim->SetSwitchTime (mstime);
}

View file

@ -6,6 +6,17 @@
#include "tarray.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 FAnimFrame
@ -112,6 +123,9 @@ public:
FixAnimations();
InitSwitchList();
}
bool InitStandaloneAnimation(FStandaloneAnimation &animInfo, FTextureID tex, uint32_t curTic);
FTextureID UpdateStandaloneAnimation(FStandaloneAnimation &animInfo, double curTic);
};
extern FTextureAnimator TexAnim;

View file

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

View file

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

View file

@ -1283,13 +1283,13 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
index = 0;
actor = nullptr;
this->particle = particle;
fullbright = particle->bright;
fullbright = particle->flags & SPF_FULLBRIGHT;
if (di->isFullbrightScene())
{
Colormap.Clear();
}
else if (!particle->bright)
else if (!(particle->flags & SPF_FULLBRIGHT))
{
TArray<lightlist_t> & lightlist=sector->e->XFloor.lightlist;
double lightbottom;
@ -1332,11 +1332,18 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
ThingColor.a = 255;
const auto& vp = di->Viewpoint;
double timefrac = vp.TicFrac;
if (paused || (di->Level->isFrozen() && !(particle->flags & SPF_NOTIMEFREEZE)))
timefrac = 0.;
if (spr)
{
AdjustVisualThinker(di, spr, sector);
}
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
@ -1350,27 +1357,36 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
}
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())
{
translation = NO_TRANSLATION;
//auto tex = TexMan.GetGameTexture(lump, false);
if (lump.isValid())
{
translation = NO_TRANSLATION;
ul = 0;
ur = 1;
vt = 0;
vb = 1;
texture = TexMan.GetGameTexture(lump, true);
ul = vt = 0;
ur = vb = 1;
texture = TexMan.GetGameTexture(lump, !custom_animated_texture);
}
}
double timefrac = vp.TicFrac;
if (paused || di->Level->isFrozen())
timefrac = 0.;
float xvf = (particle->Vel.X) * timefrac;
float yvf = (particle->Vel.Y) * timefrac;
float zvf = (particle->Vel.Z) * timefrac;

View file

@ -223,7 +223,7 @@ namespace swrenderer
vis->floorclip = 0;
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);
}

View file

@ -701,17 +701,18 @@ enum ECheckBlockFlags
enum EParticleFlags
{
SPF_FULLBRIGHT = 1,
SPF_RELPOS = 1 << 1,
SPF_RELVEL = 1 << 2,
SPF_RELACCEL = 1 << 3,
SPF_RELANG = 1 << 4,
SPF_NOTIMEFREEZE = 1 << 5,
SPF_ROLL = 1 << 6,
SPF_REPLACE = 1 << 7,
SPF_NO_XY_BILLBOARD = 1 << 8,
SPF_FULLBRIGHT = 1 << 0,
SPF_RELPOS = 1 << 1,
SPF_RELVEL = 1 << 2,
SPF_RELACCEL = 1 << 3,
SPF_RELANG = 1 << 4,
SPF_NOTIMEFREEZE = 1 << 5,
SPF_ROLL = 1 << 6,
SPF_REPLACE = 1 << 7,
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

View file

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