From 09f54b09409ddb65debc0a186eed06d1cca40252 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 27 Apr 2016 00:41:00 +0200 Subject: [PATCH] - cleaned up the clip plane management for portals. Unfortunately the math behind the old clip planes is utterly impenetrable and so poorly documented that I have no idea how to set that up, so it is deactivated for now. It wasn't working anyway. --- src/gl/data/gl_matrix.cpp | 12 ++++------ src/gl/data/gl_matrix.h | 5 ++++- src/gl/renderer/gl_renderstate.cpp | 33 +++++++++++++++++++++++----- src/gl/renderer/gl_renderstate.h | 20 +++++------------ src/gl/scene/gl_flats.cpp | 2 +- src/gl/scene/gl_portal.cpp | 34 +++++++---------------------- src/gl/shaders/gl_shader.cpp | 4 ++-- src/gl/shaders/gl_shader.h | 4 ++-- wadsrc/static/glstuff/gllight.png | Bin 0 -> 13073 bytes 9 files changed, 55 insertions(+), 59 deletions(-) create mode 100644 wadsrc/static/glstuff/gllight.png diff --git a/src/gl/data/gl_matrix.cpp b/src/gl/data/gl_matrix.cpp index 35683dbb8..a16dc770f 100644 --- a/src/gl/data/gl_matrix.cpp +++ b/src/gl/data/gl_matrix.cpp @@ -10,6 +10,7 @@ This is a simplified version of VSMatrix that has been adjusted for GZDoom's nee ----------------------------------------------------*/ +#include #include "gl/system/gl_system.h" #include #include @@ -139,14 +140,9 @@ VSMatrix::translate(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z) void VSMatrix::scale(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z) { - FLOATTYPE mat[16]; - - setIdentityMatrix(mat,4); - mat[0] = x; - mat[5] = y; - mat[10] = z; - - multMatrix(mat); + mMatrix[0] *= x; mMatrix[1] *= x; mMatrix[2] *= x; mMatrix[3] *= x; + mMatrix[4] *= y; mMatrix[5] *= y; mMatrix[6] *= y; mMatrix[7] *= y; + mMatrix[8] *= z; mMatrix[9] *= z; mMatrix[10] *= z; mMatrix[11] *= z; } diff --git a/src/gl/data/gl_matrix.h b/src/gl/data/gl_matrix.h index de4b400da..3ec1f5ff4 100644 --- a/src/gl/data/gl_matrix.h +++ b/src/gl/data/gl_matrix.h @@ -48,6 +48,7 @@ class VSMatrix { #ifdef USE_DOUBLE void multMatrix(const float *aMatrix); #endif + void multVector(FLOATTYPE *aVector); void multMatrix(const FLOATTYPE *aMatrix); void multMatrix(const VSMatrix &aMatrix) { @@ -92,7 +93,9 @@ class VSMatrix { { computeNormalMatrix(aMatrix.mMatrix); } - + bool inverseMatrix(VSMatrix &result); + void transpose(); + protected: static void crossProduct(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res); static FLOATTYPE dotProduct(const FLOATTYPE *a, const FLOATTYPE * b); diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index d1e30c560..fdb0e3cc5 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -86,8 +86,8 @@ void FRenderState::Reset() mColormapState = CM_DEFAULT; mLightParms[3] = -1.f; mSpecialEffect = EFF_NONE; - mClipHeightTop = 65536.f; - mClipHeightBottom = -65536.f; + mClipHeight = 0.f; + mClipHeightDirection = 0.f; ClearClipSplit(); stSrcBlend = stDstBlend = -1; @@ -140,8 +140,8 @@ bool FRenderState::ApplyShader() activeShader->muObjectColor.Set(mObjectColor); activeShader->muDynLightColor.Set(mDynColor.vec); activeShader->muInterpolationFactor.Set(mInterpolationFactor); - activeShader->muClipHeightTop.Set(mClipHeightTop); - activeShader->muClipHeightBottom.Set(mClipHeightBottom); + activeShader->muClipHeight.Set(mClipHeight); + activeShader->muClipHeightDirection.Set(mClipHeightDirection); activeShader->muTimer.Set(gl_frameMS * mShaderTimer / 1000.f); activeShader->muAlphaThreshold.Set(mAlphaThreshold); activeShader->muLightIndex.Set(mLightIndex); // will always be -1 for now @@ -324,4 +324,27 @@ void FRenderState::ApplyLightIndex(int index) } activeShader->muLightIndex.Set(index); } -} \ No newline at end of file +} + +void FRenderState::SetClipHeight(float height, float direction) +{ + mClipHeight = height; + mClipHeightDirection = direction; + if (direction != 0.f) + { + if (gl.glslversion >= 1.3f) glEnable(GL_CLIP_DISTANCE0); + else + { + // This does not work. Need someone who understands how glClipPlane works... + //glEnable(GL_CLIP_PLANE0); + // Plane mirrors never are slopes. + //double d[4] = { 0, direction, 0, -direction * height }; + //glClipPlane(GL_CLIP_PLANE0, d); + } + } + else + { + if (gl.glslversion >= 1.3f) glDisable(GL_CLIP_DISTANCE0); + //else glDisable(GL_CLIP_PLANE0); + } +} diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index e61ab004d..ca240c398 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -63,7 +63,7 @@ class FRenderState bool mModelMatrixEnabled; bool mTextureMatrixEnabled; float mInterpolationFactor; - float mClipHeightTop, mClipHeightBottom; + float mClipHeight, mClipHeightDirection; float mShaderTimer; bool mLastDepthClamp; @@ -135,25 +135,17 @@ public: mCurrentVertexBuffer = NULL; } - void SetClipHeightTop(float clip) + float GetClipHeight() { - mClipHeightTop = clip; + return mClipHeight; } - float GetClipHeightTop() + float GetClipHeightDirection() { - return mClipHeightTop; + return mClipHeightDirection; } - void SetClipHeightBottom(float clip) - { - mClipHeightBottom = clip; - } - - float GetClipHeightBottom() - { - return mClipHeightBottom; - } + void SetClipHeight(float height, float direction); void SetColor(float r, float g, float b, float a = 1.f, int desat = 0) { diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index c90e9541b..7406bb20e 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -438,7 +438,7 @@ void GLFlat::Draw(int pass, bool trans) // trans only has meaning for GLPASS_LIG { gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false); gl_SetPlaneTextureRotation(&plane, gltexture); - DrawSubsectors(pass, true, true); + DrawSubsectors(pass, gl.lightmethod != LM_SOFTWARE, true); gl_RenderState.EnableTextureMatrix(false); } if (renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 6c2a05592..93f25f28e 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -300,11 +300,9 @@ bool GLPortal::Start(bool usestencil, bool doquery) glDisable(GL_DEPTH_TEST); } } - planestack.Push(gl_RenderState.GetClipHeightTop()); - planestack.Push(gl_RenderState.GetClipHeightBottom()); - glDisable(GL_CLIP_DISTANCE0); - gl_RenderState.SetClipHeightBottom(-65536.f); - gl_RenderState.SetClipHeightTop(65536.f); + planestack.Push(gl_RenderState.GetClipHeight()); + planestack.Push(gl_RenderState.GetClipHeightDirection()); + gl_RenderState.SetClipHeight(0., 0.); // save viewpoint savedViewPos = ViewPos; @@ -363,13 +361,10 @@ void GLPortal::End(bool usestencil) PortalAll.Clock(); GLRenderer->mCurrentPortal = NextPortal; - float f; + float f, d; + planestack.Pop(d); planestack.Pop(f); - gl_RenderState.SetClipHeightBottom(f); - if (f > -65535.f) glEnable(GL_CLIP_DISTANCE0); - planestack.Pop(f); - gl_RenderState.SetClipHeightTop(f); - if (f < 65535.f) glEnable(GL_CLIP_DISTANCE0); + gl_RenderState.SetClipHeight(f, d); if (usestencil) { @@ -804,22 +799,9 @@ void GLPlaneMirrorPortal::DrawContents() GLRenderer->SetupView(ViewPos.X, ViewPos.Y, ViewPos.Z, ViewAngle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); ClearClipper(); - if (PlaneMirrorMode < 0) - { - gl_RenderState.SetClipHeightTop(planez); // ceiling mirror: clip everything with a z lower than the portal's ceiling - gl_RenderState.SetClipHeightBottom(-65536.f); - } - else - { - gl_RenderState.SetClipHeightBottom(planez); // floor mirror: clip everything with a z higher than the portal's floor - gl_RenderState.SetClipHeightTop(65536.f); - } - - glEnable(GL_CLIP_DISTANCE0); + gl_RenderState.SetClipHeight(planez, PlaneMirrorMode < 0? -1.f : 1.f); GLRenderer->DrawScene(); - glDisable(GL_CLIP_DISTANCE0); - gl_RenderState.SetClipHeightBottom(-65536.f); - gl_RenderState.SetClipHeightTop(65536.f); + gl_RenderState.SetClipHeight(0.f, 0.f); PlaneMirrorFlag--; PlaneMirrorMode=old_pm; } diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index af6accdb5..a86c57101 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -280,8 +280,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muSplitTopPlane.Init(hShader, "uSplitTopPlane"); muFixedColormap.Init(hShader, "uFixedColormap"); muInterpolationFactor.Init(hShader, "uInterpolationFactor"); - muClipHeightTop.Init(hShader, "uClipHeightTop"); - muClipHeightBottom.Init(hShader, "uClipHeightBottom"); + muClipHeight.Init(hShader, "uClipHeight"); + muClipHeightDirection.Init(hShader, "uClipHeightDirection"); muAlphaThreshold.Init(hShader, "uAlphaThreshold"); muTimer.Init(hShader, "timer"); diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index cf6480d9b..a7f7e4277 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -221,8 +221,8 @@ class FShader FUniform4f muSplitBottomPlane; FUniform4f muSplitTopPlane; FBufferedUniform1f muInterpolationFactor; - FBufferedUniform1f muClipHeightTop; - FBufferedUniform1f muClipHeightBottom; + FBufferedUniform1f muClipHeight; + FBufferedUniform1f muClipHeightDirection; FBufferedUniform1f muAlphaThreshold; FBufferedUniform1f muTimer; diff --git a/wadsrc/static/glstuff/gllight.png b/wadsrc/static/glstuff/gllight.png new file mode 100644 index 0000000000000000000000000000000000000000..a3a296b9f856f4654aae244e3400a4f33fd95902 GIT binary patch literal 13073 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 literal 0 HcmV?d00001