From 105b92bbfd2a623a7bc6f59c4f7110c5b259c9b2 Mon Sep 17 00:00:00 2001
From: havoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Mon, 28 Oct 2002 09:17:44 +0000
Subject: [PATCH] implemented scissor rect clipping of lights in realtime
 lighting mode DP is no longer fillrate limited on my GF4 at 640x480 :)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@2571 d7cf8633-e32d-0410-b094-e92efae38249
---
 gl_backend.c |  29 +++++++++++
 gl_backend.h |   1 +
 gl_rmain.c   |  14 +++++
 glquake.h    |   6 +++
 r_shadow.c   | 144 +++++++++++++++++++++++++++++++++++++++++++++++++--
 r_shadow.h   |   1 +
 vid_shared.c |   2 +
 7 files changed, 194 insertions(+), 3 deletions(-)

diff --git a/gl_backend.c b/gl_backend.c
index d735341a..8f681d23 100644
--- a/gl_backend.c
+++ b/gl_backend.c
@@ -80,6 +80,7 @@ static matrix4x4_t backend_viewmatrix;
 static matrix4x4_t backend_modelmatrix;
 static matrix4x4_t backend_modelviewmatrix;
 static matrix4x4_t backend_glmodelviewmatrix;
+static matrix4x4_t backend_projectmatrix;
 
 static int backendunits, backendactive;
 static qbyte *varray_bcolor;
@@ -289,6 +290,22 @@ void GL_SetupView_Mode_PerspectiveInfiniteFarClip (double fovx, double fovy, dou
 	qglLoadMatrixd(m);
 	qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
 	GL_SetupView_Orientation_Identity();
+	backend_projectmatrix.m[0][0] = m[0];
+	backend_projectmatrix.m[1][0] = m[1];
+	backend_projectmatrix.m[2][0] = m[2];
+	backend_projectmatrix.m[3][0] = m[3];
+	backend_projectmatrix.m[0][1] = m[4];
+	backend_projectmatrix.m[1][1] = m[5];
+	backend_projectmatrix.m[2][1] = m[6];
+	backend_projectmatrix.m[3][1] = m[7];
+	backend_projectmatrix.m[0][2] = m[8];
+	backend_projectmatrix.m[1][2] = m[9];
+	backend_projectmatrix.m[2][2] = m[10];
+	backend_projectmatrix.m[3][2] = m[11];
+	backend_projectmatrix.m[0][3] = m[12];
+	backend_projectmatrix.m[1][3] = m[13];
+	backend_projectmatrix.m[2][3] = m[14];
+	backend_projectmatrix.m[3][3] = m[15];
 }
 
 void GL_SetupView_Mode_Ortho (double x1, double y1, double x2, double y2, double zNear, double zFar)
@@ -447,6 +464,18 @@ void GL_Color(float cr, float cg, float cb, float ca)
 	qglColor4f(cr, cg, cb, ca);
 }
 
+void GL_TransformToScreen(const vec4_t in, vec4_t out)
+{
+	vec4_t temp;
+	float iw;
+	Matrix4x4_Transform4 (&backend_viewmatrix, in, temp);
+	Matrix4x4_Transform4 (&backend_projectmatrix, temp, out);
+	iw = 1.0f / out[3];
+	out[0] = r_refdef.x + (out[0] * iw + 1.0f) * r_refdef.width * 0.5f;
+	out[1] = r_refdef.y + (out[1] * iw + 1.0f) * r_refdef.height * 0.5f;
+	out[2] = out[2] * iw;
+}
+
 // called at beginning of frame
 void R_Mesh_Start(void)
 {
diff --git a/gl_backend.h b/gl_backend.h
index 40bb6cee..f26fa654 100644
--- a/gl_backend.h
+++ b/gl_backend.h
@@ -17,6 +17,7 @@ void GL_SetupView_Mode_PerspectiveInfiniteFarClip (double fovx, double fovy, dou
 void GL_SetupView_Mode_Ortho (double x1, double y1, double x2, double y2, double zNear, double zFar);
 void GL_UseColorArray(void);
 void GL_Color(float cr, float cg, float cb, float ca);
+void GL_TransformToScreen(const vec4_t in, vec4_t out);
 
 extern cvar_t gl_lockarrays;
 
diff --git a/gl_rmain.c b/gl_rmain.c
index 7ba84023..24c9baa0 100644
--- a/gl_rmain.c
+++ b/gl_rmain.c
@@ -865,6 +865,16 @@ void R_ShadowVolumeLighting (int visiblevolumes)
 				if (clipmaxs[2] < leaf->maxs[2]) clipmaxs[2] = leaf->maxs[2];
 			}
 		}
