From 00fcf4bc06d1af43990bb08ca51c5fa1f3986f05 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Sun, 13 Jul 2014 13:25:42 +0200
Subject: [PATCH] - for some reason using world coordinates for clipping in the
 shader is somewhat imprecise so the clip plane heights have to be adjusted a
 bit for it.

---
 src/gl/renderer/gl_renderstate.cpp        |  6 ++++--
 src/gl/renderer/gl_renderstate.h          | 20 +++++++++++++++-----
 src/gl/scene/gl_portal.cpp                | 16 ++++++++++------
 src/gl/shaders/gl_shader.cpp              | 22 +++++++++++++++-------
 src/gl/shaders/gl_shader.h                |  5 +++--
 wadsrc/static/shaders/glsl/fogboundary.fp |  4 ++--
 wadsrc/static/shaders/glsl/main.fp        |  4 ++--
 wadsrc/static/shaders/glsl/shaderdefs.i   |  2 +-
 8 files changed, 52 insertions(+), 27 deletions(-)

diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp
index 83b630c91..6a510f85a 100644
--- a/src/gl/renderer/gl_renderstate.cpp
+++ b/src/gl/renderer/gl_renderstate.cpp
@@ -83,7 +83,8 @@ void FRenderState::Reset()
 	mColormapState = CM_DEFAULT;
 	mLightParms[3] = -1.f;
 	mSpecialEffect = EFF_NONE;
-	mClipHeight = 0.f;
+	mClipHeightTop = 65536.f;
+	mClipHeightBottom = -65536.f;
 }
 
 
@@ -145,7 +146,8 @@ bool FRenderState::ApplyShader()
 	activeShader->muObjectColor.Set(mObjectColor);
 	activeShader->muDynLightColor.Set(mDynColor.vec);
 	activeShader->muInterpolationFactor.Set(mInterpolationFactor);
-	activeShader->muClipHeight.Set(mClipHeight);
+	activeShader->muClipHeightTop.Set(mClipHeightTop);
+	activeShader->muClipHeightBottom.Set(mClipHeightBottom);
 
 	if (mGlowEnabled)
 	{
diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h
index 6653a7c0d..815974d97 100644
--- a/src/gl/renderer/gl_renderstate.h
+++ b/src/gl/renderer/gl_renderstate.h
@@ -57,7 +57,7 @@ class FRenderState
 	int mBlendEquation;
 	bool m2D;
 	float mInterpolationFactor;
-	float mClipHeight;
+	float mClipHeightTop, mClipHeightBottom;
 
 	FVertexBuffer *mVertexBuffer, *mCurrentVertexBuffer;
 	FStateVec4 mColor;
@@ -101,14 +101,24 @@ public:
 		mCurrentVertexBuffer = NULL;
 	}
 
-	void SetClipHeight(float clip)
+	void SetClipHeightTop(float clip)
 	{
-		mClipHeight = clip;
+		mClipHeightTop = clip;
 	}
 
-	float GetClipHeight()
+	float GetClipHeightTop()
 	{
-		return mClipHeight;
+		return mClipHeightTop;
+	}
+
+	void SetClipHeightBottom(float clip)
+	{
+		mClipHeightBottom = clip;
+	}
+
+	float GetClipHeightBottom()
+	{
+		return mClipHeightBottom;
 	}
 
 	void SetColor(float r, float g, float b, float a = 1.f, int desat = 0)
diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp
index 276679b74..28b9a1282 100644
--- a/src/gl/scene/gl_portal.cpp
+++ b/src/gl/scene/gl_portal.cpp
@@ -287,8 +287,10 @@ bool GLPortal::Start(bool usestencil, bool doquery)
 			glDisable(GL_DEPTH_TEST);
 		}
 	}
-	planestack.Push(gl_RenderState.GetClipHeight());
-	gl_RenderState.SetClipHeight(0.f);
+	planestack.Push(gl_RenderState.GetClipHeightTop());
+	planestack.Push(gl_RenderState.GetClipHeightBottom());
+	gl_RenderState.SetClipHeightTop(65536.f);
+	gl_RenderState.SetClipHeightBottom(-65536.f);
 
 	// save viewpoint
 	savedviewx=viewx;
@@ -351,7 +353,9 @@ void GLPortal::End(bool usestencil)
 
 	float f;
 	planestack.Pop(f);
-	gl_RenderState.SetClipHeight(f);
+	gl_RenderState.SetClipHeightBottom(f);
+	planestack.Pop(f);
+	gl_RenderState.SetClipHeightTop(f);
 
 	if (usestencil)
 	{
@@ -790,9 +794,9 @@ void GLPlaneMirrorPortal::DrawContents()
 	validcount++;
 
 	float f = FIXED2FLOAT(planez);
-	if (PlaneMirrorMode < 0) f -= 65536.f;	// ceiling mirror: clip everytihng with a z lower than the portal's ceiling
-	else f += 65536.f;	// floor mirror: clip everything with a z higher than the portal's floor
-	gl_RenderState.SetClipHeight(f);
+	// the coordinate fudging is needed because for some reason this is nowhere near precise and leaves gaps. Strange...
+	if (PlaneMirrorMode < 0) gl_RenderState.SetClipHeightTop(f+0.3f);	// ceiling mirror: clip everytihng with a z lower than the portal's ceiling
+	else gl_RenderState.SetClipHeightBottom(f-0.3f);	// floor mirror: clip everything with a z higher than the portal's floor
 
 	PlaneMirrorFlag++;
 	GLRenderer->SetupView(viewx, viewy, viewz, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1));
diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp
index f3b483461..50b67c4ca 100644
--- a/src/gl/shaders/gl_shader.cpp
+++ b/src/gl/shaders/gl_shader.cpp
@@ -200,7 +200,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 	muGlowTopPlane.Init(hShader, "uGlowTopPlane");
 	muFixedColormap.Init(hShader, "uFixedColormap");
 	muInterpolationFactor.Init(hShader, "uInterpolationFactor");
-	muClipHeight.Init(hShader, "uClipHeight");
+	muClipHeightTop.Init(hShader, "uClipHeightTop");
+	muClipHeightBottom.Init(hShader, "uClipHeightBottom");
 
 	timer_index = glGetUniformLocation(hShader, "timer");
 	lights_index = glGetUniformLocation(hShader, "lights");
@@ -249,16 +250,18 @@ bool FShader::Bind()
 //
 //==========================================================================
 
-FShader *FShaderManager::Compile (const char *ShaderName, const char *ShaderPath)
+FShader *FShaderManager::Compile (const char *ShaderName, const char *ShaderPath, bool usediscard)
 {
+	FString defines;
 	// this can't be in the shader code due to ATI strangeness.
-	const char *str = (gl.MaxLights() == 128)? "#define MAXLIGHTS128\n" : "";
+	if (gl.MaxLights() == 128) defines += "#define MAXLIGHTS128\n";
+	if (!usediscard) defines += "#define NO_DISCARD\n";
 
 	FShader *shader = NULL;
 	try
 	{
 		shader = new FShader(ShaderName);
-		if (!shader->Load(ShaderName, "shaders/glsl/main.vp", "shaders/glsl/main.fp", ShaderPath, str))
+		if (!shader->Load(ShaderName, "shaders/glsl/main.vp", "shaders/glsl/main.fp", ShaderPath, defines.GetChars()))
 		{
 			I_FatalError("Unable to load shader %s\n", ShaderName);
 		}
@@ -352,11 +355,16 @@ FShaderManager::~FShaderManager()
 
 void FShaderManager::CompileShaders()
 {
-	mActiveShader = mEffectShaders[0] = mEffectShaders[1] = NULL;
+	mActiveShader = NULL;
+
+	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);
+		FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, true);
 		mTextureEffects.Push(shc);
 	}
 
@@ -365,7 +373,7 @@ void FShaderManager::CompileShaders()
 		FString name = ExtractFileBase(usershaders[i]);
 		FName sfn = name;
 
-		FShader *shc = Compile(sfn, usershaders[i]);
+		FShader *shc = Compile(sfn, usershaders[i], true);
 		mTextureEffects.Push(shc);
 	}
 
diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h
index eea46c8c8..da3be18cb 100644
--- a/src/gl/shaders/gl_shader.h
+++ b/src/gl/shaders/gl_shader.h
@@ -193,7 +193,8 @@ class FShader
 	FUniform4f muGlowBottomPlane;
 	FUniform4f muGlowTopPlane;
 	FBufferedUniform1f muInterpolationFactor;
-	FBufferedUniform1f muClipHeight;
+	FBufferedUniform1f muClipHeightTop;
+	FBufferedUniform1f muClipHeightBottom;
 	
 	int timer_index;
 	int lights_index;
@@ -244,7 +245,7 @@ class FShaderManager
 public:
 	FShaderManager();
 	~FShaderManager();
-	FShader *Compile(const char *ShaderName, const char *ShaderPath);
+	FShader *Compile(const char *ShaderName, const char *ShaderPath, bool usediscard);
 	int Find(const char *mame);
 	FShader *BindEffect(int effect);
 	void SetActiveShader(FShader *sh);
diff --git a/wadsrc/static/shaders/glsl/fogboundary.fp b/wadsrc/static/shaders/glsl/fogboundary.fp
index 264f8e8cd..edf9384c6 100644
--- a/wadsrc/static/shaders/glsl/fogboundary.fp
+++ b/wadsrc/static/shaders/glsl/fogboundary.fp
@@ -11,8 +11,8 @@ void main()
 {
 #ifndef NO_DISCARD
 	// clip plane emulation for plane reflections. These are always perfectly horizontal so a simple check of the pixelpos's y coordinate is sufficient.
-	if (pixelpos.y > uClipHeight + 65536.0) discard;
-	if (pixelpos.y < uClipHeight - 65536.0) discard;
+	if (pixelpos.y > uClipHeightTop) discard;
+	if (pixelpos.y < uClipHeightBottom) discard;
 #endif
 
 	float fogdist;
diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp
index 467723330..5655493bd 100644
--- a/wadsrc/static/shaders/glsl/main.fp
+++ b/wadsrc/static/shaders/glsl/main.fp
@@ -218,8 +218,8 @@ void main()
 {
 #ifndef NO_DISCARD
 	// clip plane emulation for plane reflections. These are always perfectly horizontal so a simple check of the pixelpos's y coordinate is sufficient.
-	if (pixelpos.y > uClipHeight + 65536.0) discard;
-	if (pixelpos.y < uClipHeight - 65536.0) discard;
+	if (pixelpos.y > uClipHeightTop) discard;
+	if (pixelpos.y < uClipHeightBottom) discard;
 #endif
 
 	vec4 frag = ProcessTexel();
diff --git a/wadsrc/static/shaders/glsl/shaderdefs.i b/wadsrc/static/shaders/glsl/shaderdefs.i
index 198992fe6..d281ee231 100644
--- a/wadsrc/static/shaders/glsl/shaderdefs.i
+++ b/wadsrc/static/shaders/glsl/shaderdefs.i
@@ -1,7 +1,7 @@
 // This file contains common data definitions for both vertex and fragment shader
 
 uniform vec4 uCameraPos;
-uniform float uClipHeight;
+uniform float uClipHeightTop, uClipHeightBottom;
 
 uniform int uTextureMode;