diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 629c9e25e2e..a1861d93fe5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -718,6 +718,7 @@ set ( SWRENDER_SOURCES swrenderer/viewport/r_viewport.cpp swrenderer/viewport/r_walldrawer.cpp swrenderer/line/r_line.cpp + swrenderer/line/r_farclip_line.cpp swrenderer/line/r_walldraw.cpp swrenderer/line/r_wallsetup.cpp swrenderer/line/r_fogboundary.cpp @@ -1006,6 +1007,7 @@ set (PCH_SOURCES gl/shaders/gl_shader.cpp gl/shaders/gl_texshader.cpp gl/shaders/gl_shaderprogram.cpp + gl/shaders/gl_postprocessshader.cpp gl/shaders/gl_shadowmapshader.cpp gl/shaders/gl_presentshader.cpp gl/shaders/gl_present3dRowshader.cpp diff --git a/src/g_game.cpp b/src/g_game.cpp index 0a0a4c438ef..183473d78c0 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1765,6 +1765,8 @@ void G_DoReborn (int playernum, bool freshbot) } else { + bool isUnfriendly = players[playernum].mo && !(players[playernum].mo->flags & MF_FRIENDLY); + // respawn at the start // first disassociate the corpse if (players[playernum].mo) @@ -1774,7 +1776,7 @@ void G_DoReborn (int playernum, bool freshbot) } // spawn at random spot if in deathmatch - if (deathmatch) + if (deathmatch || isUnfriendly) { G_DeathMatchSpawnPlayer (playernum); return; diff --git a/src/gi.cpp b/src/gi.cpp index 85335554cf7..ecc3016241a 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -380,6 +380,7 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_BOOL(nightmarefast, "nightmarefast") GAMEINFOKEY_COLOR(dimcolor, "dimcolor") GAMEINFOKEY_FLOAT(dimamount, "dimamount") + GAMEINFOKEY_FLOAT(bluramount, "bluramount") GAMEINFOKEY_INT(definventorymaxamount, "definventorymaxamount") GAMEINFOKEY_INT(defaultrespawntime, "defaultrespawntime") GAMEINFOKEY_INT(defaultrespawntime, "defaultrespawntime") diff --git a/src/gi.h b/src/gi.h index 5c9dc1ae58b..be46aabfa95 100644 --- a/src/gi.h +++ b/src/gi.h @@ -159,6 +159,7 @@ struct gameinfo_t FString CursorPic; uint32_t dimcolor; float dimamount; + float bluramount; int definventorymaxamount; int defaultrespawntime; int defaultdropstyle; diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index ad096a1213d..0fff6219810 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -62,6 +62,7 @@ #include "gl/shaders/gl_lensshader.h" #include "gl/shaders/gl_fxaashader.h" #include "gl/shaders/gl_presentshader.h" +#include "gl/shaders/gl_postprocessshader.h" #include "gl/renderer/gl_2ddrawer.h" #include "gl/stereo3d/gl_stereo3d.h" @@ -145,10 +146,12 @@ CUSTOM_CVAR(Bool, gl_paltonemap_reverselookup, true, CVAR_ARCHIVE | CVAR_NOINITC GLRenderer->ClearTonemapPalette(); } +CVAR(Float, gl_menu_blur, -1.0f, CVAR_ARCHIVE) EXTERN_CVAR(Float, vid_brightness) EXTERN_CVAR(Float, vid_contrast) - +EXTERN_CVAR(Float, vid_saturation) +EXTERN_CVAR(Int, gl_satformula) void FGLRenderer::RenderScreenQuad() { @@ -161,11 +164,13 @@ void FGLRenderer::PostProcessScene(int fixedcm) { mBuffers->BlitSceneToTexture(); UpdateCameraExposure(); + mCustomPostProcessShaders->Run("beforebloom"); BloomScene(fixedcm); TonemapScene(); ColormapScene(fixedcm); LensDistortScene(); ApplyFXAA(); + mCustomPostProcessShaders->Run("scene"); } //----------------------------------------------------------------------------- @@ -467,6 +472,86 @@ void FGLRenderer::BloomScene(int fixedcm) FGLDebug::PopGroup(); } +//----------------------------------------------------------------------------- +// +// Blur the scene +// +//----------------------------------------------------------------------------- + +void FGLRenderer::BlurScene(float gameinfobluramount) +{ + // first, respect the CVar + float blurAmount = gl_menu_blur; + + // if CVar is negative, use the gameinfo entry + if (gl_menu_blur < 0) + blurAmount = gameinfobluramount; + + // if blurAmount == 0 or somehow still returns negative, exit to prevent a crash, clearly we don't want this + if ((blurAmount <= 0.0) || !FGLRenderBuffers::IsEnabled()) + return; + + FGLDebug::PushGroup("BlurScene"); + + FGLPostProcessState savedState; + savedState.SaveTextureBindings(2); + + int sampleCount = 9; + int numLevels = 3; // Must be 4 or less (since FGLRenderBuffers::NumBloomLevels is 4 and we are using its buffers). + assert(numLevels <= FGLRenderBuffers::NumBloomLevels); + + const auto &viewport = mScreenViewport; // The area we want to blur. Could also be mSceneViewport if only the scene area is to be blured + + const auto &level0 = mBuffers->BloomLevels[0]; + + // Grab the area we want to bloom: + glBindFramebuffer(GL_READ_FRAMEBUFFER, mBuffers->GetCurrentFB()); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, level0.VFramebuffer); + glBlitFramebuffer(viewport.left, viewport.top, viewport.width, viewport.height, 0, 0, level0.Width, level0.Height, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + // Blur and downscale: + for (int i = 0; i < numLevels - 1; i++) + { + const auto &level = mBuffers->BloomLevels[i]; + const auto &next = mBuffers->BloomLevels[i + 1]; + mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); + mBlurShader->BlurVertical(this, blurAmount, sampleCount, level.HTexture, next.VFramebuffer, next.Width, next.Height); + } + + // Blur and upscale: + for (int i = numLevels - 1; i > 0; i--) + { + const auto &level = mBuffers->BloomLevels[i]; + const auto &next = mBuffers->BloomLevels[i - 1]; + + mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); + mBlurShader->BlurVertical(this, blurAmount, sampleCount, level.HTexture, level.VFramebuffer, level.Width, level.Height); + + // Linear upscale: + glBindFramebuffer(GL_FRAMEBUFFER, next.VFramebuffer); + glViewport(0, 0, next.Width, next.Height); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, level.VTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + mBloomCombineShader->Bind(); + mBloomCombineShader->BloomTexture.Set(0); + RenderScreenQuad(); + } + + mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height); + mBlurShader->BlurVertical(this, blurAmount, sampleCount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height); + + // Copy blur back to scene texture: + glBindFramebuffer(GL_READ_FRAMEBUFFER, level0.VFramebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mBuffers->GetCurrentFB()); + glBlitFramebuffer(0, 0, level0.Width, level0.Height, viewport.left, viewport.top, viewport.width, viewport.height, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + + FGLDebug::PopGroup(); +} + //----------------------------------------------------------------------------- // // Tonemap scene texture and place the result in the HUD/2D texture @@ -734,6 +819,8 @@ void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma) m2DDrawer->Draw(); // draw all pending 2D stuff before copying the buffer m2DDrawer->Clear(); + mCustomPostProcessShaders->Run("screen"); + FGLDebug::PushGroup("CopyToBackbuffer"); if (FGLRenderBuffers::IsEnabled()) { @@ -777,12 +864,15 @@ void FGLRenderer::DrawPresentTexture(const GL_IRECT &box, bool applyGamma) mPresentShader->InvGamma.Set(1.0f); mPresentShader->Contrast.Set(1.0f); mPresentShader->Brightness.Set(0.0f); + mPresentShader->Saturation.Set(1.0f); } else { mPresentShader->InvGamma.Set(1.0f / clamp(Gamma, 0.1f, 4.f)); mPresentShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); + mPresentShader->Saturation.Set(clamp(vid_saturation, -15.0f, 15.f)); + mPresentShader->GrayFormula.Set(static_cast(gl_satformula)); } mPresentShader->Scale.Set(mScreenViewport.width / (float)mBuffers->GetWidth(), mScreenViewport.height / (float)mBuffers->GetHeight()); RenderScreenQuad(); diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index c6aec6286c3..1c8ea1690ed 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -43,6 +43,8 @@ class FGLRenderBuffers void BindNextFB(); void NextTexture(); + int GetCurrentFB() const { return mPipelineFB[mCurrentPipelineTexture]; } + void BindOutputFB(); void BlitToEyeTexture(int eye); diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 77d8d6bc303..bac55edb897 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -62,6 +62,7 @@ #include "gl/shaders/gl_presentshader.h" #include "gl/shaders/gl_present3dRowshader.h" #include "gl/shaders/gl_shadowmapshader.h" +#include "gl/shaders/gl_postprocessshader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -128,6 +129,7 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mFXAAShader = nullptr; mFXAALumaShader = nullptr; mShadowMapShader = nullptr; + mCustomPostProcessShaders = nullptr; } void gl_LoadModels(); @@ -157,6 +159,7 @@ void FGLRenderer::Initialize(int width, int height) mPresent3dColumnShader = new FPresent3DColumnShader(); mPresent3dRowShader = new FPresent3DRowShader(); mShadowMapShader = new FShadowMapShader(); + mCustomPostProcessShaders = new FCustomPostProcessShaders(); m2DDrawer = new F2DDrawer; // needed for the core profile, because someone decided it was a good idea to remove the default VAO. @@ -232,6 +235,7 @@ FGLRenderer::~FGLRenderer() if (mColormapShader) delete mColormapShader; if (mLensShader) delete mLensShader; if (mShadowMapShader) delete mShadowMapShader; + delete mCustomPostProcessShaders; delete mFXAAShader; delete mFXAALumaShader; } diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 1db0f2db3bc..e3924ad4086 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -42,6 +42,7 @@ class FPresent3DRowShader; class F2DDrawer; class FHardwareTexture; class FShadowMapShader; +class FCustomPostProcessShaders; inline float DEG2RAD(float deg) { @@ -126,6 +127,7 @@ class FGLRenderer FPresent3DColumnShader *mPresent3dColumnShader; FPresent3DRowShader *mPresent3dRowShader; FShadowMapShader *mShadowMapShader; + FCustomPostProcessShaders *mCustomPostProcessShaders; FShadowMap mShadowMap; @@ -182,6 +184,7 @@ class FGLRenderer void ClearTonemapPalette(); void LensDistortScene(); void ApplyFXAA(); + void BlurScene(float gameinfobluramount); void CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma); void DrawPresentTexture(const GL_IRECT &box, bool applyGamma); void Flush(); diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp index dcdf260efd0..ff2321da5ee 100644 --- a/src/gl/scene/gl_weapon.cpp +++ b/src/gl/scene/gl_weapon.cpp @@ -98,7 +98,7 @@ void GLSceneDrawer::DrawPSprite (player_t * player,DPSprite *psp, float sx, floa // calculate edges of the shape scalex = (320.0f / (240.0f * r_viewwindow.WidescreenRatio)) * vw / 320; - tx = sx - (160 - r.left); + tx = (psp->Flags & PSPF_MIRROR) ? ((160 - r.width) - (sx + r.left)) : (sx - (160 - r.left)); x1 = tx * scalex + vw/2; if (x1 > vw) return; // off the right side x1 += viewwindowx; @@ -108,7 +108,6 @@ void GLSceneDrawer::DrawPSprite (player_t * player,DPSprite *psp, float sx, floa if (x2 < 0) return; // off the left side x2 += viewwindowx; - // killough 12/98: fix psprite positioning problem ftexturemid = 100.f - sy - r.top; @@ -130,7 +129,8 @@ void GLSceneDrawer::DrawPSprite (player_t * player,DPSprite *psp, float sx, floa y1 = viewwindowy + vh / 2 - (ftexturemid * scale); y2 = y1 + (r.height * scale) + 1; - if (!(mirror) != !(psp->Flags & PSPF_FLIP)) + + if (!(mirror) != !(psp->Flags & (PSPF_FLIP))) { fU2 = tex->GetSpriteUL(); fV1 = tex->GetSpriteVT(); @@ -437,7 +437,7 @@ void GLSceneDrawer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) if (psp->Flags & PSPF_ADDBOB) { - sx += bobx; + sx += (psp->Flags & PSPF_MIRROR) ? -bobx : bobx; sy += boby; } diff --git a/src/gl/shaders/gl_postprocessshader.cpp b/src/gl/shaders/gl_postprocessshader.cpp new file mode 100644 index 00000000000..0baf065312e --- /dev/null +++ b/src/gl/shaders/gl_postprocessshader.cpp @@ -0,0 +1,194 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2017 Magnus Norddahl +// 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/ +// +//-------------------------------------------------------------------------- +// + +#include "gl/system/gl_system.h" +#include "m_swap.h" +#include "v_video.h" +#include "gl/gl_functions.h" +#include "vectors.h" +#include "w_wad.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_debug.h" +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_postprocessstate.h" +#include "gl/renderer/gl_renderbuffers.h" +#include "gl/shaders/gl_postprocessshader.h" + +CVAR(Bool, gl_custompost, true, 0) + +TArray PostProcessShaders; + +FCustomPostProcessShaders::FCustomPostProcessShaders() +{ + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + mShaders.push_back(std::unique_ptr(new PostProcessShaderInstance(&PostProcessShaders[i]))); + } +} + +FCustomPostProcessShaders::~FCustomPostProcessShaders() +{ +} + +void FCustomPostProcessShaders::Run(FString target) +{ + if (!gl_custompost) + return; + + for (auto &shader : mShaders) + { + if (shader->Desc->Target == target) + { + shader->Run(); + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +void PostProcessShaderInstance::Run() +{ + if (!IsShaderSupported()) + return; + + CompileShader(); + + if (!Desc->Enabled) + return; + + FGLDebug::PushGroup(Desc->ShaderLumpName.GetChars()); + + FGLPostProcessState savedState; + + GLRenderer->mBuffers->BindNextFB(); + GLRenderer->mBuffers->BindCurrentTexture(0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + mProgram.Bind(); + + UpdateUniforms(); + + mInputTexture.Set(0); + + GLRenderer->RenderScreenQuad(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GLRenderer->mBuffers->NextTexture(); + + FGLDebug::PopGroup(); +} + +bool PostProcessShaderInstance::IsShaderSupported() +{ + int activeShaderVersion = (int)round(gl.glslversion * 10) * 10; + return activeShaderVersion >= Desc->ShaderVersion; +} + +void PostProcessShaderInstance::CompileShader() +{ + if (mProgram) + return; + + // Get the custom shader + const char *lumpName = Desc->ShaderLumpName.GetChars(); + int lump = Wads.CheckNumForFullName(lumpName); + if (lump == -1) I_FatalError("Unable to load '%s'", lumpName); + FString code = Wads.ReadLump(lump).GetString().GetChars(); + + // Build an uniform block to use be used as input + // (this is technically not an uniform block, but it could be changed into that for Vulkan GLSL support) + FString uniformBlock; + TMap::Iterator it(Desc->Uniforms); + TMap::Pair *pair; + while (it.NextPair(pair)) + { + FString type; + FString name = pair->Key; + + switch (pair->Value.Type) + { + case PostProcessUniformType::Float: type = "float"; break; + case PostProcessUniformType::Int: type = "int"; break; + case PostProcessUniformType::Vec2: type = "vec2"; break; + case PostProcessUniformType::Vec3: type = "vec3"; break; + default: break; + } + + if (!type.IsEmpty()) + uniformBlock.AppendFormat("uniform %s %s;\n", type.GetChars(), name.GetChars()); + } + + // Build the input textures + FString uniformTextures; + uniformTextures += "uniform sampler2D InputTexture;\n"; + + // Setup pipeline + FString pipelineInOut; + pipelineInOut += "in vec2 TexCoord;\n"; + pipelineInOut += "out vec4 FragColor;\n"; + + FString prolog; + prolog += uniformBlock; + prolog += uniformTextures; + prolog += pipelineInOut; + + mProgram.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", Desc->ShaderVersion); + mProgram.Compile(FShaderProgram::Fragment, lumpName, code, prolog.GetChars(), Desc->ShaderVersion); + mProgram.SetFragDataLocation(0, "FragColor"); + mProgram.Link(Desc->ShaderLumpName.GetChars()); + mProgram.SetAttribLocation(0, "PositionInProjection"); + mInputTexture.Init(mProgram, "InputTexture"); +} + +void PostProcessShaderInstance::UpdateUniforms() +{ + TMap::Iterator it(Desc->Uniforms); + TMap::Pair *pair; + while (it.NextPair(pair)) + { + int location = glGetUniformLocation(mProgram, pair->Key.GetChars()); + if (location != -1) + { + switch (pair->Value.Type) + { + case PostProcessUniformType::Float: + glUniform1f(location, (float)pair->Value.Values[0]); + break; + case PostProcessUniformType::Int: + glUniform1i(location, (int)pair->Value.Values[0]); + break; + case PostProcessUniformType::Vec2: + glUniform2f(location, (float)pair->Value.Values[0], (float)pair->Value.Values[1]); + break; + case PostProcessUniformType::Vec3: + glUniform3f(location, (float)pair->Value.Values[0], (float)pair->Value.Values[1], (float)pair->Value.Values[2]); + break; + default: + break; + } + } + } +} diff --git a/src/gl/shaders/gl_postprocessshader.h b/src/gl/shaders/gl_postprocessshader.h new file mode 100644 index 00000000000..876cb9f56c2 --- /dev/null +++ b/src/gl/shaders/gl_postprocessshader.h @@ -0,0 +1,67 @@ +#pragma once + +#include "gl_shaderprogram.h" + +class PostProcessShaderInstance; + +enum class PostProcessUniformType +{ + Undefined, + Int, + Float, + Vec2, + Vec3 +}; + +struct PostProcessUniformValue +{ + PostProcessUniformType Type = PostProcessUniformType::Undefined; + double Values[4] = { 0.0, 0.0, 0.0, 0.0 }; +}; + +struct PostProcessShader +{ + FString Target; + FString ShaderLumpName; + int ShaderVersion = 0; + + FString Name; + bool Enabled = false; + + TMap Uniforms; +}; + +extern TArray PostProcessShaders; + +class PostProcessShaderInstance +{ +public: + PostProcessShaderInstance(PostProcessShader *desc) : Desc(desc) { } + + void Run(); + + PostProcessShader *Desc; + +private: + bool IsShaderSupported(); + void CompileShader(); + void UpdateUniforms(); + + FShaderProgram mProgram; + FBufferedUniformSampler mInputTexture; +}; + +class FCustomPostProcessShaders +{ +public: + FCustomPostProcessShaders(); + ~FCustomPostProcessShaders(); + + void Run(FString target); + +private: + std::vector> mShaders; + + FCustomPostProcessShaders(const FCustomPostProcessShaders &) = delete; + FCustomPostProcessShaders &operator=(const FCustomPostProcessShaders &) = delete; +}; diff --git a/src/gl/shaders/gl_presentshader.cpp b/src/gl/shaders/gl_presentshader.cpp index 5fe040db9a6..06056f539e5 100644 --- a/src/gl/shaders/gl_presentshader.cpp +++ b/src/gl/shaders/gl_presentshader.cpp @@ -46,6 +46,8 @@ void FPresentShaderBase::Init(const char * vtx_shader_name, const char * program InvGamma.Init(mShader, "InvGamma"); Contrast.Init(mShader, "Contrast"); Brightness.Init(mShader, "Brightness"); + Saturation.Init(mShader, "Saturation"); + GrayFormula.Init(mShader, "GrayFormula"); Scale.Init(mShader, "UVScale"); } diff --git a/src/gl/shaders/gl_presentshader.h b/src/gl/shaders/gl_presentshader.h index dcf42cdf8bc..377a6a4f29d 100644 --- a/src/gl/shaders/gl_presentshader.h +++ b/src/gl/shaders/gl_presentshader.h @@ -12,6 +12,8 @@ class FPresentShaderBase FBufferedUniform1f InvGamma; FBufferedUniform1f Contrast; FBufferedUniform1f Brightness; + FBufferedUniform1f Saturation; + FBufferedUniform1i GrayFormula; FBufferedUniform2f Scale; protected: diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 69ac1b32a3a..b0b3ea32b41 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -36,6 +36,8 @@ #include "v_palette.h" #include "sc_man.h" #include "cmdlib.h" +#include "vm.h" +#include "d_player.h" #include "gl/system/gl_interface.h" #include "gl/system/gl_debug.h" @@ -46,6 +48,7 @@ #include "gl/system/gl_cvars.h" #include "gl/shaders/gl_shader.h" #include "gl/shaders/gl_shaderprogram.h" +#include "gl/shaders/gl_postprocessshader.h" #include "gl/textures/gl_material.h" #include "gl/dynlights/gl_lightbuffer.h" @@ -672,61 +675,253 @@ void gl_DestroyUserShaders() void gl_ParseHardwareShader(FScanner &sc, int deflump) { - int type = FTexture::TEX_Any; - bool disable_fullbright=false; - bool thiswad = false; - bool iwad = false; - int maplump = -1; - FString maplumpname; - float speed = 1.f; - sc.MustGetString(); - if (sc.Compare("texture")) type = FTexture::TEX_Wall; - else if (sc.Compare("flat")) type = FTexture::TEX_Flat; - else if (sc.Compare("sprite")) type = FTexture::TEX_Sprite; - else sc.UnGet(); + if (sc.Compare("postprocess")) + { + sc.MustGetString(); - sc.MustGetString(); - FTextureID no = TexMan.CheckForTexture(sc.String, type); - FTexture *tex = TexMan[no]; + PostProcessShader shaderdesc; + shaderdesc.Target = sc.String; + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + sc.MustGetString(); + if (sc.Compare("shader")) + { + sc.MustGetString(); + shaderdesc.ShaderLumpName = sc.String; + + sc.MustGetNumber(); + shaderdesc.ShaderVersion = sc.Number; + } + else if (sc.Compare("name")) + { + sc.MustGetString(); + shaderdesc.Name = sc.String; + } + else if (sc.Compare("uniform")) + { + sc.MustGetString(); + FString uniformType = sc.String; + uniformType.ToLower(); + + sc.MustGetString(); + FString uniformName = sc.String; + + PostProcessUniformType parsedType = PostProcessUniformType::Undefined; + + if (uniformType.Compare("int") == 0) + parsedType = PostProcessUniformType::Int; + else if (uniformType.Compare("float") == 0) + parsedType = PostProcessUniformType::Float; + else if (uniformType.Compare("vec2") == 0) + parsedType = PostProcessUniformType::Vec2; + else if (uniformType.Compare("vec3") == 0) + parsedType = PostProcessUniformType::Vec3; + + if (parsedType != PostProcessUniformType::Undefined) + shaderdesc.Uniforms[uniformName].Type = parsedType; + } + else if (sc.Compare("enabled")) + { + shaderdesc.Enabled = true; + } + } - sc.MustGetToken('{'); - while (!sc.CheckToken('}')) + PostProcessShaders.Push(shaderdesc); + } + else { + int type = FTexture::TEX_Any; + + if (sc.Compare("texture")) type = FTexture::TEX_Wall; + else if (sc.Compare("flat")) type = FTexture::TEX_Flat; + else if (sc.Compare("sprite")) type = FTexture::TEX_Sprite; + else sc.UnGet(); + + bool disable_fullbright = false; + bool thiswad = false; + bool iwad = false; + int maplump = -1; + FString maplumpname; + float speed = 1.f; + sc.MustGetString(); - if (sc.Compare("shader")) + FTextureID no = TexMan.CheckForTexture(sc.String, type); + FTexture *tex = TexMan[no]; + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) { sc.MustGetString(); - maplumpname = sc.String; + if (sc.Compare("shader")) + { + sc.MustGetString(); + maplumpname = sc.String; + } + else if (sc.Compare("speed")) + { + sc.MustGetFloat(); + speed = float(sc.Float); + } + } + if (!tex) + { + return; } - else if (sc.Compare("speed")) + + if (maplumpname.IsNotEmpty()) + { + if (tex->bWarped != 0) + { + Printf("Cannot combine warping with hardware shader on texture '%s'\n", tex->Name.GetChars()); + return; + } + tex->gl_info.shaderspeed = speed; + for (unsigned i = 0; i < usershaders.Size(); i++) + { + if (!usershaders[i].CompareNoCase(maplumpname)) + { + tex->gl_info.shaderindex = i + FIRST_USER_SHADER; + return; + } + } + tex->gl_info.shaderindex = usershaders.Push(maplumpname) + FIRST_USER_SHADER; + } + } +} + +static bool IsConsolePlayer(player_t *player) +{ + AActor *activator = player ? player->mo : nullptr; + if (activator == nullptr || activator->player == nullptr) + return false; + return int(activator->player - players) == consoleplayer; +} + +DEFINE_ACTION_FUNCTION(_Shader, SetEnabled) +{ + PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); + PARAM_STRING(shaderName); + PARAM_BOOL_DEF(value); + + if (IsConsolePlayer(player)) + { + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) { - sc.MustGetFloat(); - speed = float(sc.Float); + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + shader.Enabled = value; } } - if (!tex) + return 0; +} + +DEFINE_ACTION_FUNCTION(_Shader, SetUniform1f) +{ + PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); + PARAM_STRING(shaderName); + PARAM_STRING(uniformName); + PARAM_FLOAT_DEF(value); + + if (IsConsolePlayer(player)) { - return; + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + { + double *vec4 = shader.Uniforms[uniformName].Values; + vec4[0] = value; + vec4[1] = 0.0; + vec4[2] = 0.0; + vec4[3] = 1.0; + } + } } + return 0; +} - if (maplumpname.IsNotEmpty()) +DEFINE_ACTION_FUNCTION(_Shader, SetUniform2f) +{ + PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); + PARAM_STRING(shaderName); + PARAM_STRING(uniformName); + PARAM_FLOAT_DEF(x); + PARAM_FLOAT_DEF(y); + + if (IsConsolePlayer(player)) { - if (tex->bWarped != 0) + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) { - Printf("Cannot combine warping with hardware shader on texture '%s'\n", tex->Name.GetChars()); - return; + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + { + double *vec4 = shader.Uniforms[uniformName].Values; + vec4[0] = x; + vec4[1] = y; + vec4[2] = 0.0; + vec4[3] = 1.0; + } } - tex->gl_info.shaderspeed = speed; - for(unsigned i=0;igl_info.shaderindex = i + FIRST_USER_SHADER; - return; + double *vec4 = shader.Uniforms[uniformName].Values; + vec4[0] = x; + vec4[1] = y; + vec4[2] = z; + vec4[3] = 1.0; } } - tex->gl_info.shaderindex = usershaders.Push(maplumpname) + FIRST_USER_SHADER; - } + } + return 0; } +DEFINE_ACTION_FUNCTION(_Shader, SetUniform1i) +{ + PARAM_PROLOGUE; + PARAM_POINTER_DEF(player, player_t); + PARAM_STRING(shaderName); + PARAM_STRING(uniformName); + PARAM_INT_DEF(value); + + if (IsConsolePlayer(player)) + { + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + PostProcessShader &shader = PostProcessShaders[i]; + if (shader.Name == shaderName) + { + double *vec4 = shader.Uniforms[uniformName].Values; + vec4[0] = (double)value; + vec4[1] = 0.0; + vec4[2] = 0.0; + vec4[3] = 1.0; + } + } + } + return 0; +} diff --git a/src/gl/stereo3d/gl_interleaved3d.cpp b/src/gl/stereo3d/gl_interleaved3d.cpp index 40aed85a97b..4729fbf7730 100644 --- a/src/gl/stereo3d/gl_interleaved3d.cpp +++ b/src/gl/stereo3d/gl_interleaved3d.cpp @@ -41,8 +41,10 @@ #include "gl/system/gl_framebuffer.h" #include "gl/shaders/gl_present3dRowshader.h" +EXTERN_CVAR(Float, vid_saturation) EXTERN_CVAR(Float, vid_brightness) EXTERN_CVAR(Float, vid_contrast) +EXTERN_CVAR(Int, gl_satformula) EXTERN_CVAR(Bool, fullscreen) EXTERN_CVAR(Int, win_x) // screen pixel position of left of display window EXTERN_CVAR(Int, win_y) // screen pixel position of top of display window @@ -100,12 +102,15 @@ static void prepareInterleavedPresent(FPresentStereoShaderBase& shader) shader.InvGamma.Set(1.0f); shader.Contrast.Set(1.0f); shader.Brightness.Set(0.0f); + shader.Saturation.Set(1.0f); } else { shader.InvGamma.Set(1.0f / clamp(Gamma, 0.1f, 4.f)); shader.Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); shader.Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); + shader.Saturation.Set(clamp(vid_saturation, -15.0f, 15.0f)); + shader.GrayFormula.Set(static_cast(gl_satformula)); } shader.Scale.Set( GLRenderer->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(), diff --git a/src/gl/system/gl_menu.cpp b/src/gl/system/gl_menu.cpp index 8dffbc4f21e..489ad4a9d25 100644 --- a/src/gl/system/gl_menu.cpp +++ b/src/gl/system/gl_menu.cpp @@ -73,6 +73,22 @@ CUSTOM_CVAR (Float, vid_contrast, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } +CUSTOM_CVAR (Float, vid_saturation, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma(Gamma); + } +} + +CUSTOM_CVAR(Int, gl_satformula, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma(Gamma); + } +} + // Do some tinkering with the menus so that certain options only appear // when they are actually valid. diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 8893b57698f..65a5d5a99bf 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -57,6 +57,7 @@ #include "textures/textures.h" #include "vm.h" #include "events.h" +#include "gl/renderer/gl_renderer.h" // for menu blur // // Todo: Move these elsewhere @@ -784,6 +785,8 @@ void M_Drawer (void) if (CurrentMenu != nullptr && menuactive != MENU_Off) { + if (GLRenderer) + GLRenderer->BlurScene(gameinfo.bluramount); if (!CurrentMenu->DontDim) { screen->Dim(fade); diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 047b599240a..d44d3e44464 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -1889,6 +1889,9 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params) if (!(player->mo->flags & MF_SHOOTABLE)) continue; // not shootable (observer or dead) + if (!((actor->flags ^ player->mo->flags) & MF_FRIENDLY)) + continue; // same +MF_FRIENDLY, ignore + if (player->cheats & CF_NOTARGET) continue; // no target @@ -1988,7 +1991,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Look) targ = NULL; } - if (targ && targ->player && (targ->player->cheats & CF_NOTARGET)) + if (targ && targ->player && ((targ->player->cheats & CF_NOTARGET) || !(targ->flags & MF_FRIENDLY))) { return 0; } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index a241647d9e2..1b3e0f028da 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -79,6 +79,9 @@ CVAR (Bool, cl_showsprees, true, CVAR_ARCHIVE) CVAR (Bool, cl_showmultikills, true, CVAR_ARCHIVE) EXTERN_CVAR (Bool, show_obituaries) +CVAR (Float, sv_damagefactormobj, 1.0, CVAR_SERVERINFO|CVAR_NOSAVE) +CVAR (Float, sv_damagefactorfriendly, 1.0, CVAR_SERVERINFO|CVAR_NOSAVE) +CVAR (Float, sv_damagefactorplayer, 1.0, CVAR_SERVERINFO|CVAR_NOSAVE) FName MeansOfDeath; @@ -1036,7 +1039,17 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da if (player && damage > 1) { // Take half damage in trainer mode - damage = int(damage * G_SkillProperty(SKILLP_DamageFactor)); + damage = int(damage * G_SkillProperty(SKILLP_DamageFactor) * sv_damagefactorplayer); + } + else if (!player && damage > 1 && !(target->flags & MF_FRIENDLY)) + { + // inflict scaled damage to non-players + damage = int(damage * sv_damagefactormobj); + } + else if (!player && damage > 1 && (target->flags & MF_FRIENDLY)) + { + // inflict scaled damage to non-player friends + damage = int(damage * sv_damagefactorfriendly); } // Special damage types if (inflictor) @@ -1865,7 +1878,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPain return; } // Take half damage in trainer mode - damage = int(damage * G_SkillProperty(SKILLP_DamageFactor)); + damage = int(damage * G_SkillProperty(SKILLP_DamageFactor) * sv_damagefactorplayer); // Handle passive damage modifiers (e.g. PowerProtection) damage = target->GetModifiedDamage(player->poisontype, damage, true); // Modify with damage factors diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 8d8655d610e..e2646ade8dd 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -6718,7 +6718,7 @@ bool P_CheckMissileSpawn (AActor* th, double maxdist) th->tics = 1; } - DVector3 newpos = th->Pos(); + DVector3 newpos = { 0,0,0 }; if (maxdist > 0) { @@ -6736,6 +6736,9 @@ bool P_CheckMissileSpawn (AActor* th, double maxdist) newpos += advance; } + newpos = th->Vec3Offset(newpos); + th->SetXYZ(newpos); + FCheckPosition tm(!!(th->flags2 & MF2_RIP)); // killough 8/12/98: for non-missile objects (e.g. grenades) @@ -7351,7 +7354,7 @@ bool AActor::IsTeammate (AActor *other) } else if (!deathmatch && player && other->player) { - return true; + return (!((flags ^ other->flags) & MF_FRIENDLY)); } else if (teamplay) { @@ -7432,6 +7435,9 @@ bool AActor::IsFriend (AActor *other) other->FriendPlayer == 0 || players[FriendPlayer-1].mo->IsTeammate(players[other->FriendPlayer-1].mo); } + // [SP] If friendly flags match, then they are on the same team. + /*if (!((flags ^ other->flags) & MF_FRIENDLY)) + return true;*/ return false; } diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 2404b8b9c75..45e92a8d03f 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -141,6 +141,7 @@ DEFINE_FIELD_BIT(DPSprite, Flags, bAddBob, PSPF_ADDBOB) DEFINE_FIELD_BIT(DPSprite, Flags, bPowDouble, PSPF_POWDOUBLE) DEFINE_FIELD_BIT(DPSprite, Flags, bCVarFast, PSPF_CVARFAST) DEFINE_FIELD_BIT(DPSprite, Flags, bFlip, PSPF_FLIP) +DEFINE_FIELD_BIT(DPSprite, Flags, bMirror, PSPF_MIRROR) //------------------------------------------------------------------------ // diff --git a/src/p_pspr.h b/src/p_pspr.h index 5964ace2085..c98918ded5a 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -68,6 +68,7 @@ enum PSPFlags PSPF_FLIP = 1 << 6, PSPF_FORCEALPHA = 1 << 7, PSPF_FORCESTYLE = 1 << 8, + PSPF_MIRROR = 1 << 9, }; class DPSprite : public DObject diff --git a/src/p_setup.cpp b/src/p_setup.cpp index f89ad6704ee..1bb0e9d8b01 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4119,6 +4119,22 @@ void P_SetupLevel (const char *lumpname, int position) } } + // [SP] move unfriendly players around + // horribly hacky - yes, this needs rewritten. + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].mo != NULL) + { + if (!(players[i].mo->flags & MF_FRIENDLY)) + { + AActor * oldSpawn = players[i].mo; + G_DeathMatchSpawnPlayer (i); + oldSpawn->Destroy(); + } + } + } + + // Don't count monsters in end-of-level sectors if option is on if (dmflags2 & DF2_NOCOUNTENDMONST) { diff --git a/src/polyrenderer/scene/poly_playersprite.cpp b/src/polyrenderer/scene/poly_playersprite.cpp index ac8af89ceaa..e3563976cd0 100644 --- a/src/polyrenderer/scene/poly_playersprite.cpp +++ b/src/polyrenderer/scene/poly_playersprite.cpp @@ -232,7 +232,7 @@ void RenderPolyPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float if (pspr->Flags & PSPF_ADDBOB) { - sx += bobx; + sx += (pspr->Flags & PSPF_MIRROR) ? -bobx : bobx; sy += boby; } @@ -248,17 +248,20 @@ void RenderPolyPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float double pspriteyscale = pspritexscale * yaspectMul; double pspritexiscale = 1 / pspritexscale; + int tleft = tex->GetScaledLeftOffset(); + int twidth = tex->GetScaledWidth(); + // calculate edges of the shape - tx = sx - BASEXCENTER; + //tx = sx - BASEXCENTER; + tx = (pspr->Flags & PSPF_MIRROR) ? ((BASEXCENTER - twidth) - (sx - tleft)) : ((sx - BASEXCENTER) - tleft); - tx -= tex->GetScaledLeftOffset(); x1 = xs_RoundToInt(viewwindow.centerx + tx * pspritexscale); // off the right side if (x1 > viewwidth) return; - tx += tex->GetScaledWidth(); + tx += twidth; x2 = xs_RoundToInt(viewwindow.centerx + tx * pspritexscale); // off the left side diff --git a/src/swrenderer/line/r_farclip_line.cpp b/src/swrenderer/line/r_farclip_line.cpp new file mode 100644 index 00000000000..95e559a245b --- /dev/null +++ b/src/swrenderer/line/r_farclip_line.cpp @@ -0,0 +1,190 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2016 Magnus Norddahl +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "r_sky.h" +#include "v_video.h" +#include "m_swap.h" +#include "w_wad.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "r_wallsetup.h" +#include "v_palette.h" +#include "r_utility.h" +#include "r_data/colormaps.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/line/r_farclip_line.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/viewport/r_skydrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + FarClipLine::FarClipLine(RenderThread *thread) + { + Thread = thread; + } + + void FarClipLine::Render(seg_t *line, subsector_t *subsector, VisiblePlane *linefloorplane, VisiblePlane *lineceilingplane) + { + mSubsector = subsector; + mFrontSector = mSubsector->sector; + mLineSegment = line; + mFloorPlane = linefloorplane; + mCeilingPlane = lineceilingplane; + + DVector2 pt1 = line->v1->fPos() - Thread->Viewport->viewpoint.Pos; + DVector2 pt2 = line->v2->fPos() - Thread->Viewport->viewpoint.Pos; + + // Reject lines not facing viewer + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + return; + + if (WallC.Init(Thread, pt1, pt2, 32.0 / (1 << 12))) + return; + + RenderPortal *renderportal = Thread->Portal.get(); + if (WallC.sx1 >= renderportal->WindowRight || WallC.sx2 <= renderportal->WindowLeft) + return; + + if (line->linedef == nullptr) + return; + + // reject lines that aren't seen from the portal (if any) + // [ZZ] 10.01.2016: lines inside a skybox shouldn't be clipped, although this imposes some limitations on portals in skyboxes. + if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && P_ClipLineToPortal(line->linedef, renderportal->CurrentPortal->dst, Thread->Viewport->viewpoint.Pos)) + return; + + mFrontCeilingZ1 = mFrontSector->ceilingplane.ZatPoint(line->v1); + mFrontFloorZ1 = mFrontSector->floorplane.ZatPoint(line->v1); + mFrontCeilingZ2 = mFrontSector->ceilingplane.ZatPoint(line->v2); + mFrontFloorZ2 = mFrontSector->floorplane.ZatPoint(line->v2); + + mPrepped = false; + + Thread->ClipSegments->Clip(WallC.sx1, WallC.sx2, true, this); + } + + bool FarClipLine::RenderWallSegment(int x1, int x2) + { + if (!mPrepped) + { + mPrepped = true; + + //walltop.Project(Thread->Viewport.get(), mFrontSector->ceilingplane, &WallC, mLineSegment, Thread->Portal->MirrorFlags & RF_XFLIP); + wallbottom.Project(Thread->Viewport.get(), mFrontSector->floorplane, &WallC, mLineSegment, Thread->Portal->MirrorFlags & RF_XFLIP); + memcpy(walltop.ScreenY, wallbottom.ScreenY, sizeof(short) * MAXWIDTH); + } + + ClipSegmentTopBottom(x1, x2); + MarkCeilingPlane(x1, x2); + MarkFloorPlane(x1, x2); + + return true; + } + + void FarClipLine::ClipSegmentTopBottom(int x1, int x2) + { + // clip wall to the floor and ceiling + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + for (int x = x1; x < x2; ++x) + { + if (walltop.ScreenY[x] < ceilingclip[x]) + { + walltop.ScreenY[x] = ceilingclip[x]; + } + if (wallbottom.ScreenY[x] > floorclip[x]) + { + wallbottom.ScreenY[x] = floorclip[x]; + } + } + } + + void FarClipLine::MarkCeilingPlane(int x1, int x2) + { + if (mCeilingPlane) + { + mCeilingPlane = Thread->PlaneList->GetRange(mCeilingPlane, x1, x2); + + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + for (int x = x1; x < x2; ++x) + { + short top = (clip3d->fakeFloor && clip3d->fake3D & FAKE3D_FAKECEILING) ? clip3d->fakeFloor->ceilingclip[x] : ceilingclip[x]; + short bottom = MIN(walltop.ScreenY[x], floorclip[x]); + if (top < bottom) + { + mCeilingPlane->top[x] = top; + mCeilingPlane->bottom[x] = bottom; + } + } + } + } + + void FarClipLine::MarkFloorPlane(int x1, int x2) + { + if (mFloorPlane) + { + mFloorPlane = Thread->PlaneList->GetRange(mFloorPlane, x1, x2); + + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + for (int x = x1; x < x2; ++x) + { + short top = MAX(wallbottom.ScreenY[x], ceilingclip[x]); + short bottom = (clip3d->fakeFloor && clip3d->fake3D & FAKE3D_FAKEFLOOR) ? clip3d->fakeFloor->floorclip[x] : floorclip[x]; + if (top < bottom) + { + assert(bottom <= viewheight); + mFloorPlane->top[x] = top; + mFloorPlane->bottom[x] = bottom; + } + } + } + } +} diff --git a/src/swrenderer/line/r_farclip_line.h b/src/swrenderer/line/r_farclip_line.h new file mode 100644 index 00000000000..c450a638363 --- /dev/null +++ b/src/swrenderer/line/r_farclip_line.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2016 Magnus Norddahl +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// + +#pragma once + +#include "r_line.h" + +namespace swrenderer +{ + class FarClipLine : VisibleSegmentRenderer + { + public: + FarClipLine(RenderThread *thread); + void Render(seg_t *line, subsector_t *subsector, VisiblePlane *linefloorplane, VisiblePlane *lineceilingplane); + + RenderThread *Thread = nullptr; + + private: + bool RenderWallSegment(int x1, int x2) override; + + void ClipSegmentTopBottom(int x1, int x2); + void MarkCeilingPlane(int x1, int x2); + void MarkFloorPlane(int x1, int x2); + + subsector_t *mSubsector; + sector_t *mFrontSector; + seg_t *mLineSegment; + VisiblePlane *mFloorPlane; + VisiblePlane *mCeilingPlane; + + double mFrontCeilingZ1; + double mFrontCeilingZ2; + double mFrontFloorZ1; + double mFrontFloorZ2; + + FWallCoords WallC; + + bool mPrepped; + + ProjectedWallLine walltop; + ProjectedWallLine wallbottom; + }; +} diff --git a/src/swrenderer/r_all.cpp b/src/swrenderer/r_all.cpp index 20a9f4ee85b..296fc04cf98 100644 --- a/src/swrenderer/r_all.cpp +++ b/src/swrenderer/r_all.cpp @@ -9,6 +9,7 @@ #include "drawers/r_thread.cpp" #include "line/r_fogboundary.cpp" #include "line/r_line.cpp" +#include "line/r_farclip_line.cpp" #include "line/r_renderdrawsegment.cpp" #include "line/r_walldraw.cpp" #include "line/r_wallsetup.cpp" diff --git a/src/swrenderer/scene/r_opaque_pass.cpp b/src/swrenderer/scene/r_opaque_pass.cpp index da820d7d26f..b772d62a4e8 100644 --- a/src/swrenderer/scene/r_opaque_pass.cpp +++ b/src/swrenderer/scene/r_opaque_pass.cpp @@ -47,6 +47,7 @@ #include "swrenderer/things/r_particle.h" #include "swrenderer/segments/r_clipsegment.h" #include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_farclip_line.h" #include "swrenderer/scene/r_scene.h" #include "swrenderer/scene/r_light.h" #include "swrenderer/viewport/r_viewport.h" @@ -72,6 +73,36 @@ EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); EXTERN_CVAR(Bool, r_drawvoxels); +namespace +{ + double sprite_distance_cull = 1e16; + double line_distance_cull = 1e16; +} + +CUSTOM_CVAR(Float, r_sprite_distance_cull, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (r_sprite_distance_cull > 0.0) + { + sprite_distance_cull = r_sprite_distance_cull * r_sprite_distance_cull; + } + else + { + sprite_distance_cull = 1e16; + } +} + +CUSTOM_CVAR(Float, r_line_distance_cull, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (r_line_distance_cull > 0.0) + { + line_distance_cull = r_line_distance_cull * r_line_distance_cull; + } + else + { + line_distance_cull = 1e16; + } +} + namespace swrenderer { RenderOpaquePass::RenderOpaquePass(RenderThread *thread) : renderline(thread) @@ -730,10 +761,19 @@ namespace swrenderer count = sub->numlines; line = sub->firstline; + DVector2 viewpointPos = Thread->Viewport->viewpoint.Pos.XY(); + basecolormap = GetColorTable(frontsector->Colormap, frontsector->SpecialColors[sector_t::walltop]); while (count--) { - if (!outersubsector || line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) + double dist1 = (line->v1->fPos() - viewpointPos).LengthSquared(); + double dist2 = (line->v2->fPos() - viewpointPos).LengthSquared(); + if (dist1 > line_distance_cull && dist2 > line_distance_cull) + { + FarClipLine farclip(Thread); + farclip.Render(line, InSubsector, floorplane, ceilingplane); + } + else if (!outersubsector || line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) { // kg3D - fake planes bounding calculation if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) @@ -934,6 +974,10 @@ namespace swrenderer if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && !!P_PointOnLineSidePrecise(thing->Pos(), renderportal->CurrentPortal->dst)) return false; + double distanceSquared = (thing->Pos() - Thread->Viewport->viewpoint.Pos).LengthSquared(); + if (distanceSquared > sprite_distance_cull) + return false; + return true; } diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp index 9fd4a44e29f..e67f5d761b1 100644 --- a/src/swrenderer/things/r_playersprite.cpp +++ b/src/swrenderer/things/r_playersprite.cpp @@ -240,7 +240,7 @@ namespace swrenderer if (pspr->Flags & PSPF_ADDBOB) { - sx += bobx; + sx += (pspr->Flags & PSPF_MIRROR) ? -bobx : bobx; sy += boby; } @@ -256,17 +256,18 @@ namespace swrenderer double pspriteyscale = pspritexscale * viewport->YaspectMul; double pspritexiscale = 1 / pspritexscale; - // calculate edges of the shape - tx = sx - BASEXCENTER; + int tleft = tex->GetScaledLeftOffset(); + int twidth = tex->GetScaledWidth(); - tx -= tex->GetScaledLeftOffset(); + // calculate edges of the shape + tx = (pspr->Flags & PSPF_MIRROR) ? ((BASEXCENTER - twidth) - (sx - tleft)) : ((sx - BASEXCENTER) - tleft); x1 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); // off the right side if (x1 > viewwidth) return; - tx += tex->GetScaledWidth(); + tx += twidth; x2 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); // off the left side diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 18ee5505c07..01d316c243e 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2657,7 +2657,8 @@ MUSIC_DM2INT = "dm2int"; DSPLYMNU_GLOPT = "OpenGL Renderer"; DSPLYMNU_SWOPT = "Software Renderer"; DSPLYMNU_GAMMA = "Gamma correction"; -DSPLYMNU_CONTRAST ="Contrast"; +DSPLYMNU_CONTRAST = "Contrast"; +DSPLYMNU_SATURATION = "Saturation"; DSPLYMNU_HWGAMMA = "Hardware Gamma"; // OpenGL Options @@ -2708,6 +2709,7 @@ GLPREFMNU_SPRBILLFACECAMERA = "Sprites face camera"; GLPREFMNU_PARTICLESTYLE = "Particle style"; GLPREFMNU_AMBLIGHT = "Ambient light level"; GLPREFMNU_RENDERQUALITY = "Rendering quality"; +GLPREFMNU_MENUBLUR = "Menu Blur"; GLPREFMNU_VRMODE = "Stereo 3D VR"; GLPREFMNU_VRQUADSTEREO = "Enable Quad Stereo"; GLPREFMNU_MULTISAMPLE = "Multisample"; diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index fe852542f45..87ecf5aa452 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -38,6 +38,7 @@ gameinfo weaponslot = 7, "LAZDevice" dimcolor = "ff d7 00" dimamount = 0.2 + bluramount = 0.0 definventorymaxamount = 25 defaultrespawntime = 12 defaultdropstyle = 1 diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index 82eb14d22f7..f38d699dc5e 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -39,6 +39,7 @@ gameinfo weaponslot = 7, "BFG9000" dimcolor = "ff d7 00" dimamount = 0.2 + bluramount = 0.0 definventorymaxamount = 25 defaultrespawntime = 12 defaultdropstyle = 1 diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index 73310ad4742..3481f82af98 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -38,6 +38,7 @@ gameinfo weaponslot = 7, "Mace" dimcolor = "00 00 ff" dimamount = 0.2 + bluramount = 0 definventorymaxamount = 16 defaultrespawntime = 12 defaultdropstyle = 1 diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index 618fd4be74e..64cf3b4f7c3 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -37,6 +37,7 @@ gameinfo weaponslot = 4, "FWeapQuietus", "CWeapWraithverge", "MWeapBloodscourge" dimcolor = "00 00 ff" dimamount = 0.2 + bluramount = 0.0 definventorymaxamount = 25 defaultrespawntime = 12 defaultdropstyle = 1 diff --git a/wadsrc/static/mapinfo/mindefaults.txt b/wadsrc/static/mapinfo/mindefaults.txt index 26ab0d9395e..4199a0a6f42 100644 --- a/wadsrc/static/mapinfo/mindefaults.txt +++ b/wadsrc/static/mapinfo/mindefaults.txt @@ -27,6 +27,7 @@ gameinfo intermissioncounter = true dimcolor = "6f 00 6b" dimamount = 0.8 + bluramount = 0.0 definventorymaxamount = 25 defaultrespawntime = 12 defaultdropstyle = 1 diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index 3a763d4cb1f..5d320dd26b9 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -38,6 +38,7 @@ gameinfo weaponslot = 8, "Sigil" dimcolor = "ff d7 00" dimamount = 0.2 + bluramount = 0 definventorymaxamount = 25 defaultrespawntime = 16 defaultdropstyle = 2 diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index ba7db57f3f4..148238c9ce9 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -734,16 +734,17 @@ OptionMenu "VideoOptions" protected Submenu "$DSPLYMNU_GLOPT", "OpenGLOptions" Submenu "$DSPLYMNU_SWOPT", "SWROptions" - Submenu "$GLMNU_DYNLIGHT", "GLLightOptions" + Submenu "$GLMNU_DYNLIGHT", "GLLightOptions" Submenu "$DSPLYMNU_SCOREBOARD", "ScoreboardOptions" StaticText " " - Slider "$DSPLYMNU_SCREENSIZE", "screenblocks", 3.0, 12.0, 1.0, 0 + Slider "$DSPLYMNU_SCREENSIZE", "screenblocks", 3.0, 12.0, 1.0, 0 - Slider "$DSPLYMNU_GAMMA", "Gamma", 0.75, 3.0, 0.05, 2 + Slider "$DSPLYMNU_GAMMA", "Gamma", 0.75, 3.0, 0.05, 2 Slider "$DSPLYMNU_BRIGHTNESS", "vid_brightness", -0.8,0.8, 0.05,2 - Slider "$DSPLYMNU_CONTRAST", "vid_contrast", 0.1, 3.0, 0.1 + Slider "$DSPLYMNU_CONTRAST", "vid_contrast", 0.1, 3.0, 0.1 + Slider "$DSPLYMNU_SATURATION", "vid_saturation", -3.0, 3.0, 0.25, 2 Option "$DSPLYMNU_HWGAMMA", "vid_hwgamma", "HWGammaModes" - + StaticText " " Option "$DSPLYMNU_VSYNC", "vid_vsync", "OnOff" Option "$DSPLYMNU_CAPFPS", "cl_capfps", "OffOn" Slider "$DSPLYMNU_BLOODFADE", "blood_fade_scalar", 0.0, 1.0, 0.05, 2 @@ -2160,6 +2161,8 @@ OptionMenu "OpenGLOptions" protected Option "$GLPREFMNU_PARTICLESTYLE", gl_particles_style, "Particles" Option "$GLPREFMNU_RENDERQUALITY", gl_render_precise, "Precision" StaticText " " + Slider "$GLPREFMNU_MENUBLUR", gl_menu_blur, 0, 5.0, 0.5, 2 + StaticText " " Option "$GLPREFMNU_VRMODE", vr_mode, "VRMode" Option "$GLPREFMNU_VRQUADSTEREO", vr_enable_quadbuffered, "OnOff" StaticText " " diff --git a/wadsrc/static/shaders/glsl/present.fp b/wadsrc/static/shaders/glsl/present.fp index 31c1217cb86..4690895c204 100644 --- a/wadsrc/static/shaders/glsl/present.fp +++ b/wadsrc/static/shaders/glsl/present.fp @@ -6,10 +6,17 @@ uniform sampler2D InputTexture; uniform float InvGamma; uniform float Contrast; uniform float Brightness; +uniform float Saturation; +uniform int GrayFormula; vec4 ApplyGamma(vec4 c) { - vec3 val = c.rgb * Contrast - (Contrast - 1.0) * 0.5; + vec3 valgray; + if (GrayFormula == 0) + valgray = vec3(c.r + c.g + c.b) * (1 - Saturation) / 3 + c.rgb * Saturation; + else + valgray = mix(vec3(dot(c.rgb, vec3(0.3,0.56,0.14))), c.rgb, Saturation); + vec3 val = valgray * Contrast - (Contrast - 1.0) * 0.5; val += Brightness * 0.5; val = pow(max(val, vec3(0.0)), vec3(InvGamma)); return vec4(val, c.a); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 726e46fccc1..f35e39993c7 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -775,3 +775,11 @@ class Lighting : SectorEffect native { } +struct Shader native +{ + native clearscope static void SetEnabled(PlayerInfo player, string shaderName, bool enable); + native clearscope static void SetUniform1f(PlayerInfo player, string shaderName, string uniformName, float value); + native clearscope static void SetUniform2f(PlayerInfo player, string shaderName, string uniformName, vector2 value); + native clearscope static void SetUniform3f(PlayerInfo player, string shaderName, string uniformName, vector3 value); + native clearscope static void SetUniform1i(PlayerInfo player, string shaderName, string uniformName, int value); +} diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index b698f966f2c..b0bf1b65de2 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -726,6 +726,7 @@ enum EPSpriteFlags PSPF_FLIP = 1 << 6, PSPF_FORCEALPHA = 1 << 7, PSPF_FORCESTYLE = 1 << 8, + PSPF_MIRROR = 1 << 9, }; // Default psprite layers