From 1151781920e0ab925cd2e5578474b7da9ebd6a75 Mon Sep 17 00:00:00 2001
From: vortex <vortex@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Mon, 2 May 2011 02:09:59 +0000
Subject: [PATCH] Transparent sorting: added maxdist and array size cvars which
 can be tweaked to increase performance (can give up to 10-20% with no quality
 loss on scenes with large amount of blended surfaces). Added "dpnortlight"
 shader keyword which disables full rtlight rendering on a surface, useful
 with grass which (when blended) can cause deadly speed loss.

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@11090 d7cf8633-e32d-0410-b094-e92efae38249
::stable-branch::merge=ec581e708c6874f1e1fa64343421683fd16f8609
---
 cl_particles.c |  7 +++---
 gl_rmain.c     |  9 ++++---
 gl_rsurf.c     |  3 ++-
 meshqueue.c    | 66 +++++++++++++++++++++++++++++++++++---------------
 meshqueue.h    |  3 +++
 model_brush.h  |  2 ++
 model_shared.c |  4 +++
 model_shared.h |  1 +
 8 files changed, 68 insertions(+), 27 deletions(-)

diff --git a/cl_particles.c b/cl_particles.c
index 392b46b7..43fb79f4 100644
--- a/cl_particles.c
+++ b/cl_particles.c
@@ -2281,14 +2281,13 @@ static void r_part_newmap(void)
 	CL_Particles_LoadEffectInfo();
 }
 
-#define BATCHSIZE 256
-unsigned short particle_elements[BATCHSIZE*6];
-float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
+unsigned short particle_elements[MESHQUEUE_TRANSPARENT_BATCHSIZE*6];
+float particle_vertex3f[MESHQUEUE_TRANSPARENT_BATCHSIZE*12], particle_texcoord2f[MESHQUEUE_TRANSPARENT_BATCHSIZE*8], particle_color4f[MESHQUEUE_TRANSPARENT_BATCHSIZE*16];
 
 void R_Particles_Init (void)
 {
 	int i;
-	for (i = 0;i < BATCHSIZE;i++)
+	for (i = 0;i < MESHQUEUE_TRANSPARENT_BATCHSIZE;i++)
 	{
 		particle_elements[i*6+0] = i*4+0;
 		particle_elements[i*6+1] = i*4+1;
diff --git a/gl_rmain.c b/gl_rmain.c
index ffcf1e68..4facb05d 100644
--- a/gl_rmain.c
+++ b/gl_rmain.c
@@ -121,6 +121,8 @@ cvar_t r_fog_exp2 = {0, "r_fog_exp2", "0", "uses GL_EXP2 fog (as in Nehahra) rat
 cvar_t r_fog_clear = {0, "r_fog_clear", "1", "clears renderbuffer with fog color before render starts"};
 cvar_t r_drawfog = {CVAR_SAVE, "r_drawfog", "1", "allows one to disable fog rendering"};
 cvar_t r_transparentdepthmasking = {CVAR_SAVE, "r_transparentdepthmasking", "0", "enables depth writes on transparent meshes whose materially is normally opaque, this prevents seeing the inside of a transparent mesh"};
+cvar_t r_transparent_sortmaxdist = {CVAR_SAVE, "r_transparent_sortmaxdist", "32768", "upper distance limit for transparent sorting"};
+cvar_t r_transparent_sortarraysize = {CVAR_SAVE, "r_transparent_sortarraysize", "4096", "number of distance-sorting layers"};
 
 cvar_t gl_fogenable = {0, "gl_fogenable", "0", "nehahra fog enable (for Nehahra compatibility only)"};
 cvar_t gl_fogdensity = {0, "gl_fogdensity", "0.25", "nehahra fog density (recommend values below 0.1) (for Nehahra compatibility only)"};
@@ -4101,6 +4103,8 @@ void GL_Main_Init(void)
 	Cvar_RegisterVariable(&r_fog_clear);
 	Cvar_RegisterVariable(&r_drawfog);
 	Cvar_RegisterVariable(&r_transparentdepthmasking);
+	Cvar_RegisterVariable(&r_transparent_sortmaxdist);
+	Cvar_RegisterVariable(&r_transparent_sortarraysize);
 	Cvar_RegisterVariable(&r_texture_dds_load);
 	Cvar_RegisterVariable(&r_texture_dds_save);
 	Cvar_RegisterVariable(&r_texture_sRGB_2d);
@@ -9965,8 +9969,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
 	int texturenumsurfaces, endsurface;
 	texture_t *texture;
 	const msurface_t *surface;
-#define MAXBATCH_TRANSPARENTSURFACES 256
-	const msurface_t *texturesurfacelist[MAXBATCH_TRANSPARENTSURFACES];
+	const msurface_t *texturesurfacelist[MESHQUEUE_TRANSPARENT_BATCHSIZE];
 
 	// if the model is static it doesn't matter what value we give for
 	// wantnormals and wanttangents, so this logic uses only rules applicable
@@ -10051,7 +10054,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
 		texture = surface->texture;
 		rsurface.texture = R_GetCurrentTexture(texture);
 		// scan ahead until we find a different texture
-		endsurface = min(i + MAXBATCH_TRANSPARENTSURFACES, numsurfaces);
+		endsurface = min(i + MESHQUEUE_TRANSPARENT_BATCHSIZE, numsurfaces);
 		texturenumsurfaces = 0;
 		texturesurfacelist[texturenumsurfaces++] = surface;
 		if(FAKELIGHT_ENABLED)
diff --git a/gl_rsurf.c b/gl_rsurf.c
index 06d8fb03..9d5bca92 100644
--- a/gl_rsurf.c
+++ b/gl_rsurf.c
@@ -1487,7 +1487,8 @@ void R_Q1BSP_DrawLight(entity_render_t *ent, int numsurfaces, const int *surface
 			for (kend = k;kend < batchnumsurfaces && tex == batchsurfacelist[kend]->texture;kend++)
 				;
 			// now figure out what to do with this particular range of surfaces
-			if (!(rsurface.texture->currentmaterialflags & MATERIALFLAG_WALL))
+			// VorteX: added MATERIALFLAG_NORTLIGHT
+			if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WALL + MATERIALFLAG_NORTLIGHT)) != MATERIALFLAG_WALL)
 				continue;
 			if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)))
 				continue;
diff --git a/meshqueue.c b/meshqueue.c
index a5510be7..aecb6ab6 100644
--- a/meshqueue.c
+++ b/meshqueue.c
@@ -13,7 +13,14 @@ typedef struct meshqueue_s
 }
 meshqueue_t;
 
+int trans_sortarraysize;
+meshqueue_t **trans_hash = NULL;
+meshqueue_t ***trans_hashpointer = NULL;
+extern cvar_t r_transparent_sortarraysize;
+extern cvar_t r_transparent_sortmaxdist;
+
 float mqt_viewplanedist;
+float mqt_viewmindist;
 float mqt_viewmaxdist;
 meshqueue_t *mqt_array;
 int mqt_count;
@@ -24,6 +31,7 @@ void R_MeshQueue_BeginScene(void)
 	mqt_count = 0;
 	mqt_viewplanedist = DotProduct(r_refdef.view.origin, r_refdef.view.forward);
 	mqt_viewmaxdist = 0;
+	mqt_viewmindist = 999999999;
 }
 
 void R_MeshQueue_AddTransparent(const vec3_t center, void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist), const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
@@ -49,47 +57,67 @@ void R_MeshQueue_AddTransparent(const vec3_t center, void (*callback)(const enti
 	mq->dist = DotProduct(center, r_refdef.view.forward) - mqt_viewplanedist;
 	mq->next = NULL;
 	mqt_viewmaxdist = max(mqt_viewmaxdist, mq->dist);
+	mqt_viewmindist = min(mqt_viewmindist, mq->dist);
 }
 
 void R_MeshQueue_RenderTransparent(void)
 {
-	int i;
-	int hashdist;
-	int batchnumsurfaces;
+	int i, hashindex, maxhashindex, batchnumsurfaces;
 	float distscale;
 	const entity_render_t *ent;
 	const rtlight_t *rtlight;
 	void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfaceindices);
+	int batchsurfaceindex[MESHQUEUE_TRANSPARENT_BATCHSIZE];
 	meshqueue_t *mqt;
-	static meshqueue_t *hash[4096], **hashpointer[4096];
-	int batchsurfaceindex[256];
+
 	if (!mqt_count)
 		return;
-	memset(hash, 0, sizeof(hash));
-	for (i = 0;i < 4096;i++)
-		hashpointer[i] = &hash[i];
-	distscale = 4095.0f / max(mqt_viewmaxdist, 4095);
-	for (i = 0, mqt = mqt_array;i < mqt_count;i++, mqt++)
+
+	// check for bad cvars
+	if (r_transparent_sortarraysize.integer < 1 || r_transparent_sortarraysize.integer > 32768)
+		Cvar_SetValueQuick(&r_transparent_sortarraysize, bound(1, r_transparent_sortarraysize.integer, 32768));
+	if (r_transparent_sortmaxdist.integer < 1 || r_transparent_sortmaxdist.integer > 32768)
+		Cvar_SetValueQuick(&r_transparent_sortmaxdist, bound(1, r_transparent_sortmaxdist.integer, 32768));
+
+	// update hash array
+	if (trans_sortarraysize != r_transparent_sortarraysize.integer)
 	{
-		// generate index
-		hashdist = (int) (mqt->dist * distscale);
-		hashdist = bound(0, hashdist, 4095);
+		trans_sortarraysize = r_transparent_sortarraysize.integer;
+		if (trans_hash)
+			Mem_Free(trans_hash);
+		trans_hash = Mem_Alloc(cls.permanentmempool, sizeof(trans_hash) * trans_sortarraysize); 
+		if (trans_hashpointer)
+			Mem_Free(trans_hashpointer);
+		trans_hashpointer = Mem_Alloc(cls.permanentmempool, sizeof(trans_hashpointer) * trans_sortarraysize); 
+	}
+
+	// build index
+	memset(trans_hash, 0, sizeof(trans_hash) * trans_sortarraysize);
+	for (i = 0; i < trans_sortarraysize; i++)
+		trans_hashpointer[i] = &trans_hash[i];
+	distscale = (trans_sortarraysize - 1) / max( min(mqt_viewmaxdist, r_transparent_sortmaxdist.integer) - mqt_viewmindist, 64 );
+	maxhashindex = trans_sortarraysize - 1;
+	for (i = 0, mqt = mqt_array; i < mqt_count; i++, mqt++)
+	{
+		hashindex = bound(0, (int)(min(mqt->dist - mqt_viewmindist, r_transparent_sortmaxdist.integer) * distscale - 0.1), maxhashindex);
 		// link to tail of hash chain (to preserve render order)
 		mqt->next = NULL;
-		*hashpointer[hashdist] = mqt;
-		hashpointer[hashdist] = &mqt->next;
+		*trans_hashpointer[hashindex] = mqt;
+		trans_hashpointer[hashindex] = &mqt->next;
 	}
 	callback = NULL;
 	ent = NULL;
 	rtlight = NULL;
 	batchnumsurfaces = 0;
-	for (i = 4095;i >= 0;i--)
+
+	// draw
+	for (i = maxhashindex; i >= 0; i--)
 	{
-		if (hash[i])
+		if (trans_hash[i])
 		{
-			for (mqt = hash[i];mqt;mqt = mqt->next)
+			for (mqt = trans_hash[i]; mqt; mqt = mqt->next)
 			{
-				if (ent != mqt->ent || rtlight != mqt->rtlight || callback != mqt->callback || batchnumsurfaces >= 256)
+				if (ent != mqt->ent || rtlight != mqt->rtlight || callback != mqt->callback || batchnumsurfaces >= MESHQUEUE_TRANSPARENT_BATCHSIZE)
 				{
 					if (batchnumsurfaces)
 						callback(ent, rtlight, batchnumsurfaces, batchsurfaceindex);
diff --git a/meshqueue.h b/meshqueue.h
index 8e5b36e7..f54c4ce3 100644
--- a/meshqueue.h
+++ b/meshqueue.h
@@ -2,6 +2,9 @@
 #ifndef MESHQUEUE_H
 #define MESHQUEUE_H
 
+// VorteX: seems this value is hardcoded in other several defines as it's changing makes mess
+#define MESHQUEUE_TRANSPARENT_BATCHSIZE 256
+
 void R_MeshQueue_BeginScene(void);
 void R_MeshQueue_AddTransparent(const vec3_t center, void (*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist), const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight);
 void R_MeshQueue_RenderTransparent(void);
diff --git a/model_brush.h b/model_brush.h
index cec78463..906594ad 100644
--- a/model_brush.h
+++ b/model_brush.h
@@ -116,6 +116,8 @@ mplane_t;
 #define MATERIALFLAG_TRANSDEPTH 33554432
 // like refraction, but doesn't distort etc.
 #define MATERIALFLAG_CAMERA 67108864
+// disable rtlight on surface, use R_LightPoint instead
+#define MATERIALFLAG_NORTLIGHT 134217728
 // combined mask of all attributes that require depth sorted rendering
 #define MATERIALFLAGMASK_DEPTHSORTED (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST)
 // combined mask of all attributes that cause some sort of transparency
diff --git a/model_shared.c b/model_shared.c
index 9ea0751a..5257863d 100644
--- a/model_shared.c
+++ b/model_shared.c
@@ -2073,6 +2073,8 @@ void Mod_LoadQ3Shaders(void)
 					shader.dpshadow = true;
 				else if (!strcasecmp(parameter[0], "dpnoshadow"))
 					shader.dpnoshadow = true;
+				else if (!strcasecmp(parameter[0], "dpnortlight"))
+					shader.dpnortlight = true;
 				else if (!strcasecmp(parameter[0], "dpreflectcube"))
 					strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
 				else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
@@ -2420,6 +2422,8 @@ nothing                GL_ZERO GL_ONE
 			texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
 		if (shader->dpnoshadow)
 			texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
+		if (shader->dpnortlight)
+			texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
 		memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
 		texture->reflectmin = shader->reflectmin;
 		texture->reflectmax = shader->reflectmax;
diff --git a/model_shared.h b/model_shared.h
index b79c1330..a7ecd4fe 100644
--- a/model_shared.h
+++ b/model_shared.h
@@ -445,6 +445,7 @@ typedef struct q3shaderinfo_s
 	// dp-specific additions:
 
 	// shadow control
+	qboolean dpnortlight;
 	qboolean dpshadow;
 	qboolean dpnoshadow;
 
-- 
2.39.5