+		if (clipmins[0] < wl->mins[0]) clipmins[0] = wl->mins[0];
+		if (clipmins[1] < wl->mins[1]) clipmins[1] = wl->mins[1];
+		if (clipmins[2] < wl->mins[2]) clipmins[2] = wl->mins[2];
+		if (clipmaxs[0] > wl->maxs[0]) clipmaxs[0] = wl->maxs[0];
+		if (clipmaxs[1] > wl->maxs[1]) clipmaxs[1] = wl->maxs[1];
+		if (clipmaxs[2] > wl->maxs[2]) clipmaxs[2] = wl->maxs[2];
+
+		if (R_Shadow_ScissorForBBoxAndSphere(clipmins, clipmaxs, wl->origin, wl->cullradius))
+			continue;
+
 		// mark the leafs we care about so only things in those leafs will matter
 		for (i = 0;i < wl->numleafs;i++)
 			wl->leafs[i]->worldnodeframe = shadowframecount;
@@ -1046,6 +1056,9 @@ void R_ShadowVolumeLighting (int visiblevolumes)
 		clipmaxs[1] = rd->origin[1] + cullradius;
 		clipmaxs[2] = rd->origin[2] + cullradius;
 
+		if (R_Shadow_ScissorForBBoxAndSphere(clipmins, clipmaxs, rd->origin, rd->cullradius))
+			continue;
+
 		if (!visiblevolumes)
 			R_Shadow_Stage_ShadowVolumes();
 		R_TestAndDrawShadowVolume(&cl_entities[0].render, rd->origin, cullradius, lightradius, clipmins, clipmaxs);
@@ -1102,6 +1115,7 @@ void R_ShadowVolumeLighting (int visiblevolumes)
 
 	if (!visiblevolumes)
 		R_Shadow_Stage_End();
+	qglDisable(GL_SCISSOR_TEST);
 }
 
 static void R_SetFrustum (void)
diff --git a/glquake.h b/glquake.h
index f68e815f..ef65f0d1 100644
--- a/glquake.h
+++ b/glquake.h
@@ -337,6 +337,12 @@ extern int gl_dot3ext;
 #endif
 */
 
+#ifndef GL_SCISSOR_TEST
+#define GL_SCISSOR_TEST				0x0C11
+#define GL_SCISSOR_BOX				0x0C10
+#endif
+
+extern void (GLAPIENTRY *qglScissor)(GLint x, GLint y, GLsizei width, GLsizei height);
 
 extern void (GLAPIENTRY *qglClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
 
diff --git a/r_shadow.c b/r_shadow.c
index 4ee1b62c..aa664707 100644
--- a/r_shadow.c
+++ b/r_shadow.c
@@ -37,6 +37,7 @@ cvar_t r_shadow_erasebydrawing = {0, "r_shadow_erasebydrawing", "0"};
 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "0"};
 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
+cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
 
 void R_Shadow_ClearWorldLights(void);
 void r_shadow_start(void)
@@ -92,6 +93,7 @@ void R_Shadow_Init(void)
 	Cvar_RegisterVariable(&r_shadow_gloss);
 	Cvar_RegisterVariable(&r_shadow_debuglight);
 	Cvar_RegisterVariable(&r_shadow_erasebydrawing);
