From d6941f91374ee93b2c98439505fd2dc7be050197 Mon Sep 17 00:00:00 2001
From: havoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Thu, 24 May 2007 07:35:30 +0000
Subject: [PATCH] support multiple tcmod commands on a single layer

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7364 d7cf8633-e32d-0410-b094-e92efae38249
---
 gl_rmain.c     | 97 +++++++++++++++++++++++++++++---------------------
 model_shared.c | 43 ++++++++++++----------
 model_shared.h | 13 +++----
 3 files changed, 88 insertions(+), 65 deletions(-)

diff --git a/gl_rmain.c b/gl_rmain.c
index 30b45979..373187a6 100644
--- a/gl_rmain.c
+++ b/gl_rmain.c
@@ -3242,46 +3242,58 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
 	if (t->backgroundnumskinframes && !(t->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED))
 		t->currentmaterialflags |= MATERIALFLAG_VERTEXTEXTUREBLEND;
 
-	switch(t->tcmod)
+	for (i = 0;i < Q3MAXTCMODS && (t->tcmod[i] || i < 1);i++)
 	{
-	case Q3TCMOD_COUNT:
-	case Q3TCMOD_NONE:
-		if (t->currentmaterialflags & MATERIALFLAG_WATER && r_waterscroll.value != 0)
-			t->currenttexmatrix = r_waterscrollmatrix;
+		matrix4x4_t matrix;
+		switch(t->tcmod[i])
+		{
+		case Q3TCMOD_COUNT:
+		case Q3TCMOD_NONE:
+			if (t->currentmaterialflags & MATERIALFLAG_WATER && r_waterscroll.value != 0)
+				matrix = r_waterscrollmatrix;
+			else
+				matrix = identitymatrix;
+			break;
+		case Q3TCMOD_ENTITYTRANSLATE:
+			// this is used in Q3 to allow the gamecode to control texcoord
+			// scrolling on the entity, which is not supported in darkplaces yet.
+			Matrix4x4_CreateTranslate(&matrix, 0, 0, 0);
+			break;
+		case Q3TCMOD_ROTATE:
+			Matrix4x4_CreateTranslate(&matrix, 0.5, 0.5, 0);
+			Matrix4x4_ConcatRotate(&matrix, t->tcmod_parms[i][0] * r_refdef.time, 0, 0, 1);
+			Matrix4x4_ConcatTranslate(&matrix, -0.5, -0.5, 0);
+			break;
+		case Q3TCMOD_SCALE:
+			Matrix4x4_CreateScale3(&matrix, t->tcmod_parms[i][0], t->tcmod_parms[i][1], 1);
+			break;
+		case Q3TCMOD_SCROLL:
+			Matrix4x4_CreateTranslate(&matrix, t->tcmod_parms[i][0] * r_refdef.time, t->tcmod_parms[i][1] * r_refdef.time, 0);
+			break;
+		case Q3TCMOD_STRETCH:
+			f = 1.0f / R_EvaluateQ3WaveFunc(t->tcmod_wavefunc[i], t->tcmod_parms[i]);
+			Matrix4x4_CreateFromQuakeEntity(&matrix, 0.5f * (1 - f), 0.5 * (1 - f), 0, 0, 0, 0, f);
+			break;
+		case Q3TCMOD_TRANSFORM:
+			VectorSet(tcmat +  0, t->tcmod_parms[i][0], t->tcmod_parms[i][1], 0);
+			VectorSet(tcmat +  3, t->tcmod_parms[i][2], t->tcmod_parms[i][3], 0);
+			VectorSet(tcmat +  6, 0                   , 0                , 1);
+			VectorSet(tcmat +  9, t->tcmod_parms[i][4], t->tcmod_parms[i][5], 0);
+			Matrix4x4_FromArray12FloatGL(&matrix, tcmat);
+			break;
+		case Q3TCMOD_TURBULENT:
+			// this is handled in the RSurf_PrepareVertices function
+			matrix = identitymatrix;
+			break;
+		}
+		// either replace or concatenate the transformation
+		if (i < 1)
+			t->currenttexmatrix = matrix;
 		else
-			t->currenttexmatrix = identitymatrix;
-		break;
-	case Q3TCMOD_ENTITYTRANSLATE:
-		// this is used in Q3 to allow the gamecode to control texcoord
-		// scrolling on the entity, which is not supported in darkplaces yet.
-		Matrix4x4_CreateTranslate(&t->currenttexmatrix, 0, 0, 0);
-		break;
-	case Q3TCMOD_ROTATE:
-		Matrix4x4_CreateTranslate(&t->currenttexmatrix, 0.5, 0.5, 0);
-		Matrix4x4_ConcatRotate(&t->currenttexmatrix, t->tcmod_parms[0] * r_refdef.time, 0, 0, 1);
-		Matrix4x4_ConcatTranslate(&t->currenttexmatrix, -0.5, -0.5, 0);
-		break;
-	case Q3TCMOD_SCALE:
-		Matrix4x4_CreateScale3(&t->currenttexmatrix, t->tcmod_parms[0], t->tcmod_parms[1], 1);
-		break;
-	case Q3TCMOD_SCROLL:
-		Matrix4x4_CreateTranslate(&t->currenttexmatrix, t->tcmod_parms[0] * r_refdef.time, t->tcmod_parms[1] * r_refdef.time, 0);
-		break;
-	case Q3TCMOD_STRETCH:
-		f = 1.0f / R_EvaluateQ3WaveFunc(t->tcmod_wavefunc, t->tcmod_parms);
-		Matrix4x4_CreateFromQuakeEntity(&t->currenttexmatrix, 0.5f * (1 - f), 0.5 * (1 - f), 0, 0, 0, 0, f);
-		break;
-	case Q3TCMOD_TRANSFORM:
-		VectorSet(tcmat +  0, t->tcmod_parms[0], t->tcmod_parms[1], 0);
-		VectorSet(tcmat +  3, t->tcmod_parms[2], t->tcmod_parms[3], 0);
-		VectorSet(tcmat +  6, 0                , 0                , 1);
-		VectorSet(tcmat +  9, t->tcmod_parms[4], t->tcmod_parms[5], 0);
-		Matrix4x4_FromArray12FloatGL(&t->currenttexmatrix, tcmat);
-		break;
-	case Q3TCMOD_TURBULENT:
-		// this is handled in the RSurf_PrepareVertices function
-		t->currenttexmatrix = identitymatrix;
-		break;
+		{
+			matrix4x4_t temp = t->currenttexmatrix;
+			Matrix4x4_Concat(&t->currenttexmatrix, &matrix, &temp);
+		}
 	}
 
 	t->colormapping = VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
