mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-27 04:00:42 +00:00
- updated backend from GZDoom.
This commit is contained in:
parent
88eea8269a
commit
5728241c1c
107 changed files with 5548 additions and 778 deletions
|
@ -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} )
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -30,14 +30,14 @@
|
|||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
**/
|
||||
|
||||
#include <algorithm>
|
||||
#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) {}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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
|
||||
{
|
|
@ -2,7 +2,7 @@
|
|||
#define __GL_POSTPROCESSSTATE_H
|
||||
|
||||
#include <string.h>
|
||||
#include "gl_load/gl_interface.h"
|
||||
#include "gl_interface.h"
|
||||
#include "matrix.h"
|
||||
#include "c_cvars.h"
|
||||
|
|
@ -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 <random>
|
||||
|
||||
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.)
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
583
source/common/rendering/gl/gl_renderstate.cpp
Normal file
583
source/common/rendering/gl/gl_renderstate.cpp
Normal file
|
@ -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<GLDataBuffer*>(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<GLVertexBuffer*>(mVertexBuffer)->Bind(mVertexOffsets);
|
||||
mCurrentVertexBuffer = mVertexBuffer;
|
||||
mCurrentVertexOffsets[0] = mVertexOffsets[0];
|
||||
mCurrentVertexOffsets[1] = mVertexOffsets[1];
|
||||
}
|
||||
if (mIndexBuffer != mCurrentIndexBuffer)
|
||||
{
|
||||
if (mIndexBuffer) static_cast<GLIndexBuffer*>(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<FCanvasTexture*>(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<FHardwareTexture*>(mat->GetLayer(0, translation, &layer));
|
||||
|
||||
if (base->BindOrCreate(tex->GetTexture(), 0, clampmode, translation, layer->scaleFlags))
|
||||
{
|
||||
for (int i = 1; i<numLayers; i++)
|
||||
{
|
||||
auto systex = static_cast<FHardwareTexture*>(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;
|
||||
}
|
||||
|
||||
}
|
149
source/common/rendering/gl/gl_renderstate.h
Normal file
149
source/common/rendering/gl/gl_renderstate.h
Normal file
|
@ -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 <algorithm>
|
||||
#include <string.h>
|
||||
#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
|
866
source/common/rendering/gl/gl_shader.cpp
Normal file
866
source/common/rendering/gl/gl_shader.cpp
Normal file
|
@ -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 <map>
|
||||
#include <memory>
|
||||
|
||||
namespace OpenGLRenderer
|
||||
{
|
||||
|
||||
struct ProgramBinary
|
||||
{
|
||||
uint32_t format;
|
||||
TArray<uint8_t> data;
|
||||
};
|
||||
|
||||
static const char *ShaderMagic = "ZDSC";
|
||||
|
||||
static std::map<FString, std::unique_ptr<ProgramBinary>> 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<ProgramBinary> 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<FileWriter> 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<uint8_t> 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<uint8_t> &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<uint8_t> 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;i<MAX_EFFECTS;i++)
|
||||
{
|
||||
FShader *eff = new FShader(effectshaders[i].ShaderName);
|
||||
if (!eff->Load(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;i<mMaterialShaders.Size();i++)
|
||||
{
|
||||
if (mMaterialShaders[i]->mName == 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
|
||||
}
|
||||
|
||||
}
|
350
source/common/rendering/gl/gl_shader.h
Normal file
350
source/common/rendering/gl/gl_shader.h
Normal file
|
@ -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<FShaderCollection*> mPassShaders;
|
||||
|
||||
friend class FShader;
|
||||
};
|
||||
|
||||
class FShaderCollection
|
||||
{
|
||||
TArray<FShader*> mMaterialShaders;
|
||||
TArray<FShader*> 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
|
||||
|
|
@ -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<uint8_t> 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<ShadowMapUniforms, POSTPROCESS_BINDINGPOINT> Uniforms;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FShaderProgram> mShader;
|
||||
};
|
||||
|
||||
}
|
|
@ -42,6 +42,7 @@ struct FFlatVertex
|
|||
|
||||
class FFlatVertexBuffer
|
||||
{
|
||||
public:
|
||||
TArray<FFlatVertex> vbo_shadowdata;
|
||||
TArray<uint32_t> 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:
|
166
source/common/rendering/hwrenderer/data/hw_aabbtree.cpp
Normal file
166
source/common/rendering/hwrenderer/data/hw_aabbtree.cpp
Normal file
|
@ -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 <algorithm>
|
||||
#include "hw_aabbtree.h"
|
||||
|
||||
namespace hwrenderer
|
||||
{
|
||||
|
||||
TArray<int> 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<int> 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
82
source/common/rendering/hwrenderer/data/hw_aabbtree.h
Normal file
82
source/common/rendering/hwrenderer/data/hw_aabbtree.h
Normal file
|
@ -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<AABBTreeNode> nodes;
|
||||
|
||||
// Line segments for the leaf nodes in the tree.
|
||||
TArray<AABBTreeLine> 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<int> 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
|
139
source/common/rendering/hwrenderer/data/hw_cvars.cpp
Normal file
139
source/common/rendering/hwrenderer/data/hw_cvars.cpp
Normal file
|
@ -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;
|
||||
}
|
|
@ -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)
|
59
source/common/rendering/hwrenderer/data/hw_dynlightdata.h
Normal file
59
source/common/rendering/hwrenderer/data/hw_dynlightdata.h
Normal file
|
@ -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<float> 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
|
139
source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp
Normal file
139
source/common/rendering/hwrenderer/data/hw_lightbuffer.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
48
source/common/rendering/hwrenderer/data/hw_lightbuffer.h
Normal file
48
source/common/rendering/hwrenderer/data/hw_lightbuffer.h
Normal file
|
@ -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 <atomic>
|
||||
#include <mutex>
|
||||
|
||||
class FRenderState;
|
||||
|
||||
class FLightBuffer
|
||||
{
|
||||
IDataBuffer *mBuffer;
|
||||
|
||||
bool mBufferType;
|
||||
std::atomic<unsigned int> 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
|
||||
|
112
source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp
Normal file
112
source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp
Normal file
|
@ -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<FModelVertex*>(mVertexBuffer->Lock(size * sizeof(FModelVertex)));
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FModelVertexBuffer::UnlockVertexBuffer()
|
||||
{
|
||||
mVertexBuffer->Unlock();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
unsigned int *FModelVertexBuffer::LockIndexBuffer(unsigned int size)
|
||||
{
|
||||
if (mIndexBuffer) return static_cast<unsigned int*>(mIndexBuffer->Lock(size * sizeof(unsigned int)));
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FModelVertexBuffer::UnlockIndexBuffer()
|
||||
{
|
||||
if (mIndexBuffer) mIndexBuffer->Unlock();
|
||||
}
|
||||
|
||||
|
|
@ -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; }
|
||||
};
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "v_palette.h"
|
||||
#include "vectors.h"
|
||||
#include "matrix.h"
|
||||
#include "hw_material.h"
|
||||
|
|
|
@ -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" },
|
||||
};
|
155
source/common/rendering/hwrenderer/data/hw_shadowmap.cpp
Normal file
155
source/common/rendering/hwrenderer/data/hw_shadowmap.cpp
Normal file
|
@ -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();
|
||||
}
|
||||
|
83
source/common/rendering/hwrenderer/data/hw_shadowmap.h
Normal file
83
source/common/rendering/hwrenderer/data/hw_shadowmap.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hw_aabbtree.h"
|
||||
#include "stats.h"
|
||||
#include <memory>
|
||||
|
||||
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<void()> 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<float> 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<void()> CollectLights = nullptr;
|
||||
|
||||
};
|
|
@ -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();
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
180
source/common/rendering/hwrenderer/data/hw_vrmodes.cpp
Normal file
180
source/common/rendering/hwrenderer/data/hw_vrmodes.cpp
Normal file
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
47
source/common/rendering/hwrenderer/data/hw_vrmodes.h
Normal file
47
source/common/rendering/hwrenderer/data/hw_vrmodes.h
Normal file
|
@ -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;
|
||||
};
|
|
@ -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 <random>
|
||||
|
@ -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)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "hwrenderer/data/shaderuniforms.h"
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#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<UniformFieldDesc> 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<PPExposureLevel> 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<std::unique_ptr<PPCustomShaderInstance>> mShaders;
|
||||
};
|
||||
|
||||
class PPShadowMap
|
||||
{
|
||||
public:
|
||||
void Update(PPRenderState* renderstate);
|
||||
|
||||
private:
|
||||
PPShader ShadowMap = { "shaders/pp/shadowmap.fp", "", ShadowMapUniforms::Desc() };
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Postprocess
|
||||
|
|
43
source/common/rendering/i_modelvertexbuffer.h
Normal file
43
source/common/rendering/i_modelvertexbuffer.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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__
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 <functional>
|
||||
|
||||
#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();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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<GLDataBuffer*>(mLights->GetBuffer())->BindBase();
|
||||
#endif
|
||||
|
||||
mDebug = std::make_shared<FGLDebug>();
|
||||
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<FHardwareTexture*>(mat->GetLayer(0, translation));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#ifndef __I_VIDEO_H__
|
||||
#define __I_VIDEO_H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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__
|
|
@ -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 <math.h>
|
||||
#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 <x> <y> [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;
|
||||
}
|
||||
}
|
|
@ -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__
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -84,5 +84,8 @@ const char *GetVersionString();
|
|||
#define GAME_DIR ".config/" GAMENAMELOWERCASE
|
||||
#endif
|
||||
|
||||
const int SAVEPICWIDTH = 240;
|
||||
const int SAVEPICHEIGHT = 180;
|
||||
|
||||
|
||||
#endif //__VERSION_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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
14
wadsrc/static/shaders/glsl/burn.fp
Normal file
14
wadsrc/static/shaders/glsl/burn.fp
Normal file
|
@ -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);
|
||||
}
|
37
wadsrc/static/shaders/glsl/fogboundary.fp
Normal file
37
wadsrc/static/shaders/glsl/fogboundary.fp
Normal file
|
@ -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
|
||||
}
|
||||
|
5
wadsrc/static/shaders/glsl/func_defaultlight.fp
Normal file
5
wadsrc/static/shaders/glsl/func_defaultlight.fp
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
vec4 ProcessLight(Material material, vec4 color)
|
||||
{
|
||||
return color;
|
||||
}
|
7
wadsrc/static/shaders/glsl/func_defaultmat.fp
Normal file
7
wadsrc/static/shaders/glsl/func_defaultmat.fp
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
void SetupMaterial(inout Material material)
|
||||
{
|
||||
material.Base = ProcessTexel();
|
||||
material.Normal = ApplyNormalMap(vTexCoord.st);
|
||||
material.Bright = texture(brighttexture, vTexCoord.st);
|
||||
}
|
6
wadsrc/static/shaders/glsl/func_defaultmat2.fp
Normal file
6
wadsrc/static/shaders/glsl/func_defaultmat2.fp
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
void SetupMaterial(inout Material material)
|
||||
{
|
||||
vec2 texCoord = GetTexCoord();
|
||||
SetMaterialProps(material, texCoord);
|
||||
}
|
5
wadsrc/static/shaders/glsl/func_normal.fp
Normal file
5
wadsrc/static/shaders/glsl/func_normal.fp
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
void SetupMaterial(inout Material material)
|
||||
{
|
||||
SetMaterialProps(material, vTexCoord.st);
|
||||
}
|
6
wadsrc/static/shaders/glsl/func_notexture.fp
Normal file
6
wadsrc/static/shaders/glsl/func_notexture.fp
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
vec4 ProcessTexel()
|
||||
{
|
||||
return desaturate(uObjectColor);
|
||||
}
|
||||
|
10
wadsrc/static/shaders/glsl/func_paletted.fp
Normal file
10
wadsrc/static/shaders/glsl/func_paletted.fp
Normal file
|
@ -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;
|
||||
}
|
||||
|
8
wadsrc/static/shaders/glsl/func_pbr.fp
Normal file
8
wadsrc/static/shaders/glsl/func_pbr.fp
Normal file
|
@ -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;
|
||||
}
|
8
wadsrc/static/shaders/glsl/func_spec.fp
Normal file
8
wadsrc/static/shaders/glsl/func_spec.fp
Normal file
|
@ -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;
|
||||
}
|
19
wadsrc/static/shaders/glsl/func_warp1.fp
Normal file
19
wadsrc/static/shaders/glsl/func_warp1.fp
Normal file
|
@ -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());
|
||||
}
|
||||
|
20
wadsrc/static/shaders/glsl/func_warp2.fp
Normal file
20
wadsrc/static/shaders/glsl/func_warp2.fp
Normal file
|
@ -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());
|
||||
}
|
||||
|
21
wadsrc/static/shaders/glsl/func_warp3.fp
Normal file
21
wadsrc/static/shaders/glsl/func_warp3.fp
Normal file
|
@ -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());
|
||||
}
|
||||
|
17
wadsrc/static/shaders/glsl/func_wavex.fp
Normal file
17
wadsrc/static/shaders/glsl/func_wavex.fp
Normal file
|
@ -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());
|
||||
}
|
||||
|
24
wadsrc/static/shaders/glsl/fuzz_jagged.fp
Normal file
24
wadsrc/static/shaders/glsl/fuzz_jagged.fp
Normal file
|
@ -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;
|
||||
}
|
21
wadsrc/static/shaders/glsl/fuzz_noise.fp
Normal file
21
wadsrc/static/shaders/glsl/fuzz_noise.fp
Normal file
|
@ -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;
|
||||
}
|
18
wadsrc/static/shaders/glsl/fuzz_smooth.fp
Normal file
18
wadsrc/static/shaders/glsl/fuzz_smooth.fp
Normal file
|
@ -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;
|
||||
}
|
19
wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp
Normal file
19
wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp
Normal file
|
@ -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;
|
||||
}
|
18
wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp
Normal file
18
wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp
Normal file
|
@ -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;
|
||||
}
|
51
wadsrc/static/shaders/glsl/fuzz_software.fp
Normal file
51
wadsrc/static/shaders/glsl/fuzz_software.fp
Normal file
|
@ -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);
|
||||
}
|
22
wadsrc/static/shaders/glsl/fuzz_standard.fp
Normal file
22
wadsrc/static/shaders/glsl/fuzz_standard.fp
Normal file
|
@ -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;
|
||||
}
|
18
wadsrc/static/shaders/glsl/fuzz_swirly.fp
Normal file
18
wadsrc/static/shaders/glsl/fuzz_swirly.fp
Normal file
|
@ -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;
|
||||
}
|
763
wadsrc/static/shaders/glsl/main.fp
Normal file
763
wadsrc/static/shaders/glsl/main.fp
Normal file
|
@ -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
|
||||
}
|
144
wadsrc/static/shaders/glsl/main.vp
Normal file
144
wadsrc/static/shaders/glsl/main.vp
Normal file
|
@ -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;
|
||||
}
|
5
wadsrc/static/shaders/glsl/material_nolight.fp
Normal file
5
wadsrc/static/shaders/glsl/material_nolight.fp
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
vec3 ProcessMaterialLight(Material material, vec3 color)
|
||||
{
|
||||
return material.Base.rgb * clamp(color + desaturate(uDynLightColor).rgb, 0.0, 1.4);
|
||||
}
|
82
wadsrc/static/shaders/glsl/material_normal.fp
Normal file
82
wadsrc/static/shaders/glsl/material_normal.fp
Normal file
|
@ -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<lightRange.y; i+=4)
|
||||
{
|
||||
dynlight.rgb += lightContribution(i, normal);
|
||||
}
|
||||
|
||||
// subtractive lights
|
||||
for(int i=lightRange.y; i<lightRange.z; i+=4)
|
||||
{
|
||||
dynlight.rgb -= lightContribution(i, normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec3 frag = material.Base.rgb * clamp(color + desaturate(dynlight).rgb, 0.0, 1.4);
|
||||
|
||||
if (uLightIndex >= 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<lightRange.w; i+=4)
|
||||
{
|
||||
addlight.rgb += lightContribution(i, normal);
|
||||
}
|
||||
|
||||
frag = clamp(frag + desaturate(addlight).rgb, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
return frag;
|
||||
}
|
195
wadsrc/static/shaders/glsl/material_pbr.fp
Normal file
195
wadsrc/static/shaders/glsl/material_pbr.fp
Normal file
|
@ -0,0 +1,195 @@
|
|||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
float DistributionGGX(vec3 N, vec3 H, float roughness)
|
||||
{
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float NdotH = max(dot(N, H), 0.0);
|
||||
float NdotH2 = NdotH*NdotH;
|
||||
|
||||
float nom = a2;
|
||||
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
|
||||
denom = PI * denom * denom;
|
||||
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float NdotV, float roughness)
|
||||
{
|
||||
float r = (roughness + 1.0);
|
||||
float k = (r * r) / 8.0;
|
||||
|
||||
float nom = NdotV;
|
||||
float denom = NdotV * (1.0 - k) + k;
|
||||
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
|
||||
{
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
|
||||
vec3 fresnelSchlick(float cosTheta, vec3 F0)
|
||||
{
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
|
||||
{
|
||||
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
float quadraticDistanceAttenuation(vec4 lightpos)
|
||||
{
|
||||
float strength = (1.0 + lightpos.w * lightpos.w * 0.25) * 0.5;
|
||||
|
||||
vec3 distVec = lightpos.xyz - pixelpos.xyz;
|
||||
float attenuation = strength / (1.0 + dot(distVec, distVec));
|
||||
if (attenuation <= 1.0 / 256.0) return 0.0;
|
||||
|
||||
return attenuation;
|
||||
}
|
||||
|
||||
float linearDistanceAttenuation(vec4 lightpos)
|
||||
{
|
||||
float lightdistance = distance(lightpos.xyz, pixelpos.xyz);
|
||||
return clamp((lightpos.w - lightdistance) / lightpos.w, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 ProcessMaterialLight(Material material, vec3 ambientLight)
|
||||
{
|
||||
vec3 worldpos = pixelpos.xyz;
|
||||
|
||||
vec3 albedo = pow(material.Base.rgb, vec3(2.2)); // sRGB to linear
|
||||
ambientLight = pow(ambientLight, vec3(2.2));
|
||||
|
||||
float metallic = material.Metallic;
|
||||
float roughness = material.Roughness;
|
||||
float ao = material.AO;
|
||||
|
||||
vec3 N = material.Normal;
|
||||
vec3 V = normalize(uCameraPos.xyz - worldpos);
|
||||
|
||||
vec3 F0 = mix(vec3(0.04), albedo, metallic);
|
||||
|
||||
vec3 Lo = uDynLightColor.rgb;
|
||||
|
||||
if (uLightIndex >= 0)
|
||||
{
|
||||
ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1);
|
||||
if (lightRange.z > lightRange.x)
|
||||
{
|
||||
//
|
||||
// modulated lights
|
||||
//
|
||||
for(int i=lightRange.x; i<lightRange.y; i+=4)
|
||||
{
|
||||
vec4 lightpos = lights[i];
|
||||
vec4 lightcolor = lights[i+1];
|
||||
vec4 lightspot1 = lights[i+2];
|
||||
vec4 lightspot2 = lights[i+3];
|
||||
|
||||
vec3 L = normalize(lightpos.xyz - worldpos);
|
||||
vec3 H = normalize(V + L);
|
||||
|
||||
float attenuation = linearDistanceAttenuation(lightpos);
|
||||
if (lightspot1.w == 1.0)
|
||||
attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y);
|
||||
if (lightcolor.a < 0.0)
|
||||
attenuation *= clamp(dot(N, L), 0.0, 1.0); // Sign bit is the attenuated light flag
|
||||
|
||||
if (attenuation > 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<lightRange.z; i+=4)
|
||||
{
|
||||
vec4 lightpos = lights[i];
|
||||
vec4 lightcolor = lights[i+1];
|
||||
vec4 lightspot1 = lights[i+2];
|
||||
vec4 lightspot2 = lights[i+3];
|
||||
|
||||
vec3 L = normalize(lightpos.xyz - worldpos);
|
||||
vec3 H = normalize(V + L);
|
||||
|
||||
float attenuation = linearDistanceAttenuation(lightpos);
|
||||
if (lightspot1.w == 1.0)
|
||||
attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y);
|
||||
if (lightcolor.a < 0.0)
|
||||
attenuation *= clamp(dot(N, L), 0.0, 1.0); // Sign bit is the attenuated light flag
|
||||
|
||||
if (attenuation > 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));
|
||||
}
|
95
wadsrc/static/shaders/glsl/material_specular.fp
Normal file
95
wadsrc/static/shaders/glsl/material_specular.fp
Normal file
|
@ -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<lightRange.y; i+=4)
|
||||
{
|
||||
vec4 lightcolor = lights[i+1];
|
||||
vec2 attenuation = lightAttenuation(i, normal, viewdir, lightcolor.a);
|
||||
dynlight.rgb += lightcolor.rgb * attenuation.x;
|
||||
specular.rgb += lightcolor.rgb * attenuation.y;
|
||||
}
|
||||
|
||||
// subtractive lights
|
||||
for(int i=lightRange.y; i<lightRange.z; i+=4)
|
||||
{
|
||||
vec4 lightcolor = lights[i+1];
|
||||
vec2 attenuation = lightAttenuation(i, normal, viewdir, lightcolor.a);
|
||||
dynlight.rgb -= lightcolor.rgb * attenuation.x;
|
||||
specular.rgb -= lightcolor.rgb * attenuation.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynlight.rgb = clamp(color + desaturate(dynlight).rgb, 0.0, 1.4);
|
||||
specular.rgb = clamp(desaturate(specular).rgb, 0.0, 1.4);
|
||||
|
||||
vec3 frag = material.Base.rgb * dynlight.rgb + material.Specular * specular.rgb;
|
||||
|
||||
if (uLightIndex >= 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<lightRange.w; i+=4)
|
||||
{
|
||||
vec4 lightcolor = lights[i+1];
|
||||
vec2 attenuation = lightAttenuation(i, normal, viewdir, lightcolor.a);
|
||||
addlight.rgb += lightcolor.rgb * attenuation.x;
|
||||
}
|
||||
|
||||
frag = clamp(frag + desaturate(addlight).rgb, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
return frag;
|
||||
}
|
15
wadsrc/static/shaders/glsl/stencil.fp
Normal file
15
wadsrc/static/shaders/glsl/stencil.fp
Normal file
|
@ -0,0 +1,15 @@
|
|||
layout(location=0) out vec4 FragColor;
|
||||
#ifdef GBUFFER_PASS
|
||||
layout(location=1) out vec4 FragFog;
|
||||
layout(location=2) out vec4 FragNormal;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = vec4(1.0, 1.0, 1.0, 0.0);
|
||||
#ifdef GBUFFER_PASS
|
||||
FragFog = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
FragNormal = vec4(0.5, 0.5, 0.5, 1.0);
|
||||
#endif
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue