From 25999c1c6a4d7a7d2986a4408048f9005eebeb6a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 28 Mar 2018 19:07:43 +0200 Subject: [PATCH] - looks like the 2D drawer sources did not get committed... - This also adds the missing draw modes to the GLSL shader. --- src/v_2ddrawer.cpp | 516 +++++++++++++++++++++++++++++ src/v_2ddrawer.h | 141 ++++++++ wadsrc/static/shaders/glsl/main.fp | 26 +- 3 files changed, 679 insertions(+), 4 deletions(-) create mode 100644 src/v_2ddrawer.cpp create mode 100644 src/v_2ddrawer.h diff --git a/src/v_2ddrawer.cpp b/src/v_2ddrawer.cpp new file mode 100644 index 000000000..02db68277 --- /dev/null +++ b/src/v_2ddrawer.cpp @@ -0,0 +1,516 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016-2018 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** v_2ddrawer.h +** Device independent 2D draw list +** +**/ + +#include +#include "doomtype.h" +#include "templates.h" +#include "r_utility.h" +#include "v_video.h" + +EXTERN_CVAR(Float, transsouls) + + +//========================================================================== +// +// +// +//========================================================================== + +int F2DDrawer::AddCommand(const RenderCommand *data) +{ + if (mData.Size() > 0 && data->isCompatible(mData.Last())) + { + // Merge with the last command. + mData.Last().mIndexCount += data->mIndexCount; + return mData.Size(); + } + else + { + return mData.Push(*data); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddIndices(int firstvert, int count, ...) +{ + va_list ap; + va_start(ap, count); + int addr = mIndices.Reserve(count); + for (int i = 0; i < count; i++) + { + mIndices[addr + i] = firstvert + va_arg(ap, int); + } +} + +//========================================================================== +// +// SetStyle +// +// Patterned after R_SetPatchStyle. +// +//========================================================================== + +bool F2DDrawer::SetStyle(FTexture *tex, DrawParms &parms, PalEntry &vertexcolor, RenderCommand &quad) +{ + auto fmt = tex->GetFormat(); + FRenderStyle style = parms.style; + float alpha; + bool stencilling; + + if (style.Flags & STYLEF_TransSoulsAlpha) + { + alpha = transsouls; + } + else if (style.Flags & STYLEF_Alpha1) + { + alpha = 1; + } + else + { + alpha = clamp(parms.Alpha, 0.f, 1.f); + } + + style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Shadow || style.BlendOp == STYLEOP_Fuzz) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = 0.3f; + parms.fillcolor = 0; + } + else if (style.BlendOp == STYLEOP_FuzzOrAdd) + { + style.BlendOp = STYLEOP_Add; + } + else if (style.BlendOp == STYLEOP_FuzzOrSub) + { + style.BlendOp = STYLEOP_Sub; + } + else if (style.BlendOp == STYLEOP_FuzzOrRevSub) + { + style.BlendOp = STYLEOP_RevSub; + } + + stencilling = false; + + if (style.Flags & STYLEF_InvertOverlay) + { + // Only the overlay color is inverted, not the overlay alpha. + parms.colorOverlay.r = 255 - parms.colorOverlay.r; + parms.colorOverlay.g = 255 - parms.colorOverlay.g; + parms.colorOverlay.b = 255 - parms.colorOverlay.b; + } + + SetColorOverlay(parms.colorOverlay, alpha, vertexcolor, quad.mColor1); + quad.mColorOverlay = parms.colorOverlay; + + if (style.Flags & STYLEF_ColorIsFixed) + { + if (style.Flags & STYLEF_InvertSource) + { // Since the source color is a constant, we can invert it now + // without spending time doing it in the shader. + parms.fillcolor.r = 255 - parms.fillcolor.r; + parms.fillcolor.g = 255 - parms.fillcolor.g; + parms.fillcolor.b = 255 - parms.fillcolor.b; + } + // Set up the color mod to replace the color from the image data. + vertexcolor.r = parms.fillcolor.r; + vertexcolor.g = parms.fillcolor.g; + vertexcolor.b = parms.fillcolor.b; + + if (style.Flags & STYLEF_RedIsAlpha) + { + quad.mDrawMode = DTM_AlphaTexture; + } + else + { + quad.mDrawMode = DTM_Stencil; + } + } + else + { + if (style.Flags & STYLEF_RedIsAlpha) + { + quad.mDrawMode = DTM_AlphaTexture; + } + else if (style.Flags & STYLEF_InvertSource) + { + quad.mDrawMode = DTM_Invert; + } + + if (parms.specialcolormap != nullptr) + { // Emulate an invulnerability or similar colormap. + float *start, *end; + start = parms.specialcolormap->ColorizeStart; + end = parms.specialcolormap->ColorizeEnd; + if (quad.mDrawMode == DTM_Invert) + { + quad.mDrawMode = DTM_Normal; + std::swap(start, end); + } + quad.mFlags |= DTF_SpecialColormap; + // SpecialColormap uses the two color uniforms to set its ramp. + quad.mColor1.r = (uint8_t)(start[0] * (255 / 2)); + quad.mColor1.g = (uint8_t)(start[1] * (255 / 2)); + quad.mColor1.b = (uint8_t)(start[2] * (255 / 2)); + quad.mColor2.r = (uint8_t)(end[0] * (255 / 2)); + quad.mColor2.g = (uint8_t)(end[1] * (255 / 2)); + quad.mColor2.b = (uint8_t)(end[2] * (255 / 2)); + } + else if (parms.colormapstyle != nullptr) + { + // Emulate the fading from an in-game colormap (colorized, faded, and desaturated) + // This only gets used to render the weapon sprite for the software renderer. + quad.mDesaturate = parms.colormapstyle->Desaturate; + vertexcolor.r = parms.colormapstyle->Color.r; + vertexcolor.g = parms.colormapstyle->Color.g; + vertexcolor.b = parms.colormapstyle->Color.b; + + // fade uses premultiplied alpha. This uses mColor2 so that it can be combined with color overlays. + double fadelevel = parms.colormapstyle->FadeLevel; + quad.mColor2.r = uint8_t(parms.colormapstyle->Fade.r * fadelevel); + quad.mColor2.g = uint8_t(parms.colormapstyle->Fade.g * fadelevel); + quad.mColor2.b = uint8_t(parms.colormapstyle->Fade.b * fadelevel); + quad.mFlags |= DTF_IngameLighting; + } + } + // apply the element's own color. This is being blended with anything that came before. + vertexcolor = PalEntry((vertexcolor.a * parms.color.a) / 255, (vertexcolor.r * parms.color.r) / 255, (vertexcolor.g * parms.color.g) / 255, (vertexcolor.b * parms.color.b) / 255); + + if (!parms.masked) + { + // For DTM_AlphaTexture and DTM_Stencil the mask cannot be turned off because it would not yield a usable result. + if (quad.mDrawMode == DTM_Normal) quad.mDrawMode = DTM_Opaque; + else if (quad.mDrawMode == DTM_Invert) quad.mDrawMode = DTM_InvertOpaque; + } + quad.mRenderStyle = parms.style; // this contains the blend mode and blend equation settings. + return true; +} + +//========================================================================== +// +// Draws a texture +// +//========================================================================== + +void F2DDrawer::SetColorOverlay(PalEntry color, float alpha, PalEntry &vertexcolor, PalEntry &overlaycolor) +{ + if (APART(color) != 0) + { + // overlay color uses premultiplied alpha. The alpha channel is what the main color gets multiplied with in the blending. + int a = APART(color) * 256 / 255; + overlaycolor.r = (color.r * a) >> 8; + overlaycolor.g = (color.g * a) >> 8; + overlaycolor.b = (color.b * a) >> 8; + overlaycolor.a = 255 - color.a; + + vertexcolor = PalEntry(int(alpha * 255), color.a, color.a, color.a); + } + else + { + overlaycolor = 0; + vertexcolor = PalEntry(int(alpha * 255), 255, 255, 255); + } + // The real color gets multiplied into vertexcolor later. +} + +//========================================================================== +// +// Draws a texture +// +//========================================================================== + +void F2DDrawer::AddTexture(FTexture *img, DrawParms &parms) +{ + double xscale = parms.destwidth / parms.texwidth; + double yscale = parms.destheight / parms.texheight; + double x = parms.x - parms.left * xscale; + double y = parms.y - parms.top * yscale; + double w = parms.destwidth; + double h = parms.destheight; + double u1, v1, u2, v2; + int light = 255; + PalEntry vertexcolor; + + RenderCommand dg; + + dg.mType = DrawTypeTriangles; + dg.mVertCount = 4; + dg.mTexture = img; + + if (parms.colorOverlay && (parms.colorOverlay & 0xffffff) == 0) + { + // handle black overlays as reduced light. + light = 255 - APART(parms.colorOverlay); + parms.colorOverlay = 0; + } + + dg.mTranslation = 0; + SetStyle(img, parms, vertexcolor, dg); + + if (!img->bHasCanvas && parms.remap != nullptr && !parms.remap->Inactive) + { + dg.mTranslation = parms.remap; + } + u1 = parms.srcx; + v1 = parms.srcy; + u2 = parms.srcx + parms.srcwidth; + v2 = parms.srcy + parms.srcheight; + + if (parms.flipX) + std::swap(u1, u2); + + if (parms.flipY) + std::swap(v1, v2); + + // This is crap. Only kept for backwards compatibility with scripts that may have used it. + // Note that this only works for unflipped full textures. + if (parms.windowleft > 0 || parms.windowright < parms.texwidth) + { + double wi = MIN(parms.windowright, parms.texwidth); + x += parms.windowleft * xscale; + w -= (parms.texwidth - wi + parms.windowleft) * xscale; + + u1 = float(u1 + parms.windowleft / parms.texwidth); + u2 = float(u2 - (parms.texwidth - wi) / parms.texwidth); + } + + if (x < (double)parms.lclip || y < (double)parms.uclip || x + w >(double)parms.lclip || y + h >(double)parms.dclip) + { + dg.mScissor[0] = parms.lclip; + dg.mScissor[1] = parms.uclip; + dg.mScissor[2] = parms.rclip; + dg.mScissor[3] = parms.dclip; + dg.mFlags |= DTF_Scissor; + } + else + { + memset(dg.mScissor, 0, sizeof(dg.mScissor)); + } + + dg.mVertCount = 4; + dg.mVertIndex = (int)mVertices.Reserve(4); + TwoDVertex *ptr = &mVertices[dg.mVertIndex]; + ptr->Set(x, y, 0, u1, v1, vertexcolor); ptr++; + ptr->Set(x, y + h, 0, u1, v2, vertexcolor); ptr++; + ptr->Set(x + w, y, 0, u2, v1, vertexcolor); ptr++; + ptr->Set(x + w, y + h, 0, u2, v2, vertexcolor); ptr++; + AddIndices(6, dg.mVertIndex, 0, 1, 2, 1, 3, 2); + AddCommand(&dg); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddPoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + DAngle rotation, const FColormap &colormap, PalEntry flatcolor, int lightlevel) +{ + // Use an equation similar to player sprites to determine shade + + // Convert a light level into an unbounded colormap index (shade). + // Why the +12? I wish I knew, but experimentation indicates it + // is necessary in order to best reproduce Doom's original lighting. + double map = (NUMCOLORMAPS * 2.) - ((lightlevel + 12) * (NUMCOLORMAPS / 128.)); + double fadelevel = clamp((map - 12) / NUMCOLORMAPS, 0.0, 1.0); + + RenderCommand poly; + + poly.mType = DrawTypeTriangles; + poly.mTexture = texture; + poly.mRenderStyle = DefaultRenderStyle(); + poly.mFlags |= DTF_Wrap | DTF_IngameLighting; + poly.mDesaturate = colormap.Desaturation; + + PalEntry color0 = colormap.LightColor; color0.a = 255; + + poly.mColor2.a = uint8_t((1 - fadelevel) * 255); + poly.mColor2.r = uint8_t(colormap.FadeColor.r * fadelevel); + poly.mColor2.g = uint8_t(colormap.FadeColor.g * fadelevel); + poly.mColor2.b = uint8_t(colormap.FadeColor.b * fadelevel); + + bool dorotate = rotation != 0; + + float cosrot = (float)cos(rotation.Radians()); + float sinrot = (float)sin(rotation.Radians()); + + float uscale = float(1.f / (texture->GetScaledWidth() * scalex)); + float vscale = float(1.f / (texture->GetScaledHeight() * scaley)); + float ox = float(originx); + float oy = float(originy); + + poly.mVertCount = npoints; + poly.mVertIndex = (int)mVertices.Reserve(npoints); + for (int i = 0; i < npoints; ++i) + { + float u = points[i].X - 0.5f - ox; + float v = points[i].Y - 0.5f - oy; + if (dorotate) + { + float t = u; + u = t * cosrot - v * sinrot; + v = v * cosrot + t * sinrot; + } + mVertices[poly.mVertIndex+i].Set(points[i].X, points[i].Y, 0, u*uscale, v*vscale, color0); + } + for (int i = 2; i < npoints; ++i) + { + AddIndices(poly.mVertIndex, 3, 0, i - 1, i); + } + + AddCommand(&poly); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin) +{ + float fU1, fU2, fV1, fV2; + + RenderCommand dg; + + dg.mType = DrawTypeTriangles; + dg.mRenderStyle = DefaultRenderStyle(); + dg.mTexture = src; + dg.mVertCount = 4; + dg.mTexture = src; + + // scaling is not used here. + if (!local_origin) + { + fU1 = float(left) / src->GetWidth(); + fV1 = float(top) / src->GetHeight(); + fU2 = float(right) / src->GetWidth(); + fV2 = float(bottom) / src->GetHeight(); + } + else + { + fU1 = 0; + fV1 = 0; + fU2 = float(right - left) / src->GetWidth(); + fV2 = float(bottom - top) / src->GetHeight(); + } + dg.mVertIndex = (int)mVertices.Reserve(4); + auto ptr = &mVertices[dg.mVertIndex]; + + ptr->Set(left, top, 0, fU1, fV1, 0xffffffff); ptr++; + ptr->Set(left, bottom, 0, fU1, fV2, 0xffffffff); ptr++; + ptr->Set(right, top, 0, fU2, fV1, 0xffffffff); ptr++; + ptr->Set(right, bottom, 0, fU2, fV2, 0xffffffff); ptr++; + AddIndices(6, dg.mVertIndex, 0, 1, 2, 1, 3, 2); + AddCommand(&dg); +} + + +//=========================================================================== +// +// +// +//=========================================================================== + +void F2DDrawer::AddColorOnlyQuad(int x1, int y1, int w, int h, PalEntry color) +{ + RenderCommand dg; + + dg.mType = DrawTypeTriangles; + dg.mVertCount = 4; + dg.mVertIndex = (int)mVertices.Reserve(4); + dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; + auto ptr = &mVertices[dg.mVertIndex]; + ptr->Set(x1, y1, 0, 0, 0, color); ptr++; + ptr->Set(x1, y1 + h, 0, 0, 0, color); ptr++; + ptr->Set(x1 + w, y1 + h, 0, 0, 0, color); ptr++; + ptr->Set(x1 + w, y1, 0, 0, 0, color); ptr++; + AddIndices(6, dg.mVertIndex, 0, 1, 2, 1, 3, 2); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color) +{ + PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor]; + p.a = 255; + + RenderCommand dg; + + dg.mType = DrawTypeLines; + dg.mVertCount = 2; + dg.mVertIndex = (int)mVertices.Reserve(2); + mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p); + mVertices[dg.mVertIndex+1].Set(x2, y2, 0, 0, 0, p); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32_t color) +{ + PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor]; + p.a = 255; + + RenderCommand dg; + + dg.mType = DrawTypePoints; + dg.mVertCount = 1; + dg.mVertIndex = (int)mVertices.Reserve(1); + mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::Clear() +{ + mVertices.Clear(); + mIndices.Clear(); + mData.Clear(); +} diff --git a/src/v_2ddrawer.h b/src/v_2ddrawer.h new file mode 100644 index 000000000..7c325c877 --- /dev/null +++ b/src/v_2ddrawer.h @@ -0,0 +1,141 @@ +#ifndef __2DDRAWER_H +#define __2DDRAWER_H + +#include "tarray.h" +#include "textures.h" +#include "v_palette.h" +#include "r_data/renderstyle.h" +#include "r_data/colormaps.h" + +struct DrawParms; + +class F2DDrawer +{ +public: + + enum EDrawType : uint8_t + { + DrawTypeTriangles, + DrawTypeLines, + DrawTypePoints, + }; + + enum ETextureDrawMode : uint8_t + { + DTM_Normal = 0, + DTM_Stencil = 1, + DTM_Opaque = 2, + DTM_AlphaTexture = 3, + DTM_Invert = 4, + DTM_InvertOpaque = 5, + }; + + enum ETextureFlags : uint8_t + { + DTF_Wrap = 1, + DTF_Scissor = 2, + DTF_SpecialColormap = 4, + DTF_IngameLighting = 8 + }; + + + // This vertex type is hardware independent and needs conversion when put into a buffer. + struct TwoDVertex + { + float x, y, z; + float u, v; + PalEntry color0; + + void Set(float xx, float zz, float yy) + { + x = xx; + z = zz; + y = yy; + u = 0; + v = 0; + color0 = 0; + } + + void Set(double xx, double zz, double yy, double uu, double vv, PalEntry col) + { + x = (float)xx; + z = (float)zz; + y = (float)yy; + u = (float)uu; + v = (float)vv; + color0 = col; + } + + }; + + struct RenderCommand + { + EDrawType mType; + int mVertIndex; + int mVertCount; + int mIndexIndex; + int mIndexCount; + + FTexture *mTexture; + FRemapTable *mTranslation; + int mScissor[4]; + uint32_t mColorOverlay; + int mDesaturate; + FRenderStyle mRenderStyle; + PalEntry mColor1, mColor2; // Can either be the overlay color or the special colormap ramp. Both features can not be combined. + ETextureDrawMode mDrawMode; + uint8_t mFlags; + + RenderCommand() + { + memset(this, 0, sizeof(*this)); + } + + // If these fields match, two draw commands can be batched. + bool isCompatible(const RenderCommand &other) const + { + return mTexture == other.mTexture && + mType == other.mType && + mTranslation == other.mTranslation && + !memcmp(mScissor, other.mScissor, sizeof(mScissor)) && + other.mColorOverlay == 0 && mColorOverlay == 0 && + mDesaturate == other.mDesaturate && + mRenderStyle == other.mRenderStyle && + mDrawMode == other.mDrawMode && + mFlags == other.mFlags && + mColor1 == other.mColor1 && + mColor2 == other.mColor2; + + } + }; + + TArray mIndices; + TArray mVertices; + TArray mData; + + int AddCommand(const RenderCommand *data); + void AddIndices(int firstvert, int count, ...); + bool SetStyle(FTexture *tex, DrawParms &parms, PalEntry &color0, RenderCommand &quad); + void SetColorOverlay(PalEntry color, float alpha, PalEntry &vertexcolor, PalEntry &overlaycolor); + +public: + void AddTexture(FTexture *img, DrawParms &parms); + void AddPoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + DAngle rotation, const FColormap &colormap, PalEntry flatcolor, int lightlevel); + void AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin); + + void AddColorOnlyQuad(int left, int top, int width, int height, PalEntry color); + + void AddDim(PalEntry color, float damount, int x1, int y1, int w, int h); + void AddClear(int left, int top, int right, int bottom, int palcolor, uint32_t color); + + + void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color); + void AddPixel(int x1, int y1, int palcolor, uint32_t color); + + void Clear(); +}; + + +#endif diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 2ff3ef483..2ca28cca3 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -74,6 +74,10 @@ vec4 getTexel(vec2 st) texel.a = 0.0; } break; + + case 6: // TM_OPAQUEINVERSE + texel = vec4(1.0-texel.r, 1.0-texel.b, 1.0-texel.g, 1.0); + break; } if (uObjectColor2.a == 0.0) texel *= uObjectColor; else texel *= mix(uObjectColor, uObjectColor2, glowdist.z); @@ -415,7 +419,7 @@ void main() switch (uFixedColormap) { - case 0: + case 0: // in-game rendering. { float fogdist = 0.0; float fogfactor = 0.0; @@ -451,7 +455,7 @@ void main() break; } - case 1: + case 1: // special colormap { float gray = (frag.r * 0.3 + frag.g * 0.56 + frag.b * 0.14); vec4 cm = uFixedColormapStart + gray * uFixedColormapRange; @@ -459,13 +463,13 @@ void main() break; } - case 2: + case 2: // fullscreen tint. { frag = vColor * frag * uFixedColormapStart; break; } - case 3: + case 3: // fog layer { float fogdist; float fogfactor; @@ -486,6 +490,20 @@ void main() frag = vec4(uFogColor.rgb, (1.0 - fogfactor) * frag.a * 0.75 * vColor.a); break; } + + case 4: // simple 2D + { + frag = uObjectColor + frag * vColor; + break; + } + + case 5: // 2D with in-game lighting (for textured automap) + { + frag = frag * vColor; + frag.rgb = uObjectColor.rgb + frag.rgb * uObjectColor2.aaa + uObjectColor2.rgb; + break; + } + } FragColor = frag; #ifdef GBUFFER_PASS