+	Cvar_RegisterVariable(&r_shadow_scissor);
 	R_Shadow_EditLights_Init();
 	R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
 }
@@ -532,6 +534,8 @@ void R_Shadow_Stage_ShadowVolumes(void)
 	qglEnable(GL_CULL_FACE);
 	qglEnable(GL_DEPTH_TEST);
 	r_shadowstage = SHADOWSTAGE_STENCIL;
+	if (!r_shadow_erasebydrawing.integer)
+		qglClear(GL_STENCIL_BUFFER_BIT);
 }
 
 void R_Shadow_Stage_Light(void)
@@ -578,10 +582,7 @@ int R_Shadow_Stage_EraseShadowVolumes(void)
 		return true;
 	}
 	else
-	{
-		qglClear(GL_STENCIL_BUFFER_BIT);
 		return false;
-	}
 }
 
 void R_Shadow_Stage_End(void)
@@ -594,6 +595,7 @@ void R_Shadow_Stage_End(void)
 	// now restore the rest of the state to normal
 	GL_Color(1, 1, 1, 1);
 	qglColorMask(1, 1, 1, 1);
+	qglDisable(GL_SCISSOR_TEST);
 	qglDepthFunc(GL_LEQUAL);
 	qglDisable(GL_STENCIL_TEST);
 	qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
@@ -611,6 +613,142 @@ void R_Shadow_Stage_End(void)
 	r_shadowstage = SHADOWSTAGE_NONE;
 }
 
+int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
+{
+	int i, ix1, iy1, ix2, iy2;
+	float x1, y1, x2, y2, x, y;
+	vec3_t smins, smaxs;
+	vec4_t v, v2;
+	if (!r_shadow_scissor.integer)
+		return false;
+	// if view is inside the box, just say yes it's visible
+	if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
+	 && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
+	 && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
+	{
+		qglDisable(GL_SCISSOR_TEST);
+		return false;
+	}
+	VectorSubtract(r_origin, origin, v);
+	if (DotProduct(v, v) < radius * radius)
+	{
+		qglDisable(GL_SCISSOR_TEST);
+		return false;
+	}
+	// create viewspace bbox
+	for (i = 0;i < 8;i++)
+	{
+		v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
+		v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
+		v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
+		v2[0] = DotProduct(v, vright);
+		v2[1] = DotProduct(v, vup);
+		v2[2] = DotProduct(v, vpn);
+		if (i)
+		{
+			if (smins[0] > v2[0]) smins[0] = v2[0];
+			if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
+			if (smins[1] > v2[1]) smins[1] = v2[1];
+			if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
+			if (smins[2] > v2[2]) smins[2] = v2[2];
+			if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
+		}
+		else
+		{
+			smins[0] = smaxs[0] = v2[0];
+			smins[1] = smaxs[1] = v2[1];
+			smins[2] = smaxs[2] = v2[2];
+		}
+	}
+	// now we have a bbox in viewspace
+	// clip it to the viewspace version of the sphere
+	v[0] = origin[0] - r_origin[0];
+	v[1] = origin[1] - r_origin[1];
+	v[2] = origin[2] - r_origin[2];
+	v2[0] = DotProduct(v, vright);
+	v2[1] = DotProduct(v, vup);
+	v2[2] = DotProduct(v, vpn);
+	if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
+	if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
+	if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
+	if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
+	if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
+	if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
+	// clip it to the view plane
+	if (smins[2] < 1)
+		smins[2] = 1;
+	// return true if that culled the box
+	if (smins[2] >= smaxs[2])
+		return true;
+	// ok some of it is infront of the view, transform each corner back to
+	// worldspace and then to screenspace and make screen rect
+	for (i = 0;i < 8;i++)
+	{
+		v2[0] = (i & 1) ? smins[0] : smaxs[0];
+		v2[1] = (i & 2) ? smins[1] : smaxs[1];
+		v2[2] = (i & 4) ? smins[2] : smaxs[2];
+		v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
+		v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
+		v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
+		v[3] = 1.0f;
+		GL_TransformToScreen(v, v2);
+		//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
+		x = v2[0];
+		y = v2[1];
+		if (i)
+		{
+			if (x1 > x) x1 = x;
+			if (x2 < x) x2 = x;
+			if (y1 > y) y1 = y;
+			if (y2 < y) y2 = y;
+		}
+		else
+		{
+			x1 = x2 = x;
+			y1 = y2 = y;
+		}
+	}
+	/*
+	// this code doesn't handle boxes with any points behind view properly
+	x1 = 1000;x2 = -1000;
+	y1 = 1000;y2 = -1000;
+	for (i = 0;i < 8;i++)
+	{
+		v[0] = (i & 1) ? mins[0] : maxs[0];
+		v[1] = (i & 2) ? mins[1] : maxs[1];
+		v[2] = (i & 4) ? mins[2] : maxs[2];
+		v[3] = 1.0f;
+		GL_TransformToScreen(v, v2);
+		//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
+		if (v2[2] > 0)
+		{
+			x = v2[0];
+			y = v2[1];
+
+			if (x1 > x) x1 = x;
+			if (x2 < x) x2 = x;
+			if (y1 > y) y1 = y;
+			if (y2 < y) y2 = y;
+		}
+	}
+	*/
+	ix1 = x1 - 1.0f;
+	iy1 = y1 - 1.0f;
+	ix2 = x2 + 1.0f;
+	iy2 = y2 + 1.0f;
+	//Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
+	if (ix1 < r_refdef.x) ix1 = r_refdef.x;
+	if (iy1 < r_refdef.y) iy1 = r_refdef.y;
+	if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
+	if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
+	if (ix2 <= ix1 || iy2 <= iy1)
+		return true;
+	// set up the scissor rectangle
+	qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
+	qglEnable(GL_SCISSOR_TEST);
+	return false;
+}
+
 void R_Shadow_GenTexCoords_Attenuation2D1D(float *out2d, float *out1d, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, float lightradius)
 {
 	int i;
diff --git a/r_shadow.h b/r_shadow.h
index 1aa85320..9b119357 100644
--- a/r_shadow.h
+++ b/r_shadow.h
@@ -24,6 +24,7 @@ void R_Shadow_Stage_Light(void);
 // otherwise clears stencil
 int R_Shadow_Stage_EraseShadowVolumes(void);
 void R_Shadow_Stage_End(void);
+int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius);
 
 typedef struct worldlight_s
 {
diff --git a/vid_shared.c b/vid_shared.c
index a65d5a72..88f2dbb8 100644
--- a/vid_shared.c
+++ b/vid_shared.c
@@ -179,6 +179,7 @@ void (GLAPIENTRY *qglTexImage3D)(GLenum target, GLint level, GLenum internalForm
 void (GLAPIENTRY *qglTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
 void (GLAPIENTRY *qglCopyTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
 
+void (GLAPIENTRY *qglScissor)(GLint x, GLint y, GLsizei width, GLsizei height);
 
 int GL_CheckExtension(const char *name, const gl_extensionfunctionlist_t *funcs, const char *disableparm, int silent)
 {
@@ -303,6 +304,7 @@ static gl_extensionfunctionlist_t opengl110funcs[] =
 	{"glCopyTexImage2D", (void **) &qglCopyTexImage2D},
 	{"glCopyTexSubImage1D", (void **) &qglCopyTexSubImage1D},
 	{"glCopyTexSubImage2D", (void **) &qglCopyTexSubImage2D},
+	{"glScissor", (void **) &qglScissor},
 	{NULL, NULL}
 };
 
-- 
2.39.5