diff --git a/src/r_plane.cpp b/src/r_plane.cpp index e8fc31ec1..1bddfc685 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -989,198 +989,6 @@ static const BYTE *R_GetTwoSkyColumns (FTexture *fronttex, int x) } } -static void R_DrawSkyColumnStripe(int start_x, int y1, int y2, int columns, double scale, double texturemid, double yrepeat) -{ - uint32_t height = frontskytex->GetHeight(); - - for (int i = 0; i < columns; i++) - { - double uv_stepd = skyiscale * yrepeat; - double v = (texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; - double v_step = uv_stepd / height; - - uint32_t uv_pos = (uint32_t)(v * 0x01000000); - uint32_t uv_step = (uint32_t)(v_step * 0x01000000); - - int x = start_x + i; - if (MirrorFlags & RF_XFLIP) - x = (viewwidth - x); - - DWORD ang, angle1, angle2; - - if (r_linearsky) - { - angle_t xangle = (angle_t)((0.5 - x / (double)viewwidth) * FocalTangent * ANGLE_90); - ang = (skyangle + xangle) ^ skyflip; - } - else - { - ang = (skyangle + xtoviewangle[x]) ^ skyflip; - } - angle1 = (DWORD)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); - angle2 = (DWORD)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); - - if (r_swtruecolor) - { - bufplce[i] = (const BYTE *)frontskytex->GetColumnBgra(angle1, nullptr); - bufplce2[i] = backskytex ? (const BYTE *)backskytex->GetColumnBgra(angle2, nullptr) : nullptr; - } - else - { - bufplce[i] = (const BYTE *)frontskytex->GetColumn(angle1, nullptr); - bufplce2[i] = backskytex ? (const BYTE *)backskytex->GetColumn(angle2, nullptr) : nullptr; - } - vince[i] = uv_step; - vplce[i] = uv_pos; - } - - bufheight[0] = height; - bufheight[1] = backskytex ? backskytex->GetHeight() : height; - int pixelsize = r_swtruecolor ? 4 : 1; - dc_dest = (ylookup[y1] + start_x) * pixelsize + dc_destorg; - dc_count = y2 - y1; - - uint32_t solid_top = frontskytex->GetSkyCapColor(false); - uint32_t solid_bottom = frontskytex->GetSkyCapColor(true); - - if (r_swtruecolor) - { - if (columns == 4) - if (!backskytex) - R_DrawSingleSkyCol4_rgba(solid_top, solid_bottom); - else - R_DrawDoubleSkyCol4_rgba(solid_top, solid_bottom); - else - if (!backskytex) - R_DrawSingleSkyCol1_rgba(solid_top, solid_bottom); - else - R_DrawDoubleSkyCol1_rgba(solid_top, solid_bottom); - } - else - { - if (columns == 4) - if (!backskytex) - R_DrawSingleSkyCol4(solid_top, solid_bottom); - else - R_DrawDoubleSkyCol4(solid_top, solid_bottom); - else - if (!backskytex) - R_DrawSingleSkyCol1(solid_top, solid_bottom); - else - R_DrawDoubleSkyCol1(solid_top, solid_bottom); - } -} - -static void R_DrawSkyColumn(int start_x, int y1, int y2, int columns) -{ - if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) - { - double texturemid = skymid * frontskytex->Scale.Y + frontskytex->GetHeight(); - R_DrawSkyColumnStripe(start_x, y1, y2, columns, frontskytex->Scale.Y, texturemid, frontskytex->Scale.Y); - } - else - { - double yrepeat = frontskytex->Scale.Y; - double scale = frontskytex->Scale.Y * skyscale; - double iscale = 1 / scale; - short drawheight = short(frontskytex->GetHeight() * scale); - double topfrac = fmod(skymid + iscale * (1 - CenterY), frontskytex->GetHeight()); - if (topfrac < 0) topfrac += frontskytex->GetHeight(); - double texturemid = topfrac - iscale * (1 - CenterY); - R_DrawSkyColumnStripe(start_x, y1, y2, columns, scale, texturemid, yrepeat); - } -} - -static void R_DrawCapSky(visplane_t *pl) -{ - if (!r_swtruecolor) - R_SetColorMapLight(fixedcolormap, 0, 0); - - int x1 = pl->left; - int x2 = pl->right; - short *uwal = (short *)pl->top; - short *dwal = (short *)pl->bottom; - - // Calculate where 4 column alignment begins and ends: - int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); - int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); - - // First unaligned columns: - for (int x = x1; x < aligned_x1; x++) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - R_DrawSkyColumn(x, y1, y2, 1); - } - - // The aligned columns - for (int x = aligned_x1; x < aligned_x2; x += 4) - { - // Find y1, y2, light and uv values for four columns: - int y1[4] = { uwal[x], uwal[x + 1], uwal[x + 2], uwal[x + 3] }; - int y2[4] = { dwal[x], dwal[x + 1], dwal[x + 2], dwal[x + 3] }; - - // Figure out where we vertically can start and stop drawing 4 columns in one go - int middle_y1 = y1[0]; - int middle_y2 = y2[0]; - for (int i = 1; i < 4; i++) - { - middle_y1 = MAX(y1[i], middle_y1); - middle_y2 = MIN(y2[i], middle_y2); - } - - // If we got an empty column in our set we cannot draw 4 columns in one go: - bool empty_column_in_set = false; - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - empty_column_in_set = true; - } - if (empty_column_in_set || middle_y2 <= middle_y1) - { - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - continue; - - R_DrawSkyColumn(x + i, y1[i], y2[i], 1); - } - continue; - } - - // Draw the first rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (y1[i] < middle_y1) - R_DrawSkyColumn(x + i, y1[i], middle_y1, 1); - } - - // Draw the area where all 4 columns are active - R_DrawSkyColumn(x, middle_y1, middle_y2, 4); - - // Draw the last rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (middle_y2 < y2[i]) - R_DrawSkyColumn(x + i, middle_y2, y2[i], 1); - } - } - - // The last unaligned columns: - for (int x = aligned_x2; x < x2; x++) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - R_DrawSkyColumn(x, y1, y2, 1); - } -} - static void R_DrawCubeSky(visplane_t *pl) { int x1 = pl->left; @@ -1271,6 +1079,188 @@ static void R_DrawCubeSky(visplane_t *pl) R_DrawTriangles(objectToWorld, cube, 6 * 6, x1, x2 - 1, uwal, dwal); } +static void R_DrawSkyColumnStripe(int start_x, int y1, int y2, int columns, double scale, double texturemid, double yrepeat) +{ + uint32_t height = frontskytex->GetHeight(); + + for (int i = 0; i < columns; i++) + { + double uv_stepd = skyiscale * yrepeat; + double v = (texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; + double v_step = uv_stepd / height; + + uint32_t uv_pos = (uint32_t)(v * 0x01000000); + uint32_t uv_step = (uint32_t)(v_step * 0x01000000); + + int x = start_x + i; + if (MirrorFlags & RF_XFLIP) + x = (viewwidth - x); + + DWORD ang, angle1, angle2; + + ang = (skyangle + xtoviewangle[x]) ^ skyflip; + angle1 = (DWORD)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); + angle2 = (DWORD)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); + + if (r_swtruecolor) + { + bufplce[i] = (const BYTE *)frontskytex->GetColumnBgra(angle1, nullptr); + bufplce2[i] = backskytex ? (const BYTE *)backskytex->GetColumnBgra(angle2, nullptr) : nullptr; + } + else + { + bufplce[i] = (const BYTE *)frontskytex->GetColumn(angle1, nullptr); + bufplce2[i] = backskytex ? (const BYTE *)backskytex->GetColumn(angle2, nullptr) : nullptr; + } + + vince[i] = uv_step; + vplce[i] = uv_pos; + } + + bufheight[0] = height; + bufheight[1] = backskytex ? backskytex->GetHeight() : height; + int pixelsize = r_swtruecolor ? 4 : 1; + dc_dest = (ylookup[y1] + start_x) * pixelsize + dc_destorg; + dc_count = y2 - y1; + + uint32_t solid_top = frontskytex->GetSWSkyCapColor(false); + uint32_t solid_bottom = frontskytex->GetSWSkyCapColor(true); + + if (r_swtruecolor) + { + if (columns == 4) + if (!backskytex) + R_DrawSingleSkyCol4_rgba(solid_top, solid_bottom); + else + R_DrawDoubleSkyCol4_rgba(solid_top, solid_bottom); + else + if (!backskytex) + R_DrawSingleSkyCol1_rgba(solid_top, solid_bottom); + else + R_DrawDoubleSkyCol1_rgba(solid_top, solid_bottom); + } + else + { + if (columns == 4) + if (!backskytex) + R_DrawSingleSkyCol4(solid_top, solid_bottom); + else + R_DrawDoubleSkyCol4(solid_top, solid_bottom); + else + if (!backskytex) + R_DrawSingleSkyCol1(solid_top, solid_bottom); + else + R_DrawDoubleSkyCol1(solid_top, solid_bottom); + } +} + +static void R_DrawSkyColumn(int start_x, int y1, int y2, int columns) +{ + if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) + { + double texturemid = skymid * frontskytex->Scale.Y + frontskytex->GetHeight(); + R_DrawSkyColumnStripe(start_x, y1, y2, columns, frontskytex->Scale.Y, texturemid, frontskytex->Scale.Y); + } + else + { + double yrepeat = frontskytex->Scale.Y; + double scale = frontskytex->Scale.Y * skyscale; + double iscale = 1 / scale; + short drawheight = short(frontskytex->GetHeight() * scale); + double topfrac = fmod(skymid + iscale * (1 - CenterY), frontskytex->GetHeight()); + if (topfrac < 0) topfrac += frontskytex->GetHeight(); + double texturemid = topfrac - iscale * (1 - CenterY); + R_DrawSkyColumnStripe(start_x, y1, y2, columns, scale, texturemid, yrepeat); + } +} + +static void R_DrawCapSky(visplane_t *pl) +{ + int x1 = pl->left; + int x2 = pl->right; + short *uwal = (short *)pl->top; + short *dwal = (short *)pl->bottom; + + // Calculate where 4 column alignment begins and ends: + int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); + int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); + + // First unaligned columns: + for (int x = x1; x < aligned_x1; x++) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + R_DrawSkyColumn(x, y1, y2, 1); + } + + // The aligned columns + for (int x = aligned_x1; x < aligned_x2; x += 4) + { + // Find y1, y2, light and uv values for four columns: + int y1[4] = { uwal[x], uwal[x + 1], uwal[x + 2], uwal[x + 3] }; + int y2[4] = { dwal[x], dwal[x + 1], dwal[x + 2], dwal[x + 3] }; + + // Figure out where we vertically can start and stop drawing 4 columns in one go + int middle_y1 = y1[0]; + int middle_y2 = y2[0]; + for (int i = 1; i < 4; i++) + { + middle_y1 = MAX(y1[i], middle_y1); + middle_y2 = MIN(y2[i], middle_y2); + } + + // If we got an empty column in our set we cannot draw 4 columns in one go: + bool empty_column_in_set = false; + for (int i = 0; i < 4; i++) + { + if (y2[i] <= y1[i]) + empty_column_in_set = true; + } + if (empty_column_in_set || middle_y2 <= middle_y1) + { + for (int i = 0; i < 4; i++) + { + if (y2[i] <= y1[i]) + continue; + + R_DrawSkyColumn(x + i, y1[i], y2[i], 1); + } + continue; + } + + // Draw the first rows where not all 4 columns are active + for (int i = 0; i < 4; i++) + { + if (y1[i] < middle_y1) + R_DrawSkyColumn(x + i, y1[i], middle_y1, 1); + } + + // Draw the area where all 4 columns are active + R_DrawSkyColumn(x, middle_y1, middle_y2, 4); + + // Draw the last rows where not all 4 columns are active + for (int i = 0; i < 4; i++) + { + if (middle_y2 < y2[i]) + R_DrawSkyColumn(x + i, middle_y2, y2[i], 1); + } + } + + // The last unaligned columns: + for (int x = aligned_x2; x < x2; x++) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + R_DrawSkyColumn(x, y1, y2, 1); + } +} + static void R_DrawSky (visplane_t *pl) { if (r_swtruecolor && r_cubesky) diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp index 12e9d8549..53311a66c 100644 --- a/src/textures/texture.cpp +++ b/src/textures/texture.cpp @@ -841,6 +841,78 @@ void FTexture::SetScaledSize(int fitwidth, int fitheight) if (int(Scale.Y * fitheight) != Height) Scale.Y += (1 / 65536.); } +//=========================================================================== +// +// Gets the average color of a texture for use as a sky cap color +// +//=========================================================================== + +namespace +{ + PalEntry averageColor(const DWORD *data, int size, int maxout) + { + int i; + unsigned int r, g, b; + + // First clear them. + r = g = b = 0; + if (size == 0) + { + return PalEntry(255, 255, 255); + } + for (i = 0; i < size; i++) + { + r += BPART(data[i]); + g += GPART(data[i]); + b += RPART(data[i]); + } + + r = r / size; + g = g / size; + b = b / size; + + int maxv = MAX(MAX(r, g), b); + + if (maxv && maxout) + { + r = Scale(r, maxout, maxv); + g = Scale(g, maxout, maxv); + b = Scale(b, maxout, maxv); + } + return PalEntry(255, r, g, b); + } +} + +PalEntry FTexture::GetSWSkyCapColor(bool bottom) +{ + PalEntry col; + int w; + int h; + + if (!bSWSkyColorDone) + { + bSWSkyColorDone = true; + + FBitmap bitmap; + bitmap.Create(GetWidth(), GetHeight()); + CopyTrueColorPixels(&bitmap, 0, 0); + int w = GetWidth(); + int h = GetHeight(); + + const uint32_t *buffer = (const uint32_t *)bitmap.GetPixels(); + if (buffer) + { + SWCeilingSkyColor = averageColor((DWORD *)buffer, w * MIN(30, h), 0); + if (h>30) + { + SWFloorSkyColor = averageColor(((DWORD *)buffer) + (h - 30)*w, w * 30, 0); + } + else SWFloorSkyColor = SWCeilingSkyColor; + } + } + return bottom ? SWFloorSkyColor : SWCeilingSkyColor; +} + FDummyTexture::FDummyTexture () { diff --git a/src/textures/textures.h b/src/textures/textures.h index 26b73c6ec..f168ca21d 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -272,6 +272,7 @@ public: } void SetScaledSize(int fitwidth, int fitheight); + PalEntry GetSWSkyCapColor(bool bottom); virtual void HackHack (int newheight); // called by FMultipatchTexture to discover corrupt patches. @@ -303,6 +304,11 @@ protected: void GenerateBgraMipmapsFast(); int MipmapLevels() const; +private: + bool bSWSkyColorDone = false; + PalEntry SWFloorSkyColor; + PalEntry SWCeilingSkyColor; + public: static void FlipSquareBlock (BYTE *block, int x, int y); static void FlipSquareBlockBgra (uint32_t *block, int x, int y); diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index b1b790235..1db0510a1 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1786,6 +1786,7 @@ DSPLYMNU_BLOODFADE = "Blood Flash Intensity"; DSPLYMNU_PICKUPFADE = "Pickup Flash Intensity"; DSPLYMNU_PALLETEHACK = "DirectDraw palette hack"; // Not used DSPLYMNU_ATTACHEDSURFACES = "Use attached surfaces"; // Not used +DSPLYMNU_CAPSKY = "Cap skies with solid color"; DSPLYMNU_STRETCHSKY = "Stretch short skies"; DSPLYMNU_LINEARSKY = "Linear skies"; DSPLYMNU_GZDFULLBRIGHT = "Emulate GZDoom FullBright"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index ceae53d82..1f79c24c3 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -700,6 +700,7 @@ OptionMenu "VideoOptions" //Option "$DSPLYMNU_ATTACHEDSURFACES", "vid_attachedsurfaces", "OnOff" } + Option "$DSPLYMNU_CAPSKY", "r_capsky", "OnOff" Option "$DSPLYMNU_STRETCHSKY", "r_stretchsky", "OnOff" Option "$DSPLYMNU_LINEARSKY", "r_linearsky", "OnOff" Option "$DSPLYMNU_GZDFULLBRIGHT", "r_fullbrightignoresectorcolor", "OnOff"