@@ -3827,10 +3839,13 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
 	}
 	// the only tcmod that needs software vertex processing is turbulent, so
 	// check for it here and apply the changes if needed
-	if (rsurface.texture->tcmod == Q3TCMOD_TURBULENT)
+	// and we only support that as the first one
+	// (handling a mixture of turbulent and other tcmods would be problematic
+	//  without punting it entirely to a software path)
+	if (rsurface.texture->tcmod[0] == Q3TCMOD_TURBULENT)
 	{
-		amplitude = rsurface.texture->tcmod_parms[1];
-		animpos = rsurface.texture->tcmod_parms[2] + r_refdef.time * rsurface.texture->tcmod_parms[3];
+		amplitude = rsurface.texture->tcmod_parms[0][1];
+		animpos = rsurface.texture->tcmod_parms[0][2] + r_refdef.time * rsurface.texture->tcmod_parms[0][3];
 		for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
 		{
 			const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
diff --git a/model_shared.c b/model_shared.c
index e573c12b..05432325 100644
--- a/model_shared.c
+++ b/model_shared.c
@@ -1152,7 +1152,6 @@ void Mod_LoadQ3Shaders(void)
 						layer->rgbgen = Q3RGBGEN_IDENTITY;
 						layer->alphagen = Q3ALPHAGEN_IDENTITY;
 						layer->tcgen = Q3TCGEN_TEXTURE;
-						layer->tcmod = Q3TCMOD_NONE;
 						layer->blendfunc[0] = GL_ONE;
 						layer->blendfunc[1] = GL_ZERO;
 					}
@@ -1303,7 +1302,7 @@ void Mod_LoadQ3Shaders(void)
 						}
 						else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
 						{
-							int i;
+							int i, tcmodindex;
 							// observed values:
 							// tcmod rotate #
 							// tcmod scale # #
@@ -1314,22 +1313,30 @@ void Mod_LoadQ3Shaders(void)
 							// tcmod turb # # # #
 							// tcmod turb sin # # # #  (this is bogus)
 							// no other values have been observed in real shaders
-							for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
-								layer->tcmod_parms[i] = atof(parameter[i+2]);
-							     if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmod = Q3TCMOD_ENTITYTRANSLATE;
-							else if (!strcasecmp(parameter[1], "rotate"))          layer->tcmod = Q3TCMOD_ROTATE;
-							else if (!strcasecmp(parameter[1], "scale"))           layer->tcmod = Q3TCMOD_SCALE;
-							else if (!strcasecmp(parameter[1], "scroll"))          layer->tcmod = Q3TCMOD_SCROLL;
-							else if (!strcasecmp(parameter[1], "stretch"))
+							for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
+								if (!layer->tcmod[tcmodindex])
+									break;
+							if (tcmodindex < Q3MAXTCMODS)
 							{
-								layer->tcmod = Q3TCMOD_STRETCH;
-								for (i = 0;i < numparameters - 3 && i < Q3TCMOD_MAXPARMS;i++)
-									layer->tcmod_parms[i] = atof(parameter[i+3]);
-								layer->tcmod_wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
+								for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
+									layer->tcmod_parms[tcmodindex][i] = atof(parameter[i+2]);
+									 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmod[tcmodindex] = Q3TCMOD_ENTITYTRANSLATE;
+								else if (!strcasecmp(parameter[1], "rotate"))          layer->tcmod[tcmodindex] = Q3TCMOD_ROTATE;
+								else if (!strcasecmp(parameter[1], "scale"))           layer->tcmod[tcmodindex] = Q3TCMOD_SCALE;
+								else if (!strcasecmp(parameter[1], "scroll"))          layer->tcmod[tcmodindex] = Q3TCMOD_SCROLL;
+								else if (!strcasecmp(parameter[1], "stretch"))
+								{
+									layer->tcmod[tcmodindex] = Q3TCMOD_STRETCH;
+									for (i = 0;i < numparameters - 3 && i < Q3TCMOD_MAXPARMS;i++)
+										layer->tcmod_parms[tcmodindex][i] = atof(parameter[i+3]);
+									layer->tcmod_wavefunc[tcmodindex] = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
+								}
+								else if (!strcasecmp(parameter[1], "transform"))       layer->tcmod[tcmodindex] = Q3TCMOD_TRANSFORM;
+								else if (!strcasecmp(parameter[1], "turb"))            layer->tcmod[tcmodindex] = Q3TCMOD_TURBULENT;
+								else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
 							}
-							else if (!strcasecmp(parameter[1], "transform"))       layer->tcmod = Q3TCMOD_TRANSFORM;
-							else if (!strcasecmp(parameter[1], "turb"))            layer->tcmod = Q3TCMOD_TURBULENT;
-							else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
+							else
+								Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
 						}
 						// break out a level if it was }
 						if (!strcasecmp(com_token, "}"))
