Added particle rendering to VisualThinkers.

To activate, use `SetParticleType(int type)`. To deactivate, use `DisableParticle()`.

Types are:
- PT_DEFAULT (default value; uses `gl_particles_style`)
- PT_SQUARE
- PT_ROUND
- PT_SMOOTH

While in this mode:
- `Texture` & `Translation` are ignored
- `Scale.X` sets the size
- `SColor` sets the color

Misc changes:
- Removed warning on textureless destruction
This commit is contained in:
MajorCooke 2025-03-08 14:14:53 -06:00 committed by Ricardo Luís Vaz Silva
parent 74594e4c34
commit 210ee1780e
6 changed files with 110 additions and 14 deletions

View file

@ -1108,7 +1108,8 @@ DEFINE_ACTION_FUNCTION_NATIVE(FLevelLocals, SpawnVisualThinker, SpawnVisualThink
void DVisualThinker::UpdateSpriteInfo()
{
PT.style = ERenderStyle(GetRenderStyle());
if((PT.flags & SPF_LOCAL_ANIM) && PT.texture != AnimatedTexture)
if ((PT.flags & SPF_LOCAL_ANIM) && PT.texture != AnimatedTexture)
{
AnimatedTexture = PT.texture;
TexAnim.InitStandaloneAnimation(PT.animData, PT.texture, Level->maptime);
@ -1127,6 +1128,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(DVisualThinker, UpdateSpriteInfo, UpdateSpriteInfo
return 0;
}
bool DVisualThinker::ValidTexture()
{
return ((flags & VTF_IsParticle) || PT.texture.isValid());
}
// This runs just like Actor's, make sure to call Super.Tick() in ZScript.
void DVisualThinker::Tick()
@ -1134,10 +1139,8 @@ void DVisualThinker::Tick()
if (ObjectFlags & OF_EuthanizeMe)
return;
// There won't be a standard particle for this, it's only for graphics.
if (!PT.texture.isValid())
if (!ValidTexture())
{
Printf("No valid texture, destroyed");
Destroy();
return;
}
@ -1156,7 +1159,6 @@ void DVisualThinker::Tick()
PT.Pos.X = newxy.X;
PT.Pos.Y = newxy.Y;
PT.Pos.Z += PT.Vel.Z;
subsector_t * ss = Level->PointInRenderSubsector(PT.Pos);
// Handle crossing a sector portal.
@ -1246,7 +1248,33 @@ DEFINE_ACTION_FUNCTION_NATIVE(DVisualThinker, SetTranslation, SetTranslation)
return 0;
}
static int IsFrozen(DVisualThinker * self)
int DVisualThinker::GetParticleType() const
{
int flag = (flags & VTF_IsParticle);
switch (flag)
{
case VTF_ParticleSquare:
return PT_SQUARE;
case VTF_ParticleRound:
return PT_ROUND;
case VTF_ParticleSmooth:
return PT_SMOOTH;
}
return PT_DEFAULT;
}
static int GetParticleType(DVisualThinker* self)
{
return self->GetParticleType();
}
DEFINE_ACTION_FUNCTION_NATIVE(DVisualThinker, GetParticleType, GetParticleType)
{
PARAM_SELF_PROLOGUE(DVisualThinker);
ACTION_RETURN_INT(self->GetParticleType());
}
static int IsFrozen(DVisualThinker* self)
{
return !!(self->Level->isFrozen() && !(self->PT.flags & SPF_NOTIMEFREEZE));
}

View file

@ -53,6 +53,14 @@ struct FLevelLocals;
// [RH] Particle details
enum EParticleStyle
{
PT_DEFAULT = -1, // Use gl_particles_style
PT_SQUARE = 0,
PT_ROUND = 1,
PT_SMOOTH = 2,
};
enum EParticleFlags
{
SPF_FULLBRIGHT = 1 << 0,

View file

@ -20,6 +20,12 @@ enum EVisualThinkerFlags
VTF_FlipY = 1 << 3, // flip the sprite on the x/y axis.
VTF_DontInterpolate = 1 << 4, // disable all interpolation
VTF_AddLightLevel = 1 << 5, // adds sector light level to 'LightLevel'
VTF_ParticleDefault = 0x40,
VTF_ParticleSquare = 0x80,
VTF_ParticleRound = 0xC0,
VTF_ParticleSmooth = 0x100,
VTF_IsParticle = 0x1C0, // Renders as a particle instead
};
class DVisualThinker : public DThinker
@ -53,6 +59,8 @@ public:
void SetTranslation(FName trname);
int GetRenderStyle() const;
bool isFrozen();
bool ValidTexture();
int GetParticleType() const;
int GetLightLevel(sector_t *rendersector) const;
FVector3 InterpolatedPosition(double ticFrac) const;
float InterpolatedRoll(double ticFrac) const;

View file

@ -1409,7 +1409,7 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
if (!particle || particle->alpha <= 0)
return;
if (spr && spr->PT.texture.isNull())
if (spr && !spr->ValidTexture())
return;
lightlevel = hw_ClampLight(spr ? spr->GetLightLevel(sector) : sector->GetSpriteLight());
@ -1477,17 +1477,29 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
if (paused || (di->Level->isFrozen() && !(particle->flags & SPF_NOTIMEFREEZE)))
timefrac = 0.;
if (spr)
if (spr && !(spr->flags & VTF_IsParticle))
{
AdjustVisualThinker(di, spr, sector);
}
else
{
bool has_texture = particle->texture.isValid();
bool custom_animated_texture = (particle->flags & SPF_LOCAL_ANIM) && particle->animData.ok;
int particle_style = has_texture ? 2 : gl_particles_style; // Treat custom texture the same as smooth particles
bool has_texture = false;
bool custom_animated_texture = false;
int particle_style = 0;
float size = particle->size;
if (!spr)
{
has_texture = particle->texture.isValid();
custom_animated_texture = (particle->flags & SPF_LOCAL_ANIM) && particle->animData.ok;
particle_style = has_texture ? 2 : gl_particles_style; // Treat custom texture the same as smooth particles
}
else
{
size = float(spr->Scale.X);
const int ptype = spr->GetParticleType();
particle_style = (ptype != PT_DEFAULT) ? ptype : gl_particles_style;
}
// [BB] Load the texture for round or smooth particles
if (particle_style)
{
@ -1549,7 +1561,7 @@ void HWSprite::ProcessParticle(HWDrawInfo *di, particle_t *particle, sector_t *s
if (particle_style == 1) factor = 1.3f / 7.f;
else if (particle_style == 2) factor = 2.5f / 7.f;
else factor = 1 / 7.f;
float scalefac=particle->size * factor;
float scalefac= size * factor;
float ps = di->Level->pixelstretch;

View file

@ -1532,4 +1532,18 @@ enum EVisualThinkerFlags
VTF_FlipY = 1 << 3, // flip the sprite on the x/y axis.
VTF_DontInterpolate = 1 << 4, // disable all interpolation
VTF_AddLightLevel = 1 << 5, // adds sector light level to 'LightLevel'
VTF_ParticleDefault = 0x40,
VTF_ParticleSquare = 0x80,
VTF_ParticleRound = 0xC0,
VTF_ParticleSmooth = 0x100,
VTF_IsParticle = 0x1C0
};
enum EParticleStyle
{
PT_DEFAULT = -1, // Use gl_particles_style
PT_SQUARE = 0,
PT_ROUND = 1,
PT_SMOOTH = 2,
};

View file

@ -60,4 +60,30 @@ Class VisualThinker : Thinker native
}
return p;
}
native int GetParticleType() const;
void SetParticleType(EParticleStyle type = PT_DEFAULT)
{
switch(type)
{
Default:
VisualThinkerFlags = (VisualThinkerFlags & ~VTF_IsParticle) | VTF_ParticleDefault;
break;
case PT_SQUARE:
VisualThinkerFlags = (VisualThinkerFlags & ~VTF_IsParticle) | VTF_ParticleSquare;
break;
case PT_ROUND:
VisualThinkerFlags = (VisualThinkerFlags & ~VTF_IsParticle) | VTF_ParticleRound;
break;
case PT_SMOOTH:
VisualThinkerFlags = (VisualThinkerFlags & ~VTF_IsParticle) | VTF_ParticleSmooth;
break;
}
}
void DisableParticle()
{
VisualThinkerFlags &= ~VTF_IsParticle;
}
}