diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 0b76592e2..d2ca855c8 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -989,6 +989,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 ee6008c2b..8f9afa5f2 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -366,7 +366,8 @@ void AActor::Serialize(FSerializer &arc) A("spawntime", SpawnTime) A("spawnorder", SpawnOrder) A("friction", Friction) - A("userlights", UserLights); + A("userlights", UserLights) + A("SpriteOffset", SpriteOffset); } #undef A diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index f215e1ef4..56d914591 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -846,12 +846,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; @@ -862,31 +861,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; @@ -894,6 +901,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t y2 = y - viewvecX*rightfac; break; } + } } else { diff --git a/src/rendering/polyrenderer/scene/poly_sprite.cpp b/src/rendering/polyrenderer/scene/poly_sprite.cpp index 5428f8de6..e6866950a 100644 --- a/src/rendering/polyrenderer/scene/poly_sprite.cpp +++ b/src/rendering/polyrenderer/scene/poly_sprite.cpp @@ -66,6 +66,8 @@ bool RenderPolySprite::GetLine(AActor *thing, DVector2 &left, DVector2 &right) else offsetX = tex->GetLeftOffsetPo() * thingxscalemul; + offsetX -= thing->SpriteOffset.X; + left = DVector2(pos.X - viewpoint.Sin * offsetX, pos.Y + viewpoint.Cos * offsetX); right = DVector2(left.X + viewpoint.Sin * spriteWidth, left.Y - viewpoint.Cos * spriteWidth); return true; @@ -113,7 +115,7 @@ void RenderPolySprite::Render(PolyRenderThread *thread, AActor *thing, subsector double spriteHeight = thingyscalemul * tex->GetHeight(); posZ -= (tex->GetHeight() - tex->GetTopOffsetPo()) * thingyscalemul; - posZ = PerformSpriteClipAdjustment(thing, thingpos, spriteHeight, posZ); + posZ = PerformSpriteClipAdjustment(thing, thingpos, spriteHeight, posZ) - thing->SpriteOffset.Y; //double depth = 1.0; //visstyle_t visstyle = GetSpriteVisStyle(thing, depth); diff --git a/src/rendering/polyrenderer/scene/poly_wallsprite.cpp b/src/rendering/polyrenderer/scene/poly_wallsprite.cpp index d826186db..767c42382 100644 --- a/src/rendering/polyrenderer/scene/poly_wallsprite.cpp +++ b/src/rendering/polyrenderer/scene/poly_wallsprite.cpp @@ -37,7 +37,7 @@ void RenderPolyWallSprite::Render(PolyRenderThread *thread, AActor *thing, subse const auto &viewpoint = PolyRenderer::Instance()->Viewpoint; DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac); - pos.Z += thing->GetBobOffset(viewpoint.TicFrac); + pos.Z += thing->GetBobOffset(viewpoint.TicFrac) - thing->SpriteOffset.Y; bool flipTextureX = false; FSoftwareTexture *tex = RenderPolySprite::GetSpriteTexture(thing, flipTextureX); @@ -56,7 +56,7 @@ void RenderPolyWallSprite::Render(PolyRenderThread *thread, AActor *thing, subse // Determine left and right edges of sprite. The sprite's angle is its normal, // so the edges are 90 degrees each side of it. double x2 = tex->GetScaledWidth() * spriteScale.X; - double x1 = tex->GetScaledLeftOffsetPo() * spriteScale.X; + double x1 = (tex->GetScaledLeftOffsetPo() * spriteScale.X) - thing->SpriteOffset.X; DVector2 left, right; left.X = pos.X - x1 * angcos; left.Y = pos.Y - x1 * angsin; diff --git a/src/rendering/swrenderer/scene/r_opaque_pass.cpp b/src/rendering/swrenderer/scene/r_opaque_pass.cpp index 2674e782d..8542ee4dd 100644 --- a/src/rendering/swrenderer/scene/r_opaque_pass.cpp +++ b/src/rendering/swrenderer/scene/r_opaque_pass.cpp @@ -1002,9 +1002,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 1ae8119c2..e1d21cbe6 100644 --- a/src/rendering/swrenderer/things/r_sprite.cpp +++ b/src/rendering/swrenderer/things/r_sprite.cpp @@ -95,6 +95,9 @@ namespace swrenderer } //tx2 = tx >> 4; + // The Y offsetting is done in r_opaque_pass.cpp, in RenderOpaquePass::GetThingSprite(). + tx += thing->SpriteOffset.X; + // too far off the side? if (fabs(tx / 64) > fabs(tz)) { diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index e2548489f..a137ff9bb 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -1711,6 +1711,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 95b162a7a..64e2ee759 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;