From 5728241c1c923d6d757c5455a25cbda771435522 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 26 Apr 2020 23:17:54 +0200 Subject: [PATCH] - updated backend from GZDoom. --- source/CMakeLists.txt | 24 +- source/build/src/polymost.cpp | 27 - .../rendering/gl}/gl_buffers.cpp | 20 +- .../rendering/gl}/gl_buffers.h | 0 .../rendering/gl}/gl_postprocess.cpp | 46 +- .../rendering/gl}/gl_postprocessstate.cpp | 6 +- .../rendering/gl}/gl_postprocessstate.h | 2 +- .../rendering/gl}/gl_renderbuffers.cpp | 87 +- .../rendering/gl}/gl_renderbuffers.h | 13 +- source/common/rendering/gl/gl_renderstate.cpp | 583 ++++++++++++ source/common/rendering/gl/gl_renderstate.h | 149 +++ source/common/rendering/gl/gl_shader.cpp | 866 ++++++++++++++++++ source/common/rendering/gl/gl_shader.h | 350 +++++++ .../rendering/gl}/gl_shaderprogram.cpp | 44 +- .../rendering/gl}/gl_shaderprogram.h | 14 +- .../hwrenderer/data/flatvertices.cpp | 0 .../rendering/hwrenderer/data/flatvertices.h | 3 +- .../rendering/hwrenderer/data/hw_aabbtree.cpp | 166 ++++ .../rendering/hwrenderer/data/hw_aabbtree.h | 82 ++ .../rendering/hwrenderer/data/hw_cvars.cpp | 139 +++ .../rendering/hwrenderer/data}/hw_cvars.h | 16 +- .../hwrenderer/data/hw_dynlightdata.h | 59 ++ .../hwrenderer/data/hw_lightbuffer.cpp | 139 +++ .../hwrenderer/data/hw_lightbuffer.h | 48 + .../hwrenderer/data/hw_modelvertexbuffer.cpp | 112 +++ .../hwrenderer/data/hw_modelvertexbuffer.h | 26 + .../hwrenderer/data/hw_renderstate.h | 1 - .../hwrenderer/data}/hw_shaderpatcher.cpp | 41 +- .../hwrenderer/data}/hw_shaderpatcher.h | 0 .../hwrenderer/data/hw_shadowmap.cpp | 155 ++++ .../rendering/hwrenderer/data/hw_shadowmap.h | 83 ++ .../hwrenderer/data/hw_viewpointbuffer.cpp | 15 +- .../hwrenderer/data/hw_viewpointbuffer.h | 0 .../hwrenderer/data/hw_viewpointuniforms.h | 29 + .../rendering/hwrenderer/data/hw_vrmodes.cpp | 180 ++++ .../rendering/hwrenderer/data/hw_vrmodes.h | 47 + .../postprocessing/hw_postprocess.cpp | 43 +- .../postprocessing/hw_postprocess.h | 60 +- source/common/rendering/i_modelvertexbuffer.h | 43 + source/common/textures/texturemanager.cpp | 4 +- source/core/gamecvars.cpp | 44 - source/core/i_specialpaths.h | 22 - .../rendering/gl/renderer/gl_renderer.cpp | 107 +-- .../core/rendering/gl/renderer/gl_renderer.h | 22 +- .../rendering/gl/system/gl_framebuffer.cpp | 25 +- .../core/rendering/gl/system/gl_framebuffer.h | 2 - .../hwrenderer/scene/hw_viewpointuniforms.h | 3 +- .../rendering/hwrenderer/utility/hw_cvars.cpp | 30 - source/core/rendering/i_video.h | 38 - source/core/rendering/r_videoscale.cpp | 310 ------- source/core/rendering/r_videoscale.h | 40 - source/core/rendering/v_framebuffer.cpp | 2 - source/core/rendering/v_video.cpp | 2 - source/core/rendering/v_video.h | 20 +- source/core/savegamehelp.cpp | 3 +- source/core/version.h | 3 + source/glbackend/gl_renderstate.h | 3 +- source/glbackend/glbackend.cpp | 65 ++ wadsrc/static/shaders/glsl/burn.fp | 14 + wadsrc/static/shaders/glsl/fogboundary.fp | 37 + .../static/shaders/glsl/func_defaultlight.fp | 5 + wadsrc/static/shaders/glsl/func_defaultmat.fp | 7 + .../static/shaders/glsl/func_defaultmat2.fp | 6 + wadsrc/static/shaders/glsl/func_normal.fp | 5 + wadsrc/static/shaders/glsl/func_notexture.fp | 6 + wadsrc/static/shaders/glsl/func_paletted.fp | 10 + wadsrc/static/shaders/glsl/func_pbr.fp | 8 + wadsrc/static/shaders/glsl/func_spec.fp | 8 + wadsrc/static/shaders/glsl/func_warp1.fp | 19 + wadsrc/static/shaders/glsl/func_warp2.fp | 20 + wadsrc/static/shaders/glsl/func_warp3.fp | 21 + wadsrc/static/shaders/glsl/func_wavex.fp | 17 + wadsrc/static/shaders/glsl/fuzz_jagged.fp | 24 + wadsrc/static/shaders/glsl/fuzz_noise.fp | 21 + wadsrc/static/shaders/glsl/fuzz_smooth.fp | 18 + .../static/shaders/glsl/fuzz_smoothnoise.fp | 19 + .../shaders/glsl/fuzz_smoothtranslucent.fp | 18 + wadsrc/static/shaders/glsl/fuzz_software.fp | 51 ++ wadsrc/static/shaders/glsl/fuzz_standard.fp | 22 + wadsrc/static/shaders/glsl/fuzz_swirly.fp | 18 + wadsrc/static/shaders/glsl/main.fp | 763 +++++++++++++++ wadsrc/static/shaders/glsl/main.vp | 144 +++ .../static/shaders/glsl/material_nolight.fp | 5 + wadsrc/static/shaders/glsl/material_normal.fp | 82 ++ wadsrc/static/shaders/glsl/material_pbr.fp | 195 ++++ .../static/shaders/glsl/material_specular.fp | 95 ++ wadsrc/static/shaders/glsl/stencil.fp | 15 + .../{engine => }/shaders/pp/bloomcombine.fp | 0 .../{engine => }/shaders/pp/bloomextract.fp | 0 wadsrc/static/{engine => }/shaders/pp/blur.fp | 0 .../{engine => }/shaders/pp/colormap.fp | 0 .../{engine => }/shaders/pp/depthblur.fp | 0 .../shaders/pp/exposureaverage.fp | 0 .../shaders/pp/exposurecombine.fp | 0 .../shaders/pp/exposureextract.fp | 0 wadsrc/static/{engine => }/shaders/pp/fxaa.fp | 0 .../{engine => }/shaders/pp/lensdistortion.fp | 0 .../{engine => }/shaders/pp/lineardepth.fp | 0 .../static/{engine => }/shaders/pp/present.fp | 0 .../shaders/pp/present_checker3d.fp | 0 .../shaders/pp/present_column3d.fp | 0 .../{engine => }/shaders/pp/present_row3d.fp | 0 .../{engine => }/shaders/pp/screenquad.vp | 0 wadsrc/static/shaders/pp/shadowmap.fp | 190 ++++ wadsrc/static/{engine => }/shaders/pp/ssao.fp | 0 .../{engine => }/shaders/pp/ssaocombine.fp | 0 .../static/{engine => }/shaders/pp/tonemap.fp | 0 107 files changed, 5548 insertions(+), 778 deletions(-) rename source/{core/rendering/gl/system => common/rendering/gl}/gl_buffers.cpp (93%) rename source/{core/rendering/gl/system => common/rendering/gl}/gl_buffers.h (100%) rename source/{core/rendering/gl/renderer => common/rendering/gl}/gl_postprocess.cpp (86%) rename source/{core/rendering/gl/renderer => common/rendering/gl}/gl_postprocessstate.cpp (97%) rename source/{core/rendering/gl/renderer => common/rendering/gl}/gl_postprocessstate.h (95%) rename source/{core/rendering/gl/renderer => common/rendering/gl}/gl_renderbuffers.cpp (93%) rename source/{core/rendering/gl/renderer => common/rendering/gl}/gl_renderbuffers.h (96%) create mode 100644 source/common/rendering/gl/gl_renderstate.cpp create mode 100644 source/common/rendering/gl/gl_renderstate.h create mode 100644 source/common/rendering/gl/gl_shader.cpp create mode 100644 source/common/rendering/gl/gl_shader.h rename source/{core/rendering/gl/shaders => common/rendering/gl}/gl_shaderprogram.cpp (89%) rename source/{core/rendering/gl/shaders => common/rendering/gl}/gl_shaderprogram.h (89%) rename source/{core => common}/rendering/hwrenderer/data/flatvertices.cpp (100%) rename source/{core => common}/rendering/hwrenderer/data/flatvertices.h (92%) create mode 100644 source/common/rendering/hwrenderer/data/hw_aabbtree.cpp create mode 100644 source/common/rendering/hwrenderer/data/hw_aabbtree.h create mode 100644 source/common/rendering/hwrenderer/data/hw_cvars.cpp rename source/{core/rendering/hwrenderer/utility => common/rendering/hwrenderer/data}/hw_cvars.h (75%) create mode 100644 source/common/rendering/hwrenderer/data/hw_dynlightdata.h create mode 100644 source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp create mode 100644 source/common/rendering/hwrenderer/data/hw_lightbuffer.h create mode 100644 source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp create mode 100644 source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.h rename source/{core/rendering/hwrenderer/utility => common/rendering/hwrenderer/data}/hw_shaderpatcher.cpp (75%) rename source/{core/rendering/hwrenderer/utility => common/rendering/hwrenderer/data}/hw_shaderpatcher.h (100%) create mode 100644 source/common/rendering/hwrenderer/data/hw_shadowmap.cpp create mode 100644 source/common/rendering/hwrenderer/data/hw_shadowmap.h rename source/{core => common}/rendering/hwrenderer/data/hw_viewpointbuffer.cpp (89%) rename source/{core => common}/rendering/hwrenderer/data/hw_viewpointbuffer.h (100%) create mode 100644 source/common/rendering/hwrenderer/data/hw_viewpointuniforms.h create mode 100644 source/common/rendering/hwrenderer/data/hw_vrmodes.cpp create mode 100644 source/common/rendering/hwrenderer/data/hw_vrmodes.h create mode 100644 source/common/rendering/i_modelvertexbuffer.h delete mode 100644 source/core/i_specialpaths.h delete mode 100644 source/core/rendering/hwrenderer/utility/hw_cvars.cpp delete mode 100644 source/core/rendering/i_video.h delete mode 100644 source/core/rendering/r_videoscale.cpp delete mode 100644 source/core/rendering/r_videoscale.h create mode 100644 wadsrc/static/shaders/glsl/burn.fp create mode 100644 wadsrc/static/shaders/glsl/fogboundary.fp create mode 100644 wadsrc/static/shaders/glsl/func_defaultlight.fp create mode 100644 wadsrc/static/shaders/glsl/func_defaultmat.fp create mode 100644 wadsrc/static/shaders/glsl/func_defaultmat2.fp create mode 100644 wadsrc/static/shaders/glsl/func_normal.fp create mode 100644 wadsrc/static/shaders/glsl/func_notexture.fp create mode 100644 wadsrc/static/shaders/glsl/func_paletted.fp create mode 100644 wadsrc/static/shaders/glsl/func_pbr.fp create mode 100644 wadsrc/static/shaders/glsl/func_spec.fp create mode 100644 wadsrc/static/shaders/glsl/func_warp1.fp create mode 100644 wadsrc/static/shaders/glsl/func_warp2.fp create mode 100644 wadsrc/static/shaders/glsl/func_warp3.fp create mode 100644 wadsrc/static/shaders/glsl/func_wavex.fp create mode 100644 wadsrc/static/shaders/glsl/fuzz_jagged.fp create mode 100644 wadsrc/static/shaders/glsl/fuzz_noise.fp create mode 100644 wadsrc/static/shaders/glsl/fuzz_smooth.fp create mode 100644 wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp create mode 100644 wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp create mode 100644 wadsrc/static/shaders/glsl/fuzz_software.fp create mode 100644 wadsrc/static/shaders/glsl/fuzz_standard.fp create mode 100644 wadsrc/static/shaders/glsl/fuzz_swirly.fp create mode 100644 wadsrc/static/shaders/glsl/main.fp create mode 100644 wadsrc/static/shaders/glsl/main.vp create mode 100644 wadsrc/static/shaders/glsl/material_nolight.fp create mode 100644 wadsrc/static/shaders/glsl/material_normal.fp create mode 100644 wadsrc/static/shaders/glsl/material_pbr.fp create mode 100644 wadsrc/static/shaders/glsl/material_specular.fp create mode 100644 wadsrc/static/shaders/glsl/stencil.fp rename wadsrc/static/{engine => }/shaders/pp/bloomcombine.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/bloomextract.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/blur.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/colormap.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/depthblur.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/exposureaverage.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/exposurecombine.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/exposureextract.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/fxaa.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/lensdistortion.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/lineardepth.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/present.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/present_checker3d.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/present_column3d.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/present_row3d.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/screenquad.vp (100%) create mode 100644 wadsrc/static/shaders/pp/shadowmap.fp rename wadsrc/static/{engine => }/shaders/pp/ssao.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/ssaocombine.fp (100%) rename wadsrc/static/{engine => }/shaders/pp/tonemap.fp (100%) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 97b82f158..117a7041f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -865,13 +865,28 @@ set (PCH_SOURCES common/objects/dobjgc.cpp common/objects/dobjtype.cpp common/rendering/r_videoscale.cpp + common/rendering/hwrenderer/data/flatvertices.cpp + common/rendering/hwrenderer/data/hw_viewpointbuffer.cpp + common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp + common/rendering/hwrenderer/data/hw_cvars.cpp + common/rendering/hwrenderer/data/hw_vrmodes.cpp + common/rendering/hwrenderer/data/hw_lightbuffer.cpp + common/rendering/hwrenderer/data/hw_aabbtree.cpp + common/rendering/hwrenderer/data/hw_shadowmap.cpp + common/rendering/hwrenderer/data/hw_shaderpatcher.cpp common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp common/rendering/hwrenderer/postprocessing/hw_postprocess_cvars.cpp - common/rendering/gl_load/gl_interface.cpp + common/rendering/gl/gl_renderstate.cpp + common/rendering/gl/gl_renderbuffers.cpp + common/rendering/gl/gl_postprocess.cpp + common/rendering/gl/gl_postprocessstate.cpp common/rendering/gl/gl_debug.cpp + common/rendering/gl/gl_buffers.cpp common/rendering/gl/gl_hwtexture.cpp common/rendering/gl/gl_samplers.cpp + common/rendering/gl/gl_shader.cpp + common/rendering/gl/gl_shaderprogram.cpp common/scripting/core/dictionary.cpp common/scripting/core/dynarrays.cpp common/scripting/core/symbols.cpp @@ -914,14 +929,7 @@ set (PCH_SOURCES core/rendering/v_framebuffer.cpp core/rendering/v_video.cpp core/rendering/gl/renderer/gl_renderer.cpp - core/rendering/gl/renderer/gl_postprocess.cpp - core/rendering/gl/renderer/gl_postprocessstate.cpp - core/rendering/gl/renderer/gl_renderbuffers.cpp - core/rendering/gl/shaders/gl_shaderprogram.cpp - core/rendering/gl/system/gl_buffers.cpp core/rendering/gl/system/gl_framebuffer.cpp - core/rendering/hwrenderer/data/flatvertices.cpp - core/rendering/hwrenderer/utility/hw_shaderpatcher.cpp ) if( ${HAVE_VM_JIT} ) diff --git a/source/build/src/polymost.cpp b/source/build/src/polymost.cpp index d96eb5c97..935816f10 100644 --- a/source/build/src/polymost.cpp +++ b/source/build/src/polymost.cpp @@ -38,33 +38,6 @@ CUSTOM_CVARD(Bool, hw_useindexedcolortextures, false, CVAR_ARCHIVE | CVAR_GLOBAL } -CUSTOM_CVARD(Int, gl_texture_filter, TEXFILTER_ON, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL, "changes the texture filtering settings") -{ - static const char* const glfiltermodes[] = - { - "NEAREST", - "LINEAR", - "NEAREST_MIPMAP_NEAREST", - "LINEAR_MIPMAP_NEAREST", - "NEAREST_MIPMAP_LINEAR", - "LINEAR_MIPMAP_LINEAR", - "LINEAR_MIPMAP_LINEAR with NEAREST mag" - }; - - if (self < 0 || self > 6) self = 0; - else - { - gltexapplyprops(); - Printf("Texture filtering mode changed to %s\n", glfiltermodes[gl_texture_filter]); - } -} - -CUSTOM_CVARD(Float, gl_texture_filter_anisotropic, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL, "changes the OpenGL texture anisotropy setting") -{ - gltexapplyprops(); -} - - //{ "r_yshearing", "enable/disable y-shearing", (void*)&r_yshearing, CVAR_BOOL, 0, 1 }, disabled because not fully functional // For testing - will be removed later. diff --git a/source/core/rendering/gl/system/gl_buffers.cpp b/source/common/rendering/gl/gl_buffers.cpp similarity index 93% rename from source/core/rendering/gl/system/gl_buffers.cpp rename to source/common/rendering/gl/gl_buffers.cpp index 25a9b0450..9b49d431a 100644 --- a/source/core/rendering/gl/system/gl_buffers.cpp +++ b/source/common/rendering/gl/gl_buffers.cpp @@ -30,14 +30,14 @@ ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** -*/ - +**/ #include #include "gl_load.h" -//#include "glbackend.h" #include "gl_buffers.h" +#include "gl_renderstate.h" #include "v_video.h" +#include "flatvertices.h" namespace OpenGLRenderer { @@ -48,6 +48,11 @@ namespace OpenGLRenderer // //========================================================================== +static inline void InvalidateBufferState() +{ + gl_RenderState.ResetVertexBuffer(); // force rebinding of buffers on next Apply call. +} + GLBuffer::GLBuffer(int usetype) : mUseType(usetype) { @@ -94,6 +99,7 @@ void GLBuffer::SetData(size_t size, const void *data, bool staticdata) if (!staticdata) nomap = false; } buffersize = size; + InvalidateBufferState(); } void GLBuffer::SetSubData(size_t offset, size_t size, const void *data) @@ -108,7 +114,8 @@ void GLBuffer::Map() if (!mPersistent && !nomap) { Bind(); - map = glMapBufferRange(mUseType, 0, buffersize, GL_MAP_WRITE_BIT|GL_MAP_UNSYNCHRONIZED_BIT); + map = (FFlatVertex*)glMapBufferRange(mUseType, 0, buffersize, GL_MAP_WRITE_BIT|GL_MAP_UNSYNCHRONIZED_BIT); + InvalidateBufferState(); } } @@ -119,6 +126,7 @@ void GLBuffer::Unmap() { Bind(); glUnmapBuffer(mUseType); + InvalidateBufferState(); map = nullptr; } } @@ -134,6 +142,7 @@ void GLBuffer::Unlock() { Bind(); glUnmapBuffer(mUseType); + InvalidateBufferState(); } void GLBuffer::Resize(size_t newsize) @@ -157,6 +166,7 @@ void GLBuffer::Resize(size_t newsize) glBindBuffer(GL_COPY_READ_BUFFER, 0); glDeleteBuffers(1, &oldbuffer); buffersize = newsize; + InvalidateBufferState(); } } @@ -225,4 +235,4 @@ GLVertexBuffer::GLVertexBuffer() : GLBuffer(GL_ARRAY_BUFFER) {} GLIndexBuffer::GLIndexBuffer() : GLBuffer(GL_ELEMENT_ARRAY_BUFFER) {} GLDataBuffer::GLDataBuffer(int bindingpoint, bool is_ssbo) : GLBuffer(is_ssbo ? GL_SHADER_STORAGE_BUFFER : GL_UNIFORM_BUFFER), mBindingPoint(bindingpoint) {} -} +} \ No newline at end of file diff --git a/source/core/rendering/gl/system/gl_buffers.h b/source/common/rendering/gl/gl_buffers.h similarity index 100% rename from source/core/rendering/gl/system/gl_buffers.h rename to source/common/rendering/gl/gl_buffers.h diff --git a/source/core/rendering/gl/renderer/gl_postprocess.cpp b/source/common/rendering/gl/gl_postprocess.cpp similarity index 86% rename from source/core/rendering/gl/renderer/gl_postprocess.cpp rename to source/common/rendering/gl/gl_postprocess.cpp index fa3e23c35..283d6a4a6 100644 --- a/source/core/rendering/gl/renderer/gl_postprocess.cpp +++ b/source/common/rendering/gl/gl_postprocess.cpp @@ -19,23 +19,22 @@ ** 3. This notice may not be removed or altered from any source distribution. */ -#include "gl_load/gl_system.h" +#include "gl_system.h" #include "m_png.h" -#include "gl/system/gl_buffers.h" +#include "gl_buffers.h" #include "gl/system/gl_framebuffer.h" -#include "gamecvars.h" #include "gl_debug.h" -//#include "gl/renderer/gl_renderstate.h" -#include "gl/renderer/gl_renderbuffers.h" +#include "gl_renderbuffers.h" #include "gl/renderer/gl_renderer.h" -#include "gl/renderer/gl_postprocessstate.h" -#include "gl/shaders/gl_shaderprogram.h" +#include "gl_postprocessstate.h" +#include "gl_shaderprogram.h" #include "hwrenderer/postprocessing/hw_postprocess.h" #include "hwrenderer/postprocessing/hw_postprocess_cvars.h" -#include "hwrenderer/data/flatvertices.h" +#include "flatvertices.h" #include "r_videoscale.h" #include "v_video.h" #include "templates.h" +#include "hw_vrmodes.h" extern bool vid_hdr_active; @@ -87,7 +86,13 @@ void FGLRenderer::BlurScene(float gameinfobluramount) GLPPRenderState renderstate(mBuffers); + auto vrmode = VRMode::GetVRMode(true); + int eyeCount = vrmode->mEyeCount; + for (int i = 0; i < eyeCount; ++i) + { hw_postprocess.bloom.RenderBlur(&renderstate, sceneWidth, sceneHeight, gameinfobluramount); + if (eyeCount - i > 1) mBuffers->NextEye(eyeCount); + } } void FGLRenderer::ClearTonemapPalette() @@ -103,7 +108,30 @@ void FGLRenderer::ClearTonemapPalette() void FGLRenderer::Flush() { + auto vrmode = VRMode::GetVRMode(true); + if (vrmode->mEyeCount == 1) + { CopyToBackbuffer(nullptr, true); + } + else + { + // Render 2D to eye textures + int eyeCount = vrmode->mEyeCount; + for (int eye_ix = 0; eye_ix < eyeCount; ++eye_ix) + { + screen->Draw2D(); + if (eyeCount - eye_ix > 1) + mBuffers->NextEye(eyeCount); + } + screen->Clear2D(); + + FGLPostProcessState savedState; + FGLDebug::PushGroup("PresentEyes"); + // Note: This here is the ONLY place in the entire engine where the OpenGL dependent parts of the Stereo3D code need to be dealt with. + // There's absolutely no need to create a overly complex class hierarchy for just this. + PresentStereo(); + FGLDebug::PopGroup(); + } } //----------------------------------------------------------------------------- @@ -115,8 +143,10 @@ void FGLRenderer::Flush() void FGLRenderer::CopyToBackbuffer(const IntRect *bounds, bool applyGamma) { screen->Draw2D(); // draw all pending 2D stuff before copying the buffer + screen->Clear2D(); GLPPRenderState renderstate(mBuffers); + hw_postprocess.customShaders.Run(&renderstate, "screen"); FGLDebug::PushGroup("CopyToBackbuffer"); FGLPostProcessState savedState; diff --git a/source/core/rendering/gl/renderer/gl_postprocessstate.cpp b/source/common/rendering/gl/gl_postprocessstate.cpp similarity index 97% rename from source/core/rendering/gl/renderer/gl_postprocessstate.cpp rename to source/common/rendering/gl/gl_postprocessstate.cpp index e147e6adb..2e6a04f05 100644 --- a/source/core/rendering/gl/renderer/gl_postprocessstate.cpp +++ b/source/common/rendering/gl/gl_postprocessstate.cpp @@ -20,9 +20,9 @@ */ #include "templates.h" -#include "gl_load/gl_system.h" -#include "gl_load/gl_interface.h" -#include "gl/renderer/gl_postprocessstate.h" +#include "gl_system.h" +#include "gl_interface.h" +#include "gl_postprocessstate.h" namespace OpenGLRenderer { diff --git a/source/core/rendering/gl/renderer/gl_postprocessstate.h b/source/common/rendering/gl/gl_postprocessstate.h similarity index 95% rename from source/core/rendering/gl/renderer/gl_postprocessstate.h rename to source/common/rendering/gl/gl_postprocessstate.h index 70f911691..b21e73840 100644 --- a/source/core/rendering/gl/renderer/gl_postprocessstate.h +++ b/source/common/rendering/gl/gl_postprocessstate.h @@ -2,7 +2,7 @@ #define __GL_POSTPROCESSSTATE_H #include -#include "gl_load/gl_interface.h" +#include "gl_interface.h" #include "matrix.h" #include "c_cvars.h" diff --git a/source/core/rendering/gl/renderer/gl_renderbuffers.cpp b/source/common/rendering/gl/gl_renderbuffers.cpp similarity index 93% rename from source/core/rendering/gl/renderer/gl_renderbuffers.cpp rename to source/common/rendering/gl/gl_renderbuffers.cpp index 67ae6a269..2cbcdd8fb 100644 --- a/source/core/rendering/gl/renderer/gl_renderbuffers.cpp +++ b/source/common/rendering/gl/gl_renderbuffers.cpp @@ -19,22 +19,22 @@ ** 3. This notice may not be removed or altered from any source distribution. */ -#include "gl_load/gl_system.h" +#include "gl_system.h" #include "v_video.h" +#include "gl_interface.h" #include "printf.h" -#include "gl_load/gl_interface.h" -#include "gamecvars.h" +#include "hw_cvars.h" #include "gl_debug.h" #include "gl/renderer/gl_renderer.h" -#include "gl/renderer/gl_renderbuffers.h" -#include "gl/renderer/gl_postprocessstate.h" -#include "gl/shaders/gl_shaderprogram.h" -#include "gl/system/gl_buffers.h" +#include "gl_renderbuffers.h" +#include "gl_postprocessstate.h" +#include "gl_shaderprogram.h" +#include "gl_buffers.h" #include "templates.h" #include EXTERN_CVAR(Int, gl_debug_level) - +CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); namespace OpenGLRenderer { @@ -45,9 +45,8 @@ namespace OpenGLRenderer // //========================================================================== -FGLRenderBuffers::FGLRenderBuffers(int ms) +FGLRenderBuffers::FGLRenderBuffers() { - gl_multisample = ms; glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples); } @@ -62,6 +61,7 @@ FGLRenderBuffers::~FGLRenderBuffers() ClearScene(); ClearPipeline(); ClearEyeBuffers(); + ClearShadowMap(); DeleteTexture(mDitherTexture); } @@ -69,10 +69,20 @@ void FGLRenderBuffers::ClearScene() { DeleteFrameBuffer(mSceneFB); DeleteFrameBuffer(mSceneDataFB); + if (mSceneUsesTextures) + { + DeleteTexture(mSceneMultisampleTex); + DeleteTexture(mSceneFogTex); + DeleteTexture(mSceneNormalTex); + DeleteTexture(mSceneDepthStencilTex); + } + else + { DeleteRenderBuffer(mSceneMultisampleBuf); DeleteRenderBuffer(mSceneFogBuf); DeleteRenderBuffer(mSceneNormalBuf); DeleteRenderBuffer(mSceneDepthStencilBuf); + } } void FGLRenderBuffers::ClearPipeline() @@ -141,12 +151,13 @@ void FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei if (width != mWidth || height != mHeight) CreatePipeline(width, height); - if (width != mWidth || height != mHeight || mSamples != samples) + if (width != mWidth || height != mHeight || mSamples != samples || mSceneUsesTextures != needsSceneTextures) CreateScene(width, height, samples, needsSceneTextures); mWidth = width; mHeight = height; mSamples = samples; + mSceneUsesTextures = needsSceneTextures; mSceneWidth = sceneWidth; mSceneHeight = sceneHeight; @@ -584,6 +595,60 @@ void FGLRenderBuffers::BindDitherTexture(int texunit) mDitherTexture.Bind(1, GL_NEAREST, GL_REPEAT); } +//========================================================================== +// +// Shadow map texture and frame buffers +// +//========================================================================== + +void FGLRenderBuffers::BindShadowMapFB() +{ + CreateShadowMap(); + glBindFramebuffer(GL_FRAMEBUFFER, mShadowMapFB.handle); +} + +void FGLRenderBuffers::BindShadowMapTexture(int texunit) +{ + CreateShadowMap(); + glActiveTexture(GL_TEXTURE0 + texunit); + glBindTexture(GL_TEXTURE_2D, mShadowMapTexture.handle); +} + +void FGLRenderBuffers::ClearShadowMap() +{ + DeleteFrameBuffer(mShadowMapFB); + DeleteTexture(mShadowMapTexture); + mCurrentShadowMapSize = 0; +} + +void FGLRenderBuffers::CreateShadowMap() +{ + if (mShadowMapTexture.handle != 0 && gl_shadowmap_quality == mCurrentShadowMapSize) + return; + + ClearShadowMap(); + + GLint activeTex, textureBinding, frameBufferBinding; + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex); + glActiveTexture(GL_TEXTURE0); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding); + + mShadowMapTexture = Create2DTexture("ShadowMap", GL_R32F, gl_shadowmap_quality, 1024); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + mShadowMapFB = CreateFrameBuffer("ShadowMapFB", mShadowMapTexture); + + glBindTexture(GL_TEXTURE_2D, textureBinding); + glActiveTexture(activeTex); + glBindFramebuffer(GL_FRAMEBUFFER, frameBufferBinding); + + mCurrentShadowMapSize = gl_shadowmap_quality; +} + //========================================================================== // // Makes the scene frame buffer active (multisample, depth, stecil, etc.) diff --git a/source/core/rendering/gl/renderer/gl_renderbuffers.h b/source/common/rendering/gl/gl_renderbuffers.h similarity index 96% rename from source/core/rendering/gl/renderer/gl_renderbuffers.h rename to source/common/rendering/gl/gl_renderbuffers.h index 88b7360f0..da967f021 100644 --- a/source/core/rendering/gl/renderer/gl_renderbuffers.h +++ b/source/common/rendering/gl/gl_renderbuffers.h @@ -101,9 +101,8 @@ private: class FGLRenderBuffers { - int gl_multisample = 0; // intentionally overload the global CVAR public: - FGLRenderBuffers(int ms); + FGLRenderBuffers(); ~FGLRenderBuffers(); void Setup(int width, int height, int sceneWidth, int sceneHeight); @@ -132,6 +131,9 @@ public: void BindDitherTexture(int texunit); + void BindShadowMapFB(); + void BindShadowMapTexture(int index); + int GetWidth() const { return mWidth; } int GetHeight() const { return mHeight; } @@ -142,9 +144,11 @@ private: void ClearScene(); void ClearPipeline(); void ClearEyeBuffers(); + void ClearShadowMap(); void CreateScene(int width, int height, int samples, bool needsSceneTextures); void CreatePipeline(int width, int height); void CreateEyeBuffers(int eye); + void CreateShadowMap(); PPGLTexture Create2DTexture(const char *name, GLuint format, int width, int height, const void *data = nullptr); PPGLTexture Create2DMultisampleTexture(const char *name, GLuint format, int width, int height, int samples, bool fixedSampleLocations); @@ -181,6 +185,7 @@ private: PPGLRenderBuffer mSceneNormalBuf; PPGLFrameBuffer mSceneFB; PPGLFrameBuffer mSceneDataFB; + bool mSceneUsesTextures = false; // Effect/HUD buffers PPGLTexture mPipelineTexture[NumPipelineTextures]; @@ -194,7 +199,7 @@ private: // Shadow map texture PPGLTexture mShadowMapTexture; PPGLFrameBuffer mShadowMapFB; - //int mCurrentShadowMapSize = 0; + int mCurrentShadowMapSize = 0; PPGLTexture mDitherTexture; @@ -203,4 +208,4 @@ private: friend class GLPPRenderState; }; -} +} \ No newline at end of file diff --git a/source/common/rendering/gl/gl_renderstate.cpp b/source/common/rendering/gl/gl_renderstate.cpp new file mode 100644 index 000000000..7751dee52 --- /dev/null +++ b/source/common/rendering/gl/gl_renderstate.cpp @@ -0,0 +1,583 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2009-2016 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/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_renderstate.cpp +** Render state maintenance +** +*/ + +#include "templates.h" +#include "gl_system.h" +#include "gl_interface.h" +#include "hw_cvars.h" +#include "flatvertices.h" +#include "gl_shader.h" +#include "gl/renderer/gl_renderer.h" +#include "hw_lightbuffer.h" +#include "gl_renderbuffers.h" +#include "gl_hwtexture.h" +#include "gl_buffers.h" +//#include "hwrenderer/utility/hw_clock.h" +#include "hwrenderer/data/hw_viewpointbuffer.h" + +namespace OpenGLRenderer +{ + +FGLRenderState gl_RenderState; + +static VSMatrix identityMatrix(1); + +static void matrixToGL(const VSMatrix &mat, int loc) +{ + glUniformMatrix4fv(loc, 1, false, (float*)&mat); +} + +//========================================================================== +// +// This only gets called once upon setup. +// With OpenGL the state is persistent and cannot be cleared, once set up. +// +//========================================================================== + +void FGLRenderState::Reset() +{ + FRenderState::Reset(); + mVertexBuffer = mCurrentVertexBuffer = nullptr; + mGlossiness = 0.0f; + mSpecularLevel = 0.0f; + mShaderTimer = 0.0f; + + stRenderStyle = DefaultRenderStyle(); + stSrcBlend = stDstBlend = -1; + stBlendEquation = -1; + stAlphaTest = 0; + mLastDepthClamp = true; + + mEffectState = 0; + activeShader = nullptr; + + mCurrentVertexBuffer = nullptr; + mCurrentVertexOffsets[0] = mVertexOffsets[0] = 0; + mCurrentIndexBuffer = nullptr; + +} + +//========================================================================== +// +// Apply shader settings +// +//========================================================================== + +bool FGLRenderState::ApplyShader() +{ + static const float nulvec[] = { 0.f, 0.f, 0.f, 0.f }; + if (mSpecialEffect > EFF_NONE) + { + activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect, mPassType); + } + else + { + activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : SHADER_NoTexture, mAlphaThreshold >= 0.f, mPassType); + activeShader->Bind(); + } + + int fogset = 0; + + if (mFogEnabled) + { + if (mFogEnabled == 2) + { + fogset = -3; // 2D rendering with 'foggy' overlay. + } + else if ((GetFogColor() & 0xffffff) == 0) + { + fogset = gl_fogmode; + } + else + { + fogset = -gl_fogmode; + } + } + + glVertexAttrib4fv(VATTR_COLOR, &mStreamData.uVertexColor.X); + glVertexAttrib4fv(VATTR_NORMAL, &mStreamData.uVertexNormal.X); + + activeShader->muDesaturation.Set(mStreamData.uDesaturationFactor); + activeShader->muFogEnabled.Set(fogset); + + int f = mTextureModeFlags; + if (!mBrightmapEnabled) f &= ~(TEXF_Brightmap | TEXF_Glowmap); + activeShader->muTextureMode.Set((mTextureMode == TM_NORMAL && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode) | f); + activeShader->muLightParms.Set(mLightParms); + activeShader->muFogColor.Set(mStreamData.uFogColor); + activeShader->muObjectColor.Set(mStreamData.uObjectColor); + activeShader->muDynLightColor.Set(&mStreamData.uDynLightColor.X); + activeShader->muInterpolationFactor.Set(mStreamData.uInterpolationFactor); + activeShader->muTimer.Set((double)(screen->FrameTime - firstFrame) * (double)mShaderTimer / 1000.); + activeShader->muAlphaThreshold.Set(mAlphaThreshold); + activeShader->muLightIndex.Set(-1); + activeShader->muClipSplit.Set(mClipSplit); + activeShader->muSpecularMaterial.Set(mGlossiness, mSpecularLevel); + activeShader->muAddColor.Set(mStreamData.uAddColor); + activeShader->muTextureAddColor.Set(mStreamData.uTextureAddColor); + activeShader->muTextureModulateColor.Set(mStreamData.uTextureModulateColor); + activeShader->muTextureBlendColor.Set(mStreamData.uTextureBlendColor); + activeShader->muDetailParms.Set(&mStreamData.uDetailParms.X); + + if (mGlowEnabled || activeShader->currentglowstate) + { + activeShader->muGlowTopColor.Set(&mStreamData.uGlowTopColor.X); + activeShader->muGlowBottomColor.Set(&mStreamData.uGlowBottomColor.X); + activeShader->muGlowTopPlane.Set(&mStreamData.uGlowTopPlane.X); + activeShader->muGlowBottomPlane.Set(&mStreamData.uGlowBottomPlane.X); + activeShader->currentglowstate = mGlowEnabled; + } + + if (mGradientEnabled || activeShader->currentgradientstate) + { + activeShader->muObjectColor2.Set(mStreamData.uObjectColor2); + activeShader->muGradientTopPlane.Set(&mStreamData.uGradientTopPlane.X); + activeShader->muGradientBottomPlane.Set(&mStreamData.uGradientBottomPlane.X); + activeShader->currentgradientstate = mGradientEnabled; + } + + if (mSplitEnabled || activeShader->currentsplitstate) + { + activeShader->muSplitTopPlane.Set(&mStreamData.uSplitTopPlane.X); + activeShader->muSplitBottomPlane.Set(&mStreamData.uSplitBottomPlane.X); + activeShader->currentsplitstate = mSplitEnabled; + } + + + if (mTextureMatrixEnabled) + { + matrixToGL(mTextureMatrix, activeShader->texturematrix_index); + activeShader->currentTextureMatrixState = true; + } + else if (activeShader->currentTextureMatrixState) + { + activeShader->currentTextureMatrixState = false; + matrixToGL(identityMatrix, activeShader->texturematrix_index); + } + + if (mModelMatrixEnabled) + { + matrixToGL(mModelMatrix, activeShader->modelmatrix_index); + VSMatrix norm; + norm.computeNormalMatrix(mModelMatrix); + matrixToGL(norm, activeShader->normalmodelmatrix_index); + activeShader->currentModelMatrixState = true; + } + else if (activeShader->currentModelMatrixState) + { + activeShader->currentModelMatrixState = false; + matrixToGL(identityMatrix, activeShader->modelmatrix_index); + matrixToGL(identityMatrix, activeShader->normalmodelmatrix_index); + } + + int index = mLightIndex; + // Mess alert for crappy AncientGL! + if (!screen->mLights->GetBufferType() && index >= 0) + { + size_t start, size; + index = screen->mLights->GetBinding(index, &start, &size); + + if (start != mLastMappedLightIndex) + { + mLastMappedLightIndex = start; + static_cast(screen->mLights->GetBuffer())->BindRange(nullptr, start, size); + } + } + + activeShader->muLightIndex.Set(index); + return true; +} + + +//========================================================================== +// +// Apply State +// +//========================================================================== + +void FGLRenderState::ApplyState() +{ + if (mRenderStyle != stRenderStyle) + { + ApplyBlendMode(); + stRenderStyle = mRenderStyle; + } + + if (mSplitEnabled != stSplitEnabled) + { + if (mSplitEnabled) + { + glEnable(GL_CLIP_DISTANCE3); + glEnable(GL_CLIP_DISTANCE4); + } + else + { + glDisable(GL_CLIP_DISTANCE3); + glDisable(GL_CLIP_DISTANCE4); + } + stSplitEnabled = mSplitEnabled; + } + + if (mMaterial.mChanged) + { + ApplyMaterial(mMaterial.mMaterial, mMaterial.mClampMode, mMaterial.mTranslation, mMaterial.mOverrideShader); + mMaterial.mChanged = false; + } + + if (mBias.mChanged) + { + if (mBias.mFactor == 0 && mBias.mUnits == 0) + { + glDisable(GL_POLYGON_OFFSET_FILL); + } + else + { + glEnable(GL_POLYGON_OFFSET_FILL); + } + glPolygonOffset(mBias.mFactor, mBias.mUnits); + mBias.mChanged = false; + } +} + +void FGLRenderState::ApplyBuffers() +{ + if (mVertexBuffer != mCurrentVertexBuffer || mVertexOffsets[0] != mCurrentVertexOffsets[0] || mVertexOffsets[1] != mCurrentVertexOffsets[1]) + { + assert(mVertexBuffer != nullptr); + static_cast(mVertexBuffer)->Bind(mVertexOffsets); + mCurrentVertexBuffer = mVertexBuffer; + mCurrentVertexOffsets[0] = mVertexOffsets[0]; + mCurrentVertexOffsets[1] = mVertexOffsets[1]; + } + if (mIndexBuffer != mCurrentIndexBuffer) + { + if (mIndexBuffer) static_cast(mIndexBuffer)->Bind(); + mCurrentIndexBuffer = mIndexBuffer; + } +} + +void FGLRenderState::Apply() +{ + ApplyState(); + ApplyBuffers(); + ApplyShader(); +} + +//=========================================================================== +// +// Binds a texture to the renderer +// +//=========================================================================== + +void FGLRenderState::ApplyMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader) +{ + if (mat->Source()->isHardwareCanvas()) + { + mTempTM = TM_OPAQUE; + } + else + { + mTempTM = TM_NORMAL; + } + auto tex = mat->Source(); + mEffectState = overrideshader >= 0 ? overrideshader : mat->GetShaderIndex(); + mShaderTimer = tex->GetShaderSpeed(); + SetSpecular(tex->GetGlossiness(), tex->GetSpecularLevel()); + if (tex->isHardwareCanvas()) static_cast(tex->GetTexture())->NeedUpdate(); + + clampmode = tex->GetClampMode(clampmode); + + // avoid rebinding the same texture multiple times. + if (mat == lastMaterial && lastClamp == clampmode && translation == lastTranslation) return; + lastMaterial = mat; + lastClamp = clampmode; + lastTranslation = translation; + + int usebright = false; + int maxbound = 0; + + int numLayers = mat->NumLayers(); + MaterialLayerInfo* layer; + auto base = static_cast(mat->GetLayer(0, translation, &layer)); + + if (base->BindOrCreate(tex->GetTexture(), 0, clampmode, translation, layer->scaleFlags)) + { + for (int i = 1; i(mat->GetLayer(i, 0, &layer)); + // fixme: Upscale flags must be disabled for certain layers. + systex->BindOrCreate(layer->layerTexture, i, clampmode, 0, layer->scaleFlags); + maxbound = i; + } + } + // unbind everything from the last texture that's still active + for (int i = maxbound + 1; i <= maxBoundMaterial; i++) + { + FHardwareTexture::Unbind(i); + maxBoundMaterial = maxbound; + } +} + +//========================================================================== +// +// Apply blend mode from RenderStyle +// +//========================================================================== + +void FGLRenderState::ApplyBlendMode() +{ + static int blendstyles[] = { GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA }; + static int renderops[] = { 0, GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; + + int srcblend = blendstyles[mRenderStyle.SrcAlpha%STYLEALPHA_MAX]; + int dstblend = blendstyles[mRenderStyle.DestAlpha%STYLEALPHA_MAX]; + int blendequation = renderops[mRenderStyle.BlendOp & 15]; + + if (blendequation == -1) // This was a fuzz style. + { + srcblend = GL_DST_COLOR; + dstblend = GL_ONE_MINUS_SRC_ALPHA; + blendequation = GL_FUNC_ADD; + } + + // Checks must be disabled until all draw code has been converted. + //if (srcblend != stSrcBlend || dstblend != stDstBlend) + { + stSrcBlend = srcblend; + stDstBlend = dstblend; + glBlendFunc(srcblend, dstblend); + } + //if (blendequation != stBlendEquation) + { + stBlendEquation = blendequation; + glBlendEquation(blendequation); + } + +} + +//========================================================================== +// +// API dependent draw calls +// +//========================================================================== + +static int dt2gl[] = { GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP }; + +void FGLRenderState::Draw(int dt, int index, int count, bool apply) +{ + if (apply) + { + Apply(); + } + //drawcalls.Clock(); + glDrawArrays(dt2gl[dt], index, count); + //drawcalls.Unclock(); +} + +void FGLRenderState::DrawIndexed(int dt, int index, int count, bool apply) +{ + if (apply) + { + Apply(); + } + //drawcalls.Clock(); + glDrawElements(dt2gl[dt], count, GL_UNSIGNED_INT, (void*)(intptr_t)(index * sizeof(uint32_t))); + //drawcalls.Unclock(); +} + +void FGLRenderState::SetDepthMask(bool on) +{ + glDepthMask(on); +} + +void FGLRenderState::SetDepthFunc(int func) +{ + static int df2gl[] = { GL_LESS, GL_LEQUAL, GL_ALWAYS }; + glDepthFunc(df2gl[func]); +} + +void FGLRenderState::SetDepthRange(float min, float max) +{ + glDepthRange(min, max); +} + +void FGLRenderState::SetColorMask(bool r, bool g, bool b, bool a) +{ + glColorMask(r, g, b, a); +} + +void FGLRenderState::SetStencil(int offs, int op, int flags = -1) +{ + static int op2gl[] = { GL_KEEP, GL_INCR, GL_DECR }; + + glStencilFunc(GL_EQUAL, screen->stencilValue + offs, ~0); // draw sky into stencil + glStencilOp(GL_KEEP, GL_KEEP, op2gl[op]); // this stage doesn't modify the stencil + + if (flags != -1) + { + bool cmon = !(flags & SF_ColorMaskOff); + glColorMask(cmon, cmon, cmon, cmon); // don't write to the graphics buffer + glDepthMask(!(flags & SF_DepthMaskOff)); + } +} + +void FGLRenderState::ToggleState(int state, bool on) +{ + if (on) + { + glEnable(state); + } + else + { + glDisable(state); + } +} + +void FGLRenderState::SetCulling(int mode) +{ + if (mode != Cull_None) + { + glEnable(GL_CULL_FACE); + glFrontFace(mode == Cull_CCW ? GL_CCW : GL_CW); + } + else + { + glDisable(GL_CULL_FACE); + } +} + +void FGLRenderState::EnableClipDistance(int num, bool state) +{ + // Update the viewpoint-related clip plane setting. + if (!(gl.flags & RFL_NO_CLIP_PLANES)) + { + ToggleState(GL_CLIP_DISTANCE0 + num, state); + } +} + +void FGLRenderState::Clear(int targets) +{ + // This always clears to default values. + int gltarget = 0; + if (targets & CT_Depth) + { + gltarget |= GL_DEPTH_BUFFER_BIT; + glClearDepth(1); + } + if (targets & CT_Stencil) + { + gltarget |= GL_STENCIL_BUFFER_BIT; + glClearStencil(0); + } + if (targets & CT_Color) + { + gltarget |= GL_COLOR_BUFFER_BIT; + glClearColor(screen->mSceneClearColor[0], screen->mSceneClearColor[1], screen->mSceneClearColor[2], screen->mSceneClearColor[3]); + } + glClear(gltarget); +} + +void FGLRenderState::EnableStencil(bool on) +{ + ToggleState(GL_STENCIL_TEST, on); +} + +void FGLRenderState::SetScissor(int x, int y, int w, int h) +{ + if (w > -1) + { + glEnable(GL_SCISSOR_TEST); + glScissor(x, y, w, h); + } + else + { + glDisable(GL_SCISSOR_TEST); + } +} + +void FGLRenderState::SetViewport(int x, int y, int w, int h) +{ + glViewport(x, y, w, h); +} + +void FGLRenderState::EnableDepthTest(bool on) +{ + ToggleState(GL_DEPTH_TEST, on); +} + +void FGLRenderState::EnableMultisampling(bool on) +{ + ToggleState(GL_MULTISAMPLE, on); +} + +void FGLRenderState::EnableLineSmooth(bool on) +{ + ToggleState(GL_LINE_SMOOTH, on); +} + +//========================================================================== +// +// +// +//========================================================================== +void FGLRenderState::ClearScreen() +{ + bool multi = !!glIsEnabled(GL_MULTISAMPLE); + + //screen->mViewpoints->Set2D(*this, SCREENWIDTH, SCREENHEIGHT); + SetColor(0, 0, 0); + Apply(); + + glDisable(GL_MULTISAMPLE); + glDisable(GL_DEPTH_TEST); + + glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4); + + glEnable(GL_DEPTH_TEST); + if (multi) glEnable(GL_MULTISAMPLE); +} + + + +//========================================================================== +// +// Below are less frequently altrered state settings which do not get +// buffered by the state object, but set directly instead. +// +//========================================================================== + +bool FGLRenderState::SetDepthClamp(bool on) +{ + bool res = mLastDepthClamp; + if (!on) glDisable(GL_DEPTH_CLAMP); + else glEnable(GL_DEPTH_CLAMP); + mLastDepthClamp = on; + return res; +} + +} diff --git a/source/common/rendering/gl/gl_renderstate.h b/source/common/rendering/gl/gl_renderstate.h new file mode 100644 index 000000000..948255d71 --- /dev/null +++ b/source/common/rendering/gl/gl_renderstate.h @@ -0,0 +1,149 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2009-2016 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/ +// +//-------------------------------------------------------------------------- +// + +#ifndef __GL_RENDERSTATE_H +#define __GL_RENDERSTATE_H + +#include +#include +#include "gl_interface.h" +#include "matrix.h" +#include "hw_renderstate.h" +#include "hw_material.h" +#include "c_cvars.h" + +namespace OpenGLRenderer +{ + +class FShader; +struct HWSectorPlane; + +class FGLRenderState final : public FRenderState +{ + uint8_t mLastDepthClamp : 1; + + float mGlossiness, mSpecularLevel; + float mShaderTimer; + + int mEffectState; + int mTempTM = TM_NORMAL; + + FRenderStyle stRenderStyle; + int stSrcBlend, stDstBlend; + bool stAlphaTest; + bool stSplitEnabled; + int stBlendEquation; + + FShader *activeShader; + + int mNumDrawBuffers = 1; + + bool ApplyShader(); + void ApplyState(); + + // Texture binding state + FMaterial *lastMaterial = nullptr; + int lastClamp = 0; + int lastTranslation = 0; + int maxBoundMaterial = -1; + size_t mLastMappedLightIndex = SIZE_MAX; + + IVertexBuffer *mCurrentVertexBuffer; + int mCurrentVertexOffsets[2]; // one per binding point + IIndexBuffer *mCurrentIndexBuffer; + + +public: + + FGLRenderState() + { + Reset(); + } + + void Reset(); + + void ClearLastMaterial() + { + lastMaterial = nullptr; + } + + void ApplyMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader); + + void Apply(); + void ApplyBuffers(); + void ApplyBlendMode(); + + void ResetVertexBuffer() + { + // forces rebinding with the next 'apply' call. + mCurrentVertexBuffer = nullptr; + mCurrentIndexBuffer = nullptr; + } + + void SetSpecular(float glossiness, float specularLevel) + { + mGlossiness = glossiness; + mSpecularLevel = specularLevel; + } + + void EnableDrawBuffers(int count, bool apply = false) override + { + count = std::min(count, 3); + if (mNumDrawBuffers != count) + { + static GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; + glDrawBuffers(count, buffers); + mNumDrawBuffers = count; + } + if (apply) Apply(); + } + + void ToggleState(int state, bool on); + + void ClearScreen() override; + void Draw(int dt, int index, int count, bool apply = true) override; + void DrawIndexed(int dt, int index, int count, bool apply = true) override; + + bool SetDepthClamp(bool on) override; + void SetDepthMask(bool on) override; + void SetDepthFunc(int func) override; + void SetDepthRange(float min, float max) override; + void SetColorMask(bool r, bool g, bool b, bool a) override; + void SetStencil(int offs, int op, int flags) override; + void SetCulling(int mode) override; + void EnableClipDistance(int num, bool state) override; + void Clear(int targets) override; + void EnableStencil(bool on) override; + void SetScissor(int x, int y, int w, int h) override; + void SetViewport(int x, int y, int w, int h) override; + void EnableDepthTest(bool on) override; + void EnableMultisampling(bool on) override; + void EnableLineSmooth(bool on) override; + + +}; + +extern FGLRenderState gl_RenderState; + +} + +#endif diff --git a/source/common/rendering/gl/gl_shader.cpp b/source/common/rendering/gl/gl_shader.cpp new file mode 100644 index 000000000..e623cc279 --- /dev/null +++ b/source/common/rendering/gl/gl_shader.cpp @@ -0,0 +1,866 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2004-2016 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/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_shader.cpp +** +** GLSL shader handling +** +*/ + +#include "gl_system.h" +#include "c_cvars.h" +#include "v_video.h" +#include "filesystem.h" +#include "engineerrors.h" +#include "cmdlib.h" +#include "md5.h" +#include "gl_shader.h" +#include "hw_shaderpatcher.h" +#include "hwrenderer/data/shaderuniforms.h" +#include "hwrenderer/scene/hw_viewpointuniforms.h" +#include "hw_lightbuffer.h" +#include "i_specialpaths.h" +#include "printf.h" + +#include "gl_interface.h" +#include "gl_debug.h" +#include "matrix.h" +#include "gl/renderer/gl_renderer.h" +#include +#include + +namespace OpenGLRenderer +{ + +struct ProgramBinary +{ + uint32_t format; + TArray data; +}; + +static const char *ShaderMagic = "ZDSC"; + +static std::map> ShaderCache; // Not a TMap because it doesn't support unique_ptr move semantics + +bool IsShaderCacheActive() +{ + static bool active = true; + static bool firstcall = true; + + if (firstcall) + { + const char *vendor = (const char *)glGetString(GL_VENDOR); + active = !(strstr(vendor, "Intel") == nullptr); + firstcall = false; + } + return active; +} + +static FString CalcProgramBinaryChecksum(const FString &vertex, const FString &fragment) +{ + const GLubyte *vendor = glGetString(GL_VENDOR); + const GLubyte *renderer = glGetString(GL_RENDERER); + const GLubyte *version = glGetString(GL_VERSION); + + uint8_t digest[16]; + MD5Context md5; + md5.Update(vendor, (unsigned int)strlen((const char*)vendor)); + md5.Update(renderer, (unsigned int)strlen((const char*)renderer)); + md5.Update(version, (unsigned int)strlen((const char*)version)); + md5.Update((const uint8_t *)vertex.GetChars(), (unsigned int)vertex.Len()); + md5.Update((const uint8_t *)fragment.GetChars(), (unsigned int)fragment.Len()); + md5.Final(digest); + + char hexdigest[33]; + for (int i = 0; i < 16; i++) + { + int v = digest[i] >> 4; + hexdigest[i * 2] = v < 10 ? ('0' + v) : ('a' + v - 10); + v = digest[i] & 15; + hexdigest[i * 2 + 1] = v < 10 ? ('0' + v) : ('a' + v - 10); + } + hexdigest[32] = 0; + return hexdigest; +} + +static FString CreateProgramCacheName(bool create) +{ + FString path = M_GetCachePath(create); + if (create) CreatePath(path); + path << "/shadercache.zdsc"; + return path; +} + +static void LoadShaders() +{ + static bool loaded = false; + if (loaded) + return; + loaded = true; + + try + { + FString path = CreateProgramCacheName(false); + FileReader fr; + if (!fr.OpenFile(path)) + I_Error("Could not open shader file"); + + char magic[4]; + fr.Read(magic, 4); + if (memcmp(magic, ShaderMagic, 4) != 0) + I_Error("Not a shader cache file"); + + uint32_t count = fr.ReadUInt32(); + if (count > 512) + I_Error("Too many shaders cached"); + + for (uint32_t i = 0; i < count; i++) + { + char hexdigest[33]; + if (fr.Read(hexdigest, 32) != 32) + I_Error("Read error"); + hexdigest[32] = 0; + + std::unique_ptr binary(new ProgramBinary()); + binary->format = fr.ReadUInt32(); + uint32_t size = fr.ReadUInt32(); + if (size > 1024 * 1024) + I_Error("Shader too big, probably file corruption"); + + binary->data.Resize(size); + if (fr.Read(binary->data.Data(), binary->data.Size()) != binary->data.Size()) + I_Error("Read error"); + + ShaderCache[hexdigest] = std::move(binary); + } + } + catch (...) + { + ShaderCache.clear(); + } +} + +static void SaveShaders() +{ + FString path = CreateProgramCacheName(true); + std::unique_ptr fw(FileWriter::Open(path)); + if (fw) + { + uint32_t count = (uint32_t)ShaderCache.size(); + fw->Write(ShaderMagic, 4); + fw->Write(&count, sizeof(uint32_t)); + for (const auto &it : ShaderCache) + { + uint32_t size = it.second->data.Size(); + fw->Write(it.first.GetChars(), 32); + fw->Write(&it.second->format, sizeof(uint32_t)); + fw->Write(&size, sizeof(uint32_t)); + fw->Write(it.second->data.Data(), it.second->data.Size()); + } + } +} + +TArray LoadCachedProgramBinary(const FString &vertex, const FString &fragment, uint32_t &binaryFormat) +{ + LoadShaders(); + + auto it = ShaderCache.find(CalcProgramBinaryChecksum(vertex, fragment)); + if (it != ShaderCache.end()) + { + binaryFormat = it->second->format; + return it->second->data; + } + else + { + binaryFormat = 0; + return {}; + } +} + +void SaveCachedProgramBinary(const FString &vertex, const FString &fragment, const TArray &binary, uint32_t binaryFormat) +{ + auto &entry = ShaderCache[CalcProgramBinaryChecksum(vertex, fragment)]; + entry.reset(new ProgramBinary()); + entry->format = binaryFormat; + entry->data = binary; + + SaveShaders(); +} + +bool FShader::Load(const char * name, const char * vert_prog_lump, const char * frag_prog_lump, const char * proc_prog_lump, const char * light_fragprog, const char * defines) +{ + static char buffer[10000]; + FString error; + + FString i_data = R"( + // these settings are actually pointless but there seem to be some old ATI drivers that fail to compile the shader without setting the precision here. + precision highp int; + precision highp float; + + // This must match the HWViewpointUniforms struct + layout(std140) uniform ViewpointUBO { + mat4 ProjectionMatrix; + mat4 ViewMatrix; + mat4 NormalViewMatrix; + + vec4 uCameraPos; + vec4 uClipLine; + + float uGlobVis; // uGlobVis = R_GetGlobVis(r_visibility) / 32.0 + int uPalLightLevels; + int uViewHeight; // Software fuzz scaling + float uClipHeight; + float uClipHeightDirection; + int uShadowmapFilter; + }; + )"; + + i_data += "uniform int uTextureMode;\n"; + i_data += "uniform vec2 uClipSplit;\n"; + i_data += "uniform float uAlphaThreshold;\n"; + + // colors + i_data += "uniform vec4 uObjectColor;\n"; + i_data += "uniform vec4 uObjectColor2;\n"; + i_data += "uniform vec4 uDynLightColor;\n"; + i_data += "uniform vec4 uAddColor;\n"; + i_data += "uniform vec4 uTextureBlendColor;\n"; + i_data += "uniform vec4 uTextureModulateColor;\n"; + i_data += "uniform vec4 uTextureAddColor;\n"; + i_data += "uniform vec4 uBlendColor;\n"; + i_data += "uniform vec4 uFogColor;\n"; + i_data += "uniform float uDesaturationFactor;\n"; + i_data += "uniform float uInterpolationFactor;\n"; + + // Glowing walls stuff + i_data += "uniform vec4 uGlowTopPlane;\n"; + i_data += "uniform vec4 uGlowTopColor;\n"; + i_data += "uniform vec4 uGlowBottomPlane;\n"; + i_data += "uniform vec4 uGlowBottomColor;\n"; + + i_data += "uniform vec4 uGradientTopPlane;\n"; + i_data += "uniform vec4 uGradientBottomPlane;\n"; + + i_data += "uniform vec4 uSplitTopPlane;\n"; + i_data += "uniform vec4 uSplitBottomPlane;\n"; + + i_data += "uniform vec4 uDetailParms;\n"; + + // Lighting + Fog + i_data += "uniform vec4 uLightAttr;\n"; + i_data += "#define uLightLevel uLightAttr.a\n"; + i_data += "#define uFogDensity uLightAttr.b\n"; + i_data += "#define uLightFactor uLightAttr.g\n"; + i_data += "#define uLightDist uLightAttr.r\n"; + i_data += "uniform int uFogEnabled;\n"; + + // dynamic lights + i_data += "uniform int uLightIndex;\n"; + + // Blinn glossiness and specular level + i_data += "uniform vec2 uSpecularMaterial;\n"; + + // matrices + i_data += "uniform mat4 ModelMatrix;\n"; + i_data += "uniform mat4 NormalModelMatrix;\n"; + i_data += "uniform mat4 TextureMatrix;\n"; + + // light buffers + i_data += "#ifdef SHADER_STORAGE_LIGHTS\n"; + i_data += "layout(std430, binding = 1) buffer LightBufferSSO\n"; + i_data += "{\n"; + i_data += " vec4 lights[];\n"; + i_data += "};\n"; + i_data += "#elif defined NUM_UBO_LIGHTS\n"; + i_data += "uniform LightBufferUBO\n"; + i_data += "{\n"; + i_data += " vec4 lights[NUM_UBO_LIGHTS];\n"; + i_data += "};\n"; + i_data += "#endif\n"; + + // textures + i_data += "uniform sampler2D tex;\n"; + i_data += "uniform sampler2D ShadowMap;\n"; + i_data += "uniform sampler2D texture2;\n"; + i_data += "uniform sampler2D texture3;\n"; + i_data += "uniform sampler2D texture4;\n"; + i_data += "uniform sampler2D texture5;\n"; + i_data += "uniform sampler2D texture6;\n"; + i_data += "uniform sampler2D texture7;\n"; + i_data += "uniform sampler2D texture8;\n"; + + // timer data + i_data += "uniform float timer;\n"; + + // material types + i_data += "#if defined(SPECULAR)\n"; + i_data += "#define normaltexture texture2\n"; + i_data += "#define speculartexture texture3\n"; + i_data += "#define brighttexture texture4\n"; + i_data += "#define detailtexture texture5\n"; + i_data += "#define glowtexture texture6\n"; + i_data += "#elif defined(PBR)\n"; + i_data += "#define normaltexture texture2\n"; + i_data += "#define metallictexture texture3\n"; + i_data += "#define roughnesstexture texture4\n"; + i_data += "#define aotexture texture5\n"; + i_data += "#define brighttexture texture6\n"; + i_data += "#define detailtexture texture7\n"; + i_data += "#define glowtexture texture8\n"; + i_data += "#else\n"; + i_data += "#define brighttexture texture2\n"; + i_data += "#define detailtexture texture3\n"; + i_data += "#define glowtexture texture4\n"; + i_data += "#endif\n"; + +#ifdef __APPLE__ + // The noise functions are completely broken in macOS OpenGL drivers + // Garbage values are returned, and their infrequent usage causes extreme slowdown + // Also, these functions must return zeroes since GLSL 4.4 + i_data += "#define noise1(unused) 0.0\n"; + i_data += "#define noise2(unused) vec2(0)\n"; + i_data += "#define noise3(unused) vec3(0)\n"; + i_data += "#define noise4(unused) vec4(0)\n"; +#endif // __APPLE__ + + int vp_lump = fileSystem.CheckNumForFullName(vert_prog_lump, 0); + if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump); + FileData vp_data = fileSystem.ReadFile(vp_lump); + + int fp_lump = fileSystem.CheckNumForFullName(frag_prog_lump, 0); + if (fp_lump == -1) I_Error("Unable to load '%s'", frag_prog_lump); + FileData fp_data = fileSystem.ReadFile(fp_lump); + + + +// +// The following code uses GetChars on the strings to get rid of terminating 0 characters. Do not remove or the code may break! +// + FString vp_comb; + + assert(screen->mLights != NULL); + + bool lightbuffertype = screen->mLights->GetBufferType(); + unsigned int lightbuffersize = screen->mLights->GetBlockSize(); + if (!lightbuffertype) + { + vp_comb.Format("#version 330 core\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize); + } + else + { + // This differentiation is for Intel which do not seem to expose the full extension, even if marked as required. + if (gl.glslversion < 4.3f) + vp_comb = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n"; + else + vp_comb = "#version 430 core\n#define SHADER_STORAGE_LIGHTS\n"; + } + + if (gl.flags & RFL_SHADER_STORAGE_BUFFER) + { + vp_comb << "#define SUPPORTS_SHADOWMAPS\n"; + } + + vp_comb << defines << i_data.GetChars(); + FString fp_comb = vp_comb; + + vp_comb << "#line 1\n"; + fp_comb << "#line 1\n"; + + vp_comb << RemoveLayoutLocationDecl(vp_data.GetString(), "out").GetChars() << "\n"; + fp_comb << RemoveLayoutLocationDecl(fp_data.GetString(), "in").GetChars() << "\n"; + + if (proc_prog_lump != NULL) + { + fp_comb << "#line 1\n"; + + if (*proc_prog_lump != '#') + { + int pp_lump = fileSystem.CheckNumForFullName(proc_prog_lump); + if (pp_lump == -1) I_Error("Unable to load '%s'", proc_prog_lump); + FileData pp_data = fileSystem.ReadFile(pp_lump); + + if (pp_data.GetString().IndexOf("ProcessMaterial") < 0 && pp_data.GetString().IndexOf("SetupMaterial") < 0) + { + // this looks like an old custom hardware shader. + + if (pp_data.GetString().IndexOf("GetTexCoord") >= 0) + { + int pl_lump = fileSystem.CheckNumForFullName("shaders/glsl/func_defaultmat2.fp", 0); + if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders/glsl/func_defaultmat2.fp"); + FileData pl_data = fileSystem.ReadFile(pl_lump); + fp_comb << "\n" << pl_data.GetString().GetChars(); + } + else + { + int pl_lump = fileSystem.CheckNumForFullName("shaders/glsl/func_defaultmat.fp", 0); + if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders/glsl/func_defaultmat.fp"); + FileData pl_data = fileSystem.ReadFile(pl_lump); + fp_comb << "\n" << pl_data.GetString().GetChars(); + + if (pp_data.GetString().IndexOf("ProcessTexel") < 0) + { + // this looks like an even older custom hardware shader. + // We need to replace the ProcessTexel call to make it work. + + fp_comb.Substitute("material.Base = ProcessTexel();", "material.Base = Process(vec4(1.0));"); + } + } + + if (pp_data.GetString().IndexOf("ProcessLight") >= 0) + { + // The ProcessLight signatured changed. Forward to the old one. + fp_comb << "\nvec4 ProcessLight(vec4 color);\n"; + fp_comb << "\nvec4 ProcessLight(Material material, vec4 color) { return ProcessLight(color); }\n"; + } + } + + fp_comb << RemoveLegacyUserUniforms(pp_data.GetString()).GetChars(); + fp_comb.Substitute("gl_TexCoord[0]", "vTexCoord"); // fix old custom shaders. + + if (pp_data.GetString().IndexOf("ProcessLight") < 0) + { + int pl_lump = fileSystem.CheckNumForFullName("shaders/glsl/func_defaultlight.fp", 0); + if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders/glsl/func_defaultlight.fp"); + FileData pl_data = fileSystem.ReadFile(pl_lump); + fp_comb << "\n" << pl_data.GetString().GetChars(); + } + + // ProcessMaterial must be considered broken because it requires the user to fill in data they possibly cannot know all about. + if (pp_data.GetString().IndexOf("ProcessMaterial") >= 0 && pp_data.GetString().IndexOf("SetupMaterial") < 0) + { + // This reactivates the old logic and disables all features that cannot be supported with that method. + fp_comb.Insert(0, "#define LEGACY_USER_SHADER\n"); + } + + } + else + { + // Proc_prog_lump is not a lump name but the source itself (from generated shaders) + fp_comb << proc_prog_lump + 1; + } + } + + if (light_fragprog) + { + int pp_lump = fileSystem.CheckNumForFullName(light_fragprog, 0); + if (pp_lump == -1) I_Error("Unable to load '%s'", light_fragprog); + FileData pp_data = fileSystem.ReadFile(pp_lump); + fp_comb << pp_data.GetString().GetChars() << "\n"; + } + + if (gl.flags & RFL_NO_CLIP_PLANES) + { + // On ATI's GL3 drivers we have to disable gl_ClipDistance because it's hopelessly broken. + // This will cause some glitches and regressions but is the only way to avoid total display garbage. + vp_comb.Substitute("gl_ClipDistance", "//"); + } + + hShader = glCreateProgram(); + FGLDebug::LabelObject(GL_PROGRAM, hShader, name); + + uint32_t binaryFormat = 0; + TArray binary; + if (IsShaderCacheActive()) + binary = LoadCachedProgramBinary(vp_comb, fp_comb, binaryFormat); + + bool linked = false; + if (binary.Size() > 0 && glProgramBinary) + { + glProgramBinary(hShader, binaryFormat, binary.Data(), binary.Size()); + GLint status = 0; + glGetProgramiv(hShader, GL_LINK_STATUS, &status); + linked = (status == GL_TRUE); + } + + if (!linked) + { + hVertProg = glCreateShader(GL_VERTEX_SHADER); + hFragProg = glCreateShader(GL_FRAGMENT_SHADER); + + FGLDebug::LabelObject(GL_SHADER, hVertProg, vert_prog_lump); + FGLDebug::LabelObject(GL_SHADER, hFragProg, frag_prog_lump); + + int vp_size = (int)vp_comb.Len(); + int fp_size = (int)fp_comb.Len(); + + const char *vp_ptr = vp_comb.GetChars(); + const char *fp_ptr = fp_comb.GetChars(); + + glShaderSource(hVertProg, 1, &vp_ptr, &vp_size); + glShaderSource(hFragProg, 1, &fp_ptr, &fp_size); + + glCompileShader(hVertProg); + glCompileShader(hFragProg); + + glAttachShader(hShader, hVertProg); + glAttachShader(hShader, hFragProg); + + glLinkProgram(hShader); + + glGetShaderInfoLog(hVertProg, 10000, NULL, buffer); + if (*buffer) + { + error << "Vertex shader:\n" << buffer << "\n"; + } + glGetShaderInfoLog(hFragProg, 10000, NULL, buffer); + if (*buffer) + { + error << "Fragment shader:\n" << buffer << "\n"; + } + + glGetProgramInfoLog(hShader, 10000, NULL, buffer); + if (*buffer) + { + error << "Linking:\n" << buffer << "\n"; + } + GLint status = 0; + glGetProgramiv(hShader, GL_LINK_STATUS, &status); + linked = (status == GL_TRUE); + if (!linked) + { + // only print message if there's an error. + I_Error("Init Shader '%s':\n%s\n", name, error.GetChars()); + } + else if (glProgramBinary && IsShaderCacheActive()) + { + int binaryLength = 0; + glGetProgramiv(hShader, GL_PROGRAM_BINARY_LENGTH, &binaryLength); + binary.Resize(binaryLength); + glGetProgramBinary(hShader, binary.Size(), &binaryLength, &binaryFormat, binary.Data()); + binary.Resize(binaryLength); + SaveCachedProgramBinary(vp_comb, fp_comb, binary, binaryFormat); + } + } + else + { + hVertProg = 0; + hFragProg = 0; + } + + muDesaturation.Init(hShader, "uDesaturationFactor"); + muFogEnabled.Init(hShader, "uFogEnabled"); + muTextureMode.Init(hShader, "uTextureMode"); + muLightParms.Init(hShader, "uLightAttr"); + muClipSplit.Init(hShader, "uClipSplit"); + muLightIndex.Init(hShader, "uLightIndex"); + muFogColor.Init(hShader, "uFogColor"); + muDynLightColor.Init(hShader, "uDynLightColor"); + muObjectColor.Init(hShader, "uObjectColor"); + muObjectColor2.Init(hShader, "uObjectColor2"); + muGlowBottomColor.Init(hShader, "uGlowBottomColor"); + muGlowTopColor.Init(hShader, "uGlowTopColor"); + muGlowBottomPlane.Init(hShader, "uGlowBottomPlane"); + muGlowTopPlane.Init(hShader, "uGlowTopPlane"); + muGradientBottomPlane.Init(hShader, "uGradientBottomPlane"); + muGradientTopPlane.Init(hShader, "uGradientTopPlane"); + muSplitBottomPlane.Init(hShader, "uSplitBottomPlane"); + muSplitTopPlane.Init(hShader, "uSplitTopPlane"); + muDetailParms.Init(hShader, "uDetailParms"); + muInterpolationFactor.Init(hShader, "uInterpolationFactor"); + muAlphaThreshold.Init(hShader, "uAlphaThreshold"); + muSpecularMaterial.Init(hShader, "uSpecularMaterial"); + muAddColor.Init(hShader, "uAddColor"); + muTextureAddColor.Init(hShader, "uTextureAddColor"); + muTextureModulateColor.Init(hShader, "uTextureModulateColor"); + muTextureBlendColor.Init(hShader, "uTextureBlendColor"); + muTimer.Init(hShader, "timer"); + + lights_index = glGetUniformLocation(hShader, "lights"); + modelmatrix_index = glGetUniformLocation(hShader, "ModelMatrix"); + texturematrix_index = glGetUniformLocation(hShader, "TextureMatrix"); + normalmodelmatrix_index = glGetUniformLocation(hShader, "NormalModelMatrix"); + + if (!lightbuffertype) + { + int tempindex = glGetUniformBlockIndex(hShader, "LightBufferUBO"); + if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, LIGHTBUF_BINDINGPOINT); + } + int tempindex = glGetUniformBlockIndex(hShader, "ViewpointUBO"); + if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, VIEWPOINT_BINDINGPOINT); + + glUseProgram(hShader); + + // set up other texture units (if needed by the shader) + for (int i = 2; i<16; i++) + { + char stringbuf[20]; + mysnprintf(stringbuf, 20, "texture%d", i); + int tempindex = glGetUniformLocation(hShader, stringbuf); + if (tempindex > 0) glUniform1i(tempindex, i - 1); + } + + int shadowmapindex = glGetUniformLocation(hShader, "ShadowMap"); + if (shadowmapindex > 0) glUniform1i(shadowmapindex, 16); + + glUseProgram(0); + return linked; +} + +//========================================================================== +// +// +// +//========================================================================== + +FShader::~FShader() +{ + glDeleteProgram(hShader); + if (hVertProg != 0) + glDeleteShader(hVertProg); + if (hFragProg != 0) + glDeleteShader(hFragProg); +} + + +//========================================================================== +// +// +// +//========================================================================== + +bool FShader::Bind() +{ + GLRenderer->mShaderManager->SetActiveShader(this); + return true; +} + +//========================================================================== +// +// Since all shaders are REQUIRED, any error here needs to be fatal +// +//========================================================================== + +FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType) +{ + FString defines; + defines += shaderdefines; + // this can't be in the shader code due to ATI strangeness. + if (!usediscard) defines += "#define NO_ALPHATEST\n"; + if (passType == GBUFFER_PASS) defines += "#define GBUFFER_PASS\n"; + + FShader *shader = NULL; + try + { + shader = new FShader(ShaderName); + if (!shader->Load(ShaderName, "shaders/glsl/main.vp", "shaders/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars())) + { + I_FatalError("Unable to load shader %s\n", ShaderName); + } + } + catch(CRecoverableError &err) + { + if (shader != NULL) delete shader; + shader = NULL; + I_FatalError("Unable to load shader %s:\n%s\n", ShaderName, err.GetMessage()); + } + return shader; +} + +//========================================================================== +// +// +// +//========================================================================== + +FShaderManager::FShaderManager() +{ + for (int passType = 0; passType < MAX_PASS_TYPES; passType++) + mPassShaders.Push(new FShaderCollection((EPassType)passType)); +} + +FShaderManager::~FShaderManager() +{ + glUseProgram(0); + mActiveShader = NULL; + + for (auto collection : mPassShaders) + delete collection; +} + +void FShaderManager::SetActiveShader(FShader *sh) +{ + if (mActiveShader != sh) + { + glUseProgram(sh!= NULL? sh->GetHandle() : 0); + mActiveShader = sh; + } +} + +FShader *FShaderManager::BindEffect(int effect, EPassType passType) +{ + if (passType < mPassShaders.Size()) + return mPassShaders[passType]->BindEffect(effect); + else + return nullptr; +} + +FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType) +{ + if (passType < mPassShaders.Size()) + return mPassShaders[passType]->Get(eff, alphateston); + else + return nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +FShaderCollection::FShaderCollection(EPassType passType) +{ + CompileShaders(passType); +} + +//========================================================================== +// +// +// +//========================================================================== + +FShaderCollection::~FShaderCollection() +{ + Clean(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FShaderCollection::CompileShaders(EPassType passType) +{ + mMaterialShaders.Clear(); + mMaterialShadersNAT.Clear(); + for (int i = 0; i < MAX_EFFECTS; i++) + { + mEffectShaders[i] = NULL; + } + + for(int i=0;defaultshaders[i].ShaderName != NULL;i++) + { + FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, passType); + mMaterialShaders.Push(shc); + if (i < SHADER_NoTexture) + { + FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, passType); + mMaterialShadersNAT.Push(shc); + } + } + + for(unsigned i = 0; i < usershaders.Size(); i++) + { + FString name = ExtractFileBase(usershaders[i].shader); + FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines; + FShader *shc = Compile(name, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, passType); + mMaterialShaders.Push(shc); + } + + for(int i=0;iLoad(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].fp1, + effectshaders[i].fp2, effectshaders[i].fp3, effectshaders[i].defines)) + { + delete eff; + } + else mEffectShaders[i] = eff; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FShaderCollection::Clean() +{ + for (unsigned int i = 0; i < mMaterialShadersNAT.Size(); i++) + { + if (mMaterialShadersNAT[i] != NULL) delete mMaterialShadersNAT[i]; + } + for (unsigned int i = 0; i < mMaterialShaders.Size(); i++) + { + if (mMaterialShaders[i] != NULL) delete mMaterialShaders[i]; + } + for (int i = 0; i < MAX_EFFECTS; i++) + { + if (mEffectShaders[i] != NULL) delete mEffectShaders[i]; + mEffectShaders[i] = NULL; + } + mMaterialShaders.Clear(); + mMaterialShadersNAT.Clear(); +} + +//========================================================================== +// +// +// +//========================================================================== + +int FShaderCollection::Find(const char * shn) +{ + FName sfn = shn; + + for(unsigned int i=0;imName == sfn) + { + return i; + } + } + return -1; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FShader *FShaderCollection::BindEffect(int effect) +{ + if (effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[effect] != NULL) + { + mEffectShaders[effect]->Bind(); + return mEffectShaders[effect]; + } + return NULL; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void gl_DestroyUserShaders() +{ + // todo +} + +} diff --git a/source/common/rendering/gl/gl_shader.h b/source/common/rendering/gl/gl_shader.h new file mode 100644 index 000000000..3027376d5 --- /dev/null +++ b/source/common/rendering/gl/gl_shader.h @@ -0,0 +1,350 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2004-2016 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/ +// +//-------------------------------------------------------------------------- +// + +#ifndef __GL_SHADERS_H__ +#define __GL_SHADERS_H__ + +#include "gl_renderstate.h" +#include "name.h" + +extern bool gl_shaderactive; + +struct HWViewpointUniforms; + +namespace OpenGLRenderer +{ + class FShaderCollection; + +//========================================================================== +// +// +//========================================================================== + +class FUniform1i +{ + int mIndex; + +public: + void Init(GLuint hShader, const GLchar *name) + { + mIndex = glGetUniformLocation(hShader, name); + } + + void Set(int newvalue) + { + glUniform1i(mIndex, newvalue); + } +}; + +class FBufferedUniform1i +{ + int mBuffer; + int mIndex; + +public: + void Init(GLuint hShader, const GLchar *name) + { + mIndex = glGetUniformLocation(hShader, name); + mBuffer = 0; + } + + void Set(int newvalue) + { + if (newvalue != mBuffer) + { + mBuffer = newvalue; + glUniform1i(mIndex, newvalue); + } + } +}; + +class FBufferedUniform4i +{ + int mBuffer[4]; + int mIndex; + +public: + void Init(GLuint hShader, const GLchar *name) + { + mIndex = glGetUniformLocation(hShader, name); + memset(mBuffer, 0, sizeof(mBuffer)); + } + + void Set(const int *newvalue) + { + if (memcmp(newvalue, mBuffer, sizeof(mBuffer))) + { + memcpy(mBuffer, newvalue, sizeof(mBuffer)); + glUniform4iv(mIndex, 1, newvalue); + } + } +}; + +class FBufferedUniform1f +{ + float mBuffer; + int mIndex; + +public: + void Init(GLuint hShader, const GLchar *name) + { + mIndex = glGetUniformLocation(hShader, name); + mBuffer = 0; + } + + void Set(float newvalue) + { + if (newvalue != mBuffer) + { + mBuffer = newvalue; + glUniform1f(mIndex, newvalue); + } + } +}; + +class FBufferedUniform2f +{ + float mBuffer[2]; + int mIndex; + +public: + void Init(GLuint hShader, const GLchar *name) + { + mIndex = glGetUniformLocation(hShader, name); + memset(mBuffer, 0, sizeof(mBuffer)); + } + + void Set(const float *newvalue) + { + if (memcmp(newvalue, mBuffer, sizeof(mBuffer))) + { + memcpy(mBuffer, newvalue, sizeof(mBuffer)); + glUniform2fv(mIndex, 1, newvalue); + } + } + + void Set(float f1, float f2) + { + if (mBuffer[0] != f1 || mBuffer[1] != f2) + { + mBuffer[0] = f1; + mBuffer[1] = f2; + glUniform2fv(mIndex, 1, mBuffer); + } + } + +}; + +class FBufferedUniform4f +{ + float mBuffer[4]; + int mIndex; + +public: + void Init(GLuint hShader, const GLchar *name) + { + mIndex = glGetUniformLocation(hShader, name); + memset(mBuffer, 0, sizeof(mBuffer)); + } + + void Set(const float *newvalue) + { + if (memcmp(newvalue, mBuffer, sizeof(mBuffer))) + { + memcpy(mBuffer, newvalue, sizeof(mBuffer)); + glUniform4fv(mIndex, 1, newvalue); + } + } +}; + +class FUniform4f +{ + int mIndex; + +public: + void Init(GLuint hShader, const GLchar *name) + { + mIndex = glGetUniformLocation(hShader, name); + } + + void Set(const float *newvalue) + { + glUniform4fv(mIndex, 1, newvalue); + } + + void Set(float a, float b, float c, float d) + { + glUniform4f(mIndex, a, b, c, d); + } + + void Set(PalEntry newvalue) + { + glUniform4f(mIndex, newvalue.r / 255.f, newvalue.g / 255.f, newvalue.b / 255.f, newvalue.a / 255.f); + } + +}; + +class FBufferedUniformPE +{ + FVector4PalEntry mBuffer; + int mIndex; + +public: + void Init(GLuint hShader, const GLchar *name) + { + mIndex = glGetUniformLocation(hShader, name); + mBuffer = 0; + } + + void Set(const FVector4PalEntry &newvalue) + { + if (newvalue != mBuffer) + { + mBuffer = newvalue; + glUniform4f(mIndex, newvalue.r, newvalue.g, newvalue.b, newvalue.a); + } + } +}; + +class FShader +{ + friend class FShaderCollection; + friend class FGLRenderState; + + unsigned int hShader; + unsigned int hVertProg; + unsigned int hFragProg; + FName mName; + + FBufferedUniform1f muDesaturation; + FBufferedUniform1i muFogEnabled; + FBufferedUniform1i muTextureMode; + FBufferedUniform4f muLightParms; + FBufferedUniform2f muClipSplit; + FBufferedUniform1i muLightIndex; + FBufferedUniformPE muFogColor; + FBufferedUniform4f muDynLightColor; + FBufferedUniformPE muObjectColor; + FBufferedUniformPE muObjectColor2; + FBufferedUniformPE muAddColor; + FBufferedUniformPE muTextureBlendColor; + FBufferedUniformPE muTextureModulateColor; + FBufferedUniformPE muTextureAddColor; + FUniform4f muGlowBottomColor; + FUniform4f muGlowTopColor; + FUniform4f muGlowBottomPlane; + FUniform4f muGlowTopPlane; + FUniform4f muGradientBottomPlane; + FUniform4f muGradientTopPlane; + FUniform4f muSplitBottomPlane; + FUniform4f muSplitTopPlane; + FUniform4f muDetailParms; + FBufferedUniform1f muInterpolationFactor; + FBufferedUniform1f muAlphaThreshold; + FBufferedUniform2f muSpecularMaterial; + FBufferedUniform1f muTimer; + + int lights_index; + int modelmatrix_index; + int normalmodelmatrix_index; + int texturematrix_index; + + int currentglowstate = 0; + int currentgradientstate = 0; + int currentsplitstate = 0; + int currentcliplinestate = 0; + int currentfixedcolormap = 0; + bool currentTextureMatrixState = true;// by setting the matrix state to 'true' it is guaranteed to be set the first time the render state gets applied. + bool currentModelMatrixState = true; + +public: + FShader(const char *name) + : mName(name) + { + hShader = hVertProg = hFragProg = 0; + } + + ~FShader(); + + bool Load(const char * name, const char * vert_prog_lump, const char * fragprog, const char * fragprog2, const char * light_fragprog, const char *defines); + + bool Bind(); + unsigned int GetHandle() const { return hShader; } +}; + +//========================================================================== +// +// The global shader manager +// +//========================================================================== +class FShaderManager +{ +public: + FShaderManager(); + ~FShaderManager(); + + FShader *BindEffect(int effect, EPassType passType); + FShader *Get(unsigned int eff, bool alphateston, EPassType passType); + +private: + void SetActiveShader(FShader *sh); + + FShader *mActiveShader = nullptr; + TArray mPassShaders; + + friend class FShader; +}; + +class FShaderCollection +{ + TArray mMaterialShaders; + TArray mMaterialShadersNAT; + FShader *mEffectShaders[MAX_EFFECTS]; + + void Clean(); + void CompileShaders(EPassType passType); + +public: + FShaderCollection(EPassType passType); + ~FShaderCollection(); + FShader *Compile(const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType); + int Find(const char *mame); + FShader *BindEffect(int effect); + + FShader *Get(unsigned int eff, bool alphateston) + { + // indices 0-2 match the warping modes, 3 no texture, the following are custom + if (!alphateston && eff <= 2) + { + return mMaterialShadersNAT[eff]; // Non-alphatest shaders are only created for default, warp1+2 and brightmap. The rest won't get used anyway + } + if (eff < mMaterialShaders.Size()) + { + return mMaterialShaders[eff]; + } + return NULL; + } +}; + +} +#endif + diff --git a/source/core/rendering/gl/shaders/gl_shaderprogram.cpp b/source/common/rendering/gl/gl_shaderprogram.cpp similarity index 89% rename from source/core/rendering/gl/shaders/gl_shaderprogram.cpp rename to source/common/rendering/gl/gl_shaderprogram.cpp index 1fc6f507a..eb322fb94 100644 --- a/source/core/rendering/gl/shaders/gl_shaderprogram.cpp +++ b/source/common/rendering/gl/gl_shaderprogram.cpp @@ -19,13 +19,13 @@ ** 3. This notice may not be removed or altered from any source distribution. */ -#include "gl_load/gl_system.h" +#include "gl_system.h" #include "v_video.h" -#include "gl_load/gl_interface.h" -#include "gamecvars.h" +#include "gl_interface.h" +#include "hw_cvars.h" #include "gl_debug.h" -#include "gl/shaders/gl_shaderprogram.h" -#include "hwrenderer/utility/hw_shaderpatcher.h" +#include "gl_shaderprogram.h" +#include "hw_shaderpatcher.h" #include "filesystem.h" #include "printf.h" @@ -86,7 +86,7 @@ void FShaderProgram::CreateShader(ShaderType type) void FShaderProgram::Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion) { - int lump = fileSystem.FindFile(lumpName); + int lump = fileSystem.CheckNumForFullName(lumpName); if (lump == -1) I_FatalError("Unable to load '%s'", lumpName); FString code = fileSystem.ReadFile(lump).GetString().GetChars(); Compile(type, lumpName, code, defines, maxGlslVersion); @@ -139,10 +139,9 @@ void FShaderProgram::Link(const char *name) uint32_t binaryFormat = 0; TArray binary; -#if 0 if (IsShaderCacheActive()) binary = LoadCachedProgramBinary(mShaderSources[Vertex], mShaderSources[Fragment], binaryFormat); -#endif + bool loadedFromBinary = false; if (binary.Size() > 0 && glProgramBinary) { @@ -167,7 +166,6 @@ void FShaderProgram::Link(const char *name) { I_FatalError("Link Shader '%s':\n%s\n", name, GetProgramInfoLog(mProgram).GetChars()); } -#if 0 else if (glProgramBinary && IsShaderCacheActive()) { int binaryLength = 0; @@ -177,7 +175,6 @@ void FShaderProgram::Link(const char *name) binary.Resize(binaryLength); SaveCachedProgramBinary(mShaderSources[Vertex], mShaderSources[Fragment], binary, binaryFormat); } -#endif } // This is only for old OpenGL which didn't allow to set the binding from within the shader. @@ -300,7 +297,7 @@ void FPresentShaderBase::Init(const char * vtx_shader_name, const char * program FString prolog = Uniforms.CreateDeclaration("Uniforms", PresentUniforms::Desc()); mShader.reset(new FShaderProgram()); - mShader->Compile(FShaderProgram::Vertex, "engine/shaders/pp/screenquad.vp", prolog, 330); + mShader->Compile(FShaderProgram::Vertex, "shaders/pp/screenquad.vp", prolog, 330); mShader->Compile(FShaderProgram::Fragment, vtx_shader_name, prolog, 330); mShader->Link(program_name); mShader->SetUniformBufferLocation(Uniforms.BindingPoint(), "Uniforms"); @@ -311,7 +308,7 @@ void FPresentShader::Bind() { if (!mShader) { - Init("engine/shaders/pp/present.fp", "engine/shaders/pp/present"); + Init("shaders/pp/present.fp", "shaders/pp/present"); } mShader->Bind(); } @@ -322,7 +319,7 @@ void FPresent3DCheckerShader::Bind() { if (!mShader) { - Init("engine/shaders/pp/present_checker3d.fp", "engine/shaders/pp/presentChecker3d"); + Init("shaders/pp/present_checker3d.fp", "shaders/pp/presentChecker3d"); } mShader->Bind(); } @@ -331,7 +328,7 @@ void FPresent3DColumnShader::Bind() { if (!mShader) { - Init("engine/shaders/pp/present_column3d.fp", "engine/shaders/pp/presentColumn3d"); + Init("shaders/pp/present_column3d.fp", "shaders/pp/presentColumn3d"); } mShader->Bind(); } @@ -340,10 +337,27 @@ void FPresent3DRowShader::Bind() { if (!mShader) { - Init("engine/shaders/pp/present_row3d.fp", "engine/shaders/pp/presentRow3d"); + Init("shaders/pp/present_row3d.fp", "shaders/pp/presentRow3d"); } mShader->Bind(); } +///////////////////////////////////////////////////////////////////////////// + +void FShadowMapShader::Bind() +{ + if (!mShader) + { + FString prolog = Uniforms.CreateDeclaration("Uniforms", ShadowMapUniforms::Desc()); + + mShader.reset(new FShaderProgram()); + mShader->Compile(FShaderProgram::Vertex, "shaders/pp/screenquad.vp", "", 430); + mShader->Compile(FShaderProgram::Fragment, "shaders/pp/shadowmap.fp", prolog, 430); + mShader->Link("shaders/glsl/shadowmap"); + mShader->SetUniformBufferLocation(Uniforms.BindingPoint(), "Uniforms"); + Uniforms.Init(); + } + mShader->Bind(); +} } \ No newline at end of file diff --git a/source/core/rendering/gl/shaders/gl_shaderprogram.h b/source/common/rendering/gl/gl_shaderprogram.h similarity index 89% rename from source/core/rendering/gl/shaders/gl_shaderprogram.h rename to source/common/rendering/gl/gl_shaderprogram.h index b197a18ba..90e257297 100644 --- a/source/core/rendering/gl/shaders/gl_shaderprogram.h +++ b/source/common/rendering/gl/gl_shaderprogram.h @@ -1,8 +1,8 @@ #pragma once -#include "gl_load/gl_system.h" -//#include "gl_shader.h" +#include "gl_system.h" +#include "gl_shader.h" #include "hwrenderer/postprocessing/hw_postprocess.h" namespace OpenGLRenderer @@ -89,5 +89,15 @@ public: void Bind() override; }; +class FShadowMapShader +{ +public: + void Bind(); + + ShaderUniforms Uniforms; + +private: + std::unique_ptr mShader; +}; } \ No newline at end of file diff --git a/source/core/rendering/hwrenderer/data/flatvertices.cpp b/source/common/rendering/hwrenderer/data/flatvertices.cpp similarity index 100% rename from source/core/rendering/hwrenderer/data/flatvertices.cpp rename to source/common/rendering/hwrenderer/data/flatvertices.cpp diff --git a/source/core/rendering/hwrenderer/data/flatvertices.h b/source/common/rendering/hwrenderer/data/flatvertices.h similarity index 92% rename from source/core/rendering/hwrenderer/data/flatvertices.h rename to source/common/rendering/hwrenderer/data/flatvertices.h index acd88dbb6..04feb8388 100644 --- a/source/core/rendering/hwrenderer/data/flatvertices.h +++ b/source/common/rendering/hwrenderer/data/flatvertices.h @@ -42,6 +42,7 @@ struct FFlatVertex class FFlatVertexBuffer { +public: TArray vbo_shadowdata; TArray ibo_data; @@ -53,7 +54,7 @@ class FFlatVertexBuffer unsigned int mNumReserved; - static const unsigned int BUFFER_SIZE = 4000000; // Was upped from 2000000 to account for voxels not being implemented with a separate vertex buffer. + static const unsigned int BUFFER_SIZE = 2000000; static const unsigned int BUFFER_SIZE_TO_USE = BUFFER_SIZE-500; public: diff --git a/source/common/rendering/hwrenderer/data/hw_aabbtree.cpp b/source/common/rendering/hwrenderer/data/hw_aabbtree.cpp new file mode 100644 index 000000000..0a8c81900 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_aabbtree.cpp @@ -0,0 +1,166 @@ +// +//--------------------------------------------------------------------------- +// AABB-tree used for ray testing +// 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 +#include "hw_aabbtree.h" + +namespace hwrenderer +{ + +TArray LevelAABBTree::FindNodePath(unsigned int line, unsigned int node) +{ + const AABBTreeNode &n = nodes[node]; + + if (n.aabb_left > treelines[line].x || n.aabb_right < treelines[line].x || + n.aabb_top > treelines[line].y || n.aabb_bottom < treelines[line].y) + { + return {}; + } + + TArray path; + if (n.line_index == -1) + { + path = FindNodePath(line, n.left_node); + if (path.Size() == 0) + path = FindNodePath(line, n.right_node); + + if (path.Size()) + path.Push(node); + } + else if (n.line_index == (int)line) + { + path.Push(node); + } + return path; +} + +double LevelAABBTree::RayTest(const DVector3 &ray_start, const DVector3 &ray_end) +{ + // Precalculate some of the variables used by the ray/line intersection test + DVector2 raydelta = ray_end - ray_start; + double raydist2 = raydelta | raydelta; + DVector2 raynormal = DVector2(raydelta.Y, -raydelta.X); + double rayd = raynormal | ray_start; + if (raydist2 < 1.0) + return 1.0f; + + double hit_fraction = 1.0; + + // Walk the tree nodes + int stack[32]; + int stack_pos = 1; + stack[0] = nodes.Size() - 1; // root node is the last node in the list + while (stack_pos > 0) + { + int node_index = stack[stack_pos - 1]; + + if (!OverlapRayAABB(ray_start, ray_end, nodes[node_index])) + { + // If the ray doesn't overlap this node's AABB we're done for this subtree + stack_pos--; + } + else if (nodes[node_index].line_index != -1) // isLeaf(node_index) + { + // We reached a leaf node. Do a ray/line intersection test to see if we hit the line. + hit_fraction = std::min(IntersectRayLine(ray_start, ray_end, nodes[node_index].line_index, raydelta, rayd, raydist2), hit_fraction); + stack_pos--; + } + else if (stack_pos == 32) + { + stack_pos--; // stack overflow - tree is too deep! + } + else + { + // The ray overlaps the node's AABB. Examine its child nodes. + stack[stack_pos - 1] = nodes[node_index].left_node; + stack[stack_pos] = nodes[node_index].right_node; + stack_pos++; + } + } + + return hit_fraction; +} + +bool LevelAABBTree::OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const AABBTreeNode &node) +{ + // To do: simplify test to use a 2D test + DVector3 ray_start = DVector3(ray_start2d, 0.0); + DVector3 ray_end = DVector3(ray_end2d, 0.0); + DVector3 aabb_min = DVector3(node.aabb_left, node.aabb_top, -1.0); + DVector3 aabb_max = DVector3(node.aabb_right, node.aabb_bottom, 1.0); + + // Standard 3D ray/AABB overlapping test. + // The details for the math here can be found in Real-Time Rendering, 3rd Edition. + // We could use a 2D test here instead, which would probably simplify the math. + + DVector3 c = (ray_start + ray_end) * 0.5f; + DVector3 w = ray_end - c; + DVector3 h = (aabb_max - aabb_min) * 0.5f; // aabb.extents(); + + c -= (aabb_max + aabb_min) * 0.5f; // aabb.center(); + + DVector3 v = DVector3(fabs(w.X), fabs(w.Y), fabs(w.Z)); + + if (fabs(c.X) > v.X + h.X || fabs(c.Y) > v.Y + h.Y || fabs(c.Z) > v.Z + h.Z) + return false; // disjoint; + + if (fabs(c.Y * w.Z - c.Z * w.Y) > h.Y * v.Z + h.Z * v.Y || + fabs(c.X * w.Z - c.Z * w.X) > h.X * v.Z + h.Z * v.X || + fabs(c.X * w.Y - c.Y * w.X) > h.X * v.Y + h.Y * v.X) + return false; // disjoint; + + return true; // overlap; +} + +double LevelAABBTree::IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2) +{ + // Check if two line segments intersects (the ray and the line). + // The math below does this by first finding the fractional hit for an infinitely long ray line. + // If that hit is within the line segment (0 to 1 range) then it calculates the fractional hit for where the ray would hit. + // + // This algorithm is homemade - I would not be surprised if there's a much faster method out there. + + const double epsilon = 0.0000001; + const AABBTreeLine &line = treelines[line_index]; + + DVector2 raynormal = DVector2(raydelta.Y, -raydelta.X); + + DVector2 line_pos(line.x, line.y); + DVector2 line_delta(line.dx, line.dy); + + double den = raynormal | line_delta; + if (fabs(den) > epsilon) + { + double t_line = (rayd - (raynormal | line_pos)) / den; + if (t_line >= 0.0 && t_line <= 1.0) + { + DVector2 linehitdelta = line_pos + line_delta * t_line - ray_start; + double t = (raydelta | linehitdelta) / raydist2; + return t > 0.0 ? t : 1.0; + } + } + + return 1.0; +} + + +} diff --git a/source/common/rendering/hwrenderer/data/hw_aabbtree.h b/source/common/rendering/hwrenderer/data/hw_aabbtree.h new file mode 100644 index 000000000..19f3e7531 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_aabbtree.h @@ -0,0 +1,82 @@ + +#pragma once + +#include "tarray.h" +#include "vectors.h" + +struct FLevelLocals; + +namespace hwrenderer +{ + +// Node in a binary AABB tree +struct AABBTreeNode +{ + AABBTreeNode(const FVector2 &aabb_min, const FVector2 &aabb_max, int line_index) : aabb_left(aabb_min.X), aabb_top(aabb_min.Y), aabb_right(aabb_max.X), aabb_bottom(aabb_max.Y), left_node(-1), right_node(-1), line_index(line_index) { } + AABBTreeNode(const FVector2 &aabb_min, const FVector2 &aabb_max, int left, int right) : aabb_left(aabb_min.X), aabb_top(aabb_min.Y), aabb_right(aabb_max.X), aabb_bottom(aabb_max.Y), left_node(left), right_node(right), line_index(-1) { } + + // Axis aligned bounding box for the node + float aabb_left, aabb_top; + float aabb_right, aabb_bottom; + + // Child node indices + int left_node; + int right_node; + + // AABBTreeLine index if it is a leaf node. Index is -1 if it is not. + int line_index; + + // Padding to keep 16-byte length (this structure is uploaded to the GPU) + int padding; +}; + +// Line segment for leaf nodes in an AABB tree +struct AABBTreeLine +{ + float x, y; + float dx, dy; +}; + +class LevelAABBTree +{ +protected: + // Nodes in the AABB tree. Last node is the root node. + TArray nodes; + + // Line segments for the leaf nodes in the tree. + TArray treelines; + + int dynamicStartNode = 0; + int dynamicStartLine = 0; + +public: + // Shoot a ray from ray_start to ray_end and return the closest hit as a fractional value between 0 and 1. Returns 1 if no line was hit. + double RayTest(const DVector3 &ray_start, const DVector3 &ray_end); + + const void *Nodes() const { return nodes.Data(); } + const void *Lines() const { return treelines.Data(); } + size_t NodesSize() const { return nodes.Size() * sizeof(AABBTreeNode); } + size_t LinesSize() const { return treelines.Size() * sizeof(AABBTreeLine); } + unsigned int NodesCount() const { return nodes.Size(); } + + const void *DynamicNodes() const { return nodes.Data() + dynamicStartNode; } + const void *DynamicLines() const { return treelines.Data() + dynamicStartLine; } + size_t DynamicNodesSize() const { return (nodes.Size() - dynamicStartNode) * sizeof(AABBTreeNode); } + size_t DynamicLinesSize() const { return (treelines.Size() - dynamicStartLine) * sizeof(AABBTreeLine); } + size_t DynamicNodesOffset() const { return dynamicStartNode * sizeof(AABBTreeNode); } + size_t DynamicLinesOffset() const { return dynamicStartLine * sizeof(AABBTreeLine); } + + virtual bool Update() = 0; +protected: + + TArray FindNodePath(unsigned int line, unsigned int node); + // Test if a ray overlaps an AABB node or not + bool OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const AABBTreeNode &node); + + // Intersection test between a ray and a line segment + double IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2); + + +}; + +} // namespace diff --git a/source/common/rendering/hwrenderer/data/hw_cvars.cpp b/source/common/rendering/hwrenderer/data/hw_cvars.cpp new file mode 100644 index 000000000..c8a7abc28 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_cvars.cpp @@ -0,0 +1,139 @@ +/* +** hw_cvars.cpp +** +** most of the hardware renderer's CVARs. +** +**--------------------------------------------------------------------------- +** Copyright 2005-2020 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + + + +#include "c_cvars.h" +#include "c_dispatch.h" +#include "v_video.h" +#include "hw_cvars.h" +#include "menu/menu.h" + + +CUSTOM_CVAR(Int, gl_fogmode, 1, CVAR_ARCHIVE | CVAR_NOINITCALL) +{ + if (self > 2) self = 2; + if (self < 0) self = 0; +} + + +// OpenGL stuff moved here +// GL related CVARs +CVAR(Bool, gl_portals, true, 0) +CVAR(Bool,gl_mirrors,true,0) // This is for debugging only! +CVAR(Bool,gl_mirror_envmap, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) +CVAR(Bool, gl_seamless, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +CUSTOM_CVAR(Int, r_mirror_recursions,4,CVAR_GLOBALCONFIG|CVAR_ARCHIVE) +{ + if (self<0) self=0; + if (self>10) self=10; +} +bool gl_plane_reflection_i; // This is needed in a header that cannot include the CVAR stuff... +CUSTOM_CVAR(Bool, gl_plane_reflection, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) +{ + gl_plane_reflection_i = self; +} + +CUSTOM_CVAR(Bool, gl_render_precise, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + gl_seamless=self; +} + +CUSTOM_CVARD(Float, vid_gamma, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts gamma component of gamma ramp") +{ + if (self < 0) self = 1; + else if (self > 4) self = 4; +} + +CUSTOM_CVARD(Float, vid_contrast, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts contrast component of gamma ramp") +{ + if (self < 0) self = 0; + else if (self > 5) self = 5; +} + +CUSTOM_CVARD(Float, vid_brightness, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts brightness component of gamma ramp") +{ + if (self < -2) self = -2; + else if (self > 2) self = 2; +} + +CUSTOM_CVARD(Float, vid_saturation, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts saturation component of gamma ramp") +{ + if (self < -3) self = -3; + else if (self > 3) self = 3; +} + +CCMD (bumpgamma) +{ + // [RH] Gamma correction tables are now generated on the fly for *any* gamma level + // Q: What are reasonable limits to use here? + + float newgamma = vid_gamma + 0.1f; + + if (newgamma > 4.0) + newgamma = 1.0; + + vid_gamma = newgamma; + Printf ("Gamma correction level %g\n", newgamma); +} + + +CVAR(Int, gl_satformula, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); + +//========================================================================== +// +// Texture CVARs +// +//========================================================================== +CUSTOM_CVARD(Float, gl_texture_filter_anisotropic, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL, "changes the OpenGL texture anisotropy setting") +{ + screen->SetTextureFilterMode(); +} + +CUSTOM_CVARD(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL, "changes the texture filtering settings") +{ + if (self < 0 || self > 6) self=4; + screen->SetTextureFilterMode(); +} + +CVAR(Bool, gl_precache, false, CVAR_ARCHIVE) + + +CUSTOM_CVAR(Int, gl_shadowmap_filter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0 || self > 8) self = 1; +} diff --git a/source/core/rendering/hwrenderer/utility/hw_cvars.h b/source/common/rendering/hwrenderer/data/hw_cvars.h similarity index 75% rename from source/core/rendering/hwrenderer/utility/hw_cvars.h rename to source/common/rendering/hwrenderer/data/hw_cvars.h index 724dbf147..0b9d8f09b 100644 --- a/source/core/rendering/hwrenderer/utility/hw_cvars.h +++ b/source/common/rendering/hwrenderer/data/hw_cvars.h @@ -5,11 +5,9 @@ EXTERN_CVAR(Bool,gl_enhanced_nightvision) EXTERN_CVAR(Int, screenblocks); -EXTERN_CVAR(Bool, gl_texture) EXTERN_CVAR(Int, gl_texture_filter) EXTERN_CVAR(Float, gl_texture_filter_anisotropic) EXTERN_CVAR(Int, gl_texture_format) -EXTERN_CVAR(Bool, gl_texture_usehires) EXTERN_CVAR(Bool, gl_usefb) EXTERN_CVAR(Int, gl_weaponlight) @@ -25,6 +23,7 @@ EXTERN_CVAR(Bool,gl_mirror_envmap) EXTERN_CVAR(Bool,gl_mirrors) EXTERN_CVAR(Bool,gl_mirror_envmap) +EXTERN_CVAR(Bool, gl_seamless) EXTERN_CVAR(Float, gl_mask_threshold) EXTERN_CVAR(Float, gl_mask_sprite_threshold) @@ -51,19 +50,6 @@ EXTERN_CVAR(Float, gl_ssao_blur_amount) EXTERN_CVAR(Int, gl_debug_level) EXTERN_CVAR(Bool, gl_debug_breakpoint) - -EXTERN_CVAR(Bool, gl_usecolorblending) -EXTERN_CVAR(Bool, gl_sprite_blend) -EXTERN_CVAR(Int, gl_spriteclip) -EXTERN_CVAR(Float, gl_sclipthreshold) -EXTERN_CVAR(Float, gl_sclipfactor) -EXTERN_CVAR(Int, gl_particles_style) -EXTERN_CVAR(Int, gl_billboard_mode) -EXTERN_CVAR(Bool, gl_billboard_faces_camera) -EXTERN_CVAR(Bool, gl_billboard_particles) -EXTERN_CVAR(Int, gl_enhanced_nv_stealth) -EXTERN_CVAR(Int, gl_fuzztype) - EXTERN_CVAR(Int, gl_shadowmap_filter) EXTERN_CVAR(Bool, gl_brightfog) diff --git a/source/common/rendering/hwrenderer/data/hw_dynlightdata.h b/source/common/rendering/hwrenderer/data/hw_dynlightdata.h new file mode 100644 index 000000000..e24e49bed --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_dynlightdata.h @@ -0,0 +1,59 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2005-2016 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/ +// +//-------------------------------------------------------------------------- +// + +#ifndef __GLC_DYNLIGHT_H +#define __GLC_DYNLIGHT_H + +struct FDynLightData +{ + TArray arrays[3]; + + void Clear() + { + arrays[0].Clear(); + arrays[1].Clear(); + arrays[2].Clear(); + } + + void Combine(int *siz, int max) + { + siz[0] = arrays[0].Size(); + siz[1] = siz[0] + arrays[1].Size(); + siz[2] = siz[1] + arrays[2].Size(); + arrays[0].Resize(arrays[0].Size() + arrays[1].Size() + arrays[2].Size()); + memcpy(&arrays[0][siz[0]], &arrays[1][0], arrays[1].Size() * sizeof(float)); + memcpy(&arrays[0][siz[1]], &arrays[2][0], arrays[2].Size() * sizeof(float)); + siz[0]>>=2; + siz[1]>>=2; + siz[2]>>=2; + if (siz[0] > max) siz[0] = max; + if (siz[1] > max) siz[1] = max; + if (siz[2] > max) siz[2] = max; + } + + +}; + +extern thread_local FDynLightData lightdata; + + +#endif diff --git a/source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp b/source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp new file mode 100644 index 000000000..2783ab669 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp @@ -0,0 +1,139 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2014-2016 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/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_lightbuffer.cpp +** Buffer data maintenance for dynamic lights +** +**/ + +#include "hw_lightbuffer.h" +#include "hw_dynlightdata.h" +#include "shaderuniforms.h" + +static const int ELEMENTS_PER_LIGHT = 4; // each light needs 4 vec4's. +static const int ELEMENT_SIZE = (4*sizeof(float)); + + +FLightBuffer::FLightBuffer() +{ + int maxNumberOfLights = 80000; + + mBufferSize = maxNumberOfLights * ELEMENTS_PER_LIGHT; + mByteSize = mBufferSize * ELEMENT_SIZE; + + // Hack alert: On Intel's GL driver SSBO's perform quite worse than UBOs. + // We only want to disable using SSBOs for lights but not disable the feature entirely. + // Note that using an uniform buffer here will limit the number of lights per surface so it isn't done for NVidia and AMD. + if (screen->IsVulkan() || screen->IsPoly() || ((screen->hwcaps & RFL_SHADER_STORAGE_BUFFER) && !strstr(screen->vendorstring, "Intel"))) + { + mBufferType = true; + mBlockAlign = 0; + mBlockSize = mBufferSize; + mMaxUploadSize = mBlockSize; + } + else + { + mBufferType = false; + mBlockSize = screen->maxuniformblock / ELEMENT_SIZE; + mBlockAlign = screen->uniformblockalignment / ELEMENT_SIZE; + mMaxUploadSize = (mBlockSize - mBlockAlign); + mByteSize += screen->maxuniformblock; // to avoid mapping beyond the end of the buffer. + } + + mBuffer = screen->CreateDataBuffer(LIGHTBUF_BINDINGPOINT, mBufferType, false); + mBuffer->SetData(mByteSize, nullptr, false); + + Clear(); +} + +FLightBuffer::~FLightBuffer() +{ + delete mBuffer; +} + +void FLightBuffer::Clear() +{ + mIndex = 0; +} + +int FLightBuffer::UploadLights(FDynLightData &data) +{ + // All meaasurements here are in vec4's. + int size0 = data.arrays[0].Size()/4; + int size1 = data.arrays[1].Size()/4; + int size2 = data.arrays[2].Size()/4; + int totalsize = size0 + size1 + size2 + 1; + + if (totalsize > (int)mMaxUploadSize) + { + int diff = totalsize - (int)mMaxUploadSize; + + size2 -= diff; + if (size2 < 0) + { + size1 += size2; + size2 = 0; + } + if (size1 < 0) + { + size0 += size1; + size1 = 0; + } + totalsize = size0 + size1 + size2 + 1; + } + + float *mBufferPointer = (float*)mBuffer->Memory(); + assert(mBufferPointer != nullptr); + if (mBufferPointer == nullptr) return -1; + if (totalsize <= 1) return -1; // there are no lights + + unsigned thisindex = mIndex.fetch_add(totalsize); + float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) }; + + if (thisindex + totalsize <= mBufferSize) + { + float *copyptr = mBufferPointer + thisindex*4; + + memcpy(©ptr[0], parmcnt, ELEMENT_SIZE); + memcpy(©ptr[4], &data.arrays[0][0], size0 * ELEMENT_SIZE); + memcpy(©ptr[4 + 4*size0], &data.arrays[1][0], size1 * ELEMENT_SIZE); + memcpy(©ptr[4 + 4*(size0 + size1)], &data.arrays[2][0], size2 * ELEMENT_SIZE); + return thisindex; + } + else + { + return -1; // Buffer is full. Since it is being used live at the point of the upload we cannot do much here but to abort. + } +} + +int FLightBuffer::GetBinding(unsigned int index, size_t* pOffset, size_t* pSize) +{ + // this function will only get called if a uniform buffer is used. For a shader storage buffer we only need to bind the buffer once at the start. + unsigned int offset = (index / mBlockAlign) * mBlockAlign; + + *pOffset = offset * ELEMENT_SIZE; + *pSize = mBlockSize * ELEMENT_SIZE; + return (index - offset); +} + + + diff --git a/source/common/rendering/hwrenderer/data/hw_lightbuffer.h b/source/common/rendering/hwrenderer/data/hw_lightbuffer.h new file mode 100644 index 000000000..2e5849346 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_lightbuffer.h @@ -0,0 +1,48 @@ +#ifndef __GL_LIGHTBUFFER_H +#define __GL_LIGHTBUFFER_H + +#include "tarray.h" +#include "hw_dynlightdata.h" +#include "hwrenderer/data/buffers.h" +#include +#include + +class FRenderState; + +class FLightBuffer +{ + IDataBuffer *mBuffer; + + bool mBufferType; + std::atomic mIndex; + unsigned int mBlockAlign; + unsigned int mBlockSize; + unsigned int mBufferSize; + unsigned int mByteSize; + unsigned int mMaxUploadSize; + + void CheckSize(); + +public: + + FLightBuffer(); + ~FLightBuffer(); + void Clear(); + int UploadLights(FDynLightData &data); + void Map() { mBuffer->Map(); } + void Unmap() { mBuffer->Unmap(); } + unsigned int GetBlockSize() const { return mBlockSize; } + bool GetBufferType() const { return mBufferType; } + int GetBinding(unsigned int index, size_t* pOffset, size_t* pSize); + + // OpenGL needs the buffer to mess around with the binding. + IDataBuffer* GetBuffer() const + { + return mBuffer; + } + +}; + + +#endif + diff --git a/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp b/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp new file mode 100644 index 000000000..e4d5ba030 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp @@ -0,0 +1,112 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2005-2020 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/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_models.cpp +** +** hardware renderer model handling code +** +**/ + + +#include "v_video.h" +#include "cmdlib.h" +#include "hw_modelvertexbuffer.h" + +//=========================================================================== +// +// +// +//=========================================================================== + +FModelVertexBuffer::FModelVertexBuffer(bool needindex, bool singleframe) +{ + mVertexBuffer = screen->CreateVertexBuffer(); + mIndexBuffer = needindex ? screen->CreateIndexBuffer() : nullptr; + + static const FVertexBufferAttribute format[] = { + { 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(FModelVertex, x) }, + { 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FModelVertex, u) }, + { 0, VATTR_NORMAL, VFmt_Packed_A2R10G10B10, (int)myoffsetof(FModelVertex, packedNormal) }, + { 1, VATTR_VERTEX2, VFmt_Float3, (int)myoffsetof(FModelVertex, x) }, + { 1, VATTR_NORMAL2, VFmt_Packed_A2R10G10B10, (int)myoffsetof(FModelVertex, packedNormal) } + }; + mVertexBuffer->SetFormat(2, 5, sizeof(FModelVertex), format); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +FModelVertexBuffer::~FModelVertexBuffer() +{ + if (mIndexBuffer) delete mIndexBuffer; + delete mVertexBuffer; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +FModelVertex *FModelVertexBuffer::LockVertexBuffer(unsigned int size) +{ + return static_cast(mVertexBuffer->Lock(size * sizeof(FModelVertex))); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FModelVertexBuffer::UnlockVertexBuffer() +{ + mVertexBuffer->Unlock(); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +unsigned int *FModelVertexBuffer::LockIndexBuffer(unsigned int size) +{ + if (mIndexBuffer) return static_cast(mIndexBuffer->Lock(size * sizeof(unsigned int))); + else return nullptr; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FModelVertexBuffer::UnlockIndexBuffer() +{ + if (mIndexBuffer) mIndexBuffer->Unlock(); +} + + diff --git a/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.h b/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.h new file mode 100644 index 000000000..4907637d4 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "hwrenderer/data/buffers.h" +#include "i_modelvertexbuffer.h" + +class FModelRenderer; + +class FModelVertexBuffer : public IModelVertexBuffer +{ + IVertexBuffer *mVertexBuffer; + IIndexBuffer *mIndexBuffer; + +public: + + FModelVertexBuffer(bool needindex, bool singleframe); + ~FModelVertexBuffer(); + + FModelVertex *LockVertexBuffer(unsigned int size) override; + void UnlockVertexBuffer() override; + + unsigned int *LockIndexBuffer(unsigned int size) override; + void UnlockIndexBuffer() override; + + IVertexBuffer* vertexBuffer() const { return mVertexBuffer; } + IIndexBuffer* indexBuffer() const { return mIndexBuffer; } +}; diff --git a/source/common/rendering/hwrenderer/data/hw_renderstate.h b/source/common/rendering/hwrenderer/data/hw_renderstate.h index 24692656a..5da24d209 100644 --- a/source/common/rendering/hwrenderer/data/hw_renderstate.h +++ b/source/common/rendering/hwrenderer/data/hw_renderstate.h @@ -1,6 +1,5 @@ #pragma once -#include "v_palette.h" #include "vectors.h" #include "matrix.h" #include "hw_material.h" diff --git a/source/core/rendering/hwrenderer/utility/hw_shaderpatcher.cpp b/source/common/rendering/hwrenderer/data/hw_shaderpatcher.cpp similarity index 75% rename from source/core/rendering/hwrenderer/utility/hw_shaderpatcher.cpp rename to source/common/rendering/hwrenderer/data/hw_shaderpatcher.cpp index 5720e3098..0362306d3 100644 --- a/source/core/rendering/hwrenderer/utility/hw_shaderpatcher.cpp +++ b/source/common/rendering/hwrenderer/data/hw_shaderpatcher.cpp @@ -272,31 +272,28 @@ FString RemoveLayoutLocationDecl(FString code, const char *inoutkeyword) // Note: the MaterialShaderIndex enum in gl_shader.h needs to be updated whenever this array is modified. const FDefaultShader defaultshaders[] = { - {"Default", "engine/shaders/pp/func_normal.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Warp 1", "engine/shaders/pp/func_warp1.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Warp 2", "engine/shaders/pp/func_warp2.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Brightmap","engine/shaders/pp/func_brightmap.fp", "engine/shaders/pp/material_normal.fp", "#define BRIGHTMAP\n"}, - {"Specular", "engine/shaders/pp/func_spec.fp", "engine/shaders/pp/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n"}, - {"SpecularBrightmap", "engine/shaders/pp/func_spec.fp", "engine/shaders/pp/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n#define BRIGHTMAP\n"}, - {"PBR","engine/shaders/pp/func_pbr.fp", "engine/shaders/pp/material_pbr.fp", "#define PBR\n#define NORMALMAP\n"}, - {"PBRBrightmap","engine/shaders/pp/func_pbr.fp", "engine/shaders/pp/material_pbr.fp", "#define PBR\n#define NORMALMAP\n#define BRIGHTMAP\n"}, - {"Paletted", "engine/shaders/pp/func_paletted.fp", "engine/shaders/pp/material_nolight.fp", ""}, - {"No Texture", "engine/shaders/pp/func_notexture.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Basic Fuzz", "engine/shaders/pp/fuzz_standard.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Smooth Fuzz", "engine/shaders/pp/fuzz_smooth.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Swirly Fuzz", "engine/shaders/pp/fuzz_swirly.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Translucent Fuzz", "engine/shaders/pp/fuzz_smoothtranslucent.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Jagged Fuzz", "engine/shaders/pp/fuzz_jagged.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Noise Fuzz", "engine/shaders/pp/fuzz_noise.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Smooth Noise Fuzz", "engine/shaders/pp/fuzz_smoothnoise.fp", "engine/shaders/pp/material_normal.fp", ""}, - {"Software Fuzz", "engine/shaders/pp/fuzz_software.fp", "engine/shaders/pp/material_normal.fp", ""}, + {"Default", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", ""}, + {"Warp 1", "shaders/glsl/func_warp1.fp", "shaders/glsl/material_normal.fp", ""}, + {"Warp 2", "shaders/glsl/func_warp2.fp", "shaders/glsl/material_normal.fp", ""}, + {"Specular", "shaders/glsl/func_spec.fp", "shaders/glsl/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n"}, + {"PBR","shaders/glsl/func_pbr.fp", "shaders/glsl/material_pbr.fp", "#define PBR\n#define NORMALMAP\n"}, + {"Paletted", "shaders/glsl/func_paletted.fp", "shaders/glsl/material_nolight.fp", ""}, + {"No Texture", "shaders/glsl/func_notexture.fp", "shaders/glsl/material_normal.fp", "#define NO_LAYERS\n"}, + {"Basic Fuzz", "shaders/glsl/fuzz_standard.fp", "shaders/glsl/material_normal.fp", ""}, + {"Smooth Fuzz", "shaders/glsl/fuzz_smooth.fp", "shaders/glsl/material_normal.fp", ""}, + {"Swirly Fuzz", "shaders/glsl/fuzz_swirly.fp", "shaders/glsl/material_normal.fp", ""}, + {"Translucent Fuzz", "shaders/glsl/fuzz_smoothtranslucent.fp", "shaders/glsl/material_normal.fp", ""}, + {"Jagged Fuzz", "shaders/glsl/fuzz_jagged.fp", "shaders/glsl/material_normal.fp", ""}, + {"Noise Fuzz", "shaders/glsl/fuzz_noise.fp", "shaders/glsl/material_normal.fp", ""}, + {"Smooth Noise Fuzz", "shaders/glsl/fuzz_smoothnoise.fp", "shaders/glsl/material_normal.fp", ""}, + {"Software Fuzz", "shaders/glsl/fuzz_software.fp", "shaders/glsl/material_normal.fp", ""}, {nullptr,nullptr,nullptr,nullptr} }; const FEffectShader effectshaders[] = { - { "fogboundary", "engine/shaders/pp/main.vp", "engine/shaders/pp/fogboundary.fp", nullptr, nullptr, "#define NO_ALPHATEST\n" }, - { "spheremap", "engine/shaders/pp/main.vp", "engine/shaders/pp/main.fp", "engine/shaders/pp/func_normal.fp", "engine/shaders/pp/material_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" }, - { "burn", "engine/shaders/pp/main.vp", "engine/shaders/pp/burn.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" }, - { "stencil", "engine/shaders/pp/main.vp", "engine/shaders/pp/stencil.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" }, + { "fogboundary", "shaders/glsl/main.vp", "shaders/glsl/fogboundary.fp", nullptr, nullptr, "#define NO_ALPHATEST\n" }, + { "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" }, + { "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" }, + { "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" }, }; diff --git a/source/core/rendering/hwrenderer/utility/hw_shaderpatcher.h b/source/common/rendering/hwrenderer/data/hw_shaderpatcher.h similarity index 100% rename from source/core/rendering/hwrenderer/utility/hw_shaderpatcher.h rename to source/common/rendering/hwrenderer/data/hw_shaderpatcher.h diff --git a/source/common/rendering/hwrenderer/data/hw_shadowmap.cpp b/source/common/rendering/hwrenderer/data/hw_shadowmap.cpp new file mode 100644 index 000000000..cb5630594 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_shadowmap.cpp @@ -0,0 +1,155 @@ +// +//--------------------------------------------------------------------------- +// 1D dynamic shadow maps (API independent part) +// 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 "hw_shadowmap.h" +#include "hw_cvars.h" +#include "hw_dynlightdata.h" +#include "buffers.h" +#include "shaderuniforms.h" +#include "hwrenderer/postprocessing/hw_postprocess.h" + +/* + The 1D shadow maps are stored in a 1024x1024 texture as float depth values (R32F). + + Each line in the texture is assigned to a single light. For example, to grab depth values for light 20 + the fragment shader (main.fp) needs to sample from row 20. That is, the V texture coordinate needs + to be 20.5/1024. + + The texel row for each light is split into four parts. One for each direction, like a cube texture, + but then only in 2D where this reduces itself to a square. When main.fp samples from the shadow map + it first decides in which direction the fragment is (relative to the light), like cubemap sampling does + for 3D, but once again just for the 2D case. + + Texels 0-255 is Y positive, 256-511 is X positive, 512-767 is Y negative and 768-1023 is X negative. + + Generating the shadow map itself is done by FShadowMap::Update(). The shadow map texture's FBO is + bound and then a screen quad is drawn to make a fragment shader cover all texels. For each fragment + it shoots a ray and collects the distance to what it hit. + + The shadowmap.fp shader knows which light and texel it is processing by mapping gl_FragCoord.y back + to the light index, and it knows which direction to ray trace by looking at gl_FragCoord.x. For + example, if gl_FragCoord.y is 20.5, then it knows its processing light 20, and if gl_FragCoord.x is + 127.5, then it knows we are shooting straight ahead for the Y positive direction. + + Ray testing is done by uploading two GPU storage buffers - one holding AABB tree nodes, and one with + the line segments at the leaf nodes of the tree. The fragment shader then performs a test same way + as on the CPU, except everything uses indexes as pointers are not allowed in GLSL. +*/ + +cycle_t IShadowMap::UpdateCycles; +int IShadowMap::LightsProcessed; +int IShadowMap::LightsShadowmapped; + +CVAR(Bool, gl_light_shadowmap, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +ADD_STAT(shadowmap) +{ + FString out; + out.Format("upload=%04.2f ms lights=%d shadowmapped=%d", IShadowMap::UpdateCycles.TimeMS(), IShadowMap::LightsProcessed, IShadowMap::LightsShadowmapped); + return out; +} + +CUSTOM_CVAR(Int, gl_shadowmap_quality, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + switch (self) + { + case 128: + case 256: + case 512: + case 1024: + break; + default: + self = 128; + break; + } +} + +bool IShadowMap::ShadowTest(const DVector3 &lpos, const DVector3 &pos) +{ + if (mAABBTree && gl_light_shadowmap) + return mAABBTree->RayTest(lpos, pos) >= 1.0f; + else + return true; +} + +bool IShadowMap::PerformUpdate() +{ + UpdateCycles.Reset(); + + LightsProcessed = 0; + LightsShadowmapped = 0; + + if (gl_light_shadowmap && (screen->hwcaps & RFL_SHADER_STORAGE_BUFFER) && CollectLights != nullptr) + { + UpdateCycles.Clock(); + UploadAABBTree(); + UploadLights(); + return true; + } + return false; +} + +void IShadowMap::UploadLights() +{ + mLights.Resize(1024 * 4); + CollectLights(); + + if (mLightList == nullptr) + mLightList = screen->CreateDataBuffer(LIGHTLIST_BINDINGPOINT, true, false); + + mLightList->SetData(sizeof(float) * mLights.Size(), &mLights[0]); +} + + +void IShadowMap::UploadAABBTree() +{ + if (mNewTree) + { + mNewTree = false; + + if (!mNodesBuffer) + mNodesBuffer = screen->CreateDataBuffer(LIGHTNODES_BINDINGPOINT, true, false); + mNodesBuffer->SetData(mAABBTree->NodesSize(), mAABBTree->Nodes()); + + if (!mLinesBuffer) + mLinesBuffer = screen->CreateDataBuffer(LIGHTLINES_BINDINGPOINT, true, false); + mLinesBuffer->SetData(mAABBTree->LinesSize(), mAABBTree->Lines()); + } + else if (mAABBTree->Update()) + { + mNodesBuffer->SetSubData(mAABBTree->DynamicNodesOffset(), mAABBTree->DynamicNodesSize(), mAABBTree->DynamicNodes()); + mLinesBuffer->SetSubData(mAABBTree->DynamicLinesOffset(), mAABBTree->DynamicLinesSize(), mAABBTree->DynamicLines()); + } +} + +void IShadowMap::Reset() +{ + delete mLightList; mLightList = nullptr; + delete mNodesBuffer; mNodesBuffer = nullptr; + delete mLinesBuffer; mLinesBuffer = nullptr; +} + +IShadowMap::~IShadowMap() +{ + Reset(); +} + diff --git a/source/common/rendering/hwrenderer/data/hw_shadowmap.h b/source/common/rendering/hwrenderer/data/hw_shadowmap.h new file mode 100644 index 000000000..5a65e7620 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_shadowmap.h @@ -0,0 +1,83 @@ + +#pragma once + +#include "hw_aabbtree.h" +#include "stats.h" +#include + +class IDataBuffer; + +class IShadowMap +{ +public: + IShadowMap() { } + virtual ~IShadowMap(); + + void Reset(); + + // Test if a world position is in shadow relative to the specified light and returns false if it is + bool ShadowTest(const DVector3 &lpos, const DVector3 &pos); + + static cycle_t UpdateCycles; + static int LightsProcessed; + static int LightsShadowmapped; + + bool PerformUpdate(); + void FinishUpdate() + { + UpdateCycles.Clock(); + } + + unsigned int NodesCount() const + { + assert(mAABBTree); + return mAABBTree->NodesCount(); + } + + void SetAABBTree(hwrenderer::LevelAABBTree* tree) + { + mAABBTree = tree; + mNewTree = true; + } + + void SetCollectLights(std::function func) + { + CollectLights = std::move(func); + } + + void SetLight(int index, float x, float y, float z, float r) + { + index *= 4; + mLights[index] = x; + mLights[index + 1] = y; + mLights[index + 2] = z; + mLights[index + 3] = r; + } + +protected: + // Upload the AABB-tree to the GPU + void UploadAABBTree(); + void UploadLights(); + + // Working buffer for creating the list of lights. Stored here to avoid allocating memory each frame + TArray mLights; + + // AABB-tree of the level, used for ray tests, owned by the playsim, not the renderer. + hwrenderer::LevelAABBTree* mAABBTree = nullptr; + bool mNewTree = false; + + IShadowMap(const IShadowMap &) = delete; + IShadowMap &operator=(IShadowMap &) = delete; + + // OpenGL storage buffer with the list of lights in the shadow map texture + // These buffers need to be accessed by the OpenGL backend directly so that they can be bound. +public: + IDataBuffer *mLightList = nullptr; + + // OpenGL storage buffers for the AABB tree + IDataBuffer *mNodesBuffer = nullptr; + IDataBuffer *mLinesBuffer = nullptr; + + std::function CollectLights = nullptr; + +}; diff --git a/source/core/rendering/hwrenderer/data/hw_viewpointbuffer.cpp b/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.cpp similarity index 89% rename from source/core/rendering/hwrenderer/data/hw_viewpointbuffer.cpp rename to source/common/rendering/hwrenderer/data/hw_viewpointbuffer.cpp index 3846a4ccd..d11284228 100644 --- a/source/core/rendering/hwrenderer/data/hw_viewpointbuffer.cpp +++ b/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.cpp @@ -26,9 +26,10 @@ **/ #include "hwrenderer/data/shaderuniforms.h" -#include "hwrenderer/scene/hw_viewpointuniforms.h" -#include "hwrenderer/scene/hw_renderstate.h" +#include "hw_viewpointuniforms.h" +#include "hw_renderstate.h" #include "hw_viewpointbuffer.h" +#include "hw_cvars.h" static const int INITIAL_BUFFER_SIZE = 100; // 100 viewpoints per frame should nearly always be enough @@ -77,7 +78,15 @@ void HWViewpointBuffer::Set2D(FRenderState &di, int width, int height) if (width != m2DWidth || height != m2DHeight) { HWViewpointUniforms matrices; - matrices.SetDefaults(nullptr); + + matrices.mViewMatrix.loadIdentity(); + matrices.mNormalViewMatrix.loadIdentity(); + matrices.mViewHeight = 0; + matrices.mGlobVis = 1.f; + matrices.mPalLightLevels = 0; + matrices.mClipLine.X = -10000000.0f; + matrices.mShadowmapFilter = gl_shadowmap_filter; + matrices.mProjectionMatrix.ortho(0, (float)width, (float)height, 0, -1.0f, 1.0f); matrices.CalcDependencies(); mBuffer->Map(); diff --git a/source/core/rendering/hwrenderer/data/hw_viewpointbuffer.h b/source/common/rendering/hwrenderer/data/hw_viewpointbuffer.h similarity index 100% rename from source/core/rendering/hwrenderer/data/hw_viewpointbuffer.h rename to source/common/rendering/hwrenderer/data/hw_viewpointbuffer.h diff --git a/source/common/rendering/hwrenderer/data/hw_viewpointuniforms.h b/source/common/rendering/hwrenderer/data/hw_viewpointuniforms.h new file mode 100644 index 000000000..3d28f5c4a --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_viewpointuniforms.h @@ -0,0 +1,29 @@ +#pragma once + +#include "matrix.h" + +struct HWDrawInfo; + +struct HWViewpointUniforms +{ + VSMatrix mProjectionMatrix; + VSMatrix mViewMatrix; + VSMatrix mNormalViewMatrix; + FVector4 mCameraPos; + FVector4 mClipLine; + + float mGlobVis = 1.f; + int mPalLightLevels = 0; + int mViewHeight = 0; + float mClipHeight = 0.f; + float mClipHeightDirection = 0.f; + int mShadowmapFilter = 1; + + void CalcDependencies() + { + mNormalViewMatrix.computeNormalMatrix(mViewMatrix); + } +}; + + + diff --git a/source/common/rendering/hwrenderer/data/hw_vrmodes.cpp b/source/common/rendering/hwrenderer/data/hw_vrmodes.cpp new file mode 100644 index 000000000..a105f48c2 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_vrmodes.cpp @@ -0,0 +1,180 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2015 Christopher Bruns +// 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/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_stereo_leftright.cpp +** Offsets for left and right eye views +** +*/ + +#include "vectors.h" // RAD2DEG +#include "hw_cvars.h" +#include "hw_vrmodes.h" +#include "v_video.h" +#include "version.h" + +// Set up 3D-specific console variables: +CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG) + +// switch left and right eye views +CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG) + +// intraocular distance in meters +CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS + +// distance between viewer and the display screen +CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS + +// default conversion between (vertical) DOOM units and meters +CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS + + +#define isqrt2 0.7071067812f +static VRMode vrmi_mono = { 1, 1.f, 1.f, 1.f,{ { 0.f, 1.f },{ 0.f, 0.f } } }; +static VRMode vrmi_stereo = { 2, 1.f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } }; +static VRMode vrmi_sbsfull = { 2, .5f, 1.f, 2.f,{ { -.5f, .5f },{ .5f, .5f } } }; +static VRMode vrmi_sbssquished = { 2, .5f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } }; +static VRMode vrmi_lefteye = { 1, 1.f, 1.f, 1.f, { { -.5f, 1.f },{ 0.f, 0.f } } }; +static VRMode vrmi_righteye = { 1, 1.f, 1.f, 1.f,{ { .5f, 1.f },{ 0.f, 0.f } } }; +static VRMode vrmi_topbottom = { 2, 1.f, .5f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } }; +static VRMode vrmi_checker = { 2, isqrt2, isqrt2, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } }; + +const VRMode *VRMode::GetVRMode(bool toscreen) +{ +#ifdef VR3D_ENABLED + switch (toscreen && vid_rendermode == 4 ? vr_mode : 0) + { + default: + case VR_MONO: + return &vrmi_mono; + + case VR_GREENMAGENTA: + case VR_REDCYAN: + case VR_QUADSTEREO: + case VR_AMBERBLUE: + return &vrmi_stereo; + + case VR_SIDEBYSIDESQUISHED: + case VR_COLUMNINTERLEAVED: + return &vrmi_sbssquished; + + case VR_SIDEBYSIDEFULL: + return &vrmi_sbsfull; + + case VR_TOPBOTTOM: + case VR_ROWINTERLEAVED: + return &vrmi_topbottom; + + case VR_LEFTEYEVIEW: + return &vrmi_lefteye; + + case VR_RIGHTEYEVIEW: + return &vrmi_righteye; + + case VR_CHECKERINTERLEAVED: + return &vrmi_checker; + } +#else + return &vrmi_mono; +#endif +} + +void VRMode::AdjustViewport(DFrameBuffer *screen) const +{ + screen->mSceneViewport.height = (int)(screen->mSceneViewport.height * mVerticalViewportScale); + screen->mSceneViewport.top = (int)(screen->mSceneViewport.top * mVerticalViewportScale); + screen->mSceneViewport.width = (int)(screen->mSceneViewport.width * mHorizontalViewportScale); + screen->mSceneViewport.left = (int)(screen->mSceneViewport.left * mHorizontalViewportScale); + + screen->mScreenViewport.height = (int)(screen->mScreenViewport.height * mVerticalViewportScale); + screen->mScreenViewport.top = (int)(screen->mScreenViewport.top * mVerticalViewportScale); + screen->mScreenViewport.width = (int)(screen->mScreenViewport.width * mHorizontalViewportScale); + screen->mScreenViewport.left = (int)(screen->mScreenViewport.left * mHorizontalViewportScale); +} + +VSMatrix VRMode::GetHUDSpriteProjection() const +{ + VSMatrix mat; + int w = screen->GetWidth(); + int h = screen->GetHeight(); + float scaled_w = w / mWeaponProjectionScale; + float left_ofs = (w - scaled_w) / 2.f; + mat.ortho(left_ofs, left_ofs + scaled_w, (float)h, 0, -1.0f, 1.0f); + return mat; +} + +float VREyeInfo::getShift() const +{ + auto res = mShiftFactor * vr_ipd; + return vr_swap_eyes ? -res : res; +} + +VSMatrix VREyeInfo::GetProjection(float fov, float aspectRatio, float fovRatio) const +{ + VSMatrix result; + + if (mShiftFactor == 0) + { + float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio))); + result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar()); + return result; + } + else + { + double zNear = screen->GetZNear(); + double zFar = screen->GetZFar(); + + // For stereo 3D, use asymmetric frustum shift in projection matrix + // Q: shouldn't shift vary with roll angle, at least for desktop display? + // A: No. (lab) roll is not measured on desktop display (yet) + double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units + // double frustumShift = 0; // Turning off shift for debugging + double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio; + double fW = fH * aspectRatio * mScaleFactor; + double left = -fW - frustumShift; + double right = fW - frustumShift; + double bottom = -fH; + double top = fH; + + VSMatrix result(1); + result.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar); + return result; + } +} + + + +/* virtual */ +DVector3 VREyeInfo::GetViewShift(float yaw) const +{ + if (mShiftFactor == 0) + { + // pass-through for Mono view + return { 0,0,0 }; + } + else + { + double dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); + double dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift(); + return { dx, dy, 0 }; + } +} + diff --git a/source/common/rendering/hwrenderer/data/hw_vrmodes.h b/source/common/rendering/hwrenderer/data/hw_vrmodes.h new file mode 100644 index 000000000..a80ecaf57 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_vrmodes.h @@ -0,0 +1,47 @@ +#pragma once + +#include "matrix.h" + +class DFrameBuffer; + +enum +{ + VR_MONO = 0, + VR_GREENMAGENTA = 1, + VR_REDCYAN = 2, + VR_SIDEBYSIDEFULL = 3, + VR_SIDEBYSIDESQUISHED = 4, + VR_LEFTEYEVIEW = 5, + VR_RIGHTEYEVIEW = 6, + VR_QUADSTEREO = 7, + VR_AMBERBLUE = 9, + VR_TOPBOTTOM = 11, + VR_ROWINTERLEAVED = 12, + VR_COLUMNINTERLEAVED = 13, + VR_CHECKERINTERLEAVED = 14 +}; + +struct VREyeInfo +{ + float mShiftFactor; + float mScaleFactor; + + VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const; + DVector3 GetViewShift(float yaw) const; +private: + float getShift() const; + +}; + +struct VRMode +{ + int mEyeCount; + float mHorizontalViewportScale; + float mVerticalViewportScale; + float mWeaponProjectionScale; + VREyeInfo mEyes[2]; + + static const VRMode *GetVRMode(bool toscreen = true); + void AdjustViewport(DFrameBuffer *fb) const; + VSMatrix GetHUDSpriteProjection() const; +}; diff --git a/source/common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp b/source/common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp index db991145a..1dc670de0 100644 --- a/source/common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp +++ b/source/common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp @@ -21,7 +21,7 @@ #include "v_video.h" #include "hw_postprocess.h" -#include "hwrenderer/utility/hw_cvars.h" +#include "hw_cvars.h" #include "hwrenderer/postprocessing/hw_postprocess_cvars.h" #include "hwrenderer/postprocessing/hw_postprocessshader.h" #include @@ -395,8 +395,8 @@ void PPFXAA::CreateShaders() if (LastQuality == gl_fxaa) return; - FXAALuma = { "shaders/glsl/fxaa.fp", "#define FXAA_LUMA_PASS\n", {} }; - FXAA = { "shaders/glsl/fxaa.fp", GetDefines(), FXAAUniforms::Desc(), GetMaxVersion() }; + FXAALuma = { "shaders/pp/fxaa.fp", "#define FXAA_LUMA_PASS\n", {} }; + FXAA = { "shaders/pp/fxaa.fp", GetDefines(), FXAAUniforms::Desc(), GetMaxVersion() }; LastQuality = gl_fxaa; } @@ -681,14 +681,14 @@ void PPAmbientOcclusion::CreateShaders() #define NUM_STEPS %d.0 )", numDirections, numSteps); - LinearDepth = { "shaders/glsl/lineardepth.fp", "", LinearDepthUniforms::Desc() }; - LinearDepthMS = { "shaders/glsl/lineardepth.fp", "#define MULTISAMPLE\n", LinearDepthUniforms::Desc() }; - AmbientOcclude = { "shaders/glsl/ssao.fp", defines, SSAOUniforms::Desc() }; - AmbientOccludeMS = { "shaders/glsl/ssao.fp", defines + "\n#define MULTISAMPLE\n", SSAOUniforms::Desc() }; - BlurVertical = { "shaders/glsl/depthblur.fp", "#define BLUR_VERTICAL\n", DepthBlurUniforms::Desc() }; - BlurHorizontal = { "shaders/glsl/depthblur.fp", "#define BLUR_HORIZONTAL\n", DepthBlurUniforms::Desc() }; - Combine = { "shaders/glsl/ssaocombine.fp", "", AmbientCombineUniforms::Desc() }; - CombineMS = { "shaders/glsl/ssaocombine.fp", "#define MULTISAMPLE\n", AmbientCombineUniforms::Desc() }; + LinearDepth = { "shaders/pp/lineardepth.fp", "", LinearDepthUniforms::Desc() }; + LinearDepthMS = { "shaders/pp/lineardepth.fp", "#define MULTISAMPLE\n", LinearDepthUniforms::Desc() }; + AmbientOcclude = { "shaders/pp/ssao.fp", defines, SSAOUniforms::Desc() }; + AmbientOccludeMS = { "shaders/pp/ssao.fp", defines + "\n#define MULTISAMPLE\n", SSAOUniforms::Desc() }; + BlurVertical = { "shaders/pp/depthblur.fp", "#define BLUR_VERTICAL\n", DepthBlurUniforms::Desc() }; + BlurHorizontal = { "shaders/pp/depthblur.fp", "#define BLUR_HORIZONTAL\n", DepthBlurUniforms::Desc() }; + Combine = { "shaders/pp/ssaocombine.fp", "", AmbientCombineUniforms::Desc() }; + CombineMS = { "shaders/pp/ssaocombine.fp", "#define MULTISAMPLE\n", AmbientCombineUniforms::Desc() }; LastQuality = gl_ssao; } @@ -866,6 +866,27 @@ PPPresent::PPPresent() ///////////////////////////////////////////////////////////////////////////// + +void PPShadowMap::Update(PPRenderState* renderstate) +{ + ShadowMapUniforms uniforms; + uniforms.ShadowmapQuality = (float)gl_shadowmap_quality; + uniforms.NodesCount = screen->mShadowMap.NodesCount(); + + renderstate->PushGroup("shadowmap"); + + renderstate->Clear(); + renderstate->Shader = &ShadowMap; + renderstate->Uniforms.Set(uniforms); + renderstate->Viewport = { 0, 0, gl_shadowmap_quality, 1024 }; + renderstate->SetShadowMapBuffers(true); + renderstate->SetOutputShadowMap(); + renderstate->SetNoBlend(); + renderstate->Draw(); + + renderstate->PopGroup(); +} + ///////////////////////////////////////////////////////////////////////////// CVAR(Bool, gl_custompost, true, 0) diff --git a/source/common/rendering/hwrenderer/postprocessing/hw_postprocess.h b/source/common/rendering/hwrenderer/postprocessing/hw_postprocess.h index 8c74af83e..09c0d2553 100644 --- a/source/common/rendering/hwrenderer/postprocessing/hw_postprocess.h +++ b/source/common/rendering/hwrenderer/postprocessing/hw_postprocess.h @@ -3,6 +3,7 @@ #include "hwrenderer/data/shaderuniforms.h" #include #include +#include "intrect.h" struct PostProcessShader; @@ -303,7 +304,7 @@ public: void ResetBackend() override { Backend.reset(); } - FString VertexShader = "shaders/glsl/screenquad.vp"; + FString VertexShader = "shaders/pp/screenquad.vp"; FString FragmentShader; FString Defines; std::vector Uniforms; @@ -376,10 +377,10 @@ private: int lastWidth = 0; int lastHeight = 0; - PPShader BloomCombine = { "shaders/glsl/bloomcombine.fp", "", {} }; - PPShader BloomExtract = { "shaders/glsl/bloomextract.fp", "", ExtractUniforms::Desc() }; - PPShader BlurVertical = { "shaders/glsl/blur.fp", "#define BLUR_VERTICAL\n", BlurUniforms::Desc() }; - PPShader BlurHorizontal = { "shaders/glsl/blur.fp", "#define BLUR_HORIZONTAL\n", BlurUniforms::Desc() }; + PPShader BloomCombine = { "shaders/pp/bloomcombine.fp", "", {} }; + PPShader BloomExtract = { "shaders/pp/bloomextract.fp", "", ExtractUniforms::Desc() }; + PPShader BlurVertical = { "shaders/pp/blur.fp", "#define BLUR_VERTICAL\n", BlurUniforms::Desc() }; + PPShader BlurHorizontal = { "shaders/pp/blur.fp", "#define BLUR_HORIZONTAL\n", BlurUniforms::Desc() }; }; ///////////////////////////////////////////////////////////////////////////// @@ -412,7 +413,7 @@ public: void Render(PPRenderState *renderstate); private: - PPShader Lens = { "shaders/glsl/lensdistortion.fp", "", LensUniforms::Desc() }; + PPShader Lens = { "shaders/pp/lensdistortion.fp", "", LensUniforms::Desc() }; }; ///////////////////////////////////////////////////////////////////////////// @@ -504,9 +505,9 @@ private: std::vector ExposureLevels; bool FirstExposureFrame = true; - PPShader ExposureExtract = { "shaders/glsl/exposureextract.fp", "", ExposureExtractUniforms::Desc() }; - PPShader ExposureAverage = { "shaders/glsl/exposureaverage.fp", "", {}, 400 }; - PPShader ExposureCombine = { "shaders/glsl/exposurecombine.fp", "", ExposureCombineUniforms::Desc() }; + PPShader ExposureExtract = { "shaders/pp/exposureextract.fp", "", ExposureExtractUniforms::Desc() }; + PPShader ExposureAverage = { "shaders/pp/exposureaverage.fp", "", {}, 400 }; + PPShader ExposureCombine = { "shaders/pp/exposurecombine.fp", "", ExposureCombineUniforms::Desc() }; }; ///////////////////////////////////////////////////////////////////////////// @@ -532,7 +533,7 @@ public: void Render(PPRenderState *renderstate, int fixedcm); private: - PPShader Colormap = { "shaders/glsl/colormap.fp", "", ColormapUniforms::Desc() }; + PPShader Colormap = { "shaders/pp/colormap.fp", "", ColormapUniforms::Desc() }; }; ///////////////////////////////////////////////////////////////////////////// @@ -548,11 +549,11 @@ private: PPTexture PaletteTexture; - PPShader LinearShader = { "shaders/glsl/tonemap.fp", "#define LINEAR\n", {} }; - PPShader ReinhardShader = { "shaders/glsl/tonemap.fp", "#define REINHARD\n", {} }; - PPShader HejlDawsonShader = { "shaders/glsl/tonemap.fp", "#define HEJLDAWSON\n", {} }; - PPShader Uncharted2Shader = { "shaders/glsl/tonemap.fp", "#define UNCHARTED2\n", {} }; - PPShader PaletteShader = { "shaders/glsl/tonemap.fp", "#define PALETTE\n", {} }; + PPShader LinearShader = { "shaders/pp/tonemap.fp", "#define LINEAR\n", {} }; + PPShader ReinhardShader = { "shaders/pp/tonemap.fp", "#define REINHARD\n", {} }; + PPShader HejlDawsonShader = { "shaders/pp/tonemap.fp", "#define HEJLDAWSON\n", {} }; + PPShader Uncharted2Shader = { "shaders/pp/tonemap.fp", "#define UNCHARTED2\n", {} }; + PPShader PaletteShader = { "shaders/pp/tonemap.fp", "#define PALETTE\n", {} }; enum TonemapMode { @@ -753,10 +754,10 @@ public: PPTexture Dither; - PPShader Present = { "shaders/glsl/present.fp", "", PresentUniforms::Desc() }; - PPShader Checker3D = { "shaders/glsl/present_checker3d.fp", "", PresentUniforms::Desc() }; - PPShader Column3D = { "shaders/glsl/present_column3d.fp", "", PresentUniforms::Desc() }; - PPShader Row3D = { "shaders/glsl/present_row3d.fp", "", PresentUniforms::Desc() }; + PPShader Present = { "shaders/pp/present.fp", "", PresentUniforms::Desc() }; + PPShader Checker3D = { "shaders/pp/present_checker3d.fp", "", PresentUniforms::Desc() }; + PPShader Column3D = { "shaders/pp/present_column3d.fp", "", PresentUniforms::Desc() }; + PPShader Row3D = { "shaders/pp/present_row3d.fp", "", PresentUniforms::Desc() }; }; struct ShadowMapUniforms @@ -777,15 +778,6 @@ struct ShadowMapUniforms } }; -class PPShadowMap -{ -public: - void Update(PPRenderState *renderstate); - -private: - PPShader ShadowMap = { "shaders/glsl/shadowmap.fp", "", ShadowMapUniforms::Desc() }; -}; - class PPCustomShaderInstance { public: @@ -819,6 +811,18 @@ private: std::vector> mShaders; }; +class PPShadowMap +{ +public: + void Update(PPRenderState* renderstate); + +private: + PPShader ShadowMap = { "shaders/pp/shadowmap.fp", "", ShadowMapUniforms::Desc() }; +}; + + + + ///////////////////////////////////////////////////////////////////////////// class Postprocess diff --git a/source/common/rendering/i_modelvertexbuffer.h b/source/common/rendering/i_modelvertexbuffer.h new file mode 100644 index 000000000..febec3c71 --- /dev/null +++ b/source/common/rendering/i_modelvertexbuffer.h @@ -0,0 +1,43 @@ +#pragma once + +#include "templates.h" + +struct FModelVertex +{ + float x, y, z; // world position + float u, v; // texture coordinates + unsigned packedNormal; // normal vector as GL_INT_2_10_10_10_REV. + + void Set(float xx, float yy, float zz, float uu, float vv) + { + x = xx; + y = yy; + z = zz; + u = uu; + v = vv; + } + + void SetNormal(float nx, float ny, float nz) + { + int inx = clamp(int(nx * 512), -512, 511); + int iny = clamp(int(ny * 512), -512, 511); + int inz = clamp(int(nz * 512), -512, 511); + int inw = 0; + packedNormal = (inw << 30) | ((inz & 1023) << 20) | ((iny & 1023) << 10) | (inx & 1023); + } +}; + +#define VMO ((FModelVertex*)nullptr) + +class IModelVertexBuffer +{ +public: + virtual ~IModelVertexBuffer() { } + + virtual FModelVertex *LockVertexBuffer(unsigned int size) = 0; + virtual void UnlockVertexBuffer() = 0; + + virtual unsigned int *LockIndexBuffer(unsigned int size) = 0; + virtual void UnlockIndexBuffer() = 0; +}; + diff --git a/source/common/textures/texturemanager.cpp b/source/common/textures/texturemanager.cpp index c7cffd07c..80458a6ab 100644 --- a/source/common/textures/texturemanager.cpp +++ b/source/common/textures/texturemanager.cpp @@ -625,8 +625,8 @@ void FTextureManager::AddHiresTextures (int wadnum) auto gtex = MakeGameTexture(newtex, nullptr, ETextureType::Override); gtex->SetWorldPanning(true); gtex->SetDisplaySize(oldtex->GetDisplayWidth(), oldtex->GetDisplayHeight()); - gtex->SetOffsets(0, xs_RoundToInt(oldtex->GetDisplayLeftOffset(0) * gtex->GetScaleX()), xs_RoundToInt(oldtex->GetDisplayTopOffset(0) * gtex->GetScaleY())); - gtex->SetOffsets(1, xs_RoundToInt(oldtex->GetDisplayLeftOffset(1) * gtex->GetScaleX()), xs_RoundToInt(oldtex->GetDisplayTopOffset(1) * gtex->GetScaleY())); + gtex->SetOffsets(0, oldtex->GetTexelLeftOffset(0), oldtex->GetTexelTopOffset(0)); + gtex->SetOffsets(1, oldtex->GetTexelLeftOffset(1), oldtex->GetTexelTopOffset(1)); ReplaceTexture(tlist[i], gtex, true); } } diff --git a/source/core/gamecvars.cpp b/source/core/gamecvars.cpp index 2f38c7a1b..9559e0a0e 100644 --- a/source/core/gamecvars.cpp +++ b/source/core/gamecvars.cpp @@ -497,50 +497,6 @@ CUSTOM_CVAR(String, playername, "Player", CVAR_ARCHIVE | CVAR_USERINFO) //Net_SendClientInfo(); This is in the client code. Todo. } -CUSTOM_CVARD(Float, vid_gamma, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts gamma component of gamma ramp") -{ - if (self < 0) self = 0; - else if (self > 4) self = 4; -} - -CUSTOM_CVARD(Float, vid_contrast, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts contrast component of gamma ramp") -{ - if (self < 0) self = 0; - else if (self > 5) self = 5; -} - -CUSTOM_CVARD(Float, vid_brightness, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts brightness component of gamma ramp") -{ - if (self < -2) self = -2; - else if (self > 2) self = 2; -} - -CUSTOM_CVARD(Float, vid_saturation, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adjusts saturation component of gamma ramp") -{ - if (self < -3) self = -3; - else if (self > 3) self = 3; -} - -CVAR(Int, gl_satformula, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); - -CCMD (bumpgamma) -{ - // [RH] Gamma correction tables are now generated on the fly for *any* gamma level - // Q: What are reasonable limits to use here? - - float newgamma = vid_gamma + 0.1f; - - if (newgamma > 3.0) - newgamma = 1.0; - - vid_gamma = newgamma; - Printf ("Gamma correction level %g\n", newgamma); -} - -//{ "vid_contrast","adjusts contrast component of gamma ramp",(void *) &vid_contrast, CVAR_FLOAT|CVAR_FUNCPTR, 0, 10 }, -//{ "vid_brightness","adjusts brightness component of gamma ramp",(void *) &vid_brightness, CVAR_FLOAT|CVAR_FUNCPTR, 0, 10 }, - CUSTOM_CVAR(String, rtsname, "", CVAR_ARCHIVE | CVAR_USERINFO) { diff --git a/source/core/i_specialpaths.h b/source/core/i_specialpaths.h deleted file mode 100644 index 4d61bc3d7..000000000 --- a/source/core/i_specialpaths.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "zstring.h" - -#ifdef __unix__ -FString GetUserFile (const char *path); -#endif -FString M_GetAppDataPath(bool create); -FString M_GetCachePath(bool create); -FString M_GetAutoexecPath(); -FString M_GetConfigPath(bool for_reading); -FString M_GetScreenshotsPath(); -FString M_GetSavegamesPath(); -FString M_GetDocumentsPath(); -FString M_GetDemoPath(); - -FString M_GetNormalizedPath(const char* path); - -#ifdef __APPLE__ -FString M_GetMacAppSupportPath(const bool create = true); -void M_GetMacSearchDirectories(FString& user_docs, FString& user_app_support, FString& local_app_support); -#endif // __APPLE__ diff --git a/source/core/rendering/gl/renderer/gl_renderer.cpp b/source/core/rendering/gl/renderer/gl_renderer.cpp index 5963fc204..1f716c891 100644 --- a/source/core/rendering/gl/renderer/gl_renderer.cpp +++ b/source/core/rendering/gl/renderer/gl_renderer.cpp @@ -32,7 +32,7 @@ ** */ -#include "gl_load/gl_system.h" +#include "gl_system.h" #include "files.h" #include "v_video.h" #include "m_png.h" @@ -40,7 +40,8 @@ #include "i_time.h" #include "cmdlib.h" #include "m_png.h" -//#include "swrenderer/r_swscene.h" +#include "version.h" +#include "texturemanager.h" //#include "hwrenderer/utility/hw_clock.h" #include "gl_load/gl_interface.h" @@ -48,17 +49,17 @@ #include "gamecvars.h" #include "gl_debug.h" #include "gl/renderer/gl_renderer.h" -//#include "gl/renderer/gl_renderstate.h" -#include "gl/renderer/gl_renderbuffers.h" -#include "gl/shaders/gl_shaderprogram.h" -//#include "hwrenderer/data/flatvertices.h" +#include "gl_renderstate.h" +#include "gl_renderbuffers.h" +#include "gl_shaderprogram.h" +#include "flatvertices.h" #include "gl_samplers.h" -//#include "hwrenderer/dynlights/hw_lightbuffer.h" +#include "hw_lightbuffer.h" //#include "hwrenderer/data/hw_viewpointbuffer.h" #include "r_videoscale.h" //#include "r_data/models/models.h" -#include "gl/renderer/gl_postprocessstate.h" -#include "gl/system/gl_buffers.h" +#include "gl_postprocessstate.h" +#include "gl_buffers.h" #include "gl_hwtexture.h" #include "build.h" @@ -89,15 +90,14 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) void FGLRenderer::Initialize(int width, int height) { - mScreenBuffers = new FGLRenderBuffers(gl_multisample); - mSaveBuffers = new FGLRenderBuffers(0); + mScreenBuffers = new FGLRenderBuffers(); + mSaveBuffers = new FGLRenderBuffers(); mBuffers = mScreenBuffers; mPresentShader = new FPresentShader(); mPresent3dCheckerShader = new FPresent3DCheckerShader(); mPresent3dColumnShader = new FPresent3DColumnShader(); mPresent3dRowShader = new FPresent3DRowShader(); - - //glGenQueries(1, &PortalQueryObject); + mShadowMapShader = new FShadowMapShader(); // needed for the core profile, because someone decided it was a good idea to remove the default VAO. glGenVertexArrays(1, &mVAOID); @@ -107,15 +107,15 @@ void FGLRenderer::Initialize(int width, int height) mFBID = 0; mOldFBID = 0; - //mShaderManager = new FShaderManager; + mShaderManager = new FShaderManager; mSamplerManager = new FSamplerManager; } FGLRenderer::~FGLRenderer() { //FlushModels(); - //TexMan.FlushAll(); - //if (mShaderManager != nullptr) delete mShaderManager; + TexMan.FlushAll(); + if (mShaderManager != nullptr) delete mShaderManager; if (mSamplerManager != nullptr) delete mSamplerManager; if (mFBID != 0) glDeleteFramebuffers(1, &mFBID); if (mVAOID != 0) @@ -123,15 +123,13 @@ FGLRenderer::~FGLRenderer() glBindVertexArray(0); glDeleteVertexArrays(1, &mVAOID); } - //if (PortalQueryObject != 0) glDeleteQueries(1, &PortalQueryObject); - - //if (swdrawer) delete swdrawer; if (mBuffers) delete mBuffers; if (mSaveBuffers) delete mSaveBuffers; if (mPresentShader) delete mPresentShader; if (mPresent3dCheckerShader) delete mPresent3dCheckerShader; if (mPresent3dColumnShader) delete mPresent3dColumnShader; if (mPresent3dRowShader) delete mPresent3dRowShader; + if (mShadowMapShader) delete mShadowMapShader; } //=========================================================================== @@ -184,70 +182,6 @@ void FGLRenderer::BindToFrameBuffer(FGameTexture *mat) BaseLayer->BindToFrameBuffer(mat->GetTexelWidth()*4, mat->GetTexelHeight()*4); } -//=========================================================================== -// -// Render the view to a savegame picture -// -//=========================================================================== - -void FGLRenderer::WriteSavePic ( FileWriter *file, int width, int height) -{ - IntRect bounds; - bounds.left = 0; - bounds.top = 0; - bounds.width = width; - bounds.height = height; - - // we must be sure the GPU finished reading from the buffer before we fill it with new data. - glFinish(); - - // Switch to render buffers dimensioned for the savepic - mBuffers = mSaveBuffers; - mBuffers->BindSceneFB(false); - screen->SetViewportRects(&bounds); - - - int oldx = xdim; - int oldy = ydim; - auto oldwindowxy1 = windowxy1; - auto oldwindowxy2 = windowxy2; - - xdim = width; - ydim = height; - videoSetViewableArea(0, 0, width - 1, height - 1); - renderSetAspect(65536, 65536); - bool didit = gi->GenerateSavePic(); - - xdim = oldx; - ydim = oldy; - videoSetViewableArea(oldwindowxy1.x, oldwindowxy1.y, oldwindowxy2.x, oldwindowxy2.y); - - // The 2D drawers can contain some garbage from the dirty render setup. Get rid of that first. - twodgen.Clear(); - twodpsp.Clear(); - CopyToBackbuffer(&bounds, false); - - // strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers - glFinish(); - - if (didit) - { - int numpixels = width * height; - uint8_t* scr = (uint8_t*)Xmalloc(numpixels * 3); - glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, scr); - M_CreatePNG(file, scr + ((height - 1) * width * 3), nullptr, SS_RGB, width, height, -width * 3, vid_gamma); - M_FinishPNG(file); - Xfree(scr); - } - - // Switch back the screen render buffers - screen->SetViewportRects(nullptr); - mBuffers = mScreenBuffers; - bool useSSAO = (gl_ssao != 0); - mBuffers->BindSceneFB(useSSAO); -} - - //=========================================================================== // // @@ -257,7 +191,12 @@ void FGLRenderer::WriteSavePic ( FileWriter *file, int width, int height) void FGLRenderer::BeginFrame() { mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height); - mSaveBuffers->Setup(240, 180, 240, 180); + mSaveBuffers->Setup(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT); +} + +void FGLRenderer::PresentStereo() +{ + } } diff --git a/source/core/rendering/gl/renderer/gl_renderer.h b/source/core/rendering/gl/renderer/gl_renderer.h index a635b37d5..c731ba116 100644 --- a/source/core/rendering/gl/renderer/gl_renderer.h +++ b/source/core/rendering/gl/renderer/gl_renderer.h @@ -4,7 +4,7 @@ #include "v_video.h" #include "vectors.h" #include "matrix.h" -#include "gl/renderer/gl_renderbuffers.h" +#include "gl_renderbuffers.h" #include #ifdef _MSC_VER @@ -15,7 +15,6 @@ struct particle_t; class FCanvasTexture; class FFlatVertexBuffer; class FSkyVertexBuffer; -class FShaderManager; class HWPortal; class FLightBuffer; class DPSprite; @@ -27,6 +26,7 @@ struct FRenderViewpoint; namespace OpenGLRenderer { + class FShaderManager; class FSamplerManager; class OpenGLFrameBuffer; class FPresentShaderBase; @@ -34,6 +34,7 @@ namespace OpenGLRenderer class FPresent3DCheckerShader; class FPresent3DColumnShader; class FPresent3DRowShader; + class FShadowMapShader; class FGLRenderer { @@ -42,11 +43,10 @@ public: OpenGLFrameBuffer *framebuffer; int mMirrorCount = 0; int mPlaneMirrorCount = 0; - //FShaderManager *mShaderManager = nullptr; + FShaderManager *mShaderManager = nullptr; FSamplerManager *mSamplerManager = nullptr; unsigned int mFBID; unsigned int mVAOID; - //unsigned int PortalQueryObject; unsigned int mStencilValue = 0; int mOldFBID; @@ -58,11 +58,10 @@ public: FPresent3DCheckerShader *mPresent3dCheckerShader = nullptr; FPresent3DColumnShader *mPresent3dColumnShader = nullptr; FPresent3DRowShader *mPresent3dRowShader = nullptr; + FShadowMapShader *mShadowMapShader = nullptr; //FRotator mAngles; - //SWSceneDrawer *swdrawer = nullptr; - FGLRenderer(OpenGLFrameBuffer *fb); ~FGLRenderer() ; @@ -80,10 +79,8 @@ public: void DrawPresentTexture(const IntRect &box, bool applyGamma); void Flush(); //void Draw2D(F2DDrawer *data); - void WriteSavePic(FileWriter *file, int width, int height); void BeginFrame(); - bool StartOffscreen(); void EndOffscreen(); @@ -93,6 +90,15 @@ private: void DrawScene(HWDrawInfo *di, int drawmode); + bool QuadStereoCheckInitialRenderContextState(); + void PresentAnaglyph(bool r, bool g, bool b); + void PresentSideBySide(); + void PresentTopBottom(); + void prepareInterleavedPresent(FPresentShaderBase& shader); + void PresentColumnInterleaved(); + void PresentRowInterleaved(); + void PresentCheckerInterleaved(); + void PresentQuadStereo(); }; diff --git a/source/core/rendering/gl/system/gl_framebuffer.cpp b/source/core/rendering/gl/system/gl_framebuffer.cpp index 316fb18d6..890865b5c 100644 --- a/source/core/rendering/gl/system/gl_framebuffer.cpp +++ b/source/core/rendering/gl/system/gl_framebuffer.cpp @@ -46,8 +46,9 @@ #include "gl_load/gl_interface.h" #include "gl/system/gl_framebuffer.h" #include "gl/renderer/gl_renderer.h" -#include "gl/renderer/gl_renderbuffers.h" -#include "hwrenderer/data/flatvertices.h" +#include "gl_renderbuffers.h" +#include "flatvertices.h" +#include "hw_lightbuffer.h" /* #include "gl/textures/gl_samplers.h" #include "hwrenderer/utility/hw_clock.h" @@ -55,7 +56,6 @@ #include "hwrenderer/models/hw_models.h" #include "hwrenderer/scene/hw_skydome.h" #include "hwrenderer/data/hw_viewpointbuffer.h" -#include "hwrenderer/dynlights/hw_lightbuffer.h" #include "gl/shaders/gl_shaderprogram.h" */ #include "gl_debug.h" @@ -113,9 +113,9 @@ OpenGLFrameBuffer::~OpenGLFrameBuffer() #ifdef IMPLEMENT_IT if (mSkyData != nullptr) delete mSkyData; if (mViewpoints != nullptr) delete mViewpoints; +#endif if (mLights != nullptr) delete mLights; mShadowMap.Reset(); -#endif if (GLRenderer) { @@ -179,13 +179,11 @@ void OpenGLFrameBuffer::InitializeState() #ifdef IMPLEMENT_IT mSkyData = new FSkyVertexBuffer; mViewpoints = new HWViewpointBuffer; - mLights = new FLightBuffer(); #endif + mLights = new FLightBuffer(); GLRenderer = new FGLRenderer(this); GLRenderer->Initialize(GetWidth(), GetHeight()); -#ifdef IMPLEMENT_IT static_cast(mLights->GetBuffer())->BindBase(); -#endif mDebug = std::make_shared(); mDebug->Update(); @@ -215,15 +213,10 @@ void OpenGLFrameBuffer::Update() //=========================================================================== // -// Render the view to a savegame picture +// // //=========================================================================== -void OpenGLFrameBuffer::WriteSavePic(FileWriter *file, int width, int height) -{ - GLRenderer->WriteSavePic(file, width, height); -} - const char* OpenGLFrameBuffer::DeviceName() const { @@ -279,10 +272,6 @@ void OpenGLFrameBuffer::SetVSync(bool vsync) // //=========================================================================== -void OpenGLFrameBuffer::CleanForRestart() -{ -} - IHardwareTexture *OpenGLFrameBuffer::CreateHardwareTexture(int numchannels) { return new FHardwareTexture(numchannels); @@ -302,7 +291,7 @@ void OpenGLFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation) if (tex->isSWCanvas()) return; // Textures that are already scaled in the texture lump will not get replaced by hires textures. - int flags = mat->isExpanded() ? CTF_Expand : (gl_texture_usehires && !tex->isScaled()) ? CTF_CheckHires : 0; + int flags = mat->isExpanded() ? CTF_Expand : (!tex->isScaled()) ? CTF_CheckHires : 0; int numLayers = mat->GetLayers(); auto base = static_cast(mat->GetLayer(0, translation)); diff --git a/source/core/rendering/gl/system/gl_framebuffer.h b/source/core/rendering/gl/system/gl_framebuffer.h index 0f1944734..830514ba6 100644 --- a/source/core/rendering/gl/system/gl_framebuffer.h +++ b/source/core/rendering/gl/system/gl_framebuffer.h @@ -24,9 +24,7 @@ public: void Update() override; void Draw2D() override; - void CleanForRestart() override; const char* DeviceName() const override; - void WriteSavePic(FileWriter* file, int width, int height) override; IHardwareTexture *CreateHardwareTexture(int numchannels) override; void SetTextureFilterMode() override; diff --git a/source/core/rendering/hwrenderer/scene/hw_viewpointuniforms.h b/source/core/rendering/hwrenderer/scene/hw_viewpointuniforms.h index 5a1a1749d..427e0afa3 100644 --- a/source/core/rendering/hwrenderer/scene/hw_viewpointuniforms.h +++ b/source/core/rendering/hwrenderer/scene/hw_viewpointuniforms.h @@ -1,7 +1,6 @@ #pragma once #include "matrix.h" -#include "r_utility.h" struct HWDrawInfo; @@ -25,7 +24,7 @@ struct HWViewpointUniforms mNormalViewMatrix.computeNormalMatrix(mViewMatrix); } - void SetDefaults(HWDrawInfo *drawInfo); + void SetDefaults(int lightmode); }; diff --git a/source/core/rendering/hwrenderer/utility/hw_cvars.cpp b/source/core/rendering/hwrenderer/utility/hw_cvars.cpp deleted file mode 100644 index da12cd15a..000000000 --- a/source/core/rendering/hwrenderer/utility/hw_cvars.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "c_cvars.h" -#include "c_dispatch.h" -#include "v_video.h" -#include "hw_cvars.h" -#include "menu/menu.h" - -//========================================================================== -// -// Texture CVARs -// -//========================================================================== -#if 0 // left as a reminder that this will have to be refactored later. -CUSTOM_CVAR(Float,gl_texture_filter_anisotropic,8.0f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) -{ - screen->TextureFilterChanged(); -} - -CCMD(gl_flush) -{ - //TexMan.FlushAll(); -} - -CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) -{ - if (self < 0 || self > 6) self=4; - screen->TextureFilterChanged(); -} -CVAR(Bool, gl_precache, false, CVAR_ARCHIVE) -#endif - diff --git a/source/core/rendering/i_video.h b/source/core/rendering/i_video.h deleted file mode 100644 index d9a9b9701..000000000 --- a/source/core/rendering/i_video.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef __I_VIDEO_H__ -#define __I_VIDEO_H__ - -#include - -class DFrameBuffer; - - -class IVideo -{ -public: - virtual ~IVideo() {} - - virtual DFrameBuffer *CreateFrameBuffer() = 0; - - bool SetResolution(); - - virtual void DumpAdapters(); -}; - -void I_InitGraphics(); -void I_ShutdownGraphics(); - -extern IVideo *Video; - -void I_PolyPresentInit(); -uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch); -void I_PolyPresentUnlock(int x, int y, int w, int h); -void I_PolyPresentDeinit(); - - -// Pause a bit. -// [RH] Despite the name, it apparently never waited for the VBL, even in -// the original DOS version (if the Heretic/Hexen source is any indicator). -void I_WaitVBL(int count); - - -#endif // __I_VIDEO_H__ diff --git a/source/core/rendering/r_videoscale.cpp b/source/core/rendering/r_videoscale.cpp deleted file mode 100644 index 4dde60d89..000000000 --- a/source/core/rendering/r_videoscale.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/*--------------------------------------------------------------------------- -** -** Copyright(C) 2017 Magnus Norddahl -** Copyright(C) 2017-2020 Rachael Alexanderson -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include -#include "c_dispatch.h" -#include "c_cvars.h" -#include "v_video.h" -#include "templates.h" -#include "r_videoscale.h" -#include "cmdlib.h" -#include "v_draw.h" -#include "i_interface.h" - -#include "c_console.h" -#include "menu/menu.h" - -#define NUMSCALEMODES countof(vScaleTable) - -bool setsizeneeded; - -EXTERN_CVAR(Int, vid_aspect) - - -CUSTOM_CVAR(Int, vid_scale_customwidth, VID_MIN_WIDTH, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < VID_MIN_WIDTH) - self = VID_MIN_WIDTH; - setsizeneeded = true; -} -CUSTOM_CVAR(Int, vid_scale_customheight, VID_MIN_HEIGHT, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < VID_MIN_HEIGHT) - self = VID_MIN_HEIGHT; - setsizeneeded = true; -} -CVAR(Bool, vid_scale_linear, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CUSTOM_CVAR(Float, vid_scale_custompixelaspect, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - setsizeneeded = true; - if (self < 0.2 || self > 5.0) - self = 1.0; -} - -static const int VID_MIN_UI_WIDTH = 640; -static const int VID_MIN_UI_HEIGHT = 400; - -namespace -{ - uint32_t min_width = VID_MIN_WIDTH; - uint32_t min_height = VID_MIN_HEIGHT; - - float v_MinimumToFill(uint32_t inwidth, uint32_t inheight) - { - // sx = screen x dimension, sy = same for y - float sx = (float)inwidth, sy = (float)inheight; - static float lastsx = 0., lastsy = 0., result = 0.; - if (lastsx != sx || lastsy != sy) - { - if (sx <= 0. || sy <= 0.) - return 1.; // prevent x/0 error - // set absolute minimum scale to fill the entire screen but get as close to 640x400 as possible - float ssx = (float)(VID_MIN_UI_WIDTH) / sx, ssy = (float)(VID_MIN_UI_HEIGHT) / sy; - result = (ssx < ssy) ? ssy : ssx; - lastsx = sx; - lastsy = sy; - } - return result; - } - inline uint32_t v_mfillX(uint32_t inwidth, uint32_t inheight) - { - return (uint32_t)((float)inwidth * v_MinimumToFill(inwidth, inheight)); - } - inline uint32_t v_mfillY(uint32_t inwidth, uint32_t inheight) - { - return (uint32_t)((float)inheight * v_MinimumToFill(inwidth, inheight)); - } - inline void refresh_minimums() - { - // specialUI is tracking a state where high-res console fonts are actually required, and - // aren't actually rendered correctly in 320x200. this forces the game to revert to the 640x400 - // minimum set in GZDoom 4.0.0, but only while those fonts are required. - - static bool lastspecialUI = false; - bool isInActualMenu = false; - - bool specialUI = sysCallbacks && (!sysCallbacks->IsSpecialUI || sysCallbacks->IsSpecialUI()); - - if (specialUI == lastspecialUI) - return; - - lastspecialUI = specialUI; - setsizeneeded = true; - - if (!specialUI) - { - min_width = VID_MIN_WIDTH; - min_height = VID_MIN_HEIGHT; - } - else - { - min_width = VID_MIN_UI_WIDTH; - min_height = VID_MIN_UI_HEIGHT; - } - } - - // the odd formatting of this struct definition is meant to resemble a table header. set your tab stops to 4 when editing this file. - struct v_ScaleTable - { bool isValid; uint32_t(*GetScaledWidth)(uint32_t Width, uint32_t Height); uint32_t(*GetScaledHeight)(uint32_t Width, uint32_t Height); float pixelAspect; bool isCustom; }; - v_ScaleTable vScaleTable[] = - { - { true, [](uint32_t Width, uint32_t Height)->uint32_t { return Width; }, [](uint32_t Width, uint32_t Height)->uint32_t { return Height; }, 1.0f, false }, // 0 - Native - { true, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillX(Width, Height); }, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillY(Width, Height); }, 1.0f, false }, // 6 - Minimum Scale to Fill Entire Screen - { true, [](uint32_t Width, uint32_t Height)->uint32_t { return 640; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 400; }, 1.2f, false }, // 2 - 640x400 (formerly 320x200) - { true, [](uint32_t Width, uint32_t Height)->uint32_t { return 960; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 600; }, 1.2f, false }, // 3 - 960x600 (formerly 640x400) - { true, [](uint32_t Width, uint32_t Height)->uint32_t { return 1280; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 800; }, 1.2f, false }, // 4 - 1280x800 - { true, [](uint32_t Width, uint32_t Height)->uint32_t { return vid_scale_customwidth; }, [](uint32_t Width, uint32_t Height)->uint32_t { return vid_scale_customheight; }, 1.0f, true }, // 5 - Custom - { true, [](uint32_t Width, uint32_t Height)->uint32_t { return 320; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 200; }, 1.2f, false }, // 7 - 320x200 - }; - bool isOutOfBounds(int x) - { - return (x < 0 || x >= int(NUMSCALEMODES) || vScaleTable[x].isValid == false); - } -} - -void R_ShowCurrentScaling(); -CUSTOM_CVAR(Float, vid_scalefactor, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) -{ - setsizeneeded = true; - if (self < 0.05 || self > 2.0) - self = 1.0; - if (self != 1.0) - R_ShowCurrentScaling(); -} - -CUSTOM_CVAR(Int, vid_scalemode, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - setsizeneeded = true; - if (isOutOfBounds(self)) - self = 0; -} - -CUSTOM_CVAR(Bool, vid_cropaspect, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - setsizeneeded = true; -} - -bool ViewportLinearScale() -{ - if (isOutOfBounds(vid_scalemode)) - vid_scalemode = 0; - // always use linear if supersampling - int x = screen->GetClientWidth(), y = screen->GetClientHeight(); - float aspectmult = ViewportPixelAspect(); - if (aspectmult > 1.f) - aspectmult = 1.f / aspectmult; - if ((ViewportScaledWidth(x,y) > (x * aspectmult)) || (ViewportScaledHeight(x,y) > (y * aspectmult))) - return true; - - return vid_scale_linear; -} - -int ViewportScaledWidth(int width, int height) -{ - if (isOutOfBounds(vid_scalemode)) - vid_scalemode = 0; - refresh_minimums(); - if (vid_cropaspect && height > 0) - { - width = ((float)width/height > ActiveRatio(width, height)) ? (int)(height * ActiveRatio(width, height)) : width; - height = ((float)width/height < ActiveRatio(width, height)) ? (int)(width / ActiveRatio(width, height)) : height; - } - return (int)std::max((int32_t)min_width, (int32_t)(vid_scalefactor * vScaleTable[vid_scalemode].GetScaledWidth(width, height))); -} - -int ViewportScaledHeight(int width, int height) -{ - if (isOutOfBounds(vid_scalemode)) - vid_scalemode = 0; - if (vid_cropaspect && height > 0) - { - height = ((float)width/height < ActiveRatio(width, height)) ? (int)(width / ActiveRatio(width, height)) : height; - width = ((float)width/height > ActiveRatio(width, height)) ? (int)(height * ActiveRatio(width, height)) : width; - } - return (int)std::max((int32_t)min_height, (int32_t)(vid_scalefactor * vScaleTable[vid_scalemode].GetScaledHeight(width, height))); -} - -float ViewportPixelAspect() -{ - if (isOutOfBounds(vid_scalemode)) - vid_scalemode = 0; - // hack - use custom scaling if in "custom" mode - if (vScaleTable[vid_scalemode].isCustom) - return vid_scale_custompixelaspect; - return vScaleTable[vid_scalemode].pixelAspect; -} - -void R_ShowCurrentScaling() -{ - int x1 = screen->GetClientWidth(), y1 = screen->GetClientHeight(), x2 = ViewportScaledWidth(x1, y1), y2 = ViewportScaledHeight(x1, y1); - Printf("Current vid_scalefactor: %f\n", (float)(vid_scalefactor)); - Printf("Real resolution: %i x %i\nEmulated resolution: %i x %i\n", x1, y1, x2, y2); -} - -CCMD (vid_showcurrentscaling) -{ - R_ShowCurrentScaling(); -} - -CCMD (vid_scaletowidth) -{ - if (argv.argc() > 1) - { - // the following enables the use of ViewportScaledWidth to get the proper dimensions in custom scale modes - vid_scalefactor = 1; - vid_scalefactor = (float)((double)atof(argv[1]) / ViewportScaledWidth(screen->GetClientWidth(), screen->GetClientHeight())); - } -} - -CCMD (vid_scaletoheight) -{ - if (argv.argc() > 1) - { - vid_scalefactor = 1; - vid_scalefactor = (float)((double)atof(argv[1]) / ViewportScaledHeight(screen->GetClientWidth(), screen->GetClientHeight())); - } -} - -inline bool atob(char* I) -{ - if (stricmp (I, "true") == 0 || stricmp (I, "1") == 0) - return true; - return false; -} - -CCMD (vid_setscale) -{ - if (argv.argc() > 2) - { - vid_scale_customwidth = atoi(argv[1]); - vid_scale_customheight = atoi(argv[2]); - if (argv.argc() > 3) - { - vid_scale_linear = atob(argv[3]); - if (argv.argc() > 4) - { - vid_scale_custompixelaspect = (float)atof(argv[4]); - } - } - vid_scalemode = 5; - vid_scalefactor = 1.0; - } - else - { - Printf("Usage: vid_setscale [bool linear] [float pixel-shape]\nThis command will create a custom viewport scaling mode.\n"); - } -} - -CCMD (vid_scaletolowest) -{ - uint32_t method = 0; - if (argv.argc() > 1) - method = atoi(argv[1]); - switch (method) - { - case 1: // Method 1: set a custom video scaling - vid_scalemode = 5; - vid_scalefactor = 1.0; - vid_scale_custompixelaspect = 1.0; - vid_scale_customwidth = v_mfillX(screen->GetClientWidth(), screen->GetClientHeight()); - vid_scale_customheight = v_mfillY(screen->GetClientWidth(), screen->GetClientHeight()); - break; - case 2: // Method 2: use the actual downscaling mode directly - vid_scalemode = 1; - vid_scalefactor = 1.0; - break; - default: // Default method: use vid_scalefactor to achieve the result on a default scaling mode - vid_scalemode = 0; - vid_scalefactor = v_MinimumToFill(screen->GetClientWidth(), screen->GetClientHeight()); - break; - } -} diff --git a/source/core/rendering/r_videoscale.h b/source/core/rendering/r_videoscale.h deleted file mode 100644 index 430ad1555..000000000 --- a/source/core/rendering/r_videoscale.h +++ /dev/null @@ -1,40 +0,0 @@ -/*--------------------------------------------------------------------------- -** -** Copyright(C) 2017 Magnus Norddahl -** Copyright(C) 2017-2020 Rachael Alexanderson -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#ifndef __VIDEOSCALE_H__ -#define __VIDEOSCALE_H__ -EXTERN_CVAR (Int, vid_scalemode) -bool ViewportLinearScale(); -int ViewportScaledWidth(int width, int height); -int ViewportScaledHeight(int width, int height); -float ViewportPixelAspect(); -#endif //__VIDEOSCALE_H__ \ No newline at end of file diff --git a/source/core/rendering/v_framebuffer.cpp b/source/core/rendering/v_framebuffer.cpp index 864edb51d..f201dbb9b 100644 --- a/source/core/rendering/v_framebuffer.cpp +++ b/source/core/rendering/v_framebuffer.cpp @@ -307,12 +307,10 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds) { mScreenViewport.width = mOutputLetterbox.width; mScreenViewport.height = mOutputLetterbox.height; - mSceneViewport.left = (int)round(mSceneViewport.left * scaleX); mSceneViewport.top = (int)round(mSceneViewport.top * scaleY); mSceneViewport.width = (int)round(mSceneViewport.width * scaleX); mSceneViewport.height = (int)round(mSceneViewport.height * scaleY); - } } diff --git a/source/core/rendering/v_video.cpp b/source/core/rendering/v_video.cpp index eccdb4376..8a278c896 100644 --- a/source/core/rendering/v_video.cpp +++ b/source/core/rendering/v_video.cpp @@ -309,7 +309,6 @@ bool IVideo::SetResolution () screen = buff; screen->InitializeState(); - screen->SetGamma(); V_UpdateModeSize(screen->GetWidth(), screen->GetHeight()); @@ -388,7 +387,6 @@ void V_Init2() menu_resolution_custom_height = SCREENHEIGHT; screen->SetVSync(vid_vsync); - screen->SetGamma (); FBaseCVar::ResetColors (); C_NewModeAdjust(); videoSetGameMode(vid_fullscreen, SCREENWIDTH, SCREENHEIGHT, 32, 1); diff --git a/source/core/rendering/v_video.h b/source/core/rendering/v_video.h index 4d3c1615b..323764e00 100644 --- a/source/core/rendering/v_video.h +++ b/source/core/rendering/v_video.h @@ -42,7 +42,7 @@ #include "c_cvars.h" #include "v_2ddrawer.h" #include "intrect.h" -//#include "hwrenderer/dynlights/hw_shadowmap.h" +#include "hw_shadowmap.h" static const int VID_MIN_WIDTH = 640; static const int VID_MIN_HEIGHT = 400; @@ -59,6 +59,7 @@ class FLightBuffer; struct HWDrawInfo; class FMaterial; class FGameTexture; +class FRenderState; enum EHWCaps { @@ -111,6 +112,7 @@ inline bool V_IsTrueColor() } +struct FColormap; class FileWriter; enum FTextureFormat : uint32_t; class FModelRenderer; @@ -169,21 +171,24 @@ public: //FSkyVertexBuffer *mSkyData = nullptr; // the sky vertex buffer FFlatVertexBuffer *mVertexData = nullptr; // Global vertex data //HWViewpointBuffer *mViewpoints = nullptr; // Viewpoint render data. - //FLightBuffer *mLights = nullptr; // Dynamic lights - //IShadowMap mShadowMap; + FLightBuffer *mLights = nullptr; // Dynamic lights + IShadowMap mShadowMap; IntRect mScreenViewport; IntRect mSceneViewport; IntRect mOutputLetterbox; float mSceneClearColor[4]; - public: DFrameBuffer (int width=1, int height=1); virtual ~DFrameBuffer(); virtual void InitializeState() = 0; // For stuff that needs 'screen' set. virtual bool IsVulkan() { return false; } virtual bool IsPoly() { return false; } + void SetAABBTree(hwrenderer::LevelAABBTree * tree) + { + mShadowMap.SetAABBTree(tree); + } virtual DCanvas* GetCanvas() { return nullptr; } @@ -213,8 +218,6 @@ public: // Mark the palette as changed. It will be updated on the next Update(). virtual void UpdatePalette() {} - virtual void SetGamma() {} - // Returns true if running fullscreen. virtual bool IsFullscreen () = 0; virtual void ToggleFullscreen(bool yes) {} @@ -223,7 +226,6 @@ public: virtual void SetVSync (bool vsync); // Delete any resources that need to be deleted after restarting with a different IWAD - virtual void CleanForRestart() {} virtual void SetTextureFilterMode() {} virtual IHardwareTexture *CreateHardwareTexture(int numchannels) { return nullptr; } virtual void PrecacheMaterial(FMaterial *mat, int translation) {} @@ -232,6 +234,7 @@ public: virtual void BeginFrame() {} virtual void SetWindowSize(int w, int h) {} virtual void StartPrecaching() {} + virtual FRenderState* RenderState() { return nullptr; } virtual int GetClientWidth() = 0; virtual int GetClientHeight() = 0; @@ -282,6 +285,7 @@ public: uint64_t GetLastFPS() const { return LastCount; } virtual void Draw2D() {} + void Clear2D() {} // Calculate gamma table void CalcGamma(float gamma, uint8_t gammalookup[256]); @@ -322,6 +326,8 @@ extern DFrameBuffer *screen; #define SCREENHEIGHT (screen->GetHeight ()) #define SCREENPITCH (screen->GetPitch ()) +EXTERN_CVAR (Float, vid_gamma) + // Allocates buffer screens, call before R_Init. void V_InitScreenSize(); diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp index dfa89f192..88143f61f 100644 --- a/source/core/savegamehelp.cpp +++ b/source/core/savegamehelp.cpp @@ -56,6 +56,7 @@ static CompositeSavegameWriter savewriter; static FResourceFile *savereader; void LoadEngineState(); void SaveEngineState(); +void WriteSavePic(FileWriter* file, int width, int height); CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -224,7 +225,7 @@ bool OpenSaveGameForWrite(const char* filename, const char *name) SaveEngineState(); auto picfile = WriteSavegameChunk("savepic.png"); - screen->WriteSavePic(picfile, 240, 180); + WriteSavePic(picfile, 240, 180); return true; } diff --git a/source/core/version.h b/source/core/version.h index 30748ced1..ce2dac741 100644 --- a/source/core/version.h +++ b/source/core/version.h @@ -84,5 +84,8 @@ const char *GetVersionString(); #define GAME_DIR ".config/" GAMENAMELOWERCASE #endif +const int SAVEPICWIDTH = 240; +const int SAVEPICHEIGHT = 180; + #endif //__VERSION_H__ diff --git a/source/glbackend/gl_renderstate.h b/source/glbackend/gl_renderstate.h index a485ce627..d1c3dd61d 100644 --- a/source/glbackend/gl_renderstate.h +++ b/source/glbackend/gl_renderstate.h @@ -5,6 +5,7 @@ #include "renderstyle.h" class PolymostShader; struct GLState; +class FMaterial; enum EMatrixType { @@ -82,7 +83,7 @@ struct FMaterialState { mMaterial = nullptr; mTranslation = 0; - mClampMode = CLAMP_NONE; + mClampMode = 0/*CLAMP_NONE*/; mOverrideShader = -1; mChanged = false; } diff --git a/source/glbackend/glbackend.cpp b/source/glbackend/glbackend.cpp index dd6740d9a..74b5282bd 100644 --- a/source/glbackend/glbackend.cpp +++ b/source/glbackend/glbackend.cpp @@ -49,6 +49,7 @@ #include "v_video.h" #include "flatvertices.h" #include "gl_renderer.h" +#include "build.h" float shadediv[MAXPALOOKUPS]; @@ -515,3 +516,67 @@ void PolymostRenderState::Apply(PolymostShader* shader, GLState &oldState) memset(matrixIndex, -1, sizeof(matrixIndex)); } +//=========================================================================== +// +// Render the view to a savegame picture +// +//=========================================================================== + +void WriteSavePic(FileWriter* file, int width, int height) +{ + IntRect bounds; + bounds.left = 0; + bounds.top = 0; + bounds.width = width; + bounds.height = height; + + // we must be sure the GPU finished reading from the buffer before we fill it with new data. + glFinish(); + + // Switch to render buffers dimensioned for the savepic + OpenGLRenderer::GLRenderer->mBuffers = OpenGLRenderer::GLRenderer->mSaveBuffers; + OpenGLRenderer::GLRenderer->mBuffers->BindSceneFB(false); + screen->SetViewportRects(&bounds); + + + int oldx = xdim; + int oldy = ydim; + auto oldwindowxy1 = windowxy1; + auto oldwindowxy2 = windowxy2; + + xdim = width; + ydim = height; + videoSetViewableArea(0, 0, width - 1, height - 1); + renderSetAspect(65536, 65536); + bool didit = gi->GenerateSavePic(); + + xdim = oldx; + ydim = oldy; + videoSetViewableArea(oldwindowxy1.x, oldwindowxy1.y, oldwindowxy2.x, oldwindowxy2.y); + + // The 2D drawers can contain some garbage from the dirty render setup. Get rid of that first. + twodgen.Clear(); + twodpsp.Clear(); + OpenGLRenderer::GLRenderer->CopyToBackbuffer(&bounds, false); + + // strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers + glFinish(); + + if (didit) + { + int numpixels = width * height; + uint8_t* scr = (uint8_t*)Xmalloc(numpixels * 3); + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, scr); + M_CreatePNG(file, scr + ((height - 1) * width * 3), nullptr, SS_RGB, width, height, -width * 3, vid_gamma); + M_FinishPNG(file); + Xfree(scr); + } + + // Switch back the screen render buffers + screen->SetViewportRects(nullptr); + OpenGLRenderer::GLRenderer->mBuffers = OpenGLRenderer::GLRenderer->mScreenBuffers; + bool useSSAO = (gl_ssao != 0); + OpenGLRenderer::GLRenderer->mBuffers->BindSceneFB(useSSAO); +} + + diff --git a/wadsrc/static/shaders/glsl/burn.fp b/wadsrc/static/shaders/glsl/burn.fp new file mode 100644 index 000000000..486789f54 --- /dev/null +++ b/wadsrc/static/shaders/glsl/burn.fp @@ -0,0 +1,14 @@ + +layout(location=0) in vec4 vTexCoord; +layout(location=1) in vec4 vColor; +layout(location=0) out vec4 FragColor; + +void main() +{ + vec4 frag = vColor; + + vec4 t1 = texture(tex, vTexCoord.xy); + vec4 t2 = texture(texture2, vec2(vTexCoord.x, 1.0-vTexCoord.y)); + + FragColor = frag * vec4(t1.r, t1.g, t1.b, t2.a); +} diff --git a/wadsrc/static/shaders/glsl/fogboundary.fp b/wadsrc/static/shaders/glsl/fogboundary.fp new file mode 100644 index 000000000..9494a9ca9 --- /dev/null +++ b/wadsrc/static/shaders/glsl/fogboundary.fp @@ -0,0 +1,37 @@ +layout(location=2) in vec4 pixelpos; +layout(location=0) out vec4 FragColor; +#ifdef GBUFFER_PASS +layout(location=1) out vec4 FragFog; +layout(location=2) out vec4 FragNormal; +#endif + +//=========================================================================== +// +// Main shader routine +// +//=========================================================================== + +void main() +{ + float fogdist; + float fogfactor; + + // + // calculate fog factor + // + if (uFogEnabled == -1) + { + fogdist = pixelpos.w; + } + else + { + fogdist = max(16.0, distance(pixelpos.xyz, uCameraPos.xyz)); + } + fogfactor = exp2 (uFogDensity * fogdist); + FragColor = vec4(uFogColor.rgb, 1.0 - fogfactor); +#ifdef GBUFFER_PASS + FragFog = vec4(0.0, 0.0, 0.0, 1.0); + FragNormal = vec4(0.5, 0.5, 0.5, 1.0); +#endif +} + diff --git a/wadsrc/static/shaders/glsl/func_defaultlight.fp b/wadsrc/static/shaders/glsl/func_defaultlight.fp new file mode 100644 index 000000000..6738af8f5 --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_defaultlight.fp @@ -0,0 +1,5 @@ + +vec4 ProcessLight(Material material, vec4 color) +{ + return color; +} diff --git a/wadsrc/static/shaders/glsl/func_defaultmat.fp b/wadsrc/static/shaders/glsl/func_defaultmat.fp new file mode 100644 index 000000000..d4c3c99f7 --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_defaultmat.fp @@ -0,0 +1,7 @@ + +void SetupMaterial(inout Material material) +{ + material.Base = ProcessTexel(); + material.Normal = ApplyNormalMap(vTexCoord.st); + material.Bright = texture(brighttexture, vTexCoord.st); +} diff --git a/wadsrc/static/shaders/glsl/func_defaultmat2.fp b/wadsrc/static/shaders/glsl/func_defaultmat2.fp new file mode 100644 index 000000000..b2beb254e --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_defaultmat2.fp @@ -0,0 +1,6 @@ + +void SetupMaterial(inout Material material) +{ + vec2 texCoord = GetTexCoord(); + SetMaterialProps(material, texCoord); +} diff --git a/wadsrc/static/shaders/glsl/func_normal.fp b/wadsrc/static/shaders/glsl/func_normal.fp new file mode 100644 index 000000000..49dfadd9a --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_normal.fp @@ -0,0 +1,5 @@ + +void SetupMaterial(inout Material material) +{ + SetMaterialProps(material, vTexCoord.st); +} diff --git a/wadsrc/static/shaders/glsl/func_notexture.fp b/wadsrc/static/shaders/glsl/func_notexture.fp new file mode 100644 index 000000000..9337ad6b1 --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_notexture.fp @@ -0,0 +1,6 @@ + +vec4 ProcessTexel() +{ + return desaturate(uObjectColor); +} + diff --git a/wadsrc/static/shaders/glsl/func_paletted.fp b/wadsrc/static/shaders/glsl/func_paletted.fp new file mode 100644 index 000000000..fbcd99234 --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_paletted.fp @@ -0,0 +1,10 @@ + +vec4 ProcessTexel() +{ + float index = getTexel(vTexCoord.st).r; + index = ((index * 255.0) + 0.5) / 256.0; + vec4 tex = texture(texture2, vec2(index, 0.5)); + tex.a = 1.0; + return tex; +} + diff --git a/wadsrc/static/shaders/glsl/func_pbr.fp b/wadsrc/static/shaders/glsl/func_pbr.fp new file mode 100644 index 000000000..79de3bb85 --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_pbr.fp @@ -0,0 +1,8 @@ + +void SetupMaterial(inout Material material) +{ + SetMaterialProps(material, vTexCoord.st); + material.Metallic = texture(metallictexture, vTexCoord.st).r; + material.Roughness = texture(roughnesstexture, vTexCoord.st).r; + material.AO = texture(aotexture, vTexCoord.st).r; +} diff --git a/wadsrc/static/shaders/glsl/func_spec.fp b/wadsrc/static/shaders/glsl/func_spec.fp new file mode 100644 index 000000000..7de5b1d4e --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_spec.fp @@ -0,0 +1,8 @@ + +void SetupMaterial(inout Material material) +{ + SetMaterialProps(material, vTexCoord.st); + material.Specular = texture(speculartexture, vTexCoord.st).rgb; + material.Glossiness = uSpecularMaterial.x; + material.SpecularLevel = uSpecularMaterial.y; +} diff --git a/wadsrc/static/shaders/glsl/func_warp1.fp b/wadsrc/static/shaders/glsl/func_warp1.fp new file mode 100644 index 000000000..dfadf8ada --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_warp1.fp @@ -0,0 +1,19 @@ + +vec2 GetTexCoord() +{ + vec2 texCoord = vTexCoord.st; + + const float pi = 3.14159265358979323846; + vec2 offset = vec2(0,0); + + offset.y = sin(pi * 2.0 * (texCoord.x + timer * 0.125)) * 0.1; + offset.x = sin(pi * 2.0 * (texCoord.y + timer * 0.125)) * 0.1; + + return texCoord + offset; +} + +vec4 ProcessTexel() +{ + return getTexel(GetTexCoord()); +} + diff --git a/wadsrc/static/shaders/glsl/func_warp2.fp b/wadsrc/static/shaders/glsl/func_warp2.fp new file mode 100644 index 000000000..1ee0f7fd0 --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_warp2.fp @@ -0,0 +1,20 @@ + + +vec2 GetTexCoord() +{ + vec2 texCoord = vTexCoord.st; + + const float pi = 3.14159265358979323846; + vec2 offset = vec2(0.0,0.0); + + offset.y = 0.5 + sin(pi * 2.0 * (texCoord.y + timer * 0.61 + 900.0/8192.0)) + sin(pi * 2.0 * (texCoord.x * 2.0 + timer * 0.36 + 300.0/8192.0)); + offset.x = 0.5 + sin(pi * 2.0 * (texCoord.y + timer * 0.49 + 700.0/8192.0)) + sin(pi * 2.0 * (texCoord.x * 2.0 + timer * 0.49 + 1200.0/8192.0)); + + return texCoord + offset * 0.025; +} + +vec4 ProcessTexel() +{ + return getTexel(GetTexCoord()); +} + diff --git a/wadsrc/static/shaders/glsl/func_warp3.fp b/wadsrc/static/shaders/glsl/func_warp3.fp new file mode 100644 index 000000000..9edea0004 --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_warp3.fp @@ -0,0 +1,21 @@ + + +vec2 GetTexCoord() +{ + vec2 texCoord = vTexCoord.st; + + const float pi = 3.14159265358979323846; + vec2 offset = vec2(0.0,0.0); + + float siny = sin(pi * 2.0 * (texCoord.y * 2.0 + timer * 0.75)) * 0.03; + offset.y = siny + sin(pi * 2.0 * (texCoord.x + timer * 0.75)) * 0.03; + offset.x = siny + sin(pi * 2.0 * (texCoord.x + timer * 0.45)) * 0.02; + + return texCoord + offset; +} + +vec4 ProcessTexel() +{ + return getTexel(GetTexCoord()); +} + diff --git a/wadsrc/static/shaders/glsl/func_wavex.fp b/wadsrc/static/shaders/glsl/func_wavex.fp new file mode 100644 index 000000000..3b892da9d --- /dev/null +++ b/wadsrc/static/shaders/glsl/func_wavex.fp @@ -0,0 +1,17 @@ + +vec2 GetTexCoord() +{ + vec2 texCoord = vTexCoord.st; + + const float pi = 3.14159265358979323846; + + texCoord.x += sin(pi * 2.0 * (texCoord.y + timer * 0.125)) * 0.1; + + return texCoord; +} + +vec4 ProcessTexel() +{ + return getTexel(GetTexCoord()); +} + diff --git a/wadsrc/static/shaders/glsl/fuzz_jagged.fp b/wadsrc/static/shaders/glsl/fuzz_jagged.fp new file mode 100644 index 000000000..083c22068 --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_jagged.fp @@ -0,0 +1,24 @@ +//created by Evil Space Tomato + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + + vec2 texSplat; + const float pi = 3.14159265358979323846; + texSplat.x = texCoord.x + mod(sin(pi * 2.0 * (texCoord.y + timer * 2.0)),0.1) * 0.1; + texSplat.y = texCoord.y + mod(cos(pi * 2.0 * (texCoord.x + timer * 2.0)),0.1) * 0.1; + + vec4 basicColor = getTexel(texSplat); + + float texX = sin(texCoord.x * 100.0 + timer*5.0); + float texY = cos(texCoord.x * 100.0 + timer*5.0); + float vX = (texX/texY)*21.0; + float vY = (texY/texX)*13.0; + + float test = mod(timer*2.0+(vX + vY), 0.5); + + basicColor.a = basicColor.a * test; + + return basicColor; +} diff --git a/wadsrc/static/shaders/glsl/fuzz_noise.fp b/wadsrc/static/shaders/glsl/fuzz_noise.fp new file mode 100644 index 000000000..6d1c0094c --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_noise.fp @@ -0,0 +1,21 @@ +//created by Evil Space Tomato + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + vec4 basicColor = getTexel(texCoord); + vec2 texSize = vec2(textureSize(tex, 0)); + + texCoord.x = float( int(texCoord.x * texSize.x) ) / texSize.x; + texCoord.y = float( int(texCoord.y * texSize.y) ) / texSize.y; + + float texX = sin(mod(texCoord.x * 100.0 + timer*5.0, 3.489)) + texCoord.x / 4.0; + float texY = cos(mod(texCoord.y * 100.0 + timer*5.0, 3.489)) + texCoord.y / 4.0; + float vX = (texX/texY)*21.0; + float vY = (texY/texX)*13.0; + + float test = mod(timer*2.0+(vX + vY), 0.5); + basicColor.a = basicColor.a * test; + basicColor.rgb = vec3(0.0,0.0,0.0); + return basicColor; +} diff --git a/wadsrc/static/shaders/glsl/fuzz_smooth.fp b/wadsrc/static/shaders/glsl/fuzz_smooth.fp new file mode 100644 index 000000000..3c642c399 --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_smooth.fp @@ -0,0 +1,18 @@ +//created by Evil Space Tomato + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + vec4 basicColor = getTexel(texCoord); + + float texX = texCoord.x / 3.0 + 0.66; + float texY = 0.34 - texCoord.y / 3.0; + float vX = (texX/texY)*21.0; + float vY = (texY/texX)*13.0; + float test = mod(timer*2.0+(vX + vY), 0.5); + + basicColor.a = basicColor.a * test; + basicColor.r = basicColor.g = basicColor.b = 0.0; + + return basicColor; +} diff --git a/wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp b/wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp new file mode 100644 index 000000000..d446a3d0a --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp @@ -0,0 +1,19 @@ +//created by Evil Space Tomato + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + vec4 basicColor = getTexel(texCoord); + + float texX = sin(mod(texCoord.x * 100.0 + timer*5.0, 3.489)) + texCoord.x / 4.0; + float texY = cos(mod(texCoord.y * 100.0 + timer*5.0, 3.489)) + texCoord.y / 4.0; + float vX = (texX/texY)*21.0; + float vY = (texY/texX)*13.0; + + + float test = mod(timer*2.0+(vX + vY), 0.5); + basicColor.a = basicColor.a * test; + + basicColor.rgb = vec3(0.0,0.0,0.0); + return basicColor; +} diff --git a/wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp b/wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp new file mode 100644 index 000000000..1b727a1bc --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp @@ -0,0 +1,18 @@ +//created by Evil Space Tomato + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + vec4 basicColor = getTexel(texCoord); + + float texX = sin(texCoord.x * 100.0 + timer*5.0); + float texY = cos(texCoord.x * 100.0 + timer*5.0); + float vX = (texX/texY)*21.0; + float vY = (texY/texX)*13.0; + + float test = mod(timer*2.0+(vX + vY), 0.5); + + basicColor.a = basicColor.a * test; + + return basicColor; +} diff --git a/wadsrc/static/shaders/glsl/fuzz_software.fp b/wadsrc/static/shaders/glsl/fuzz_software.fp new file mode 100644 index 000000000..7840ce8be --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_software.fp @@ -0,0 +1,51 @@ +// Fuzz effect as rendered by the software renderer + +#define FUZZTABLE 50 +#define FUZZ_RANDOM_X_SIZE 100 +#define FRACBITS 16 +#define fixed_t int + +int fuzz_random_x_offset[FUZZ_RANDOM_X_SIZE] = int[] +( + 75, 76, 21, 91, 56, 33, 62, 99, 61, 79, + 95, 54, 41, 18, 69, 43, 49, 59, 10, 84, + 94, 17, 57, 46, 9, 39, 55, 34,100, 81, + 73, 88, 92, 3, 63, 36, 7, 28, 13, 80, + 16, 96, 78, 29, 71, 58, 89, 24, 1, 35, + 52, 82, 4, 14, 22, 53, 38, 66, 12, 72, + 90, 44, 77, 83, 6, 27, 48, 30, 42, 32, + 65, 15, 97, 20, 67, 74, 98, 85, 60, 68, + 19, 26, 8, 87, 86, 64, 11, 37, 31, 47, + 25, 5, 50, 51, 23, 2, 93, 70, 40, 45 +); + +int fuzzoffset[FUZZTABLE] = int[] +( + 6, 11, 6, 11, 6, 6, 11, 6, 6, 11, + 6, 6, 6, 11, 6, 6, 6, 11, 15, 18, + 21, 6, 11, 15, 6, 6, 6, 6, 11, 6, + 11, 6, 6, 11, 15, 6, 6, 11, 15, 18, + 21, 6, 6, 6, 6, 11, 6, 6, 11, 6 +); + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + vec4 basicColor = getTexel(texCoord); + + // Ideally fuzzpos would be an uniform and differ from each sprite so that overlapping demons won't get the same shade for the same pixel + int next_random = int(abs(mod(timer * 35.0, float(FUZZ_RANDOM_X_SIZE)))); + int fuzzpos = (/*fuzzpos +*/ fuzz_random_x_offset[next_random] * FUZZTABLE / 100) % FUZZTABLE; + + int x = int(gl_FragCoord.x); + int y = int(gl_FragCoord.y); + + fixed_t fuzzscale = (200 << FRACBITS) / uViewHeight; + int scaled_x = (x * fuzzscale) >> FRACBITS; + int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + fuzzpos; + fixed_t fuzzcount = FUZZTABLE << FRACBITS; + fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; + float alpha = float(fuzzoffset[fuzz >> FRACBITS]) / 32.0; + + return vec4(0.0, 0.0, 0.0, basicColor.a * alpha); +} diff --git a/wadsrc/static/shaders/glsl/fuzz_standard.fp b/wadsrc/static/shaders/glsl/fuzz_standard.fp new file mode 100644 index 000000000..3eb3b67e6 --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_standard.fp @@ -0,0 +1,22 @@ +//created by Evil Space Tomato + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + vec4 basicColor = getTexel(texCoord); + vec2 texSize = vec2(textureSize(tex, 0)); + + texCoord.x = float( int(texCoord.x * texSize.x) ) / texSize.x; + texCoord.y = float( int(texCoord.y * texSize.y) ) / texSize.y; + + float texX = texCoord.x / 3.0 + 0.66; + float texY = 0.34 - texCoord.y / 3.0; + float vX = (texX/texY)*21.0; + float vY = (texY/texX)*13.0; + float test = mod(timer*2.0+(vX + vY), 0.5); + + basicColor.a = basicColor.a * test; + basicColor.r = basicColor.g = basicColor.b = 0.0; + + return basicColor; +} diff --git a/wadsrc/static/shaders/glsl/fuzz_swirly.fp b/wadsrc/static/shaders/glsl/fuzz_swirly.fp new file mode 100644 index 000000000..266858999 --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_swirly.fp @@ -0,0 +1,18 @@ +//created by Evil Space Tomato + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + vec4 basicColor = getTexel(texCoord); + + float texX = sin(texCoord.x * 100.0 + timer*5.0); + float texY = cos(texCoord.x * 100.0 + timer*5.0); + float vX = (texX/texY)*21.0; + float vY = (texY/texX)*13.0; + float test = mod(timer*2.0+(vX + vY), 0.5); + + basicColor.a = basicColor.a * test; + basicColor.r = basicColor.g = basicColor.b = 0.0; + + return basicColor; +} diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp new file mode 100644 index 000000000..8960103bf --- /dev/null +++ b/wadsrc/static/shaders/glsl/main.fp @@ -0,0 +1,763 @@ + +layout(location = 0) in vec4 vTexCoord; +layout(location = 1) in vec4 vColor; +layout(location = 2) in vec4 pixelpos; +layout(location = 3) in vec3 glowdist; +layout(location = 4) in vec3 gradientdist; +layout(location = 5) in vec4 vWorldNormal; +layout(location = 6) in vec4 vEyeNormal; + +#ifdef NO_CLIPDISTANCE_SUPPORT +layout(location = 7) in vec4 ClipDistanceA; +layout(location = 8) in vec4 ClipDistanceB; +#endif + +layout(location=0) out vec4 FragColor; +#ifdef GBUFFER_PASS +layout(location=1) out vec4 FragFog; +layout(location=2) out vec4 FragNormal; +#endif + +struct Material +{ + vec4 Base; + vec4 Bright; + vec4 Glow; + vec3 Normal; + vec3 Specular; + float Glossiness; + float SpecularLevel; + float Metallic; + float Roughness; + float AO; +}; + +vec4 Process(vec4 color); +vec4 ProcessTexel(); +Material ProcessMaterial(); // note that this is deprecated. Use SetupMaterial! +void SetupMaterial(inout Material mat); +vec4 ProcessLight(Material mat, vec4 color); +vec3 ProcessMaterialLight(Material material, vec3 color); +vec2 GetTexCoord(); + +// These get Or'ed into uTextureMode because it only uses its 3 lowermost bits. +const int TEXF_Brightmap = 0x10000; +const int TEXF_Detailmap = 0x20000; +const int TEXF_Glowmap = 0x40000; + +//=========================================================================== +// +// Color to grayscale +// +//=========================================================================== + +float grayscale(vec4 color) +{ + return dot(color.rgb, vec3(0.3, 0.56, 0.14)); +} + +//=========================================================================== +// +// Desaturate a color +// +//=========================================================================== + +vec4 dodesaturate(vec4 texel, float factor) +{ + if (factor != 0.0) + { + float gray = grayscale(texel); + return mix (texel, vec4(gray,gray,gray,texel.a), factor); + } + else + { + return texel; + } +} + +//=========================================================================== +// +// Desaturate a color +// +//=========================================================================== + +vec4 desaturate(vec4 texel) +{ + return dodesaturate(texel, uDesaturationFactor); +} + +//=========================================================================== +// +// Texture tinting code originally from JFDuke but with a few more options +// +//=========================================================================== + +const int Tex_Blend_Alpha = 1; +const int Tex_Blend_Screen = 2; +const int Tex_Blend_Overlay = 3; +const int Tex_Blend_Hardlight = 4; + + vec4 ApplyTextureManipulation(vec4 texel, int blendflags) + { + // Step 1: desaturate according to the material's desaturation factor. + texel = dodesaturate(texel, uTextureModulateColor.a); + + // Step 2: Invert if requested + if ((blendflags & 8) != 0) + { + texel.rgb = vec3(1.0 - texel.r, 1.0 - texel.g, 1.0 - texel.b); + } + + // Step 3: Apply additive color + texel.rgb += uTextureAddColor.rgb; + + // Step 4: Colorization, including gradient if set. + texel.rgb *= uTextureModulateColor.rgb; + + // Before applying the blend the value needs to be clamped to [0..1] range. + texel.rgb = clamp(texel.rgb, 0.0, 1.0); + + // Step 5: Apply a blend. This may just be a translucent overlay or one of the blend modes present in current Build engines. + if ((blendflags & 7) != 0) + { + vec3 tcol = texel.rgb * 255.0; // * 255.0 to make it easier to reuse the integer math. + vec4 tint = uTextureBlendColor * 255.0; + + switch (blendflags & 7) + { + default: + tcol.b = tcol.b * (1.0 - uTextureBlendColor.a) + tint.b * uTextureBlendColor.a; + tcol.g = tcol.g * (1.0 - uTextureBlendColor.a) + tint.g * uTextureBlendColor.a; + tcol.r = tcol.r * (1.0 - uTextureBlendColor.a) + tint.r * uTextureBlendColor.a; + break; + // The following 3 are taken 1:1 from the Build engine + case Tex_Blend_Screen: + tcol.b = 255.0 - (((255.0 - tcol.b) * (255.0 - tint.r)) / 256.0); + tcol.g = 255.0 - (((255.0 - tcol.g) * (255.0 - tint.g)) / 256.0); + tcol.r = 255.0 - (((255.0 - tcol.r) * (255.0 - tint.b)) / 256.0); + break; + case Tex_Blend_Overlay: + tcol.b = tcol.b < 128.0? (tcol.b * tint.b) / 128.0 : 255.0 - (((255.0 - tcol.b) * (255.0 - tint.b)) / 128.0); + tcol.g = tcol.g < 128.0? (tcol.g * tint.g) / 128.0 : 255.0 - (((255.0 - tcol.g) * (255.0 - tint.g)) / 128.0); + tcol.r = tcol.r < 128.0? (tcol.r * tint.r) / 128.0 : 255.0 - (((255.0 - tcol.r) * (255.0 - tint.r)) / 128.0); + break; + case Tex_Blend_Hardlight: + tcol.b = tint.b < 128.0 ? (tcol.b * tint.b) / 128.0 : 255.0 - (((255.0 - tcol.b) * (255.0 - tint.b)) / 128.0); + tcol.g = tint.g < 128.0 ? (tcol.g * tint.g) / 128.0 : 255.0 - (((255.0 - tcol.g) * (255.0 - tint.g)) / 128.0); + tcol.r = tint.r < 128.0 ? (tcol.r * tint.r) / 128.0 : 255.0 - (((255.0 - tcol.r) * (255.0 - tint.r)) / 128.0); + break; + } + texel.rgb = tcol / 255.0; + } + return texel; +} + +//=========================================================================== +// +// This function is common for all (non-special-effect) fragment shaders +// +//=========================================================================== + +vec4 getTexel(vec2 st) +{ + vec4 texel = texture(tex, st); + + // + // Apply texture modes + // + switch (uTextureMode & 0xffff) + { + case 1: // TM_STENCIL + texel.rgb = vec3(1.0,1.0,1.0); + break; + + case 2: // TM_OPAQUE + texel.a = 1.0; + break; + + case 3: // TM_INVERSE + texel = vec4(1.0-texel.r, 1.0-texel.b, 1.0-texel.g, texel.a); + break; + + case 4: // TM_ALPHATEXTURE + { + float gray = grayscale(texel); + texel = vec4(1.0, 1.0, 1.0, gray*texel.a); + break; + } + + case 5: // TM_CLAMPY + if (st.t < 0.0 || st.t > 1.0) + { + 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; + + case 7: //TM_FOGLAYER + return texel; + + } + + // Apply the texture modification colors. + int blendflags = int(uTextureAddColor.a); // this alpha is unused otherwise + if (blendflags != 0) + { + // only apply the texture manipulation if it contains something. + texel = ApplyTextureManipulation(texel, blendflags); + } + + // Apply the Doom64 style material colors on top of everything from the texture modification settings. + // This may be a bit redundant in terms of features but the data comes from different sources so this is unavoidable. + texel.rgb += uAddColor.rgb; + if (uObjectColor2.a == 0.0) texel *= uObjectColor; + else texel *= mix(uObjectColor, uObjectColor2, gradientdist.z); + + // Last but not least apply the desaturation from the sector's light. + return desaturate(texel); +} + +//=========================================================================== +// +// Vanilla Doom wall colormap equation +// +//=========================================================================== +float R_WallColormap(float lightnum, float z, vec3 normal) +{ + // R_ScaleFromGlobalAngle calculation + float projection = 160.0; // projection depends on SCREENBLOCKS!! 160 is the fullscreen value + vec2 line_v1 = pixelpos.xz; // in vanilla this is the first curline vertex + vec2 line_normal = normal.xz; + float texscale = projection * clamp(dot(normalize(uCameraPos.xz - line_v1), line_normal), 0.0, 1.0) / z; + + float lightz = clamp(16.0 * texscale, 0.0, 47.0); + + // scalelight[lightnum][lightz] lookup + float startmap = (15.0 - lightnum) * 4.0; + return startmap - lightz * 0.5; +} + +//=========================================================================== +// +// Vanilla Doom plane colormap equation +// +//=========================================================================== +float R_PlaneColormap(float lightnum, float z) +{ + float lightz = clamp(z / 16.0f, 0.0, 127.0); + + // zlight[lightnum][lightz] lookup + float startmap = (15.0 - lightnum) * 4.0; + float scale = 160.0 / (lightz + 1.0); + return startmap - scale * 0.5; +} + +//=========================================================================== +// +// zdoom colormap equation +// +//=========================================================================== +float R_ZDoomColormap(float light, float z) +{ + float L = light * 255.0; + float vis = min(uGlobVis / z, 24.0 / 32.0); + float shade = 2.0 - (L + 12.0) / 128.0; + float lightscale = shade - vis; + return lightscale * 31.0; +} + +float R_DoomColormap(float light, float z) +{ + if ((uPalLightLevels >> 16) == 16) // gl_lightmode 16 + { + float lightnum = clamp(light * 15.0, 0.0, 15.0); + + if (dot(vWorldNormal.xyz, vWorldNormal.xyz) > 0.5) + { + vec3 normal = normalize(vWorldNormal.xyz); + return mix(R_WallColormap(lightnum, z, normal), R_PlaneColormap(lightnum, z), abs(normal.y)); + } + else // vWorldNormal is not set on sprites + { + return R_PlaneColormap(lightnum, z); + } + } + else + { + return R_ZDoomColormap(light, z); + } +} + +//=========================================================================== +// +// Doom software lighting equation +// +//=========================================================================== +float R_DoomLightingEquation(float light) +{ + // z is the depth in view space, positive going into the screen + float z; + if (((uPalLightLevels >> 8) & 0xff) == 2) + { + z = distance(pixelpos.xyz, uCameraPos.xyz); + } + else + { + z = pixelpos.w; + } + + float colormap = R_DoomColormap(light, z); + + if ((uPalLightLevels & 0xff) != 0) + colormap = floor(colormap) + 0.5; + + // Result is the normalized colormap index (0 bright .. 1 dark) + return clamp(colormap, 0.0, 31.0) / 32.0; +} + +//=========================================================================== +// +// Check if light is in shadow according to its 1D shadow map +// +//=========================================================================== + +#ifdef SUPPORTS_SHADOWMAPS + +float shadowDirToU(vec2 dir) +{ + if (abs(dir.y) > abs(dir.x)) + { + float x = dir.x / dir.y * 0.125; + if (dir.y >= 0.0) + return 0.125 + x; + else + return (0.50 + 0.125) + x; + } + else + { + float y = dir.y / dir.x * 0.125; + if (dir.x >= 0.0) + return (0.25 + 0.125) - y; + else + return (0.75 + 0.125) - y; + } +} + +vec2 shadowUToDir(float u) +{ + u *= 4.0; + vec2 raydir; + switch (int(u)) + { + case 0: raydir = vec2(u * 2.0 - 1.0, 1.0); break; + case 1: raydir = vec2(1.0, 1.0 - (u - 1.0) * 2.0); break; + case 2: raydir = vec2(1.0 - (u - 2.0) * 2.0, -1.0); break; + case 3: raydir = vec2(-1.0, (u - 3.0) * 2.0 - 1.0); break; + } + return raydir; +} + +float sampleShadowmap(vec3 planePoint, float v) +{ + float bias = 1.0; + float negD = dot(vWorldNormal.xyz, planePoint); + + vec3 ray = planePoint; + + vec2 isize = textureSize(ShadowMap, 0); + float scale = float(isize.x) * 0.25; + + // Snap to shadow map texel grid + if (abs(ray.z) > abs(ray.x)) + { + ray.y = ray.y / abs(ray.z); + ray.x = ray.x / abs(ray.z); + ray.x = (floor((ray.x + 1.0) * 0.5 * scale) + 0.5) / scale * 2.0 - 1.0; + ray.z = sign(ray.z); + } + else + { + ray.y = ray.y / abs(ray.x); + ray.z = ray.z / abs(ray.x); + ray.z = (floor((ray.z + 1.0) * 0.5 * scale) + 0.5) / scale * 2.0 - 1.0; + ray.x = sign(ray.x); + } + + float t = negD / dot(vWorldNormal.xyz, ray) - bias; + vec2 dir = ray.xz * t; + + float u = shadowDirToU(dir); + float dist2 = dot(dir, dir); + return step(dist2, texture(ShadowMap, vec2(u, v)).x); +} + +float sampleShadowmapPCF(vec3 planePoint, float v) +{ + float bias = 1.0; + float negD = dot(vWorldNormal.xyz, planePoint); + + vec3 ray = planePoint; + + if (abs(ray.z) > abs(ray.x)) + ray.y = ray.y / abs(ray.z); + else + ray.y = ray.y / abs(ray.x); + + vec2 isize = textureSize(ShadowMap, 0); + float scale = float(isize.x); + float texelPos = floor(shadowDirToU(ray.xz) * scale); + + float sum = 0.0; + float step_count = uShadowmapFilter; + + texelPos -= step_count + 0.5; + for (float x = -step_count; x <= step_count; x++) + { + float u = fract(texelPos / scale); + vec2 dir = shadowUToDir(u); + + ray.x = dir.x; + ray.z = dir.y; + float t = negD / dot(vWorldNormal.xyz, ray) - bias; + dir = ray.xz * t; + + float dist2 = dot(dir, dir); + sum += step(dist2, texture(ShadowMap, vec2(u, v)).x); + texelPos++; + } + return sum / (uShadowmapFilter * 2.0 + 1.0); +} + +float shadowmapAttenuation(vec4 lightpos, float shadowIndex) +{ + if (shadowIndex >= 1024.0) + return 1.0; // No shadowmap available for this light + + vec3 planePoint = pixelpos.xyz - lightpos.xyz; + planePoint += 0.01; // nudge light position slightly as Doom maps tend to have their lights perfectly aligned with planes + + if (dot(planePoint.xz, planePoint.xz) < 1.0) + return 1.0; // Light is too close + + float v = (shadowIndex + 0.5) / 1024.0; + + if (uShadowmapFilter <= 0) + { + return sampleShadowmap(planePoint, v); + } + else + { + return sampleShadowmapPCF(planePoint, v); + } +} + +float shadowAttenuation(vec4 lightpos, float lightcolorA) +{ + float shadowIndex = abs(lightcolorA) - 1.0; + return shadowmapAttenuation(lightpos, shadowIndex); +} + +#else + +float shadowAttenuation(vec4 lightpos, float lightcolorA) +{ + return 1.0; +} + +#endif + +float spotLightAttenuation(vec4 lightpos, vec3 spotdir, float lightCosInnerAngle, float lightCosOuterAngle) +{ + vec3 lightDirection = normalize(lightpos.xyz - pixelpos.xyz); + float cosDir = dot(lightDirection, spotdir); + return smoothstep(lightCosOuterAngle, lightCosInnerAngle, cosDir); +} + +//=========================================================================== +// +// Adjust normal vector according to the normal map +// +//=========================================================================== + +#if defined(NORMALMAP) +mat3 cotangent_frame(vec3 n, vec3 p, vec2 uv) +{ + // get edge vectors of the pixel triangle + vec3 dp1 = dFdx(p); + vec3 dp2 = dFdy(p); + vec2 duv1 = dFdx(uv); + vec2 duv2 = dFdy(uv); + + // solve the linear system + vec3 dp2perp = cross(n, dp2); // cross(dp2, n); + vec3 dp1perp = cross(dp1, n); // cross(n, dp1); + vec3 t = dp2perp * duv1.x + dp1perp * duv2.x; + vec3 b = dp2perp * duv1.y + dp1perp * duv2.y; + + // construct a scale-invariant frame + float invmax = inversesqrt(max(dot(t,t), dot(b,b))); + return mat3(t * invmax, b * invmax, n); +} + +vec3 ApplyNormalMap(vec2 texcoord) +{ + #define WITH_NORMALMAP_UNSIGNED + #define WITH_NORMALMAP_GREEN_UP + //#define WITH_NORMALMAP_2CHANNEL + + vec3 interpolatedNormal = normalize(vWorldNormal.xyz); + + vec3 map = texture(normaltexture, texcoord).xyz; + #if defined(WITH_NORMALMAP_UNSIGNED) + map = map * 255./127. - 128./127.; // Math so "odd" because 0.5 cannot be precisely described in an unsigned format + #endif + #if defined(WITH_NORMALMAP_2CHANNEL) + map.z = sqrt(1 - dot(map.xy, map.xy)); + #endif + #if defined(WITH_NORMALMAP_GREEN_UP) + map.y = -map.y; + #endif + + mat3 tbn = cotangent_frame(interpolatedNormal, pixelpos.xyz, vTexCoord.st); + vec3 bumpedNormal = normalize(tbn * map); + return bumpedNormal; +} +#else +vec3 ApplyNormalMap(vec2 texcoord) +{ + return normalize(vWorldNormal.xyz); +} +#endif + +//=========================================================================== +// +// Sets the common material properties. +// +//=========================================================================== + +void SetMaterialProps(inout Material material, vec2 texCoord) +{ + material.Base = getTexel(texCoord.st); + material.Normal = ApplyNormalMap(texCoord.st); + +// OpenGL doesn't care, but Vulkan pukes all over the place if these texture samplings are included in no-texture shaders, even though never called. +#ifndef NO_LAYERS + if ((uTextureMode & TEXF_Brightmap) != 0) + material.Bright = texture(brighttexture, texCoord.st); + + if ((uTextureMode & TEXF_Detailmap) != 0) + { + vec4 Detail = texture(detailtexture, texCoord.st * uDetailParms.xy) * uDetailParms.z; + material.Base *= Detail; + } + + if ((uTextureMode & TEXF_Glowmap) != 0) + material.Glow = texture(glowtexture, texCoord.st); +#endif +} + +//=========================================================================== +// +// Calculate light +// +// It is important to note that the light color is not desaturated +// due to ZDoom's implementation weirdness. Everything that's added +// on top of it, e.g. dynamic lights and glows are, though, because +// the objects emitting these lights are also. +// +// This is making this a bit more complicated than it needs to +// because we can't just desaturate the final fragment color. +// +//=========================================================================== + +vec4 getLightColor(Material material, float fogdist, float fogfactor) +{ + vec4 color = vColor; + + if (uLightLevel >= 0.0) + { + float newlightlevel = 1.0 - R_DoomLightingEquation(uLightLevel); + color.rgb *= newlightlevel; + } + else if (uFogEnabled > 0) + { + // brightening around the player for light mode 2 + if (fogdist < uLightDist) + { + color.rgb *= uLightFactor - (fogdist / uLightDist) * (uLightFactor - 1.0); + } + + // + // apply light diminishing through fog equation + // + color.rgb = mix(vec3(0.0, 0.0, 0.0), color.rgb, fogfactor); + } + + // + // handle glowing walls + // + if (uGlowTopColor.a > 0.0 && glowdist.x < uGlowTopColor.a) + { + color.rgb += desaturate(uGlowTopColor * (1.0 - glowdist.x / uGlowTopColor.a)).rgb; + } + if (uGlowBottomColor.a > 0.0 && glowdist.y < uGlowBottomColor.a) + { + color.rgb += desaturate(uGlowBottomColor * (1.0 - glowdist.y / uGlowBottomColor.a)).rgb; + } + color = min(color, 1.0); + + // these cannot be safely applied by the legacy format where the implementation cannot guarantee that the values are set. +#ifndef LEGACY_USER_SHADER + // + // apply glow + // + color.rgb = mix(color.rgb, material.Glow.rgb, material.Glow.a); + + // + // apply brightmaps + // + color.rgb = min(color.rgb + material.Bright.rgb, 1.0); +#endif + + // + // apply other light manipulation by custom shaders, default is a NOP. + // + color = ProcessLight(material, color); + + // + // apply dynamic lights + // + return vec4(ProcessMaterialLight(material, color.rgb), material.Base.a * vColor.a); +} + +//=========================================================================== +// +// Applies colored fog +// +//=========================================================================== + +vec4 applyFog(vec4 frag, float fogfactor) +{ + return vec4(mix(uFogColor.rgb, frag.rgb, fogfactor), frag.a); +} + +//=========================================================================== +// +// The color of the fragment if it is fully occluded by ambient lighting +// +//=========================================================================== + +vec3 AmbientOcclusionColor() +{ + float fogdist; + float fogfactor; + + // + // calculate fog factor + // + if (uFogEnabled == -1) + { + fogdist = max(16.0, pixelpos.w); + } + else + { + fogdist = max(16.0, distance(pixelpos.xyz, uCameraPos.xyz)); + } + fogfactor = exp2 (uFogDensity * fogdist); + + return mix(uFogColor.rgb, vec3(0.0), fogfactor); +} + +//=========================================================================== +// +// Main shader routine +// +//=========================================================================== + +void main() +{ +#ifdef NO_CLIPDISTANCE_SUPPORT + if (ClipDistanceA.x < 0 || ClipDistanceA.y < 0 || ClipDistanceA.z < 0 || ClipDistanceA.w < 0 || ClipDistanceB.x < 0) discard; +#endif + +#ifndef LEGACY_USER_SHADER + Material material; + + material.Base = vec4(0.0); + material.Bright = vec4(0.0); + material.Glow = vec4(0.0); + material.Normal = vec3(0.0); + material.Specular = vec3(0.0); + material.Glossiness = 0.0; + material.SpecularLevel = 0.0; + material.Metallic = 0.0; + material.Roughness = 0.0; + material.AO = 0.0; + SetupMaterial(material); +#else + Material material = ProcessMaterial(); +#endif + vec4 frag = material.Base; + +#ifndef NO_ALPHATEST + if (frag.a <= uAlphaThreshold) discard; +#endif + + if (uFogEnabled != -3) // check for special 2D 'fog' mode. + { + float fogdist = 0.0; + float fogfactor = 0.0; + + // + // calculate fog factor + // + if (uFogEnabled != 0) + { + if (uFogEnabled == 1 || uFogEnabled == -1) + { + fogdist = max(16.0, pixelpos.w); + } + else + { + fogdist = max(16.0, distance(pixelpos.xyz, uCameraPos.xyz)); + } + fogfactor = exp2 (uFogDensity * fogdist); + } + + if ((uTextureMode & 0xffff) != 7) + { + frag = getLightColor(material, fogdist, fogfactor); + + // + // colored fog + // + if (uFogEnabled < 0) + { + frag = applyFog(frag, fogfactor); + } + } + else + { + frag = vec4(uFogColor.rgb, (1.0 - fogfactor) * frag.a * 0.75 * vColor.a); + } + } + else // simple 2D (uses the fog color to add a color overlay) + { + if ((uTextureMode & 0xffff) == 7) + { + float gray = grayscale(frag); + vec4 cm = (uObjectColor + gray * (uAddColor - uObjectColor)) * 2; + frag = vec4(clamp(cm.rgb, 0.0, 1.0), frag.a); + } + frag = frag * ProcessLight(material, vColor); + frag.rgb = frag.rgb + uFogColor.rgb; + } + FragColor = frag; +#ifdef GBUFFER_PASS + FragFog = vec4(AmbientOcclusionColor(), 1.0); + FragNormal = vec4(vEyeNormal.xyz * 0.5 + 0.5, 1.0); +#endif +} diff --git a/wadsrc/static/shaders/glsl/main.vp b/wadsrc/static/shaders/glsl/main.vp new file mode 100644 index 000000000..86da9868a --- /dev/null +++ b/wadsrc/static/shaders/glsl/main.vp @@ -0,0 +1,144 @@ + +layout(location = 0) in vec4 aPosition; +layout(location = 1) in vec2 aTexCoord; +layout(location = 2) in vec4 aColor; + +layout(location = 0) out vec4 vTexCoord; +layout(location = 1) out vec4 vColor; + +#ifndef SIMPLE // we do not need these for simple shaders +layout(location = 3) in vec4 aVertex2; +layout(location = 4) in vec4 aNormal; +layout(location = 5) in vec4 aNormal2; + +layout(location = 2) out vec4 pixelpos; +layout(location = 3) out vec3 glowdist; +layout(location = 4) out vec3 gradientdist; +layout(location = 5) out vec4 vWorldNormal; +layout(location = 6) out vec4 vEyeNormal; +#endif + +#ifdef NO_CLIPDISTANCE_SUPPORT +layout(location = 7) out vec4 ClipDistanceA; +layout(location = 8) out vec4 ClipDistanceB; +#endif + +void main() +{ + float ClipDistance0, ClipDistance1, ClipDistance2, ClipDistance3, ClipDistance4; + + vec2 parmTexCoord; + vec4 parmPosition; + + parmTexCoord = aTexCoord; + parmPosition = aPosition; + + #ifndef SIMPLE + vec4 worldcoord = ModelMatrix * mix(parmPosition, aVertex2, uInterpolationFactor); + #else + vec4 worldcoord = ModelMatrix * parmPosition; + #endif + + vec4 eyeCoordPos = ViewMatrix * worldcoord; + + #ifdef HAS_UNIFORM_VERTEX_DATA + if ((useVertexData & 1) == 0) + vColor = uVertexColor; + else + vColor = aColor; + #else + vColor = aColor; + #endif + + #ifndef SIMPLE + pixelpos.xyz = worldcoord.xyz; + pixelpos.w = -eyeCoordPos.z/eyeCoordPos.w; + + if (uGlowTopColor.a > 0 || uGlowBottomColor.a > 0) + { + float topatpoint = (uGlowTopPlane.w + uGlowTopPlane.x * worldcoord.x + uGlowTopPlane.y * worldcoord.z) * uGlowTopPlane.z; + float bottomatpoint = (uGlowBottomPlane.w + uGlowBottomPlane.x * worldcoord.x + uGlowBottomPlane.y * worldcoord.z) * uGlowBottomPlane.z; + glowdist.x = topatpoint - worldcoord.y; + glowdist.y = worldcoord.y - bottomatpoint; + glowdist.z = clamp(glowdist.x / (topatpoint - bottomatpoint), 0.0, 1.0); + } + + if (uObjectColor2.a != 0) + { + float topatpoint = (uGradientTopPlane.w + uGradientTopPlane.x * worldcoord.x + uGradientTopPlane.y * worldcoord.z) * uGradientTopPlane.z; + float bottomatpoint = (uGradientBottomPlane.w + uGradientBottomPlane.x * worldcoord.x + uGradientBottomPlane.y * worldcoord.z) * uGradientBottomPlane.z; + gradientdist.x = topatpoint - worldcoord.y; + gradientdist.y = worldcoord.y - bottomatpoint; + gradientdist.z = clamp(gradientdist.x / (topatpoint - bottomatpoint), 0.0, 1.0); + } + + if (uSplitBottomPlane.z != 0.0) + { + ClipDistance3 = ((uSplitTopPlane.w + uSplitTopPlane.x * worldcoord.x + uSplitTopPlane.y * worldcoord.z) * uSplitTopPlane.z) - worldcoord.y; + ClipDistance4 = worldcoord.y - ((uSplitBottomPlane.w + uSplitBottomPlane.x * worldcoord.x + uSplitBottomPlane.y * worldcoord.z) * uSplitBottomPlane.z); + } + + #ifdef HAS_UNIFORM_VERTEX_DATA + if ((useVertexData & 2) == 0) + vWorldNormal = NormalModelMatrix * vec4(uVertexNormal.xyz, 1.0); + else + vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0); + #else + vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0); + #endif + vEyeNormal = NormalViewMatrix * vWorldNormal; + #endif + + #ifdef SPHEREMAP + vec3 u = normalize(eyeCoordPos.xyz); + vec4 n = normalize(NormalViewMatrix * vec4(parmTexCoord.x, 0.0, parmTexCoord.y, 0.0)); + vec3 r = reflect(u, n.xyz); + float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) ); + vec2 sst = vec2(r.x/m + 0.5, r.y/m + 0.5); + vTexCoord.xy = sst; + #else + vTexCoord = TextureMatrix * vec4(parmTexCoord, 0.0, 1.0); + #endif + + gl_Position = ProjectionMatrix * eyeCoordPos; + + #ifdef VULKAN_COORDINATE_SYSTEM + gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0; + #endif + + if (uClipHeightDirection != 0.0) // clip planes used for reflective flats + { + ClipDistance0 = (worldcoord.y - uClipHeight) * uClipHeightDirection; + } + else if (uClipLine.x > -1000000.0) // and for line portals - this will never be active at the same time as the reflective planes clipping so it can use the same hardware clip plane. + { + ClipDistance0 = -( (worldcoord.z - uClipLine.y) * uClipLine.z + (uClipLine.x - worldcoord.x) * uClipLine.w ) + 1.0/32768.0; // allow a tiny bit of imprecisions for colinear linedefs. + } + else + { + ClipDistance0 = 1; + } + + // clip planes used for translucency splitting + ClipDistance1 = worldcoord.y - uClipSplit.x; + ClipDistance2 = uClipSplit.y - worldcoord.y; + + if (uSplitTopPlane == vec4(0.0)) + { + ClipDistance3 = 1.0; + ClipDistance4 = 1.0; + } + +#ifdef NO_CLIPDISTANCE_SUPPORT + ClipDistanceA = vec4(ClipDistance0, ClipDistance1, ClipDistance2, ClipDistance3); + ClipDistanceB = vec4(ClipDistance4, 0.0, 0.0, 0.0); +#else + gl_ClipDistance[0] = ClipDistance0; + gl_ClipDistance[1] = ClipDistance1; + gl_ClipDistance[2] = ClipDistance2; + gl_ClipDistance[3] = ClipDistance3; + gl_ClipDistance[4] = ClipDistance4; +#endif + + gl_PointSize = 1.0; +} diff --git a/wadsrc/static/shaders/glsl/material_nolight.fp b/wadsrc/static/shaders/glsl/material_nolight.fp new file mode 100644 index 000000000..b71404423 --- /dev/null +++ b/wadsrc/static/shaders/glsl/material_nolight.fp @@ -0,0 +1,5 @@ + +vec3 ProcessMaterialLight(Material material, vec3 color) +{ + return material.Base.rgb * clamp(color + desaturate(uDynLightColor).rgb, 0.0, 1.4); +} diff --git a/wadsrc/static/shaders/glsl/material_normal.fp b/wadsrc/static/shaders/glsl/material_normal.fp new file mode 100644 index 000000000..add128812 --- /dev/null +++ b/wadsrc/static/shaders/glsl/material_normal.fp @@ -0,0 +1,82 @@ + +vec3 lightContribution(int i, vec3 normal) +{ + vec4 lightpos = lights[i]; + vec4 lightcolor = lights[i+1]; + vec4 lightspot1 = lights[i+2]; + vec4 lightspot2 = lights[i+3]; + + float lightdistance = distance(lightpos.xyz, pixelpos.xyz); + if (lightpos.w < lightdistance) + return vec3(0.0); // Early out lights touching surface but not this fragment + + vec3 lightdir = normalize(lightpos.xyz - pixelpos.xyz); + float dotprod = dot(normal, lightdir); + if (dotprod < -0.0001) return vec3(0.0); // light hits from the backside. This can happen with full sector light lists and must be rejected for all cases. Note that this can cause precision issues. + + float attenuation = clamp((lightpos.w - lightdistance) / lightpos.w, 0.0, 1.0); + + if (lightspot1.w == 1.0) + attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); + + if (lightcolor.a < 0.0) // Sign bit is the attenuated light flag + { + attenuation *= clamp(dotprod, 0.0, 1.0); + } + + if (attenuation > 0.0) // Skip shadow map test if possible + { + attenuation *= shadowAttenuation(lightpos, lightcolor.a); + return lightcolor.rgb * attenuation; + } + else + { + return vec3(0.0); + } +} + +vec3 ProcessMaterialLight(Material material, vec3 color) +{ + vec4 dynlight = uDynLightColor; + vec3 normal = material.Normal; + + if (uLightIndex >= 0) + { + ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + if (lightRange.z > lightRange.x) + { + // modulated lights + for(int i=lightRange.x; i= 0) + { + ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + if (lightRange.w > lightRange.z) + { + vec4 addlight = vec4(0.0,0.0,0.0,0.0); + + // additive lights + for(int i=lightRange.z; i= 0) + { + ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + if (lightRange.z > lightRange.x) + { + // + // modulated lights + // + for(int i=lightRange.x; i 0.0) + { + attenuation *= shadowAttenuation(lightpos, lightcolor.a); + + vec3 radiance = lightcolor.rgb * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); + + vec3 kS = F; + vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); + + vec3 nominator = NDF * G * F; + float denominator = 4.0 * clamp(dot(N, V), 0.0, 1.0) * clamp(dot(N, L), 0.0, 1.0); + vec3 specular = nominator / max(denominator, 0.001); + + Lo += (kD * albedo / PI + specular) * radiance; + } + } + // + // subtractive lights + // + for(int i=lightRange.y; i 0.0) + { + attenuation *= shadowAttenuation(lightpos, lightcolor.a); + + vec3 radiance = lightcolor.rgb * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); + + vec3 kS = F; + vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); + + vec3 nominator = NDF * G * F; + float denominator = 4.0 * clamp(dot(N, V), 0.0, 1.0) * clamp(dot(N, L), 0.0, 1.0); + vec3 specular = nominator / max(denominator, 0.001); + + Lo -= (kD * albedo / PI + specular) * radiance; + } + } + } + } + + // Pretend we sampled the sector light level from an irradiance map + + vec3 F = fresnelSchlickRoughness(clamp(dot(N, V), 0.0, 1.0), F0, roughness); + + vec3 kS = F; + vec3 kD = 1.0 - kS; + + vec3 irradiance = ambientLight; // texture(irradianceMap, N).rgb + vec3 diffuse = irradiance * albedo; + + //kD *= 1.0 - metallic; + //const float MAX_REFLECTION_LOD = 4.0; + //vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; + //vec2 envBRDF = texture(brdfLUT, vec2(clamp(dot(N, V), 0.0, 1.0), roughness)).rg; + //vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); + + //vec3 ambient = (kD * diffuse + specular) * ao; + vec3 ambient = (kD * diffuse) * ao; + + vec3 color = max(ambient + Lo, vec3(0.0)); + + // Tonemap (reinhard) and apply sRGB gamma + //color = color / (color + vec3(1.0)); + return pow(color, vec3(1.0 / 2.2)); +} diff --git a/wadsrc/static/shaders/glsl/material_specular.fp b/wadsrc/static/shaders/glsl/material_specular.fp new file mode 100644 index 000000000..60a39ab1e --- /dev/null +++ b/wadsrc/static/shaders/glsl/material_specular.fp @@ -0,0 +1,95 @@ + +vec2 lightAttenuation(int i, vec3 normal, vec3 viewdir, float lightcolorA) +{ + vec4 lightpos = lights[i]; + vec4 lightspot1 = lights[i+2]; + vec4 lightspot2 = lights[i+3]; + + float lightdistance = distance(lightpos.xyz, pixelpos.xyz); + if (lightpos.w < lightdistance) + return vec2(0.0); // Early out lights touching surface but not this fragment + + float attenuation = clamp((lightpos.w - lightdistance) / lightpos.w, 0.0, 1.0); + + if (lightspot1.w == 1.0) + attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); + + vec3 lightdir = normalize(lightpos.xyz - pixelpos.xyz); + + if (lightcolorA < 0.0) // Sign bit is the attenuated light flag + attenuation *= clamp(dot(normal, lightdir), 0.0, 1.0); + + if (attenuation > 0.0) // Skip shadow map test if possible + attenuation *= shadowAttenuation(lightpos, lightcolorA); + + if (attenuation <= 0.0) + return vec2(0.0); + + float glossiness = uSpecularMaterial.x; + float specularLevel = uSpecularMaterial.y; + + vec3 halfdir = normalize(viewdir + lightdir); + float specAngle = clamp(dot(halfdir, normal), 0.0f, 1.0f); + float phExp = glossiness * 4.0f; + return vec2(attenuation, attenuation * specularLevel * pow(specAngle, phExp)); +} + +vec3 ProcessMaterialLight(Material material, vec3 color) +{ + vec4 dynlight = uDynLightColor; + vec4 specular = vec4(0.0, 0.0, 0.0, 1.0); + + vec3 normal = material.Normal; + vec3 viewdir = normalize(uCameraPos.xyz - pixelpos.xyz); + + if (uLightIndex >= 0) + { + ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + if (lightRange.z > lightRange.x) + { + // modulated lights + for(int i=lightRange.x; i= 0) + { + ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + if (lightRange.w > lightRange.z) + { + vec4 addlight = vec4(0.0,0.0,0.0,0.0); + + // additive lights + for(int i=lightRange.z; i v.x + h.x || abs(c.y) > v.y + h.y || abs(c.z) > v.z + h.z) + return false; // disjoint; + + if (abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y || + abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x || + abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x) + return false; // disjoint; + + return true; // overlap; +} + +// Intersection test between two line segments. +// Returns the intersection point as a value between 0-1 on the ray line segment. 1.0 if there was no hit. +float intersectRayLine(vec2 ray_start, vec2 ray_end, int line_index, vec2 raydelta, float rayd, float raydist2) +{ + const float epsilon = 0.0000001; + GPULine line = lines[line_index]; + + vec2 raynormal = vec2(raydelta.y, -raydelta.x); + + float den = dot(raynormal, line.delta); + if (abs(den) > epsilon) + { + float t_line = (rayd - dot(raynormal, line.pos)) / den; + if (t_line >= 0.0 && t_line <= 1.0) + { + vec2 linehitdelta = line.pos + line.delta * t_line - ray_start; + float t = dot(raydelta, linehitdelta) / raydist2; + return t > 0.0 ? t : 1.0; + } + } + + return 1.0; +} + +// Returns true if an AABB tree node is a leaf node. Leaf nodes contains a line. +bool isLeaf(int node_index) +{ + return nodes[node_index].line_index != -1; +} + +// Perform ray intersection test between the ray line segment and all the lines in the AABB binary tree. +// Returns the intersection point as a value between 0-1 on the ray line segment. 1.0 if there was no hit. +float rayTest(vec2 ray_start, vec2 ray_end) +{ + vec2 raydelta = ray_end - ray_start; + float raydist2 = dot(raydelta, raydelta); + vec2 raynormal = vec2(raydelta.y, -raydelta.x); + float rayd = dot(raynormal, ray_start); + if (raydist2 < 1.0) + return 1.0; + + float t = 1.0; + + // Walk the AABB binary tree searching for nodes touching the ray line segment's AABB box. + // When it reaches a leaf node, use a line segment intersection test to see if we got a hit. + + int stack[32]; + int stack_pos = 1; + stack[0] = NodesCount - 1; + while (stack_pos > 0) + { + int node_index = stack[stack_pos - 1]; + + if (!overlapRayAABB(ray_start, ray_end, nodes[node_index].aabb_min, nodes[node_index].aabb_max)) + { + stack_pos--; + } + else if (isLeaf(node_index)) + { + t = min(intersectRayLine(ray_start, ray_end, nodes[node_index].line_index, raydelta, rayd, raydist2), t); + stack_pos--; + } + else if (stack_pos == 32) + { + stack_pos--; // stack overflow + } + else + { + stack[stack_pos - 1] = nodes[node_index].left; + stack[stack_pos] = nodes[node_index].right; + stack_pos++; + } + } + + return t; +} + +void main() +{ + // Find the light that belongs to this texel in the shadowmap texture we output to: + + int lightIndex = int(gl_FragCoord.y); + + vec4 light = lights[lightIndex]; + float radius = light.w; + vec2 lightpos = light.xy; + + if (radius > 0.0) + { + // We found an active light. Calculate the ray direction for the texel. + // + // The texels are laid out so that there are four projections: + // + // * top-left to top-right + // * top-right to bottom-right + // * bottom-right to bottom-left + // * bottom-left to top-left + // + vec2 raydir; + float u = gl_FragCoord.x / ShadowmapQuality * 4.0; + switch (int(u)) + { + case 0: raydir = vec2(u * 2.0 - 1.0, 1.0); break; + case 1: raydir = vec2(1.0, 1.0 - (u - 1.0) * 2.0); break; + case 2: raydir = vec2(1.0 - (u - 2.0) * 2.0, -1.0); break; + case 3: raydir = vec2(-1.0, (u - 3.0) * 2.0 - 1.0); break; + } + + // Find the position for the ray starting at the light position and travelling until light contribution is zero: + vec2 pixelpos = lightpos + raydir * radius; + + // Check if we hit any line between the light and the end position: + float t = rayTest(lightpos, pixelpos); + + // Calculate the square distance for the hit, if any: + vec2 delta = (pixelpos - lightpos) * t; + float dist2 = dot(delta, delta); + + FragColor = vec4(dist2, 0.0, 0.0, 1.0); + } + else + { + FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } +} diff --git a/wadsrc/static/engine/shaders/pp/ssao.fp b/wadsrc/static/shaders/pp/ssao.fp similarity index 100% rename from wadsrc/static/engine/shaders/pp/ssao.fp rename to wadsrc/static/shaders/pp/ssao.fp diff --git a/wadsrc/static/engine/shaders/pp/ssaocombine.fp b/wadsrc/static/shaders/pp/ssaocombine.fp similarity index 100% rename from wadsrc/static/engine/shaders/pp/ssaocombine.fp rename to wadsrc/static/shaders/pp/ssaocombine.fp diff --git a/wadsrc/static/engine/shaders/pp/tonemap.fp b/wadsrc/static/shaders/pp/tonemap.fp similarity index 100% rename from wadsrc/static/engine/shaders/pp/tonemap.fp rename to wadsrc/static/shaders/pp/tonemap.fp