diff --git a/src/swrenderer/scene/r_translucent_pass.cpp b/src/swrenderer/scene/r_translucent_pass.cpp index d4bbb1682..06b61007d 100644 --- a/src/swrenderer/scene/r_translucent_pass.cpp +++ b/src/swrenderer/scene/r_translucent_pass.cpp @@ -165,6 +165,7 @@ namespace swrenderer { CollectPortals(); Thread->SpriteList->Sort(); + Thread->DrawSegments->BuildSegmentGroups(); Clip3DFloors *clip3d = Thread->Clip3DFloors.get(); if (clip3d->height_top == nullptr) diff --git a/src/swrenderer/segments/r_drawsegment.cpp b/src/swrenderer/segments/r_drawsegment.cpp index f84d48a7a..84753cd4c 100644 --- a/src/swrenderer/segments/r_drawsegment.cpp +++ b/src/swrenderer/segments/r_drawsegment.cpp @@ -40,6 +40,7 @@ #include "swrenderer/things/r_visiblesprite.h" #include "swrenderer/scene/r_light.h" #include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_renderthread.h" namespace swrenderer { @@ -83,4 +84,83 @@ namespace swrenderer { InterestingSegments.Push(segment); } + + void DrawSegmentList::BuildSegmentGroups() + { + SegmentGroups.Clear(); + + unsigned int groupSize = 100; + for (unsigned int index = BeginIndex(); index < EndIndex(); index += groupSize) + { + auto ds = Segment(index); + + DrawSegmentGroup group; + group.BeginIndex = index; + group.EndIndex = MIN(index + groupSize, EndIndex()); + group.x1 = ds->x1; + group.x2 = ds->x2; + group.neardepth = MIN(ds->sz1, ds->sz2); + group.fardepth = MAX(ds->sz1, ds->sz2); + + for (unsigned int groupIndex = group.BeginIndex + 1; groupIndex < group.EndIndex; groupIndex++) + { + ds = Segment(groupIndex); + group.x1 = MIN(group.x1, ds->x1); + group.x2 = MAX(group.x2, ds->x2); + group.neardepth = MIN(group.neardepth, ds->sz1); + group.neardepth = MIN(group.neardepth, ds->sz2); + group.fardepth = MAX(ds->sz1, group.fardepth); + group.fardepth = MAX(ds->sz2, group.fardepth); + } + + for (int x = group.x1; x < group.x2; x++) + { + cliptop[x] = 0; + clipbottom[x] = viewheight; + } + + for (unsigned int groupIndex = group.BeginIndex; groupIndex < group.EndIndex; groupIndex++) + { + ds = Segment(groupIndex); + + // kg3D - no clipping on fake segs + if (ds->fake) continue; + + if (ds->silhouette & SIL_BOTTOM) + { + short *clip1 = clipbottom + ds->x1; + const short *clip2 = ds->sprbottomclip; + int i = ds->x2 - ds->x1; + do + { + if (*clip1 > *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + + if (ds->silhouette & SIL_TOP) + { + short *clip1 = cliptop + ds->x1; + const short *clip2 = ds->sprtopclip; + int i = ds->x2 - ds->x1; + do + { + if (*clip1 < *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + } + + group.sprtopclip = Thread->FrameMemory->AllocMemory(group.x2 - group.x1); + group.sprbottomclip = Thread->FrameMemory->AllocMemory(group.x2 - group.x1); + memcpy(group.sprtopclip, cliptop + group.x1, (group.x2 - group.x1) * sizeof(short)); + memcpy(group.sprbottomclip, clipbottom + group.x1, (group.x2 - group.x1) * sizeof(short)); + + SegmentGroups.Push(group); + } + } } diff --git a/src/swrenderer/segments/r_drawsegment.h b/src/swrenderer/segments/r_drawsegment.h index 2fa41cd94..a82b2333b 100644 --- a/src/swrenderer/segments/r_drawsegment.h +++ b/src/swrenderer/segments/r_drawsegment.h @@ -47,11 +47,23 @@ namespace swrenderer int CurrentPortalUniq; // [ZZ] to identify the portal that this drawseg is in. used for sprite clipping. }; + struct DrawSegmentGroup + { + short x1, x2; + float neardepth, fardepth; + short *sprtopclip; + short *sprbottomclip; + unsigned int BeginIndex; + unsigned int EndIndex; + }; + class DrawSegmentList { public: DrawSegmentList(RenderThread *thread); + TArray SegmentGroups; + unsigned int BeginIndex() const { return StartIndices.Last(); } unsigned int EndIndex() const { return Segments.Size(); } DrawSegment *Segment(unsigned int index) const { return Segments[Segments.Size() - 1 - index]; } @@ -66,6 +78,8 @@ namespace swrenderer void Push(DrawSegment *segment); void PushInteresting(DrawSegment *segment); + void BuildSegmentGroups(); + RenderThread *Thread = nullptr; private: @@ -74,5 +88,9 @@ namespace swrenderer TArray InterestingSegments; // drawsegs that have something drawn on them TArray StartInterestingIndices; + + // For building segment groups + short cliptop[MAXWIDTH]; + short clipbottom[MAXWIDTH]; }; } diff --git a/src/swrenderer/things/r_visiblesprite.cpp b/src/swrenderer/things/r_visiblesprite.cpp index 1d9111e74..2bfd31f9b 100644 --- a/src/swrenderer/things/r_visiblesprite.cpp +++ b/src/swrenderer/things/r_visiblesprite.cpp @@ -285,70 +285,20 @@ namespace swrenderer // The first drawseg that is closer than the sprite is the clip seg. DrawSegmentList *segmentlist = thread->DrawSegments.get(); - for (unsigned int index = segmentlist->BeginIndex(); index != segmentlist->EndIndex(); index++) + for (unsigned int groupIndex = 0; groupIndex < segmentlist->SegmentGroups.Size(); groupIndex++) { - DrawSegment *ds = segmentlist->Segment(index); - - // [ZZ] portal handling here - //if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) - // continue; - // [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping - - // kg3D - no clipping on fake segs - if (ds->fake) continue; - // determine if the drawseg obscures the sprite - if (ds->x1 >= x2 || ds->x2 <= x1 || - (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == nullptr && - !ds->bFogBoundary)) - { - // does not cover sprite + auto &group = segmentlist->SegmentGroups[groupIndex]; + if (group.x1 >= x2 || group.x2 <= x1) continue; - } - r1 = MAX(ds->x1, x1); - r2 = MIN(ds->x2, x2); - - float neardepth, fardepth; - if (!spr->IsWallSprite()) + if (group.fardepth < spr->depth) { - if (ds->sz1 < ds->sz2) - { - neardepth = ds->sz1, fardepth = ds->sz2; - } - else - { - neardepth = ds->sz2, fardepth = ds->sz1; - } - } + r1 = MAX(group.x1, x1); + r2 = MIN(group.x2, x2); - - // Check if sprite is in front of draw seg: - if ((!spr->IsWallSprite() && neardepth > spr->depth) || ((spr->IsWallSprite() || fardepth > spr->depth) && - (spr->gpos.Y - ds->curline->v1->fY()) * (ds->curline->v2->fX() - ds->curline->v1->fX()) - - (spr->gpos.X - ds->curline->v1->fX()) * (ds->curline->v2->fY() - ds->curline->v1->fY()) <= 0)) - { - RenderPortal *renderportal = thread->Portal.get(); - - // seg is behind sprite, so draw the mid texture if it has one - if (ds->CurrentPortalUniq == renderportal->CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here - (ds->maskedtexturecol != nullptr || ds->bFogBoundary)) - { - RenderDrawSegment renderer(thread); - renderer.Render(ds, r1, r2); - } - - continue; - } - - // clip this piece of the sprite - // killough 3/27/98: optimized and made much shorter - // [RH] Optimized further (at least for VC++; - // other compilers should be at least as good as before) - - if (ds->silhouette & SIL_BOTTOM) //bottom sil - { + // Clip bottom clip1 = clipbot + r1; - clip2 = ds->sprbottomclip + r1 - ds->x1; + clip2 = group.sprbottomclip + r1 - group.x1; i = r2 - r1; do { @@ -357,12 +307,10 @@ namespace swrenderer clip1++; clip2++; } while (--i); - } - if (ds->silhouette & SIL_TOP) // top sil - { + // Clip top clip1 = cliptop + r1; - clip2 = ds->sprtopclip + r1 - ds->x1; + clip2 = group.sprtopclip + r1 - group.x1; i = r2 - r1; do { @@ -372,6 +320,97 @@ namespace swrenderer clip2++; } while (--i); } + else + { + //for (unsigned int index = segmentlist->BeginIndex(); index != segmentlist->EndIndex(); index++) + for (unsigned int index = group.BeginIndex; index != group.EndIndex; index++) + { + DrawSegment *ds = segmentlist->Segment(index); + + // [ZZ] portal handling here + //if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) + // continue; + // [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping + + // kg3D - no clipping on fake segs + if (ds->fake) continue; + // determine if the drawseg obscures the sprite + if (ds->x1 >= x2 || ds->x2 <= x1 || + (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == nullptr && + !ds->bFogBoundary)) + { + // does not cover sprite + continue; + } + + r1 = MAX(ds->x1, x1); + r2 = MIN(ds->x2, x2); + + float neardepth, fardepth; + if (!spr->IsWallSprite()) + { + if (ds->sz1 < ds->sz2) + { + neardepth = ds->sz1, fardepth = ds->sz2; + } + else + { + neardepth = ds->sz2, fardepth = ds->sz1; + } + } + + // Check if sprite is in front of draw seg: + if ((!spr->IsWallSprite() && neardepth > spr->depth) || ((spr->IsWallSprite() || fardepth > spr->depth) && + (spr->gpos.Y - ds->curline->v1->fY()) * (ds->curline->v2->fX() - ds->curline->v1->fX()) - + (spr->gpos.X - ds->curline->v1->fX()) * (ds->curline->v2->fY() - ds->curline->v1->fY()) <= 0)) + { + RenderPortal *renderportal = thread->Portal.get(); + + // seg is behind sprite, so draw the mid texture if it has one + if (ds->CurrentPortalUniq == renderportal->CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here + (ds->maskedtexturecol != nullptr || ds->bFogBoundary)) + { + RenderDrawSegment renderer(thread); + renderer.Render(ds, r1, r2); + } + + continue; + } + + // clip this piece of the sprite + // killough 3/27/98: optimized and made much shorter + // [RH] Optimized further (at least for VC++; + // other compilers should be at least as good as before) + + if (ds->silhouette & SIL_BOTTOM) //bottom sil + { + clip1 = clipbot + r1; + clip2 = ds->sprbottomclip + r1 - ds->x1; + i = r2 - r1; + do + { + if (*clip1 > *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + + if (ds->silhouette & SIL_TOP) // top sil + { + clip1 = cliptop + r1; + clip2 = ds->sprtopclip + r1 - ds->x1; + i = r2 - r1; + do + { + if (*clip1 < *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + } + } } // all clipping has been performed, so draw the sprite