From 6d642b190644d2f7c64dd24987a2940a375a4cc6 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Mon, 9 Jan 2017 14:20:47 +0100 Subject: [PATCH] Move FCoverageBuffer to r_voxel --- src/swrenderer/scene/r_things.cpp | 669 +----------------------------- src/swrenderer/things/r_voxel.cpp | 618 +++++++++++++++++++++++++++ src/swrenderer/things/r_voxel.h | 25 ++ 3 files changed, 644 insertions(+), 668 deletions(-) diff --git a/src/swrenderer/scene/r_things.cpp b/src/swrenderer/scene/r_things.cpp index 57a0700fb..c91120c7f 100644 --- a/src/swrenderer/scene/r_things.cpp +++ b/src/swrenderer/scene/r_things.cpp @@ -19,12 +19,6 @@ // DESCRIPTION: // Refresh of things, i.e. objects represented by sprites. // -// This file contains some code from the Build Engine. -// -// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman -// Ken Silverman's official web site: "http://www.advsys.net/ken" -// See the included license file "BUILDLIC.TXT" for license info. -// //----------------------------------------------------------------------------- #include @@ -85,29 +79,6 @@ namespace swrenderer { using namespace drawerargs; -// [RH] A c-buffer. Used for keeping track of offscreen voxel spans. - -struct FCoverageBuffer -{ - struct Span - { - Span *NextSpan; - short Start, Stop; - }; - - FCoverageBuffer(int size); - ~FCoverageBuffer(); - - void Clear(); - void InsertSpan(int listnum, int start, int stop); - Span *AllocSpan(); - - FMemArena SpanArena; - Span **Spans; // [0..NumLists-1] span lists - Span *FreeSpans; - unsigned int NumLists; -}; - // // Sprite rotation 0 is facing the viewer, // rotation 1 is one angle turn CLOCKWISE around the axis. @@ -128,9 +99,6 @@ FTexture *WallSpriteTile; // INITIALIZATION FUNCTIONS // -int OffscreenBufferWidth, OffscreenBufferHeight; -BYTE *OffscreenColorBuffer; -FCoverageBuffer *OffscreenCoverageBuffer; // @@ -148,6 +116,7 @@ static int vsprcount; void R_DeinitSprites() { R_DeinitVisSprites(); + R_DeinitRenderVoxel(); // Free vissprites sorter if (spritesorter != NULL) @@ -156,19 +125,6 @@ void R_DeinitSprites() spritesortersize = 0; spritesorter = NULL; } - - // Free offscreen buffer - if (OffscreenColorBuffer != NULL) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = NULL; - } - if (OffscreenCoverageBuffer != NULL) - { - delete OffscreenCoverageBuffer; - OffscreenCoverageBuffer = NULL; - } - OffscreenBufferHeight = OffscreenBufferWidth = 0; } // @@ -241,81 +197,6 @@ bool R_ClipSpriteColumnWithPortals(vissprite_t* spr) return false; } - - -#if 0 -void R_DrawVisVoxel(vissprite_t *spr, int minslabz, int maxslabz, short *cliptop, short *clipbot) -{ - int flags = 0; - - // Do setup for blending. - R_SetColorMapLight(spr->Style.BaseColormap, 0, spr->Style.ColormapNum << FRACBITS); - bool visible = R_SetPatchStyle(spr->Style.RenderStyle, spr->Style.Alpha, spr->Translation, spr->FillColor); - - if (!visible) - { - return; - } - if (colfunc == fuzzcolfunc || colfunc == R_FillColumn) - { - flags = DVF_OFFSCREEN | DVF_SPANSONLY; - } - else if (colfunc != basecolfunc) - { - flags = DVF_OFFSCREEN; - } - if (flags != 0) - { - R_CheckOffscreenBuffer(RenderTarget->GetWidth(), RenderTarget->GetHeight(), !!(flags & DVF_SPANSONLY)); - } - if (spr->bInMirror) - { - flags |= DVF_MIRRORED; - } - - // Render the voxel, either directly to the screen or offscreen. - R_DrawVoxel(spr->pa.vpos, spr->pa.vang, spr->gpos, spr->Angle, - spr->xscale, FLOAT2FIXED(spr->yscale), spr->voxel, spr->Style.BaseColormap, spr->Style.ColormapNum, cliptop, clipbot, - minslabz, maxslabz, flags); - - // Blend the voxel, if that's what we need to do. - if ((flags & ~DVF_MIRRORED) != 0) - { - int pixelsize = r_swtruecolor ? 4 : 1; - for (int x = 0; x < viewwidth; ++x) - { - if (!(flags & DVF_SPANSONLY) && (x & 3) == 0) - { - rt_initcols(OffscreenColorBuffer + x * OffscreenBufferHeight); - } - for (FCoverageBuffer::Span *span = OffscreenCoverageBuffer->Spans[x]; span != NULL; span = span->NextSpan) - { - if (flags & DVF_SPANSONLY) - { - dc_x = x; - dc_yl = span->Start; - dc_yh = span->Stop - 1; - dc_count = span->Stop - span->Start; - dc_dest = (ylookup[span->Start] + x) * pixelsize + dc_destorg; - colfunc(); - } - else - { - rt_span_coverage(x, span->Start, span->Stop - 1); - } - } - if (!(flags & DVF_SPANSONLY) && (x & 3) == 3) - { - rt_draw4cols(x - 3); - } - } - } - - R_FinishSetPatchStyle(); - NetUpdate(); -} -#endif - // // R_ProjectSprite // Generates a vissprite for a thing if it might be visible. @@ -1535,552 +1416,4 @@ inline int sgn(int v) return v < 0 ? -1 : v > 0 ? 1 : 0; } -#if 0 -void R_DrawVoxel(const FVector3 &globalpos, FAngle viewangle, - const FVector3 &dasprpos, DAngle dasprang, - fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, - FSWColormap *colormap, int colormapnum, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags) -{ - int i, j, k, x, y, syoff, ggxstart, ggystart, nxoff; - fixed_t cosang, sinang, sprcosang, sprsinang; - int backx, backy, gxinc, gyinc; - int daxscalerecip, dayscalerecip, cnt, gxstart, gystart, dazscale; - int lx, rx, nx, ny, x1=0, y1=0, x2=0, y2=0, yinc=0; - int yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc; - kvxslab_t *voxptr, *voxend; - FVoxelMipLevel *mip; - int z1a[64], z2a[64], yplc[64]; - - const int nytooclose = centerxwide * 2100, nytoofar = 32768*32768 - 1048576; - const int xdimenscale = FLOAT2FIXED(centerxwide * YaspectMul / 160); - const double centerxwide_f = centerxwide; - const double centerxwidebig_f = centerxwide_f * 65536*65536*8; - - // Convert to Build's coordinate system. - fixed_t globalposx = xs_Fix<4>::ToFix(globalpos.X); - fixed_t globalposy = xs_Fix<4>::ToFix(-globalpos.Y); - fixed_t globalposz = xs_Fix<8>::ToFix(-globalpos.Z); - - fixed_t dasprx = xs_Fix<4>::ToFix(dasprpos.X); - fixed_t daspry = xs_Fix<4>::ToFix(-dasprpos.Y); - fixed_t dasprz = xs_Fix<8>::ToFix(-dasprpos.Z); - - // Shift the scales from 16 bits of fractional precision to 6. - // Also do some magic voodoo scaling to make them the right size. - daxscale = daxscale / (0xC000 >> 6); - dayscale = dayscale / (0xC000 >> 6); - if (daxscale <= 0 || dayscale <= 0) - { - // won't be visible. - return; - } - - angle_t viewang = viewangle.BAMs(); - cosang = FLOAT2FIXED(viewangle.Cos()) >> 2; - sinang = FLOAT2FIXED(-viewangle.Sin()) >> 2; - sprcosang = FLOAT2FIXED(dasprang.Cos()) >> 2; - sprsinang = FLOAT2FIXED(-dasprang.Sin()) >> 2; - - R_SetupDrawSlab(colormap, 0.0f, colormapnum << FRACBITS); - - int pixelsize = r_swtruecolor ? 4 : 1; - - // Select mip level - i = abs(DMulScale6(dasprx - globalposx, cosang, daspry - globalposy, sinang)); - i = DivScale6(i, MIN(daxscale, dayscale)); - j = xs_Fix<13>::ToFix(FocalLengthX); - for (k = 0; i >= j && k < voxobj->NumMips; ++k) - { - i >>= 1; - } - if (k >= voxobj->NumMips) k = voxobj->NumMips - 1; - - mip = &voxobj->Mips[k]; if (mip->SlabData == NULL) return; - - minslabz >>= k; - maxslabz >>= k; - - daxscale <<= (k+8); dayscale <<= (k+8); - dazscale = FixedDiv(dayscale, FLOAT2FIXED(BaseYaspectMul)); - daxscale = fixed_t(daxscale / YaspectMul); - daxscale = Scale(daxscale, xdimenscale, centerxwide << 9); - dayscale = Scale(dayscale, FixedMul(xdimenscale, viewingrangerecip), centerxwide << 9); - - daxscalerecip = (1<<30) / daxscale; - dayscalerecip = (1<<30) / dayscale; - - fixed_t piv_x = fixed_t(mip->Pivot.X*256.); - fixed_t piv_y = fixed_t(mip->Pivot.Y*256.); - fixed_t piv_z = fixed_t(mip->Pivot.Z*256.); - - x = FixedMul(globalposx - dasprx, daxscalerecip); - y = FixedMul(globalposy - daspry, daxscalerecip); - backx = (DMulScale10(x, sprcosang, y, sprsinang) + piv_x) >> 8; - backy = (DMulScale10(y, sprcosang, x, -sprsinang) + piv_y) >> 8; - cbackx = clamp(backx, 0, mip->SizeX - 1); - cbacky = clamp(backy, 0, mip->SizeY - 1); - - sprcosang = MulScale14(daxscale, sprcosang); - sprsinang = MulScale14(daxscale, sprsinang); - - x = (dasprx - globalposx) - DMulScale18(piv_x, sprcosang, piv_y, -sprsinang); - y = (daspry - globalposy) - DMulScale18(piv_y, sprcosang, piv_x, sprsinang); - - cosang = FixedMul(cosang, dayscalerecip); - sinang = FixedMul(sinang, dayscalerecip); - - gxstart = y*cosang - x*sinang; - gystart = x*cosang + y*sinang; - gxinc = DMulScale10(sprsinang, cosang, sprcosang, -sinang); - gyinc = DMulScale10(sprcosang, cosang, sprsinang, sinang); - if ((abs(globalposz - dasprz) >> 10) >= abs(dazscale)) return; - - x = 0; y = 0; j = MAX(mip->SizeX, mip->SizeY); - fixed_t *ggxinc = (fixed_t *)alloca((j + 1) * sizeof(fixed_t) * 2); - fixed_t *ggyinc = ggxinc + (j + 1); - for (i = 0; i <= j; i++) - { - ggxinc[i] = x; x += gxinc; - ggyinc[i] = y; y += gyinc; - } - - syoff = DivScale21(globalposz - dasprz, FixedMul(dazscale, 0xE800)) + (piv_z << 7); - yoff = (abs(gxinc) + abs(gyinc)) >> 1; - - for (cnt = 0; cnt < 8; cnt++) - { - switch (cnt) - { - case 0: xs = 0; ys = 0; xi = 1; yi = 1; break; - case 1: xs = mip->SizeX-1; ys = 0; xi = -1; yi = 1; break; - case 2: xs = 0; ys = mip->SizeY-1; xi = 1; yi = -1; break; - case 3: xs = mip->SizeX-1; ys = mip->SizeY-1; xi = -1; yi = -1; break; - case 4: xs = 0; ys = cbacky; xi = 1; yi = 2; break; - case 5: xs = mip->SizeX-1; ys = cbacky; xi = -1; yi = 2; break; - case 6: xs = cbackx; ys = 0; xi = 2; yi = 1; break; - case 7: xs = cbackx; ys = mip->SizeY-1; xi = 2; yi = -1; break; - } - xe = cbackx; ye = cbacky; - if (cnt < 4) - { - if ((xi < 0) && (xe >= xs)) continue; - if ((xi > 0) && (xe <= xs)) continue; - if ((yi < 0) && (ye >= ys)) continue; - if ((yi > 0) && (ye <= ys)) continue; - } - else - { - if ((xi < 0) && (xe > xs)) continue; - if ((xi > 0) && (xe < xs)) continue; - if ((yi < 0) && (ye > ys)) continue; - if ((yi > 0) && (ye < ys)) continue; - xe += xi; ye += yi; - } - - i = sgn(ys - backy) + sgn(xs - backx) * 3 + 4; - switch(i) - { - case 6: case 7: x1 = 0; y1 = 0; break; - case 8: case 5: x1 = gxinc; y1 = gyinc; break; - case 0: case 3: x1 = gyinc; y1 = -gxinc; break; - case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break; - } - switch(i) - { - case 2: case 5: x2 = 0; y2 = 0; break; - case 0: case 1: x2 = gxinc; y2 = gyinc; break; - case 8: case 7: x2 = gyinc; y2 = -gxinc; break; - case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break; - } - BYTE oand = (1 << int(xs 0) { dagxinc = gxinc; dagyinc = FixedMul(gyinc, viewingrangerecip); } - else { dagxinc = -gxinc; dagyinc = -FixedMul(gyinc, viewingrangerecip); } - - /* Fix for non 90 degree viewing ranges */ - nxoff = FixedMul(x2 - x1, viewingrangerecip); - x1 = FixedMul(x1, viewingrangerecip); - - ggxstart = gxstart + ggyinc[ys]; - ggystart = gystart - ggxinc[ys]; - - for (x = xs; x != xe; x += xi) - { - BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; - short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; - - nx = FixedMul(ggxstart + ggxinc[x], viewingrangerecip) + x1; - ny = ggystart + ggyinc[x]; - for (y = ys; y != ye; y += yi, nx += dagyinc, ny -= dagxinc) - { - if ((ny <= nytooclose) || (ny >= nytoofar)) continue; - voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); - voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); - if (voxptr >= voxend) continue; - - lx = xs_RoundToInt(nx * centerxwide_f / (ny + y1)) + centerx; - if (lx < 0) lx = 0; - rx = xs_RoundToInt((nx + nxoff) * centerxwide_f / (ny + y2)) + centerx; - if (rx > viewwidth) rx = viewwidth; - if (rx <= lx) continue; - - if (flags & DVF_MIRRORED) - { - int t = viewwidth - lx; - lx = viewwidth - rx; - rx = t; - } - - fixed_t l1 = xs_RoundToInt(centerxwidebig_f / (ny - yoff)); - fixed_t l2 = xs_RoundToInt(centerxwidebig_f / (ny + yoff)); - for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3)) - { - const BYTE *col = voxptr->col; - int zleng = voxptr->zleng; - int ztop = voxptr->ztop; - fixed_t z1, z2; - - if (ztop < minslabz) - { - int diff = minslabz - ztop; - ztop = minslabz; - col += diff; - zleng -= diff; - } - if (ztop + zleng > maxslabz) - { - int diff = ztop + zleng - maxslabz; - zleng -= diff; - } - if (zleng <= 0) continue; - - j = (ztop << 15) - syoff; - if (j < 0) - { - k = j + (zleng << 15); - if (k < 0) - { - if ((voxptr->backfacecull & oand32) == 0) continue; - z2 = MulScale32(l2, k) + centery; /* Below slab */ - } - else - { - if ((voxptr->backfacecull & oand) == 0) continue; /* Middle of slab */ - z2 = MulScale32(l1, k) + centery; - } - z1 = MulScale32(l1, j) + centery; - } - else - { - if ((voxptr->backfacecull & oand16) == 0) continue; - z1 = MulScale32(l2, j) + centery; /* Above slab */ - z2 = MulScale32(l1, j + (zleng << 15)) + centery; - } - - if (z2 <= z1) continue; - - if (zleng == 1) - { - yinc = 0; - } - else - { - if (z2-z1 >= 1024) yinc = FixedDiv(zleng, z2 - z1); - else yinc = (((1 << 24) - 1) / (z2 - z1)) * zleng >> 8; - } - // [RH] Clip each column separately, not just by the first one. - for (int stripwidth = MIN(countof(z1a), rx - lx), lxt = lx; - lxt < rx; - (lxt += countof(z1a)), stripwidth = MIN(countof(z1a), rx - lxt)) - { - // Calculate top and bottom pixels locations - for (int xxx = 0; xxx < stripwidth; ++xxx) - { - if (zleng == 1) - { - yplc[xxx] = 0; - z1a[xxx] = MAX(z1, daumost[lxt + xxx]); - } - else - { - if (z1 < daumost[lxt + xxx]) - { - yplc[xxx] = yinc * (daumost[lxt + xxx] - z1); - z1a[xxx] = daumost[lxt + xxx]; - } - else - { - yplc[xxx] = 0; - z1a[xxx] = z1; - } - } - z2a[xxx] = MIN(z2, dadmost[lxt + xxx]); - } - // Find top and bottom pixels that match and draw them as one strip - for (int xxl = 0, xxr; xxl < stripwidth; ) - { - if (z1a[xxl] >= z2a[xxl]) - { // No column here - xxl++; - continue; - } - int z1 = z1a[xxl]; - int z2 = z2a[xxl]; - // How many columns share the same extents? - for (xxr = xxl + 1; xxr < stripwidth; ++xxr) - { - if (z1a[xxr] != z1 || z2a[xxr] != z2) - break; - } - - if (!(flags & DVF_OFFSCREEN)) - { - // Draw directly to the screen. - R_DrawSlab(xxr - xxl, yplc[xxl], z2 - z1, yinc, col, (ylookup[z1] + lxt + xxl) * pixelsize + dc_destorg); - } - else - { - // Record the area covered and possibly draw to an offscreen buffer. - dc_yl = z1; - dc_yh = z2 - 1; - dc_count = z2 - z1; - dc_iscale = yinc; - for (int x = xxl; x < xxr; ++x) - { - OffscreenCoverageBuffer->InsertSpan(lxt + x, z1, z2); - if (!(flags & DVF_SPANSONLY)) - { - dc_x = lxt + x; - rt_initcols(OffscreenColorBuffer + (dc_x & ~3) * OffscreenBufferHeight); - dc_source = col; - dc_source2 = nullptr; - dc_texturefrac = yplc[xxl]; - hcolfunc_pre(); - } - } - } - xxl = xxr; - } - } - } - } - } - } -} -#endif - -//========================================================================== -// -// FCoverageBuffer Constructor -// -//========================================================================== - -FCoverageBuffer::FCoverageBuffer(int lists) - : Spans(NULL), FreeSpans(NULL) -{ - NumLists = lists; - Spans = new Span *[lists]; - memset(Spans, 0, sizeof(Span*)*lists); -} - -//========================================================================== -// -// FCoverageBuffer Destructor -// -//========================================================================== - -FCoverageBuffer::~FCoverageBuffer() -{ - if (Spans != NULL) - { - delete[] Spans; - } -} - -//========================================================================== -// -// FCoverageBuffer :: Clear -// -//========================================================================== - -void FCoverageBuffer::Clear() -{ - SpanArena.FreeAll(); - memset(Spans, 0, sizeof(Span*)*NumLists); - FreeSpans = NULL; -} - -//========================================================================== -// -// FCoverageBuffer :: InsertSpan -// -// start is inclusive. -// stop is exclusive. -// -//========================================================================== - -void FCoverageBuffer::InsertSpan(int listnum, int start, int stop) -{ - assert(unsigned(listnum) < NumLists); - assert(start < stop); - - Span **span_p = &Spans[listnum]; - Span *span; - - if (*span_p == NULL || (*span_p)->Start > stop) - { // This list is empty or the first entry is after this one, so we can just insert the span. - goto addspan; - } - - // Insert the new span in order, merging with existing ones. - while (*span_p != NULL) - { - if ((*span_p)->Stop < start) // ===== (existing span) - { // Span ends before this one starts. // ++++ (new span) - span_p = &(*span_p)->NextSpan; - continue; - } - - // Does the new span overlap or abut the existing one? - if ((*span_p)->Start <= start) - { - if ((*span_p)->Stop >= stop) // ============= - { // The existing span completely covers this one. // +++++ - return; - } -extend: // Extend the existing span with the new one. // ====== - span = *span_p; // +++++++ - span->Stop = stop; // (or) +++++ - - // Free up any spans we just covered up. - span_p = &(*span_p)->NextSpan; - while (*span_p != NULL && (*span_p)->Start <= stop && (*span_p)->Stop <= stop) - { - Span *span = *span_p; // ====== ====== - *span_p = span->NextSpan; // +++++++++++++ - span->NextSpan = FreeSpans; - FreeSpans = span; - } - if (*span_p != NULL && (*span_p)->Start <= stop) // ======= ======== - { // Our new span connects two existing spans. // ++++++++++++++ - // They should all be collapsed into a single span. - span->Stop = (*span_p)->Stop; - span = *span_p; - *span_p = span->NextSpan; - span->NextSpan = FreeSpans; - FreeSpans = span; - } - goto check; - } - else if ((*span_p)->Start <= stop) // ===== - { // The new span extends the existing span from // ++++ - // the beginning. // (or) ++++ - (*span_p)->Start = start; - if ((*span_p)->Stop < stop) - { // The new span also extends the existing span // ====== - // at the bottom // ++++++++++++++ - goto extend; - } - goto check; - } - else // ====== - { // No overlap, so insert a new span. // +++++ - goto addspan; - } - } - // Append a new span to the end of the list. -addspan: - span = AllocSpan(); - span->NextSpan = *span_p; - span->Start = start; - span->Stop = stop; - *span_p = span; -check: -#ifdef _DEBUG - // Validate the span list: Spans must be in order, and there must be - // at least one pixel between spans. - for (span = Spans[listnum]; span != NULL; span = span->NextSpan) - { - assert(span->Start < span->Stop); - if (span->NextSpan != NULL) - { - assert(span->Stop < span->NextSpan->Start); - } - } -#endif - ; -} - -//========================================================================== -// -// FCoverageBuffer :: AllocSpan -// -//========================================================================== - -FCoverageBuffer::Span *FCoverageBuffer::AllocSpan() -{ - Span *span; - - if (FreeSpans != NULL) - { - span = FreeSpans; - FreeSpans = span->NextSpan; - } - else - { - span = (Span *)SpanArena.Alloc(sizeof(Span)); - } - return span; -} - -//========================================================================== -// -// R_CheckOffscreenBuffer -// -// Allocates the offscreen coverage buffer and optionally the offscreen -// color buffer. If they already exist but are the wrong size, they will -// be reallocated. -// -//========================================================================== - -void R_CheckOffscreenBuffer(int width, int height, bool spansonly) -{ - if (OffscreenCoverageBuffer == NULL) - { - assert(OffscreenColorBuffer == NULL && "The color buffer cannot exist without the coverage buffer"); - OffscreenCoverageBuffer = new FCoverageBuffer(width); - } - else if (OffscreenCoverageBuffer->NumLists != (unsigned)width) - { - delete OffscreenCoverageBuffer; - OffscreenCoverageBuffer = new FCoverageBuffer(width); - if (OffscreenColorBuffer != NULL) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = NULL; - } - } - else - { - OffscreenCoverageBuffer->Clear(); - } - - if (!spansonly) - { - if (OffscreenColorBuffer == NULL) - { - OffscreenColorBuffer = new BYTE[width * height * 4]; - } - else if (OffscreenBufferWidth != width || OffscreenBufferHeight != height) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = new BYTE[width * height * 4]; - } - } - OffscreenBufferWidth = width; - OffscreenBufferHeight = height; -} - } diff --git a/src/swrenderer/things/r_voxel.cpp b/src/swrenderer/things/r_voxel.cpp index f0d29f488..c02e9ac8f 100644 --- a/src/swrenderer/things/r_voxel.cpp +++ b/src/swrenderer/things/r_voxel.cpp @@ -1,5 +1,6 @@ /* ** Voxel rendering +** Copyright (c) 1998-2016 Randy Heit ** Copyright (c) 2016 Magnus Norddahl ** ** This software is provided 'as-is', without any express or implied @@ -39,6 +40,13 @@ namespace swrenderer { + namespace + { + FCoverageBuffer *OffscreenCoverageBuffer; + int OffscreenBufferWidth, OffscreenBufferHeight; + uint8_t *OffscreenColorBuffer; + } + void R_DrawVisVoxel(vissprite_t *sprite, int minZ, int maxZ, short *cliptop, short *clipbottom) { R_SetColorMapLight(sprite->Style.BaseColormap, 0, sprite->Style.ColormapNum << FRACBITS); @@ -214,4 +222,614 @@ namespace swrenderer } } } + + void R_DeinitRenderVoxel() + { + // Free offscreen buffer + if (OffscreenColorBuffer != nullptr) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = nullptr; + } + if (OffscreenCoverageBuffer != nullptr) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = nullptr; + } + OffscreenBufferHeight = OffscreenBufferWidth = 0; + } + + void R_CheckOffscreenBuffer(int width, int height, bool spansonly) + { + // Allocates the offscreen coverage buffer and optionally the offscreen + // color buffer. If they already exist but are the wrong size, they will + // be reallocated. + + if (OffscreenCoverageBuffer == nullptr) + { + assert(OffscreenColorBuffer == nullptr && "The color buffer cannot exist without the coverage buffer"); + OffscreenCoverageBuffer = new FCoverageBuffer(width); + } + else if (OffscreenCoverageBuffer->NumLists != (unsigned)width) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = new FCoverageBuffer(width); + if (OffscreenColorBuffer != nullptr) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = nullptr; + } + } + else + { + OffscreenCoverageBuffer->Clear(); + } + + if (!spansonly) + { + if (OffscreenColorBuffer == nullptr) + { + OffscreenColorBuffer = new uint8_t[width * height * 4]; + } + else if (OffscreenBufferWidth != width || OffscreenBufferHeight != height) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = new uint8_t[width * height * 4]; + } + } + OffscreenBufferWidth = width; + OffscreenBufferHeight = height; + } + + //////////////////////////////////////////////////////////////////////////// + + FCoverageBuffer::FCoverageBuffer(int lists) + : Spans(nullptr), FreeSpans(nullptr) + { + NumLists = lists; + Spans = new Span *[lists]; + memset(Spans, 0, sizeof(Span*)*lists); + } + + FCoverageBuffer::~FCoverageBuffer() + { + if (Spans != nullptr) + { + delete[] Spans; + } + } + + void FCoverageBuffer::Clear() + { + SpanArena.FreeAll(); + memset(Spans, 0, sizeof(Span*)*NumLists); + FreeSpans = nullptr; + } + + void FCoverageBuffer::InsertSpan(int listnum, int start, int stop) + { + // start is inclusive. + // stop is exclusive. + + assert(unsigned(listnum) < NumLists); + assert(start < stop); + + Span **span_p = &Spans[listnum]; + Span *span; + + if (*span_p == nullptr || (*span_p)->Start > stop) + { // This list is empty or the first entry is after this one, so we can just insert the span. + goto addspan; + } + + // Insert the new span in order, merging with existing ones. + while (*span_p != nullptr) + { + if ((*span_p)->Stop < start) // ===== (existing span) + { // Span ends before this one starts. // ++++ (new span) + span_p = &(*span_p)->NextSpan; + continue; + } + + // Does the new span overlap or abut the existing one? + if ((*span_p)->Start <= start) + { + if ((*span_p)->Stop >= stop) // ============= + { // The existing span completely covers this one. // +++++ + return; + } + extend: // Extend the existing span with the new one. // ====== + span = *span_p; // +++++++ + span->Stop = stop; // (or) +++++ + + // Free up any spans we just covered up. + span_p = &(*span_p)->NextSpan; + while (*span_p != nullptr && (*span_p)->Start <= stop && (*span_p)->Stop <= stop) + { + Span *span = *span_p; // ====== ====== + *span_p = span->NextSpan; // +++++++++++++ + span->NextSpan = FreeSpans; + FreeSpans = span; + } + if (*span_p != nullptr && (*span_p)->Start <= stop) // ======= ======== + { // Our new span connects two existing spans. // ++++++++++++++ + // They should all be collapsed into a single span. + span->Stop = (*span_p)->Stop; + span = *span_p; + *span_p = span->NextSpan; + span->NextSpan = FreeSpans; + FreeSpans = span; + } + goto check; + } + else if ((*span_p)->Start <= stop) // ===== + { // The new span extends the existing span from // ++++ + // the beginning. // (or) ++++ + (*span_p)->Start = start; + if ((*span_p)->Stop < stop) + { // The new span also extends the existing span // ====== + // at the bottom // ++++++++++++++ + goto extend; + } + goto check; + } + else // ====== + { // No overlap, so insert a new span. // +++++ + goto addspan; + } + } + // Append a new span to the end of the list. + addspan: + span = AllocSpan(); + span->NextSpan = *span_p; + span->Start = start; + span->Stop = stop; + *span_p = span; + check: +#ifdef _DEBUG + // Validate the span list: Spans must be in order, and there must be + // at least one pixel between spans. + for (span = Spans[listnum]; span != nullptr; span = span->NextSpan) + { + assert(span->Start < span->Stop); + if (span->NextSpan != nullptr) + { + assert(span->Stop < span->NextSpan->Start); + } + } +#endif + ; + } + + FCoverageBuffer::Span *FCoverageBuffer::AllocSpan() + { + Span *span; + + if (FreeSpans != nullptr) + { + span = FreeSpans; + FreeSpans = span->NextSpan; + } + else + { + span = (Span *)SpanArena.Alloc(sizeof(Span)); + } + return span; + } + + ///////////////////////////////////////////////////////////////////////// + // Old BUILD implementation follows: + // + // This file contains some code from the Build Engine. + // + // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman + // Ken Silverman's official web site: "http://www.advsys.net/ken" + // See the included license file "BUILDLIC.TXT" for license info. + +#if 0 + void R_DrawVisVoxel(vissprite_t *spr, int minslabz, int maxslabz, short *cliptop, short *clipbot) + { + int flags = 0; + + // Do setup for blending. + R_SetColorMapLight(spr->Style.BaseColormap, 0, spr->Style.ColormapNum << FRACBITS); + bool visible = R_SetPatchStyle(spr->Style.RenderStyle, spr->Style.Alpha, spr->Translation, spr->FillColor); + + if (!visible) + { + return; + } + if (colfunc == fuzzcolfunc || colfunc == R_FillColumn) + { + flags = DVF_OFFSCREEN | DVF_SPANSONLY; + } + else if (colfunc != basecolfunc) + { + flags = DVF_OFFSCREEN; + } + if (flags != 0) + { + R_CheckOffscreenBuffer(RenderTarget->GetWidth(), RenderTarget->GetHeight(), !!(flags & DVF_SPANSONLY)); + } + if (spr->bInMirror) + { + flags |= DVF_MIRRORED; + } + + // Render the voxel, either directly to the screen or offscreen. + R_DrawVoxel(spr->pa.vpos, spr->pa.vang, spr->gpos, spr->Angle, + spr->xscale, FLOAT2FIXED(spr->yscale), spr->voxel, spr->Style.BaseColormap, spr->Style.ColormapNum, cliptop, clipbot, + minslabz, maxslabz, flags); + + // Blend the voxel, if that's what we need to do. + if ((flags & ~DVF_MIRRORED) != 0) + { + int pixelsize = r_swtruecolor ? 4 : 1; + for (int x = 0; x < viewwidth; ++x) + { + if (!(flags & DVF_SPANSONLY) && (x & 3) == 0) + { + rt_initcols(OffscreenColorBuffer + x * OffscreenBufferHeight); + } + for (FCoverageBuffer::Span *span = OffscreenCoverageBuffer->Spans[x]; span != NULL; span = span->NextSpan) + { + if (flags & DVF_SPANSONLY) + { + dc_x = x; + dc_yl = span->Start; + dc_yh = span->Stop - 1; + dc_count = span->Stop - span->Start; + dc_dest = (ylookup[span->Start] + x) * pixelsize + dc_destorg; + colfunc(); + } + else + { + rt_span_coverage(x, span->Start, span->Stop - 1); + } + } + if (!(flags & DVF_SPANSONLY) && (x & 3) == 3) + { + rt_draw4cols(x - 3); + } + } + } + + R_FinishSetPatchStyle(); + NetUpdate(); + } + + void R_DrawVoxel(const FVector3 &globalpos, FAngle viewangle, + const FVector3 &dasprpos, DAngle dasprang, + fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, + FSWColormap *colormap, int colormapnum, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags) + { + int i, j, k, x, y, syoff, ggxstart, ggystart, nxoff; + fixed_t cosang, sinang, sprcosang, sprsinang; + int backx, backy, gxinc, gyinc; + int daxscalerecip, dayscalerecip, cnt, gxstart, gystart, dazscale; + int lx, rx, nx, ny, x1=0, y1=0, x2=0, y2=0, yinc=0; + int yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc; + kvxslab_t *voxptr, *voxend; + FVoxelMipLevel *mip; + int z1a[64], z2a[64], yplc[64]; + + const int nytooclose = centerxwide * 2100, nytoofar = 32768*32768 - 1048576; + const int xdimenscale = FLOAT2FIXED(centerxwide * YaspectMul / 160); + const double centerxwide_f = centerxwide; + const double centerxwidebig_f = centerxwide_f * 65536*65536*8; + + // Convert to Build's coordinate system. + fixed_t globalposx = xs_Fix<4>::ToFix(globalpos.X); + fixed_t globalposy = xs_Fix<4>::ToFix(-globalpos.Y); + fixed_t globalposz = xs_Fix<8>::ToFix(-globalpos.Z); + + fixed_t dasprx = xs_Fix<4>::ToFix(dasprpos.X); + fixed_t daspry = xs_Fix<4>::ToFix(-dasprpos.Y); + fixed_t dasprz = xs_Fix<8>::ToFix(-dasprpos.Z); + + // Shift the scales from 16 bits of fractional precision to 6. + // Also do some magic voodoo scaling to make them the right size. + daxscale = daxscale / (0xC000 >> 6); + dayscale = dayscale / (0xC000 >> 6); + if (daxscale <= 0 || dayscale <= 0) + { + // won't be visible. + return; + } + + angle_t viewang = viewangle.BAMs(); + cosang = FLOAT2FIXED(viewangle.Cos()) >> 2; + sinang = FLOAT2FIXED(-viewangle.Sin()) >> 2; + sprcosang = FLOAT2FIXED(dasprang.Cos()) >> 2; + sprsinang = FLOAT2FIXED(-dasprang.Sin()) >> 2; + + R_SetupDrawSlab(colormap, 0.0f, colormapnum << FRACBITS); + + int pixelsize = r_swtruecolor ? 4 : 1; + + // Select mip level + i = abs(DMulScale6(dasprx - globalposx, cosang, daspry - globalposy, sinang)); + i = DivScale6(i, MIN(daxscale, dayscale)); + j = xs_Fix<13>::ToFix(FocalLengthX); + for (k = 0; i >= j && k < voxobj->NumMips; ++k) + { + i >>= 1; + } + if (k >= voxobj->NumMips) k = voxobj->NumMips - 1; + + mip = &voxobj->Mips[k]; if (mip->SlabData == NULL) return; + + minslabz >>= k; + maxslabz >>= k; + + daxscale <<= (k+8); dayscale <<= (k+8); + dazscale = FixedDiv(dayscale, FLOAT2FIXED(BaseYaspectMul)); + daxscale = fixed_t(daxscale / YaspectMul); + daxscale = Scale(daxscale, xdimenscale, centerxwide << 9); + dayscale = Scale(dayscale, FixedMul(xdimenscale, viewingrangerecip), centerxwide << 9); + + daxscalerecip = (1<<30) / daxscale; + dayscalerecip = (1<<30) / dayscale; + + fixed_t piv_x = fixed_t(mip->Pivot.X*256.); + fixed_t piv_y = fixed_t(mip->Pivot.Y*256.); + fixed_t piv_z = fixed_t(mip->Pivot.Z*256.); + + x = FixedMul(globalposx - dasprx, daxscalerecip); + y = FixedMul(globalposy - daspry, daxscalerecip); + backx = (DMulScale10(x, sprcosang, y, sprsinang) + piv_x) >> 8; + backy = (DMulScale10(y, sprcosang, x, -sprsinang) + piv_y) >> 8; + cbackx = clamp(backx, 0, mip->SizeX - 1); + cbacky = clamp(backy, 0, mip->SizeY - 1); + + sprcosang = MulScale14(daxscale, sprcosang); + sprsinang = MulScale14(daxscale, sprsinang); + + x = (dasprx - globalposx) - DMulScale18(piv_x, sprcosang, piv_y, -sprsinang); + y = (daspry - globalposy) - DMulScale18(piv_y, sprcosang, piv_x, sprsinang); + + cosang = FixedMul(cosang, dayscalerecip); + sinang = FixedMul(sinang, dayscalerecip); + + gxstart = y*cosang - x*sinang; + gystart = x*cosang + y*sinang; + gxinc = DMulScale10(sprsinang, cosang, sprcosang, -sinang); + gyinc = DMulScale10(sprcosang, cosang, sprsinang, sinang); + if ((abs(globalposz - dasprz) >> 10) >= abs(dazscale)) return; + + x = 0; y = 0; j = MAX(mip->SizeX, mip->SizeY); + fixed_t *ggxinc = (fixed_t *)alloca((j + 1) * sizeof(fixed_t) * 2); + fixed_t *ggyinc = ggxinc + (j + 1); + for (i = 0; i <= j; i++) + { + ggxinc[i] = x; x += gxinc; + ggyinc[i] = y; y += gyinc; + } + + syoff = DivScale21(globalposz - dasprz, FixedMul(dazscale, 0xE800)) + (piv_z << 7); + yoff = (abs(gxinc) + abs(gyinc)) >> 1; + + for (cnt = 0; cnt < 8; cnt++) + { + switch (cnt) + { + case 0: xs = 0; ys = 0; xi = 1; yi = 1; break; + case 1: xs = mip->SizeX-1; ys = 0; xi = -1; yi = 1; break; + case 2: xs = 0; ys = mip->SizeY-1; xi = 1; yi = -1; break; + case 3: xs = mip->SizeX-1; ys = mip->SizeY-1; xi = -1; yi = -1; break; + case 4: xs = 0; ys = cbacky; xi = 1; yi = 2; break; + case 5: xs = mip->SizeX-1; ys = cbacky; xi = -1; yi = 2; break; + case 6: xs = cbackx; ys = 0; xi = 2; yi = 1; break; + case 7: xs = cbackx; ys = mip->SizeY-1; xi = 2; yi = -1; break; + } + xe = cbackx; ye = cbacky; + if (cnt < 4) + { + if ((xi < 0) && (xe >= xs)) continue; + if ((xi > 0) && (xe <= xs)) continue; + if ((yi < 0) && (ye >= ys)) continue; + if ((yi > 0) && (ye <= ys)) continue; + } + else + { + if ((xi < 0) && (xe > xs)) continue; + if ((xi > 0) && (xe < xs)) continue; + if ((yi < 0) && (ye > ys)) continue; + if ((yi > 0) && (ye < ys)) continue; + xe += xi; ye += yi; + } + + i = sgn(ys - backy) + sgn(xs - backx) * 3 + 4; + switch(i) + { + case 6: case 7: x1 = 0; y1 = 0; break; + case 8: case 5: x1 = gxinc; y1 = gyinc; break; + case 0: case 3: x1 = gyinc; y1 = -gxinc; break; + case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break; + } + switch(i) + { + case 2: case 5: x2 = 0; y2 = 0; break; + case 0: case 1: x2 = gxinc; y2 = gyinc; break; + case 8: case 7: x2 = gyinc; y2 = -gxinc; break; + case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break; + } + BYTE oand = (1 << int(xs 0) { dagxinc = gxinc; dagyinc = FixedMul(gyinc, viewingrangerecip); } + else { dagxinc = -gxinc; dagyinc = -FixedMul(gyinc, viewingrangerecip); } + + /* Fix for non 90 degree viewing ranges */ + nxoff = FixedMul(x2 - x1, viewingrangerecip); + x1 = FixedMul(x1, viewingrangerecip); + + ggxstart = gxstart + ggyinc[ys]; + ggystart = gystart - ggxinc[ys]; + + for (x = xs; x != xe; x += xi) + { + BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; + short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; + + nx = FixedMul(ggxstart + ggxinc[x], viewingrangerecip) + x1; + ny = ggystart + ggyinc[x]; + for (y = ys; y != ye; y += yi, nx += dagyinc, ny -= dagxinc) + { + if ((ny <= nytooclose) || (ny >= nytoofar)) continue; + voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); + voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); + if (voxptr >= voxend) continue; + + lx = xs_RoundToInt(nx * centerxwide_f / (ny + y1)) + centerx; + if (lx < 0) lx = 0; + rx = xs_RoundToInt((nx + nxoff) * centerxwide_f / (ny + y2)) + centerx; + if (rx > viewwidth) rx = viewwidth; + if (rx <= lx) continue; + + if (flags & DVF_MIRRORED) + { + int t = viewwidth - lx; + lx = viewwidth - rx; + rx = t; + } + + fixed_t l1 = xs_RoundToInt(centerxwidebig_f / (ny - yoff)); + fixed_t l2 = xs_RoundToInt(centerxwidebig_f / (ny + yoff)); + for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3)) + { + const BYTE *col = voxptr->col; + int zleng = voxptr->zleng; + int ztop = voxptr->ztop; + fixed_t z1, z2; + + if (ztop < minslabz) + { + int diff = minslabz - ztop; + ztop = minslabz; + col += diff; + zleng -= diff; + } + if (ztop + zleng > maxslabz) + { + int diff = ztop + zleng - maxslabz; + zleng -= diff; + } + if (zleng <= 0) continue; + + j = (ztop << 15) - syoff; + if (j < 0) + { + k = j + (zleng << 15); + if (k < 0) + { + if ((voxptr->backfacecull & oand32) == 0) continue; + z2 = MulScale32(l2, k) + centery; /* Below slab */ + } + else + { + if ((voxptr->backfacecull & oand) == 0) continue; /* Middle of slab */ + z2 = MulScale32(l1, k) + centery; + } + z1 = MulScale32(l1, j) + centery; + } + else + { + if ((voxptr->backfacecull & oand16) == 0) continue; + z1 = MulScale32(l2, j) + centery; /* Above slab */ + z2 = MulScale32(l1, j + (zleng << 15)) + centery; + } + + if (z2 <= z1) continue; + + if (zleng == 1) + { + yinc = 0; + } + else + { + if (z2-z1 >= 1024) yinc = FixedDiv(zleng, z2 - z1); + else yinc = (((1 << 24) - 1) / (z2 - z1)) * zleng >> 8; + } + // [RH] Clip each column separately, not just by the first one. + for (int stripwidth = MIN(countof(z1a), rx - lx), lxt = lx; + lxt < rx; + (lxt += countof(z1a)), stripwidth = MIN(countof(z1a), rx - lxt)) + { + // Calculate top and bottom pixels locations + for (int xxx = 0; xxx < stripwidth; ++xxx) + { + if (zleng == 1) + { + yplc[xxx] = 0; + z1a[xxx] = MAX(z1, daumost[lxt + xxx]); + } + else + { + if (z1 < daumost[lxt + xxx]) + { + yplc[xxx] = yinc * (daumost[lxt + xxx] - z1); + z1a[xxx] = daumost[lxt + xxx]; + } + else + { + yplc[xxx] = 0; + z1a[xxx] = z1; + } + } + z2a[xxx] = MIN(z2, dadmost[lxt + xxx]); + } + // Find top and bottom pixels that match and draw them as one strip + for (int xxl = 0, xxr; xxl < stripwidth; ) + { + if (z1a[xxl] >= z2a[xxl]) + { // No column here + xxl++; + continue; + } + int z1 = z1a[xxl]; + int z2 = z2a[xxl]; + // How many columns share the same extents? + for (xxr = xxl + 1; xxr < stripwidth; ++xxr) + { + if (z1a[xxr] != z1 || z2a[xxr] != z2) + break; + } + + if (!(flags & DVF_OFFSCREEN)) + { + // Draw directly to the screen. + R_DrawSlab(xxr - xxl, yplc[xxl], z2 - z1, yinc, col, (ylookup[z1] + lxt + xxl) * pixelsize + dc_destorg); + } + else + { + // Record the area covered and possibly draw to an offscreen buffer. + dc_yl = z1; + dc_yh = z2 - 1; + dc_count = z2 - z1; + dc_iscale = yinc; + for (int x = xxl; x < xxr; ++x) + { + OffscreenCoverageBuffer->InsertSpan(lxt + x, z1, z2); + if (!(flags & DVF_SPANSONLY)) + { + dc_x = lxt + x; + rt_initcols(OffscreenColorBuffer + (dc_x & ~3) * OffscreenBufferHeight); + dc_source = col; + dc_source2 = nullptr; + dc_texturefrac = yplc[xxl]; + hcolfunc_pre(); + } + } + } + xxl = xxr; + } + } + } + } + } + } + } +#endif } diff --git a/src/swrenderer/things/r_voxel.h b/src/swrenderer/things/r_voxel.h index 4c1e3f0af..a3dc12e91 100644 --- a/src/swrenderer/things/r_voxel.h +++ b/src/swrenderer/things/r_voxel.h @@ -1,5 +1,6 @@ /* ** Voxel rendering +** Copyright (c) 1998-2016 Randy Heit ** Copyright (c) 2016 Magnus Norddahl ** ** This software is provided 'as-is', without any express or implied @@ -33,4 +34,28 @@ namespace swrenderer kvxslab_t *R_GetSlabStart(const FVoxelMipLevel &mip, int x, int y); kvxslab_t *R_GetSlabEnd(const FVoxelMipLevel &mip, int x, int y); kvxslab_t *R_NextSlab(kvxslab_t *slab); + + void R_DeinitRenderVoxel(); + + // [RH] A c-buffer. Used for keeping track of offscreen voxel spans. + struct FCoverageBuffer + { + struct Span + { + Span *NextSpan; + short Start, Stop; + }; + + FCoverageBuffer(int size); + ~FCoverageBuffer(); + + void Clear(); + void InsertSpan(int listnum, int start, int stop); + Span *AllocSpan(); + + FMemArena SpanArena; + Span **Spans; // [0..NumLists-1] span lists + Span *FreeSpans; + unsigned int NumLists; + }; }