From 7eab519795ab54e806a61f5d19a6f41b221ce851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Mon, 8 Jan 2024 01:30:51 -0300 Subject: [PATCH] Implement animated particles that aren't tied to the global animation timer --- src/gamedata/textures/animations.cpp | 148 ++++++++++++------ src/gamedata/textures/animations.h | 14 ++ src/playsim/p_effect.cpp | 43 +++-- src/playsim/p_effect.h | 54 ++++--- src/rendering/hwrenderer/scene/hw_sprites.cpp | 54 ++++--- .../swrenderer/things/r_particle.cpp | 2 +- wadsrc/static/zscript/constants.zs | 21 +-- wadsrc/static/zscript/visualthinker.zs | 8 +- 8 files changed, 228 insertions(+), 116 deletions(-) diff --git a/src/gamedata/textures/animations.cpp b/src/gamedata/textures/animations.cpp index 249544317a..58cb3f1899 100644 --- a/src/gamedata/textures/animations.cpp +++ b/src/gamedata/textures/animations.cpp @@ -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); } diff --git a/src/gamedata/textures/animations.h b/src/gamedata/textures/animations.h index 639005bd20..a89edf59be 100644 --- a/src/gamedata/textures/animations.h +++ b/src/gamedata/textures/animations.h @@ -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; diff --git a/src/playsim/p_effect.cpp b/src/playsim/p_effect.cpp index b861e7bff3..21aa0a0d33 100644 --- a/src/playsim/p_effect.cpp +++ b/src/playsim/p_effect.cpp @@ -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 &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 &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; diff --git a/src/playsim/p_effect.h b/src/playsim/p_effect.h index 796d6ba404..416226cabe 100644 --- a/src/playsim/p_effect.h +++ b/src/playsim/p_effect.h @@ -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; diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 6052747d98..cfc2cce50f 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -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=sector->e->XFloor.lightlist; double lightbottom; @@ -1332,12 +1332,19 @@ 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 // [BB] Load the texture for round or 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; diff --git a/src/rendering/swrenderer/things/r_particle.cpp b/src/rendering/swrenderer/things/r_particle.cpp index 058a2609ab..ccd2fe064c 100644 --- a/src/rendering/swrenderer/things/r_particle.cpp +++ b/src/rendering/swrenderer/things/r_particle.cpp @@ -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); } diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index efbb42aa84..89ea793d7e 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -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 diff --git a/wadsrc/static/zscript/visualthinker.zs b/wadsrc/static/zscript/visualthinker.zs index 201dce9e9c..e18fe1823d 100644 --- a/wadsrc/static/zscript/visualthinker.zs +++ b/wadsrc/static/zscript/visualthinker.zs @@ -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;