@@ -1593,12 +1600,12 @@ nothing                GL_ZERO GL_ONE
 			texture->rgbgen   = shader->primarylayer->rgbgen;
 			texture->alphagen = shader->primarylayer->alphagen;
 			texture->tcgen    = shader->primarylayer->tcgen;
-			texture->tcmod    = shader->primarylayer->tcmod;
+			memcpy(texture->tcmod         , shader->primarylayer->tcmod         , sizeof(texture->tcmod));
 			memcpy(texture->rgbgen_parms  , shader->primarylayer->rgbgen_parms  , sizeof(texture->rgbgen_parms));
 			memcpy(texture->alphagen_parms, shader->primarylayer->alphagen_parms, sizeof(texture->alphagen_parms));
 			memcpy(texture->tcgen_parms   , shader->primarylayer->tcgen_parms   , sizeof(texture->tcgen_parms));
 			memcpy(texture->tcmod_parms   , shader->primarylayer->tcmod_parms   , sizeof(texture->tcmod_parms));
-			texture->tcmod_wavefunc = shader->primarylayer->tcmod_wavefunc;
+			memcpy(texture->tcmod_wavefunc, shader->primarylayer->tcmod_wavefunc, sizeof(texture->tcmod_wavefunc));
 			// load the textures
 			texture->numskinframes = shader->primarylayer->numframes;
 			texture->skinframerate = shader->primarylayer->framerate;
diff --git a/model_shared.h b/model_shared.h
index ba863c3e..5e1e54ce 100644
--- a/model_shared.h
+++ b/model_shared.h
@@ -189,6 +189,7 @@ shadowmesh_t;
 #define Q3ALPHAGEN_MAXPARMS 1
 #define Q3TCGEN_MAXPARMS 6
 #define Q3TCMOD_MAXPARMS 6
+#define Q3MAXTCMODS 4
 
 typedef enum q3wavefunc_e
 {
@@ -270,12 +271,12 @@ typedef struct q3shaderinfo_layer_s
 	q3rgbgen_t rgbgen;
 	q3alphagen_t alphagen;
 	q3tcgen_t tcgen;
-	q3tcmod_t tcmod;
+	q3tcmod_t tcmod[Q3MAXTCMODS];
 	float rgbgen_parms[Q3RGBGEN_MAXPARMS];
 	float alphagen_parms[Q3ALPHAGEN_MAXPARMS];
 	float tcgen_parms[Q3TCGEN_MAXPARMS];
-	float tcmod_parms[Q3TCMOD_MAXPARMS];
-	q3wavefunc_t tcmod_wavefunc;
+	float tcmod_parms[Q3MAXTCMODS][Q3TCMOD_MAXPARMS];
+	q3wavefunc_t tcmod_wavefunc[Q3MAXTCMODS];
 }
 q3shaderinfo_layer_t;
 
@@ -372,12 +373,12 @@ typedef struct texture_s
 	q3rgbgen_t rgbgen;
 	q3alphagen_t alphagen;
 	q3tcgen_t tcgen;
-	q3tcmod_t tcmod;
+	q3tcmod_t tcmod[Q3MAXTCMODS];
 	float rgbgen_parms[Q3RGBGEN_MAXPARMS];
 	float alphagen_parms[Q3ALPHAGEN_MAXPARMS];
 	float tcgen_parms[Q3TCGEN_MAXPARMS];
-	float tcmod_parms[Q3TCMOD_MAXPARMS];
-	q3wavefunc_t tcmod_wavefunc;
+	float tcmod_parms[Q3MAXTCMODS][Q3TCMOD_MAXPARMS];
+	q3wavefunc_t tcmod_wavefunc[Q3MAXTCMODS];
 
 	qboolean colormapping;
 	rtexture_t *basetexture;
-- 
2.39.5