diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 3ed12664a..5a9d4b388 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -995,6 +995,7 @@ public: DVector3 OldRenderPos; DVector3 Vel; + DVector2 SpriteOffset; double Speed; double FloatSpeed; diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 6945bc9a5..2b244fd62 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -365,6 +365,7 @@ void AActor::Serialize(FSerializer &arc) A("spawntime", SpawnTime) A("spawnorder", SpawnOrder) A("friction", Friction) + A("SpriteOffset", SpriteOffset) A("userlights", UserLights); SerializeTerrain(arc, "floorterrain", floorterrain, &def->floorterrain); diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 151615935..b5611cb00 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -848,12 +848,11 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t thing->renderflags ^= RF_XFLIP; r.Scale(sprscale.X, sprscale.Y); - - float rightfac = -r.left; + + float SpriteOffY = thing->SpriteOffset.Y; + float rightfac = -r.left - thing->SpriteOffset.X; float leftfac = rightfac - r.width; - float bottomfac = -r.top; - float topfac = bottomfac - r.height; - z1 = z - r.top; + z1 = z - r.top - SpriteOffY; z2 = z1 - r.height; float spriteheight = sprscale.Y * r.height; @@ -864,31 +863,39 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t PerformSpriteClipAdjustment(thing, thingpos, spriteheight); } - float viewvecX; - float viewvecY; switch (spritetype) { case RF_FACESPRITE: - viewvecX = vp.ViewVector.X; - viewvecY = vp.ViewVector.Y; + { + float viewvecX = vp.ViewVector.X; + float viewvecY = vp.ViewVector.Y; x1 = x - viewvecY*leftfac; x2 = x - viewvecY*rightfac; y1 = y + viewvecX*leftfac; y2 = y + viewvecX*rightfac; break; - + } case RF_FLATSPRITE: { + float bottomfac = -r.top - SpriteOffY; + float topfac = bottomfac - r.height; + x1 = x + leftfac; x2 = x + rightfac; y1 = y - topfac; y2 = y - bottomfac; + // [MC] Counteract in case of any potential problems. Tests so far haven't + // shown any outstanding issues but that doesn't mean they won't appear later + // when more features are added. + z1 += SpriteOffY; + z2 += SpriteOffY; + break; } - break; case RF_WALLSPRITE: - viewvecX = Angles.Yaw.Cos(); - viewvecY = Angles.Yaw.Sin(); + { + float viewvecX = Angles.Yaw.Cos(); + float viewvecY = Angles.Yaw.Sin(); x1 = x + viewvecY*leftfac; x2 = x + viewvecY*rightfac; @@ -896,6 +903,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t y2 = y - viewvecX*rightfac; break; } + } } else { diff --git a/src/rendering/swrenderer/scene/r_opaque_pass.cpp b/src/rendering/swrenderer/scene/r_opaque_pass.cpp index c8158e8cf..7bf7300e8 100644 --- a/src/rendering/swrenderer/scene/r_opaque_pass.cpp +++ b/src/rendering/swrenderer/scene/r_opaque_pass.cpp @@ -1003,9 +1003,9 @@ namespace swrenderer bool RenderOpaquePass::GetThingSprite(AActor *thing, ThingSprite &sprite) { + // The X offsetting (SpriteOffset.X) is performed in r_sprite.cpp, in RenderSprite::Project(). sprite.pos = thing->InterpolatedPosition(Thread->Viewport->viewpoint.TicFrac); - sprite.pos.Z += thing->GetBobOffset(Thread->Viewport->viewpoint.TicFrac); - + sprite.pos.Z += thing->GetBobOffset(Thread->Viewport->viewpoint.TicFrac) - thing->SpriteOffset.Y; sprite.spritenum = thing->sprite; sprite.tex = nullptr; sprite.voxel = nullptr; diff --git a/src/rendering/swrenderer/things/r_sprite.cpp b/src/rendering/swrenderer/things/r_sprite.cpp index 348cc1bf4..7964b429f 100644 --- a/src/rendering/swrenderer/things/r_sprite.cpp +++ b/src/rendering/swrenderer/things/r_sprite.cpp @@ -83,8 +83,11 @@ namespace swrenderer const double thingxscalemul = spriteScale.X / tex->GetScale().X; // Calculate billboard line for the sprite + double SpriteOffX = (thing) ? thing->SpriteOffset.X : 0.; DVector2 dir = { viewport->viewpoint.Sin, -viewport->viewpoint.Cos }; - DVector2 pt1 = pos.XY() - viewport->viewpoint.Pos.XY() - dir * (((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->GetLeftOffsetSW() - 1) : tex->GetLeftOffsetSW()) * thingxscalemul); + DVector2 trs = pos.XY() - viewport->viewpoint.Pos.XY(); + trs = { trs.X + SpriteOffX * dir.X, trs.Y + SpriteOffX * dir.Y }; + DVector2 pt1 = trs - dir * (((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->GetLeftOffsetSW() - 1) : tex->GetLeftOffsetSW()) * thingxscalemul); DVector2 pt2 = pt1 + dir * (tex->GetWidth() * thingxscalemul); FWallCoords wallc; diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index 8f233fc77..a0d8dc971 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -1759,6 +1759,7 @@ DEFINE_FIELD_NAMED(AActor, __Pos, pos) DEFINE_FIELD_NAMED(AActor, __Pos.X, x) DEFINE_FIELD_NAMED(AActor, __Pos.Y, y) DEFINE_FIELD_NAMED(AActor, __Pos.Z, z) +DEFINE_FIELD(AActor, SpriteOffset) DEFINE_FIELD(AActor, Prev) DEFINE_FIELD(AActor, SpriteAngle) DEFINE_FIELD(AActor, SpriteRotation) diff --git a/wadsrc/static/zscript/actors/actions.zs b/wadsrc/static/zscript/actors/actions.zs index 8e27a46d2..69a6542eb 100644 --- a/wadsrc/static/zscript/actors/actions.zs +++ b/wadsrc/static/zscript/actors/actions.zs @@ -95,4 +95,9 @@ extend class Actor } } + void A_SpriteOffset(double ox = 0.0, double oy = 0.0) + { + SpriteOffset.X = ox; + SpriteOffset.Y = oy; + } } diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index cade8322d..b9819ec96 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -89,6 +89,7 @@ class Actor : Thinker native native PlayerInfo Player; native readonly vector3 Pos; native vector3 Prev; + native vector2 SpriteOffset; native double spriteAngle; native double spriteRotation; native double VisibleStartAngle;