- updated backend from GZDoom.

This commit is contained in:
Christoph Oelckers 2020-04-26 23:17:54 +02:00
parent 88eea8269a
commit 5728241c1c
107 changed files with 5548 additions and 778 deletions

View file

@ -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} )

View file

@ -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.

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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
{

View file

@ -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"

View file

@ -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.)

View file

@ -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;

View 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;
}
}

View 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

View 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
}
}

View 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

View file

@ -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();
}
}

View file

@ -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;
};
}

View file

@ -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:

View 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;
}
}

View 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

View 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;
}

View file

@ -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)

View 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

View 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(&copyptr[0], parmcnt, ELEMENT_SIZE);
memcpy(&copyptr[4], &data.arrays[0][0], size0 * ELEMENT_SIZE);
memcpy(&copyptr[4 + 4*size0], &data.arrays[1][0], size1 * ELEMENT_SIZE);
memcpy(&copyptr[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);
}

View 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

View 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();
}

View file

@ -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; }
};

View file

@ -1,6 +1,5 @@
#pragma once
#include "v_palette.h"
#include "vectors.h"
#include "matrix.h"
#include "hw_material.h"

View file

@ -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" },
};

View 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();
}

View 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;
};

View file

@ -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();

View file

@ -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);
}
};

View 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 };
}
}

View 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;
};

View file

@ -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)

View file

@ -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

View 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;
};

View file

@ -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);
}
}

View file

@ -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)
{

View file

@ -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__

View file

@ -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()
{
}
}

View file

@ -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();
};

View file

@ -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));

View file

@ -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;

View file

@ -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);
};

View file

@ -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

View file

@ -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__

View file

@ -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;
}
}

View file

@ -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__

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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();

View file

@ -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;
}

View file

@ -84,5 +84,8 @@ const char *GetVersionString();
#define GAME_DIR ".config/" GAMENAMELOWERCASE
#endif
const int SAVEPICWIDTH = 240;
const int SAVEPICHEIGHT = 180;
#endif //__VERSION_H__

View file

@ -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;
}

View file

@ -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);
}

View 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);
}

View 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
}

View file

@ -0,0 +1,5 @@
vec4 ProcessLight(Material material, vec4 color)
{
return color;
}

View file

@ -0,0 +1,7 @@
void SetupMaterial(inout Material material)
{
material.Base = ProcessTexel();
material.Normal = ApplyNormalMap(vTexCoord.st);
material.Bright = texture(brighttexture, vTexCoord.st);
}

View file

@ -0,0 +1,6 @@
void SetupMaterial(inout Material material)
{
vec2 texCoord = GetTexCoord();
SetMaterialProps(material, texCoord);
}

View file

@ -0,0 +1,5 @@
void SetupMaterial(inout Material material)
{
SetMaterialProps(material, vTexCoord.st);
}

View file

@ -0,0 +1,6 @@
vec4 ProcessTexel()
{
return desaturate(uObjectColor);
}

View 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;
}

View 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;
}

View 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;
}

View 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());
}

View 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());
}

View 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());
}

View 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());
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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);
}

View 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;
}

View 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;
}

View 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
}

View 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;
}

View file

@ -0,0 +1,5 @@
vec3 ProcessMaterialLight(Material material, vec3 color)
{
return material.Base.rgb * clamp(color + desaturate(uDynLightColor).rgb, 0.0, 1.4);
}

View 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;
}

View 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));
}

View 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;
}

View 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