From 71350f2c17f011a722279130fff402606c728f39 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 2 Dec 2016 08:12:01 +0100 Subject: [PATCH] Sprite line clipping by subsector --- src/r_poly_portal.cpp | 91 ++++++++++++++++++++++++++++++------------- src/r_poly_portal.h | 44 ++++++++------------- src/r_poly_sprite.cpp | 57 ++++++++++++++++----------- src/r_poly_sprite.h | 1 + 4 files changed, 118 insertions(+), 75 deletions(-) diff --git a/src/r_poly_portal.cpp b/src/r_poly_portal.cpp index 5c4dbc89fd..9637d739c3 100644 --- a/src/r_poly_portal.cpp +++ b/src/r_poly_portal.cpp @@ -62,9 +62,8 @@ void RenderPolyPortal::Render(int portalDepth) void RenderPolyPortal::ClearBuffers() { - SectorSpriteRanges.clear(); - SectorSpriteRanges.resize(numsectors); - SortedSprites.clear(); + SeenSectors.clear(); + SubsectorDepths.clear(); TranslucentObjects.clear(); SectorPortals.clear(); LinePortals.clear(); @@ -117,35 +116,61 @@ void RenderPolyPortal::RenderSubsector(subsector_t *sub) } } - SpriteRange sprites = GetSpritesForSector(sub->sector); - for (int i = 0; i < sprites.Count; i++) - { - AActor *thing = SortedSprites[sprites.Start + i].Thing; - TranslucentObjects.push_back({ thing, sub, subsectorDepth }); - } - - TranslucentObjects.insert(TranslucentObjects.end(), SubsectorTranslucentWalls.begin(), SubsectorTranslucentWalls.end()); - SubsectorTranslucentWalls.clear(); + SeenSectors.insert(sub->sector); + SubsectorDepths[sub] = subsectorDepth; } -SpriteRange RenderPolyPortal::GetSpritesForSector(sector_t *sector) +void RenderPolyPortal::RenderSprite(AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right) { - if ((int)SectorSpriteRanges.size() < sector->sectornum || sector->sectornum < 0) - return SpriteRange(); + if (numnodes == 0) + RenderSprite(thing, sortDistance, left, right, 0.0, 1.0, subsectors); + else + RenderSprite(thing, sortDistance, left, right, 0.0, 1.0, nodes + numnodes - 1); // The head node is the last node output. +} - auto &range = SectorSpriteRanges[sector->sectornum]; - if (range.Start == -1) +void RenderPolyPortal::RenderSprite(AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node) +{ + while (!((size_t)node & 1)) // Keep going until found a subsector { - range.Start = (int)SortedSprites.size(); - range.Count = 0; - for (AActor *thing = sector->thinglist; thing != nullptr; thing = thing->snext) + node_t *bsp = (node_t *)node; + + DVector2 planePos(FIXED2DBL(bsp->x), FIXED2DBL(bsp->y)); + DVector2 planeNormal = DVector2(FIXED2DBL(-bsp->dy), FIXED2DBL(bsp->dx)); + double planeD = planeNormal | planePos; + + int sideLeft = (left | planeNormal) > planeD; + int sideRight = (right | planeNormal) > planeD; + + if (sideLeft != sideRight) { - SortedSprites.push_back({ thing, (thing->Pos() - ViewPos).LengthSquared() }); - range.Count++; + double dotLeft = planeNormal | left; + double dotRight = planeNormal | right; + double t = (planeD - dotLeft) / (dotRight - dotLeft); + + DVector2 mid = left * (1.0 - t) + right * t; + double tmid = t1 * (1.0 - t) + t2 * t; + + if (sideLeft == 0) + { + RenderSprite(thing, sortDistance, mid, right, tmid, t2, bsp->children[sideRight]); + right = mid; + t2 = tmid; + } + else + { + RenderSprite(thing, sortDistance, left, mid, t1, tmid, bsp->children[sideLeft]); + left = mid; + t1 = tmid; + } } - std::stable_sort(SortedSprites.begin() + range.Start, SortedSprites.begin() + range.Start + range.Count); + node = bsp->children[sideLeft]; } - return range; + + subsector_t *sub = (subsector_t *)((BYTE *)node - 1); + + auto it = SubsectorDepths.find(sub); + if (it != SubsectorDepths.end()) + TranslucentObjects.push_back({ thing, sub, it->second, sortDistance, (float)t1, (float)t2 }); } void RenderPolyPortal::RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth) @@ -178,12 +203,12 @@ void RenderPolyPortal::RenderLine(subsector_t *sub, seg_t *line, sector_t *front if (!(fakeFloor->flags & FF_EXISTS)) continue; if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; if (!fakeFloor->model) continue; - RenderPolyWall::Render3DFloorLine(WorldToClip, PortalPlane, line, frontsector, subsectorDepth, StencilValue, fakeFloor, SubsectorTranslucentWalls); + RenderPolyWall::Render3DFloorLine(WorldToClip, PortalPlane, line, frontsector, subsectorDepth, StencilValue, fakeFloor, TranslucentObjects); } } // Render wall, and update culling info if its an occlusion blocker - if (RenderPolyWall::RenderLine(WorldToClip, PortalPlane, line, frontsector, subsectorDepth, StencilValue, SubsectorTranslucentWalls, LinePortals)) + if (RenderPolyWall::RenderLine(WorldToClip, PortalPlane, line, frontsector, subsectorDepth, StencilValue, TranslucentObjects, LinePortals)) { if (hasSegmentRange) Cull.MarkSegmentCulled(sx1, sx2); @@ -287,6 +312,20 @@ void RenderPolyPortal::RenderTranslucent(int portalDepth) } } + for (sector_t *sector : SeenSectors) + { + for (AActor *thing = sector->thinglist; thing != nullptr; thing = thing->snext) + { + DVector2 left, right; + if (!RenderPolySprite::GetLine(thing, left, right)) + continue; + double distanceSquared = (thing->Pos() - ViewPos).LengthSquared(); + RenderSprite(thing, distanceSquared, left, right); + } + } + + std::stable_sort(TranslucentObjects.begin(), TranslucentObjects.end()); + for (auto it = TranslucentObjects.rbegin(); it != TranslucentObjects.rend(); ++it) { auto &obj = *it; diff --git a/src/r_poly_portal.h b/src/r_poly_portal.h index e2b131b323..8623f86861 100644 --- a/src/r_poly_portal.h +++ b/src/r_poly_portal.h @@ -38,40 +38,31 @@ #include "r_poly_particle.h" #include "r_poly_plane.h" #include "r_poly_cull.h" - -// Used for sorting things by distance to the camera -class PolySortedSprite -{ -public: - PolySortedSprite(AActor *thing, double distanceSquared) : Thing(thing), DistanceSquared(distanceSquared) { } - bool operator<(const PolySortedSprite &other) const { return DistanceSquared < other.DistanceSquared; } - - AActor *Thing; - double DistanceSquared; -}; +#include +#include class PolyTranslucentObject { public: PolyTranslucentObject(particle_t *particle, subsector_t *sub, uint32_t subsectorDepth) : particle(particle), sub(sub), subsectorDepth(subsectorDepth) { } - PolyTranslucentObject(AActor *thing, subsector_t *sub, uint32_t subsectorDepth) : thing(thing), sub(sub), subsectorDepth(subsectorDepth) { } + PolyTranslucentObject(AActor *thing, subsector_t *sub, uint32_t subsectorDepth, double dist, float t1, float t2) : thing(thing), sub(sub), subsectorDepth(subsectorDepth), DistanceSquared(dist), SpriteLeft(t1), SpriteRight(t2) { } PolyTranslucentObject(RenderPolyWall wall) : wall(wall) { } + bool operator<(const PolyTranslucentObject &other) const + { + return subsectorDepth != other.subsectorDepth ? subsectorDepth < other.subsectorDepth : DistanceSquared < other.DistanceSquared; + } + particle_t *particle = nullptr; AActor *thing = nullptr; subsector_t *sub = nullptr; - uint32_t subsectorDepth = 0; RenderPolyWall wall; -}; - -class SpriteRange -{ -public: - SpriteRange() = default; - SpriteRange(int start, int count) : Start(start), Count(count) { } - int Start = -1; - int Count = 0; + + uint32_t subsectorDepth = 0; + double DistanceSquared = 1.e6; + + float SpriteLeft = 0.0f, SpriteRight = 1.0f; }; class PolyDrawSectorPortal; @@ -95,18 +86,17 @@ private: void RenderSectors(); void RenderSubsector(subsector_t *sub); void RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth); - - SpriteRange GetSpritesForSector(sector_t *sector); + void RenderSprite(AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right); + void RenderSprite(AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node); TriMatrix WorldToClip; Vec4f PortalPlane; uint32_t StencilValue = 0; PolyCull Cull; uint32_t NextSubsectorDepth = 0; - std::vector SectorSpriteRanges; - std::vector SortedSprites; + std::set SeenSectors; + std::unordered_map SubsectorDepths; std::vector TranslucentObjects; - std::vector SubsectorTranslucentWalls; std::vector> SectorPortals; std::vector> LinePortals; diff --git a/src/r_poly_sprite.cpp b/src/r_poly_sprite.cpp index 46c90adc26..5d4a9ba6cb 100644 --- a/src/r_poly_sprite.cpp +++ b/src/r_poly_sprite.cpp @@ -32,11 +32,43 @@ EXTERN_CVAR(Float, transsouls) EXTERN_CVAR(Int, r_drawfuzz) -void RenderPolySprite::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue) +bool RenderPolySprite::GetLine(AActor *thing, DVector2 &left, DVector2 &right) { if (IsThingCulled(thing)) - return; + return false; + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + + bool flipTextureX = false; + FTexture *tex = GetSpriteTexture(thing, flipTextureX); + if (tex == nullptr) + return false; + + DVector2 spriteScale = thing->Scale; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + + if (flipTextureX) + pos.X -= (tex->GetWidth() - tex->LeftOffset) * thingxscalemul; + else + pos.X -= tex->LeftOffset * thingxscalemul; + + double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + pos.X += spriteHalfWidth; + + left = DVector2(pos.X - ViewSin * spriteHalfWidth, pos.Y + ViewCos * spriteHalfWidth); + right = DVector2(pos.X + ViewSin * spriteHalfWidth, pos.Y - ViewCos * spriteHalfWidth); + return true; +} + +void RenderPolySprite::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue) +{ + DVector2 points[2]; + if (!GetLine(thing, points[0], points[1])) + return; + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); pos.Z += thing->GetBobOffset(r_TicFracF); @@ -44,6 +76,7 @@ void RenderPolySprite::Render(const TriMatrix &worldToClip, const Vec4f &clipPla FTexture *tex = GetSpriteTexture(thing, flipTextureX); if (tex == nullptr) return; + DVector2 spriteScale = thing->Scale; double thingxscalemul = spriteScale.X / tex->Scale.X; double thingyscalemul = spriteScale.Y / tex->Scale.Y; @@ -61,26 +94,6 @@ void RenderPolySprite::Render(const TriMatrix &worldToClip, const Vec4f &clipPla pos.X += spriteHalfWidth; - DVector2 points[2] = - { - { pos.X - ViewSin * spriteHalfWidth, pos.Y + ViewCos * spriteHalfWidth }, - { pos.X + ViewSin * spriteHalfWidth, pos.Y - ViewCos * spriteHalfWidth } - }; - - // Is this sprite inside? (To do: clip the points) - for (int i = 0; i < 2; i++) - { - for (uint32_t i = 0; i < sub->numlines; i++) - { - seg_t *line = &sub->firstline[i]; - double nx = line->v1->fY() - line->v2->fY(); - double ny = line->v2->fX() - line->v1->fX(); - double d = -(line->v1->fX() * nx + line->v1->fY() * ny); - if (pos.X * nx + pos.Y * ny + d > 0.0) - return; - } - } - //double depth = 1.0; //visstyle_t visstyle = GetSpriteVisStyle(thing, depth); // Rumor has it that AlterWeaponSprite needs to be called with visstyle passed in somewhere around here.. diff --git a/src/r_poly_sprite.h b/src/r_poly_sprite.h index 04b0fcb942..8ce9caffdd 100644 --- a/src/r_poly_sprite.h +++ b/src/r_poly_sprite.h @@ -31,6 +31,7 @@ class RenderPolySprite public: void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue); + static bool GetLine(AActor *thing, DVector2 &left, DVector2 &right); static bool IsThingCulled(AActor *thing); static FTexture *GetSpriteTexture(AActor *thing, /*out*/ bool &flipX);