/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// tr_shade_calc.c

#include "tr_local.h"


#define	WAVEVALUE( table, base, amplitude, phase, freq )  ((base) + table[ myftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude))

static float *TableForFunc( genFunc_t func ) 
{
	switch ( func )
	{
	case GF_SIN:
		return tr.sinTable;
	case GF_TRIANGLE:
		return tr.triangleTable;
	case GF_SQUARE:
		return tr.squareTable;
	case GF_SAWTOOTH:
		return tr.sawToothTable;
	case GF_INVERSE_SAWTOOTH:
		return tr.inverseSawToothTable;
	case GF_NONE:
	default:
		break;
	}

	ri.Error( ERR_DROP, "TableForFunc called with invalid function '%d' in shader '%s'\n", func, tess.shader->name );
	return NULL;
}

/*
** EvalWaveForm
**
** Evaluates a given waveForm_t, referencing backEnd.refdef.time directly
*/
static float EvalWaveForm( const waveForm_t *wf ) 
{
	float	*table;
	float	base;
	float	amplitude;
	float	phase;
	float	frequency;

	table = TableForFunc( wf->func );

    if (wf->base != 1234567.0f) {
        base = wf->base;
    } else if (backEnd.currentEntity) {
        base = (float)backEnd.currentEntity->e.surfaces[0] / 16.0 - 8.0;
    } else {
        base = r_static_shaderdata0->value;
    }

    if (wf->amplitude != 1234567.0f) {
		amplitude = wf->amplitude;
    } else if (backEnd.currentEntity) {
		amplitude = (float)backEnd.currentEntity->e.surfaces[1] / 16.0;
    } else {
		amplitude = r_static_shaderdata1->value;
    }

    if (wf->phase != 1234567.0f) {
		phase = wf->phase;
    } else if (backEnd.currentEntity) {
		phase = (float)backEnd.currentEntity->e.surfaces[2] / 16.0 - 8.0;
    } else {
		phase = r_static_shaderdata2->value;
    }

    if (wf->frequency != 1234567.0f) {
		frequency = wf->frequency;
    } else if (backEnd.currentEntity) {
		frequency = (float)backEnd.currentEntity->e.surfaces[3] / 16.0;
    } else {
		frequency = r_static_shaderdata1->value;
    }

	if (!backEnd.currentEntity)
	{
		base *= r_static_shadermultiplier0->value;
		amplitude *= r_static_shadermultiplier1->value;
		phase *= r_static_shadermultiplier2->value;
		frequency *= r_static_shadermultiplier3->value;
	}

	return WAVEVALUE( table, base, amplitude, phase, frequency );
}

static float EvalWaveFormClamped( const waveForm_t *wf )
{
	float glow  = EvalWaveForm( wf );

	if ( glow < 0 )
	{
		return 0;
	}

	if ( glow > 1 )
	{
		return 1;
	}

	return glow;
}

/*
** RB_CalcStretchTexCoords
*/
void RB_CalcStretchTexCoords( const waveForm_t *wf, float *st )
{
	float p;
	texModInfo_t tmi;

	p = 1.0f / EvalWaveForm( wf );

	tmi.matrix[0][0] = p;
	tmi.matrix[1][0] = 0;
	tmi.translate[0] = 0.5f - 0.5f * p;

	tmi.matrix[0][1] = 0;
	tmi.matrix[1][1] = p;
	tmi.translate[1] = 0.5f - 0.5f * p;

	RB_CalcTransformTexCoords( &tmi, st );
}

/*
========================
RB_CalcTransWaveTexCoords

========================
*/
void RB_CalcTransWaveTexCoords(const waveForm_t* wf, float* st)
{
	float p;
	int i;

    p = EvalWaveForm(wf);
    for (i = 0; i < tess.numVertexes; i++, st += 2) {
        st[0] += p;
    }
}

/*
========================
RB_CalcTransWaveTexCoordsT

========================
*/
void RB_CalcTransWaveTexCoordsT(const waveForm_t* wf, float* st)
{
    float p;
    int i;

    p = EvalWaveForm(wf);
    for (i = 0; i < tess.numVertexes; i++, st += 2) {
        st[1] += p;
    }
}

/*
========================
RB_CalcBulgeTexCoords

========================
*/
void RB_CalcBulgeTexCoords(const waveForm_t* wf, float* st)
{
	int i;
	int off;
	float offset;
	float now;
	float amplitude;
	float width;

    amplitude = wf->amplitude;
    width = wf->base;
    now = backEnd.refdef.time / 1000.0 * wf->frequency + wf->phase;

	for (i = 0; i < tess.numVertexes; i++, st += 2) {
		off = (now + st[0] * width) * FUNCTABLE_SIZE;
		offset = tr.sinTable[off & FUNCTABLE_MASK] * amplitude;
	}
}

/*
====================================================================

DEFORMATIONS

====================================================================
*/

/*
========================
RB_CalcDeformVertexes

========================
*/
void RB_CalcDeformVertexes( deformStage_t *ds )
{
	int i;
	vec3_t	offset;
	float	scale;
	float	*xyz = ( float * ) tess.xyz;
	float	*normal = ( float * ) tess.normal;
	float	*table;
	float	base;
	float	amplitude;
	float	phase;
	float	frequency;

    if (ds->deformationWave.base != 1234567.0f) {
        base = ds->deformationWave.base;
    } else if (backEnd.currentEntity) {
        base = (float)backEnd.currentEntity->e.surfaces[0] / 16.0 - 8.0;
    } else {
        base = r_static_shaderdata0->value;
    }

    if (ds->deformationWave.amplitude != 1234567.0f) {
		amplitude = ds->deformationWave.amplitude;
    } else if (backEnd.currentEntity) {
		amplitude = (float)backEnd.currentEntity->e.surfaces[1] / 16.0;
    } else {
		amplitude = r_static_shaderdata1->value;
    }

    if (ds->deformationWave.phase != 1234567.0f) {
		phase = ds->deformationWave.phase;
    } else if (backEnd.currentEntity) {
		phase = (float)backEnd.currentEntity->e.surfaces[2] / 16.0 - 8.0;
    } else {
		phase = r_static_shaderdata2->value;
    }

    if (ds->deformationWave.frequency != 1234567.0f) {
		frequency = ds->deformationWave.frequency;
    } else if (backEnd.currentEntity) {
		frequency = (float)backEnd.currentEntity->e.surfaces[3] / 16.0;
    } else {
		frequency = r_static_shaderdata1->value;
    }

	if (!backEnd.currentEntity)
	{
		base *= r_static_shadermultiplier0->value;
		amplitude *= r_static_shadermultiplier1->value;
		phase *= r_static_shadermultiplier2->value;
		frequency *= r_static_shadermultiplier3->value;
	}

	if ( frequency == 0 )
	{
		scale = EvalWaveForm( &ds->deformationWave );

		for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 )
		{
			VectorScale( normal, scale, offset );
			
			xyz[0] += offset[0];
			xyz[1] += offset[1];
			xyz[2] += offset[2];
		}
	}
	else
	{
		table = TableForFunc( ds->deformationWave.func );

		for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 )
		{
			float off = ( xyz[0] + xyz[1] + xyz[2] ) * ds->deformationSpread;

			scale = WAVEVALUE( table, base, amplitude, phase + off, frequency );

			VectorScale( normal, scale, offset );
			
			xyz[0] += offset[0];
			xyz[1] += offset[1];
			xyz[2] += offset[2];
		}
	}
}

void RB_CalcFlapVertexes(deformStage_t* ds, texDirection_t coordsToUse)
{
    int    i;
    vec3_t offset;
    float  scale;
    vec4_t* xyz, * normal;
	float* table;
    float  base;
    float  amplitude;
    float  phase;
    float  frequency;
    float  min, max;
    vec2_t (*st)[2];
    float  vertexScale;

    xyz = tess.xyz;
    normal = tess.normal;
    min = ds->bulgeWidth;
    max = ds->bulgeHeight;
    st = tess.texCoords;

    if (ds->deformationWave.base != 1234567.0f) {
        base = ds->deformationWave.base;
    } else if (backEnd.currentEntity) {
        base = (float)backEnd.currentEntity->e.surfaces[0] / 16.0 - 8.0;
    } else {
        base = r_static_shaderdata0->value;
    }

    if (ds->deformationWave.amplitude != 1234567.0f) {
        amplitude = ds->deformationWave.amplitude;
    } else if (backEnd.currentEntity) {
        amplitude = (float)backEnd.currentEntity->e.surfaces[1] / 16.0;
    } else {
        amplitude = r_static_shaderdata1->value;
    }

    if (ds->deformationWave.phase != 1234567.0f) {
        phase = ds->deformationWave.phase;
    } else if (backEnd.currentEntity) {
        phase = (float)backEnd.currentEntity->e.surfaces[2] / 16.0 - 8.0;
    } else {
        phase = r_static_shaderdata2->value;
    }

    if (ds->deformationWave.frequency != 1234567.0f) {
        frequency = ds->deformationWave.frequency;
    } else if (backEnd.currentEntity) {
        frequency = (float)backEnd.currentEntity->e.surfaces[3] / 16.0;
    } else {
        frequency = r_static_shaderdata3->value;
    }

    if (!backEnd.currentEntity) {
        base = base * r_static_shadermultiplier0->value;
        amplitude = amplitude * r_static_shadermultiplier1->value;
        phase = phase * r_static_shadermultiplier2->value;
        frequency = frequency * r_static_shadermultiplier3->value;
    }

	if (frequency) {
		table = TableForFunc(ds->deformationWave.func);
		for (i = 0; i < tess.numVertexes; i++, xyz++, st++, normal++) {
			float off;

			off = ((*xyz)[0] + (*xyz)[1] + (*xyz)[2]) * ds->deformationSpread;
			scale = WAVEVALUE(table, base, amplitude, phase, frequency);
            vertexScale = (max - min) * (*st)[0][coordsToUse] + min;
            offset[0] = scale * vertexScale * (*normal)[0];
            offset[1] = scale * vertexScale * (*normal)[1];
            offset[2] = scale * vertexScale * (*normal)[2];
			VectorAdd(*xyz, offset, *xyz);
		}
	} else {
        scale = EvalWaveForm(&ds->deformationWave);
        for (i = 0; i < tess.numVertexes; i++, xyz++, st++, normal++) {
            vertexScale = (max - min) * (*st)[0][coordsToUse] + min;
            offset[0] = vertexScale * scale * (*normal)[0];
            offset[1] = vertexScale * scale * (*normal)[1];
            offset[2] = vertexScale * scale * (*normal)[2];
            VectorAdd(*xyz, offset, *xyz);
		}
	}
}

/*
=========================
RB_CalcDeformNormals

Wiggle the normals for wavy environment mapping
=========================
*/
void RB_CalcDeformNormals( deformStage_t *ds ) {
	int i;
	float	scale;
	float	*xyz = ( float * ) tess.xyz;
	float	*normal = ( float * ) tess.normal;

	for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) {
		scale = 0.98f;
		scale = R_NoiseGet4f( xyz[0] * scale, xyz[1] * scale, xyz[2] * scale,
			tess.shaderTime * ds->deformationWave.frequency );
		normal[ 0 ] += ds->deformationWave.amplitude * scale;

		scale = 0.98f;
		scale = R_NoiseGet4f( 100 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale,
			tess.shaderTime * ds->deformationWave.frequency );
		normal[ 1 ] += ds->deformationWave.amplitude * scale;

		scale = 0.98f;
		scale = R_NoiseGet4f( 200 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale,
			tess.shaderTime * ds->deformationWave.frequency );
		normal[ 2 ] += ds->deformationWave.amplitude * scale;

		VectorNormalizeFast( normal );
	}
}

/*
========================
RB_CalcBulgeVertexes

========================
*/
void RB_CalcBulgeVertexes( deformStage_t *ds ) {
	int i;
	const float *st = ( const float * ) tess.texCoords[0];
	float		*xyz = ( float * ) tess.xyz;
	float		*normal = ( float * ) tess.normal;
	float		now;

	now = backEnd.refdef.time * ds->bulgeSpeed * 0.001f;

	for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 4, normal += 4 ) {
		int		off;
		float scale;

		off = (float)( FUNCTABLE_SIZE / (M_PI*2) ) * ( st[0] * ds->bulgeWidth + now );

		scale = tr.sinTable[ off & FUNCTABLE_MASK ] * ds->bulgeHeight;
			
		xyz[0] += normal[0] * scale;
		xyz[1] += normal[1] * scale;
		xyz[2] += normal[2] * scale;
	}
}


/*
======================
RB_CalcMoveVertexes

A deformation that can move an entire surface along a wave path
======================
*/
void RB_CalcMoveVertexes( deformStage_t *ds ) {
	int			i;
	float		*xyz;
	float		*table;
	float		scale;
	vec3_t		offset;

	table = TableForFunc( ds->deformationWave.func );

	scale = WAVEVALUE( table, ds->deformationWave.base, 
		ds->deformationWave.amplitude,
		ds->deformationWave.phase,
		ds->deformationWave.frequency );

	VectorScale( ds->moveVector, scale, offset );

	xyz = ( float * ) tess.xyz;
	for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) {
		VectorAdd( xyz, offset, xyz );
	}
}


/*
=============
DeformText

Change a polygon into a bunch of text polygons
=============
*/
void DeformText( const char *text ) {
	int		i;
	vec3_t	origin, width, height;
	size_t	len;
	int		ch;
	byte	color[4];
	float	bottom, top;
	vec3_t	mid;

	height[0] = 0;
	height[1] = 0;
	height[2] = -1;
	CrossProduct( tess.normal[0], height, width );

	// find the midpoint of the box
	VectorClear( mid );
	bottom = 999999;
	top = -999999;
	for ( i = 0 ; i < 4 ; i++ ) {
		VectorAdd( tess.xyz[i], mid, mid );
		if ( tess.xyz[i][2] < bottom ) {
			bottom = tess.xyz[i][2];
		}
		if ( tess.xyz[i][2] > top ) {
			top = tess.xyz[i][2];
		}
	}
	VectorScale( mid, 0.25f, origin );

	// determine the individual character size
	height[0] = 0;
	height[1] = 0;
	height[2] = ( top - bottom ) * 0.5f;

	VectorScale( width, height[2] * -0.75f, width );

	// determine the starting position
	len = strlen( text );
	VectorMA( origin, (len-1), width, origin );

	// clear the shader indexes
	tess.numIndexes = 0;
	tess.numVertexes = 0;

	color[0] = color[1] = color[2] = color[3] = 255;

	// draw each character
	for ( i = 0 ; i < len ; i++ ) {
		ch = text[i];
		ch &= 255;

		if ( ch != ' ' ) {
			int		row, col;
			float	frow, fcol, size;

			row = ch>>4;
			col = ch&15;

			frow = row*0.0625f;
			fcol = col*0.0625f;
			size = 0.0625f;

			RB_AddQuadStampExt( origin, width, height, color, fcol, frow, fcol + size, frow + size );
		}
		VectorMA( origin, -2, width, origin );
	}
}

/*
==================
GlobalVectorToLocal
==================
*/
static void GlobalVectorToLocal( const vec3_t in, vec3_t out ) {
	out[0] = DotProduct( in, backEnd.ori.axis[0] );
	out[1] = DotProduct( in, backEnd.ori.axis[1] );
	out[2] = DotProduct( in, backEnd.ori.axis[2] );
}

/*
=====================
AutospriteDeform

Assuming all the triangles for this shader are independant
quads, rebuild them as forward facing sprites
=====================
*/
static void AutospriteDeform(void) {
    int		i;
    int		oldVerts;
    float* xyz;
    vec3_t	mid, delta;
    float	radius;
    vec3_t	left, up;
    vec3_t	leftDir, upDir;

    if (tess.numVertexes & 3) {
        ri.Printf(PRINT_WARNING, "Autosprite shader %s had odd vertex count\n", tess.shader->name);
    }
    if (tess.numIndexes != (tess.numVertexes >> 2) * 6) {
        ri.Printf(PRINT_WARNING, "Autosprite shader %s had odd index count\n", tess.shader->name);
    }

    oldVerts = tess.numVertexes;
    tess.numVertexes = 0;
    tess.numIndexes = 0;

    if (backEnd.currentEntity != &tr.worldEntity) {
        GlobalVectorToLocal(backEnd.viewParms.ori.axis[1], leftDir);
        GlobalVectorToLocal(backEnd.viewParms.ori.axis[2], upDir);
    } else {
        VectorCopy(backEnd.viewParms.ori.axis[1], leftDir);
        VectorCopy(backEnd.viewParms.ori.axis[2], upDir);
    }

    for (i = 0; i < oldVerts; i += 4) {
        // find the midpoint
        xyz = tess.xyz[i];

        mid[0] = 0.25f * (xyz[0] + xyz[4] + xyz[8] + xyz[12]);
        mid[1] = 0.25f * (xyz[1] + xyz[5] + xyz[9] + xyz[13]);
        mid[2] = 0.25f * (xyz[2] + xyz[6] + xyz[10] + xyz[14]);

        VectorSubtract(xyz, mid, delta);
        radius = VectorLength(delta) * 0.707f;		// / sqrt(2)

        VectorScale(leftDir, radius, left);
        VectorScale(upDir, radius, up);

        if (backEnd.viewParms.isMirror) {
            VectorSubtract(vec3_origin, left, left);
        }

        // compensate for scale in the axes if necessary
        if (backEnd.currentStaticModel || backEnd.currentEntity->e.nonNormalizedAxes) {
            float axisLength;

			if (backEnd.currentStaticModel) {
				axisLength = VectorLength(backEnd.currentStaticModel->axis[0]);
			} else {
				axisLength = VectorLength(backEnd.currentEntity->e.axis[0]);
			}
            
            if (!axisLength) {
                axisLength = 0;
            }
            else {
                axisLength = 1.0f / axisLength;
            }
            VectorScale(left, axisLength, left);
            VectorScale(up, axisLength, up);
        }

        RB_AddQuadStamp(mid, left, up, tess.vertexColors[i]);
    }
}


/*
=====================
Autosprite2Deform

Autosprite2 will pivot a rectangular quad along the center of its long axis
=====================
*/
int edgeVerts[6][2] = {
	{ 0, 1 },
	{ 0, 3 },
	{ 0, 2 },
	{ 1, 3 },
	{ 1, 2 },
	{ 3, 2 }
};

static void Autosprite2Deform( void ) {
	int		i, j;
	int		indexes;
	float	*xyz;
	vec3_t	forward;

	if ( tess.numVertexes & 3 ) {
		ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd vertex count\n", tess.shader->name );
	}
	if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) {
		ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd index count\n", tess.shader->name );
	}

	if ( backEnd.currentStaticModel || backEnd.currentEntity != &tr.worldEntity ) {
		GlobalVectorToLocal( backEnd.viewParms.ori.axis[0], forward );
	} else {
		VectorCopy( backEnd.viewParms.ori.axis[0], forward );
	}

	// this is a lot of work for two triangles...
	// we could precalculate a lot of it is an issue, but it would mess up
	// the shader abstraction
	for ( i = 0, indexes = 0 ; i < tess.numVertexes ; i+=4, indexes+=6 ) {
		float shortLengths[2];
		int shortNums[2];
		float longLengths[2];
		int longNums[2];
		vec3_t	mid[2];
		vec3_t	major, minor;
		float	*v1, *v2, *v3, *v4;
		qboolean firstOnLeft, secondOnLeft;
		float edgeLength;

		// find the midpoint
		xyz = tess.xyz[i];

		// identify the two shortest edges
		shortNums[0] = shortNums[1] = 0;
		shortLengths[0] = shortLengths[1] = 1000000000;
		longNums[1] = longNums[0] = 0;
		longLengths[1] = longLengths[0] = 0;

		for ( j = 0 ; j < 6 ; j++ ) {
			float	l;
			vec3_t	temp;

			v1 = xyz + 4 * edgeVerts[j][0];
			v2 = xyz + 4 * edgeVerts[j][1];

			VectorSubtract( v1, v2, temp );
			
			l = DotProduct( temp, temp );
			if ( l < shortLengths[0] ) {
				shortNums[1] = shortNums[0];
				shortLengths[1] = shortLengths[0];
				shortNums[0] = j;
				shortLengths[0] = l;
			} else if ( l < shortLengths[1] ) {
				shortNums[1] = j;
				shortLengths[1] = l;
			}

			if (l > longLengths[0]) {
				longNums[1] = longNums[0];
				longLengths[1] = longLengths[0];
				longNums[0] = j;
				longLengths[0] = l;
			} else if (l > longLengths[1]) {
				longNums[1] = j;
				longLengths[1] = l;
			}
		}

		for ( j = 0 ; j < 2 ; j++ ) {
			v1 = xyz + 4 * edgeVerts[shortNums[j]][0];
			v2 = xyz + 4 * edgeVerts[shortNums[j]][1];

			mid[j][0] = 0.5f * (v1[0] + v2[0]);
			mid[j][1] = 0.5f * (v1[1] + v2[1]);
			mid[j][2] = 0.5f * (v1[2] + v2[2]);
		}

		// find the vector of the major axis
		VectorSubtract( mid[1], mid[0], major );

		// cross this with the view direction to get minor axis
		CrossProduct( major, forward, minor );
		VectorNormalize( minor );

        v1 = xyz + 4 * edgeVerts[shortNums[0]][0];
        v2 = xyz + 4 * edgeVerts[shortNums[0]][1];

        // we need to see which direction this edge
        // is used to determine direction of projection
		if (edgeVerts[shortNums[0]][0] == edgeVerts[longNums[0]][0]
			|| edgeVerts[shortNums[0]][0] == edgeVerts[longNums[0]][1])
		{
			firstOnLeft = qtrue;
		}
		else
		{
			firstOnLeft = qfalse;
        }

        if (edgeVerts[shortNums[1]][0] == edgeVerts[longNums[1]][0]
            || edgeVerts[shortNums[1]][0] == edgeVerts[longNums[1]][1])
        {
            secondOnLeft = qtrue;
        }
        else
        {
			secondOnLeft = qfalse;
        }

		if (firstOnLeft == secondOnLeft)
        {
            v3 = xyz + 4 * edgeVerts[shortNums[1]][0];
            v4 = xyz + 4 * edgeVerts[shortNums[1]][1];
		}
		else
        {
            v3 = xyz + 4 * edgeVerts[shortNums[1]][1];
            v4 = xyz + 4 * edgeVerts[shortNums[1]][0];
		}

        // re-project the points
		edgeLength = sqrt(shortLengths[0]) * 0.5;
        v1[0] = -edgeLength * minor[0] + mid[0][0];
        v1[1] = -edgeLength * minor[1] + mid[0][1];
        v1[2] = -edgeLength * minor[2] + mid[0][2];
		v2[0] = minor[0] * edgeLength + mid[0][0];
        v2[1] = minor[1] * edgeLength + mid[0][1];
        v2[2] = minor[2] * edgeLength + mid[0][2];

        edgeLength = sqrt(shortLengths[1]) * 0.5;
		v3[0] = -edgeLength * minor[0] + mid[1][0];
        v3[1] = -edgeLength * minor[1] + mid[1][1];
        v3[2] = -edgeLength * minor[2] + mid[1][2];
		v4[0] = minor[0] * edgeLength + mid[1][0];
        v4[1] = minor[1] * edgeLength + mid[1][1];
        v4[2] = minor[2] * edgeLength + mid[1][2];
	}
}

static void LightGlowDeform() {
	int i;
	int oldVerts;
	float* xyz;
	vec3_t mid, delta;
	float radius, dist, ofs;
	vec3_t forward, left, up;
	vec3_t leftDir, upDir;

    if (tess.numVertexes & 3) {
        ri.Printf(PRINT_WARNING, "LightGlowDeform shader %s had odd vertex count\n", tess.shader->name);
    }
    if (tess.numIndexes != (tess.numVertexes >> 2) * 6) {
        ri.Printf(PRINT_WARNING, "LightGlowDeform shader %s had odd index count\n", tess.shader->name);
    }

	oldVerts = tess.numVertexes;
	tess.numVertexes = 0;
	tess.numIndexes = 0;

    if (backEnd.currentEntity == &tr.worldEntity)
    {
		VectorCopy(backEnd.viewParms.ori.axis[1], leftDir);
		VectorCopy(backEnd.viewParms.ori.axis[2], upDir);
    }
    else
    {
        GlobalVectorToLocal(backEnd.viewParms.ori.axis[1], leftDir);
        GlobalVectorToLocal(backEnd.viewParms.ori.axis[2], upDir);
    }

	for (i = 0; i < oldVerts; i += 4)
	{
		xyz = tess.xyz[i];

		mid[0] = (xyz[0] + xyz[4] + xyz[8] + xyz[12]) * 0.25f;
		mid[1] = (xyz[1] + xyz[5] + xyz[9] + xyz[13]) * 0.25f;
		mid[2] = (xyz[2] + xyz[6] + xyz[10] + xyz[14]) * 0.25f;

		VectorSubtract(xyz, mid, delta);

		radius = VectorLength(delta) * 0.707f;
		VectorAdd(mid, backEnd.ori.origin, delta);
		VectorSubtract(backEnd.viewParms.ori.origin, delta, forward);

		dist = VectorNormalize(forward) - 4.0;

		VectorScale(forward, radius, forward);
		VectorScale(leftDir, radius, left);
		VectorScale(upDir, radius, up);

		if (backEnd.viewParms.isMirror)
		{
			VectorSubtract(vec3_origin, forward, forward);
			VectorSubtract(vec3_origin, left, left);
		}

		if (backEnd.currentStaticModel || backEnd.currentEntity->e.nonNormalizedAxes)
		{
			float axisLength;

			if (backEnd.currentStaticModel) {
				axisLength = VectorLength(backEnd.currentStaticModel->axis[0]);
			} else {
                axisLength = VectorLength(backEnd.currentEntity->e.axis[0]);
			}

			if (axisLength != 0.0f) {
				VectorScale(forward, axisLength, forward);
				VectorScale(left, axisLength, left);
				VectorScale(up, axisLength, up);
			} else {
				VectorClear(forward);
				VectorClear(left);
				VectorClear(up);
			}
		}

		ofs = VectorLength(forward);
        if (ofs > dist)
        {
            VectorNormalizeFast(forward);
			VectorScale(forward, dist, forward);
        }

		VectorAdd(mid, forward, mid);
        RB_AddQuadStamp(mid, left, up, tess.vertexColors[i]);
	}
}

/*
=====================
RB_DeformTessGeometry

=====================
*/
void RB_DeformTessGeometry( void ) {
	int		i;
	deformStage_t	*ds;

	for ( i = 0 ; i < tess.shader->numDeforms ; i++ ) {
		ds = &tess.shader->deforms[ i ];

		switch ( ds->deformation ) {
        case DEFORM_NONE:
            break;
		case DEFORM_NORMALS:
			RB_CalcDeformNormals( ds );
			break;
		case DEFORM_WAVE:
			RB_CalcDeformVertexes( ds );
			break;
		case DEFORM_BULGE:
			RB_CalcBulgeVertexes( ds );
			break;
		case DEFORM_MOVE:
			RB_CalcMoveVertexes( ds );
			break;
		case DEFORM_AUTOSPRITE:
			AutospriteDeform();
			break;
		case DEFORM_AUTOSPRITE2:
			Autosprite2Deform();
			break;
		case DEFORM_LIGHTGLOW:
			LightGlowDeform();
			break;
		case DEFORM_FLAP_S:
			RB_CalcFlapVertexes(ds, USE_S_COORDS);
			break;
		case DEFORM_FLAP_T:
			RB_CalcFlapVertexes(ds, USE_T_COORDS);
			break;
		}
	}
}

/*
====================================================================

COLORS

====================================================================
*/


/*
** RB_CalcColorFromEntity
*/
void RB_CalcColorFromEntity( unsigned char *dstColors )
{
	int	i;
	int *pColors = ( int * ) dstColors;
	int c;

	if ( !backEnd.currentEntity )
		return;

	c = * ( int * ) backEnd.currentEntity->e.shaderRGBA;

	for ( i = 0; i < tess.numVertexes; i++, pColors++ )
	{
		*pColors = c;
	}
}

/*
** RB_CalcColorFromOneMinusEntity
*/
void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors )
{
	int	i;
	int *pColors = ( int * ) dstColors;
	unsigned char invModulate[4];
	int c;

	if ( !backEnd.currentEntity )
		return;

	invModulate[0] = 255 - backEnd.currentEntity->e.shaderRGBA[0];
	invModulate[1] = 255 - backEnd.currentEntity->e.shaderRGBA[1];
	invModulate[2] = 255 - backEnd.currentEntity->e.shaderRGBA[2];
	invModulate[3] = 255 - backEnd.currentEntity->e.shaderRGBA[3];	// this trashes alpha, but the AGEN block fixes it

	c = * ( int * ) invModulate;

	for ( i = 0; i < tess.numVertexes; i++, pColors++ )
	{
		*pColors = * ( int * ) invModulate;
	}
}

/*
** RB_CalcColorFromConstant
*/
void RB_CalcColorFromConstant(unsigned char* dstColors, unsigned char* constantColor)
{
	int i;

	for (i = 0; i < tess.numVertexes; i++) {
		dstColors[i * 4] = constantColor[0];
		dstColors[i * 4 + 1] = constantColor[1];
		dstColors[i * 4 + 2] = constantColor[2];
		dstColors[i * 4 + 3] = constantColor[3];
	}
}

/*
** RB_CalcRGBFromDot
*/
void RB_CalcRGBFromDot(unsigned char* colors, float alphaMin, float alphaMax)
{
	int i;
	vec3_t viewInModel;

	for (i = 0; i < tess.numVertexes; i++, colors += 4) {
		float f;

		VectorSubtract(backEnd.ori.viewOrigin, tess.xyz[i], viewInModel);
		VectorNormalizeFast(viewInModel);

        f = Square(DotProduct(tess.normal[i], viewInModel));
        f = ((alphaMax - alphaMin) * f + alphaMin) * 255.0;
		f = f - Q_max((int)(f - 255), 0);

		colors[0] = colors[1] = colors[2] = (unsigned char)Q_max(f, 0);
	}
}

/*
** RB_CalcRGBFromOneMinusDot
*/
void RB_CalcRGBFromOneMinusDot(unsigned char* colors, float alphaMin, float alphaMax)
{
	int i;
	vec3_t viewInModel;

	for (i = 0; i < tess.numVertexes; i++, colors += 4) {
		float f;

		VectorSubtract(backEnd.ori.viewOrigin, tess.xyz[i], viewInModel);
		VectorNormalizeFast(viewInModel);

        f = 1.0 - Square(DotProduct(tess.normal[i], viewInModel));
        f = ((alphaMax - alphaMin) * f + alphaMin) * 255.0;
		f = f - Q_max((int)(f - 255), 0);

		colors[0] = colors[1] = colors[2] = (unsigned char)Q_max(f, 0);
	}
}

/*
** RB_CalcAlphaFromConstant
*/
void RB_CalcAlphaFromConstant(unsigned char* dstColors, int constantAlpha)
{
	int i;

	for (i = 0; i < tess.numVertexes; i++) {
		dstColors[i * 4 + 3] = constantAlpha;
	}
}

/*
** RB_CalcAlphaFromDot
*/
void RB_CalcAlphaFromDot(unsigned char* colors, float alphaMin, float alphaMax)
{
	int i;
	vec3_t viewInModel;

	for (i = 0; i < tess.numVertexes; i++, colors += 4) {
		float f;

		VectorSubtract(backEnd.ori.viewOrigin, tess.xyz[i], viewInModel);
		VectorNormalizeFast(viewInModel);

        f = Square(DotProduct(tess.normal[i], viewInModel));
        f = ((alphaMax - alphaMin) * f + alphaMin) * 255.0;
		f = f - Q_max((int)(f - 255), 0);

		colors[0] = colors[1] = colors[2] = (unsigned char)Q_max(f, 0);
	}
}

/*
** RB_CalcAlphaFromHeightFade
*/
void RB_CalcAlphaFromHeightFade(unsigned char* colors, float alphaMin, float alphaMax)
{
	int i;
	float dist;
	float alpha;

	for (i = 0; i < tess.numVertexes; i++) {
		dist = fabs(backEnd.ori.viewOrigin[2] - tess.xyz[i][2]);
		dist = Q_clamp_float(dist, alphaMin, alphaMax);
		// Calculate the alpha height
		alpha = (dist - alphaMin) / (alphaMax - alphaMin);
		colors[i * 4 + 3] = (unsigned char)((1.f - alpha) * 255.0);
	}
}

/*
** RB_CalcAlphaFromOneMinusDot
*/
void RB_CalcAlphaFromOneMinusDot(unsigned char* colors, float alphaMin, float alphaMax)
{
	int i;
	vec3_t viewInModel;

	for (i = 0; i < tess.numVertexes; i++, colors += 4) {
		float f;

		VectorSubtract(backEnd.ori.viewOrigin, tess.xyz[i], viewInModel);
		VectorNormalizeFast(viewInModel);

        f = 1.0 - Square(DotProduct(tess.normal[i], viewInModel));
        f = ((alphaMax - alphaMin) * f + alphaMin) * 255.0;
		f = f - Q_max((int)(f - 255), 0);

		colors[0] = colors[1] = colors[2] = (unsigned char)Q_max(f, 0);
	}
}

/*
** RB_CalcAlphaFromTexCoords
*/
void RB_CalcAlphaFromTexCoords(unsigned char* colors, float alphaMin, float alphaMax, int alphaMinCap, int alphaCap, float sWeight, float tWeight, float* st)
{
	int i;

	for (i = 0; i < tess.numVertexes; i++, colors += 4, st += 2) {
		float f;

        f = sWeight * tess.texCoords[i][0][0] + tWeight * tess.texCoords[i][0][1];
        f = ((alphaMax - alphaMin) * f + alphaMin) * 255.0;
		f = f - Q_max((int)(f - alphaCap), 0);
		f = alphaMinCap + Q_max((int)(f - alphaMinCap), 0);
	
		colors[3] = (unsigned char)f;
	}
}

/*
** RB_CalcRGBFromTexCoords
*/
void RB_CalcRGBFromTexCoords(unsigned char* colors, float alphaMin, float alphaMax, int alphaMinCap, int alphaCap, float sWeight, float tWeight, float* st)
{
	int i;

	for (i = 0; i < tess.numVertexes; i++, colors += 4, st += 2) {
		float f;

        f = sWeight * tess.texCoords[i][0][0] + tWeight * tess.texCoords[i][0][1];
        f = ((alphaMax - alphaMin) * f + alphaMin) * 255.0;
		f = f - Q_max((int)(f - alphaCap), 0);
		f = alphaMinCap + Q_max((int)(f - alphaMinCap), 0);

		colors[0] = colors[1] = colors[2] = (unsigned char)f;
	}
}

/*
** RB_CalcAlphaFromEntity
*/
void RB_CalcAlphaFromEntity( unsigned char *dstColors )
{
	int	i;

	if ( !backEnd.currentEntity )
		return;

	dstColors += 3;

	for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 )
	{
		*dstColors = backEnd.currentEntity->e.shaderRGBA[3];
	}
}

/*
** RB_CalcAlphaFromOneMinusEntity
*/
void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors )
{
	int	i;

	if ( !backEnd.currentEntity )
		return;

	dstColors += 3;

	for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 )
	{
		*dstColors = 0xff - backEnd.currentEntity->e.shaderRGBA[3];
	}
}

/*
** RB_CalcWaveColor
*/
void RB_CalcWaveColor(const waveForm_t* wf, unsigned char* dstColors, unsigned char* constantColor)
{
	int i;
	int v;
	float glow;
	int *colors = ( int * ) dstColors;
	byte	color[4];


  if ( wf->func == GF_NOISE ) {
		glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude;
	} else {
		glow = EvalWaveForm( wf ) * tr.identityLight;
	}
	
	if ( glow < 0 ) {
		glow = 0;
	}
	else if ( glow > 1 ) {
		glow = 1;
	}

	if (constantColor)
	{
		color[0] = constantColor[0] * glow;
		color[1] = constantColor[1] * glow;
		color[2] = constantColor[2] * glow;
	}
	else
	{
		v = myftol(255 * glow);
		color[0] = color[1] = color[2] = v;
	}

	color[3] = 255;
	v = *(int*)color;
	
	for ( i = 0; i < tess.numVertexes; i++, colors++ ) {
		*colors = v;
	}
}

/*
** RB_CalcWaveAlpha
*/
void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors )
{
	int i;
	int v;
	float glow;

	glow = EvalWaveFormClamped( wf );

	v = 255 * glow;

	for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 )
	{
		dstColors[3] = v;
	}
}


/*
====================================================================

TEX COORDS

====================================================================
*/

/*
** RB_CalcEnvironmentTexCoords
*/
void RB_CalcEnvironmentTexCoords( float *st ) 
{
	int			i;
	float		*v, *normal;
	vec3_t		viewer, reflected;
	float		d;

	v = tess.xyz[0];
	normal = tess.normal[0];

	for (i = 0 ; i < tess.numVertexes ; i++, v += 4, normal += 4, st += 2 ) 
	{
		VectorSubtract (backEnd.ori.viewOrigin, v, viewer);
		VectorNormalizeFast (viewer);

		d = DotProduct (normal, viewer);

		reflected[0] = normal[0]*2*d - viewer[0];
		reflected[1] = normal[1]*2*d - viewer[1];
		reflected[2] = normal[2]*2*d - viewer[2];

		st[0] = 0.5 + reflected[1] * 0.5;
		st[1] = 0.5 - reflected[2] * 0.5;
	}
}

/*
** RB_CalcEnvironmentTexCoords
*/
void RB_CalcEnvironmentTexCoords2(float* st)
{
    int			i;
    float* v, * normal;
    vec3_t		viewer, reflected, worldReflected;
    float		d;

    v = tess.xyz[0];
    normal = tess.normal[0];

    for (i = 0; i < tess.numVertexes; i++, v += 4, normal += 4, st += 2)
    {
        VectorSubtract(backEnd.ori.viewOrigin, v, viewer);
        VectorNormalizeFast(viewer);

        d = DotProduct(normal, viewer);
		if (d > 0.0f) {
			VectorCopy(viewer, reflected);
		} else {
            d *= -2;
            reflected[0] = normal[0] * d + viewer[0];
            reflected[1] = normal[1] * d + viewer[1];
            reflected[2] = normal[2] * d + viewer[2];
		}

		worldReflected[0] = reflected[0] * backEnd.ori.axis[0][0];
		worldReflected[2] = reflected[0] * backEnd.ori.axis[0][2];
		worldReflected[0] = backEnd.ori.axis[1][0] * reflected[1] + worldReflected[0];
		worldReflected[2] = backEnd.ori.axis[1][2] * reflected[1] + worldReflected[2];
		worldReflected[0] = backEnd.ori.axis[2][0] * reflected[2] + worldReflected[0];
		worldReflected[2] = backEnd.ori.axis[2][2] * reflected[2] + worldReflected[2];

        st[0] = 0.5 + reflected[1] * 0.5;
        st[1] = 0.5 - reflected[2] * 0.5;
    }
}

/*
** RB_CalcSunReflectionTexCoords
*/
void RB_CalcSunReflectionTexCoords(float* st) {
	int i;
	vec3_t viewer;
	vec3_t reflected;
	vec3_t sunReflected;
	float d;
	float* v;
	float* normal;
	vec3_t sunAxis[3];

    v = tess.xyz[0];
    normal = tess.normal[0];
    sunAxis[0][0] = tr.sunDirection[0];
    sunAxis[0][1] = tr.sunDirection[1];
    sunAxis[0][2] = tr.sunDirection[2];
    sunAxis[1][0] = tr.sunDirection[1];
    sunAxis[1][1] = -tr.sunDirection[0];
    sunAxis[1][2] = 0.0;
    VectorNormalizeFast(sunAxis[1]);
    CrossProduct(sunAxis[0], sunAxis[1], sunAxis[2]);

	for (i = 0; i < tess.numVertexes; i++, v += 4, normal += 4, st += 2) {
		VectorSubtract(v, backEnd.ori.viewOrigin, viewer);
		VectorNormalizeFast(viewer);

		d = DotProduct(viewer, normal);
		if (d > 0) {
			VectorCopy(viewer, reflected);
		} else {
			d *= -2;
			VectorMA(viewer, d, normal, reflected);
		}

		VectorScale(sunAxis[0], reflected[0], sunReflected);
		VectorMA(sunReflected, reflected[2], sunAxis[1], sunReflected);
		VectorMA(sunReflected, -reflected[1], sunAxis[2], sunReflected);

		st[0] = sunReflected[1] + 0.5;
		st[1] = sunReflected[2] + 0.5;
	}
}

/*
** RB_CalcTurbulentTexCoords
*/
void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st )
{
	int i;
	float now;

	now = ( wf->phase + tess.shaderTime * wf->frequency );

	for ( i = 0; i < tess.numVertexes; i++, st += 2 )
	{
		float s = st[0];
		float t = st[1];

		st[0] = s + tr.sinTable[ ( ( int ) ( ( ( tess.xyz[i][0] + tess.xyz[i][2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude;
		st[1] = t + tr.sinTable[ ( ( int ) ( ( tess.xyz[i][1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude;
	}
}

/*
** RB_CalcScaleTexCoords
*/
void RB_CalcScaleTexCoords( const float scale[2], float *st )
{
	int i;

	for ( i = 0; i < tess.numVertexes; i++, st += 2 )
	{
		st[0] *= scale[0];
		st[1] *= scale[1];
	}
}

/*
** RB_CalcOffsetTexCoords
*/
void RB_CalcOffsetTexCoords( const float *offset, float *st )
{
	int i;
	float offsetS, offsetT;

	if (offset[0] != 1234567) {
		offsetS = offset[0];
	} else if (backEnd.currentEntity) {
        offsetS = backEnd.currentEntity->e.shader_data[0];
    } else {
        offsetS = r_static_shaderdata0->value;
    }

	if (offset[0] != 1234567) {
		offsetT = offset[1];
	} else if (backEnd.currentEntity) {
		offsetT = backEnd.currentEntity->e.shader_data[1];
    } else {
		offsetT = r_static_shaderdata1->value;
    }

    if (!backEnd.currentEntity) {
        offsetS = offsetS * r_static_shadermultiplier0->value;
        offsetT = offsetT * r_static_shadermultiplier1->value;
    }

	for (i = 0; i < tess.numVertexes; i++, st += 2) {
		float s, t;

		s = st[0];
		t = st[1];

		st[0] = s + offsetS;
		st[1] = t + offsetT;
	}
}

/*
** RB_CalcParallaxTexCoords
*/
void RB_CalcParallaxTexCoords( const float *rate, float *st )
{
	int i;
	float offsetS, offsetT;

    offsetS = tr.refdef.vieworg[0] * rate[0];
    offsetT = tr.refdef.vieworg[1] * rate[1];
    for (i = 0; i < tess.numVertexes; i++, st += 2) {
		st[0] += offsetS;
		st[1] += offsetT;
    }
}

static vec3_t rb_baseaxis[18];


void RB_TextureAxisFromPlane(const vec3_t normal, vec3_t xv, vec3_t yv)
{
	int bestaxis;
	vec_t dot;
	vec_t best;
	int i;

	if (normal[0] == 1) {
		bestaxis = 2;
	} else if (normal[0] == -1) {
		bestaxis = 3;
	} else if (normal[1] == 1) {
		bestaxis = 4;
	} else if (normal[1] == -1) {
		bestaxis = 5;
	} else if (normal[2] == 1) {
		bestaxis = 0;
	} else if (normal[2] == -1) {
		bestaxis = 1;
	} else {
		best = 0;
		bestaxis = 0;

		for (i = 0; i < 6; i++) {
			dot = DotProduct(rb_baseaxis[i * 3], normal);
			if (dot > best) {
				best = dot;
				bestaxis = i;
			}
		}
	}

	VectorCopy(rb_baseaxis[bestaxis * 3 + 1], xv);
	VectorCopy(rb_baseaxis[bestaxis * 3 + 2], yv);
}

void RB_QuakeTextureVecs(const vec3_t normal, const vec2_t scale, vec3_t mappingVecs[2])
{
	RB_TextureAxisFromPlane(normal, mappingVecs[0], mappingVecs[1]);

	VectorScale(mappingVecs[0], scale[0], mappingVecs[0]);
	VectorScale(mappingVecs[1], scale[1], mappingVecs[1]);
}

/*
** RB_CalcMacroTexCoords
*/
void RB_CalcMacroTexCoords( const  float* rate, float *st )
{
	int i;
	vec3_t vecs[2];
	vec_t* v;
	vec_t* normal;
	float width, height;
    v = tess.xyz[0];

    normal = tess.normal[0];
    width = (float)tess.shader->unfoggedStages[0]->bundle[0].image[0]->uploadWidth;
    height = (float)tess.shader->unfoggedStages[0]->bundle[0].image[0]->uploadHeight;

	for (i = 0; i < tess.numVertexes; i++, v += 4, normal += 4, st += 2) {
		RB_QuakeTextureVecs(normal, rate, vecs);
		st[0] = DotProduct(vecs[0], v) / width;
		st[1] = DotProduct(vecs[1], v) / height;
	}
}

/*
** RB_CalcScrollTexCoords
*/
void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st )
{
	int i;
	float timeScale = tess.shaderTime;
	float adjustedScrollS, adjustedScrollT;

	adjustedScrollS = scrollSpeed[0] * timeScale;
	adjustedScrollT = scrollSpeed[1] * timeScale;

	// clamp so coordinates don't continuously get larger, causing problems
	// with hardware limits
	adjustedScrollS = adjustedScrollS - floor( adjustedScrollS );
	adjustedScrollT = adjustedScrollT - floor( adjustedScrollT );

	for ( i = 0; i < tess.numVertexes; i++, st += 2 )
	{
		st[0] += adjustedScrollS;
		st[1] += adjustedScrollT;
	}
}

/*
** RB_CalcTransformTexCoords
*/
void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *st  )
{
	int i;

	for ( i = 0; i < tess.numVertexes; i++, st += 2 )
	{
		float s = st[0];
		float t = st[1];

		st[0] = s * tmi->matrix[0][0] + t * tmi->matrix[1][0] + tmi->translate[0];
		st[1] = s * tmi->matrix[0][1] + t * tmi->matrix[1][1] + tmi->translate[1];
	}
}

/*
** RB_CalcRotateTexCoords
*/
void RB_CalcRotateTexCoords(float degsPerSecond, float degsPerSecondCoef, float* st, float start)
{
	float timeScale = tess.shaderTime;
	float degs;
	int index;
	float sinValue, cosValue;
	texModInfo_t tmi;

    if (degsPerSecond != 1234567) {
		degs = -degsPerSecond * degsPerSecondCoef * tess.shaderTime - start;
	} else if (backEnd.currentEntity) {
		degs = -backEnd.currentEntity->e.shader_data[0] * degsPerSecondCoef * tess.shaderTime - start;
	} else {
		degs = r_static_shaderdata0->value;
	}
	if (!backEnd.currentEntity) {
		degs *= r_static_shadermultiplier0->value;
	}
	index = degs * ( FUNCTABLE_SIZE / 360.0f );

	sinValue = tr.sinTable[ index & FUNCTABLE_MASK ];
	cosValue = tr.sinTable[ ( index + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ];

	tmi.matrix[0][0] = cosValue;
	tmi.matrix[1][0] = -sinValue;
	tmi.translate[0] = 0.5 - 0.5 * cosValue + 0.5 * sinValue;

	tmi.matrix[0][1] = sinValue;
	tmi.matrix[1][1] = cosValue;
	tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue;

	RB_CalcTransformTexCoords( &tmi, st );
}






#if id386 && !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__ ) ) // rb010123

long myftol( float f ) {
	static int tmp;
	__asm fld f
	__asm fistp tmp
	__asm mov eax, tmp
}

#endif

/*
** RB_CalcSpecularAlpha
**
** Calculates specular coefficient and places it in the alpha channel
*/
vec3_t lightOrigin = { -960, 1980, 96 };		// FIXME: track dynamically

void RB_CalcSpecularAlpha(unsigned char* alphas, float alphaMax, vec3_t lightOrigin) {
	int			i;
	float		*v, *normal;
	vec3_t		viewer,  reflected;
	float		l, d;
	int			b;
	vec3_t		lightDir;
	int			numVertexes;

	v = tess.xyz[0];
	normal = tess.normal[0];

	alphas += 3;

	numVertexes = tess.numVertexes;
	for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4, alphas += 4) {
		float ilength;

		VectorSubtract( lightOrigin, v, lightDir );
//		ilength = Q_rsqrt( DotProduct( lightDir, lightDir ) );
		VectorNormalizeFast( lightDir );

		// calculate the specular color
		d = DotProduct (normal, lightDir);
//		d *= ilength;

		// we don't optimize for the d < 0 case since this tends to
		// cause visual artifacts such as faceted "snapping"
		reflected[0] = normal[0]*2*d - lightDir[0];
		reflected[1] = normal[1]*2*d - lightDir[1];
		reflected[2] = normal[2]*2*d - lightDir[2];

		VectorSubtract (backEnd.ori.viewOrigin, v, viewer);
		ilength = Q_rsqrt( DotProduct( viewer, viewer ) );
		l = DotProduct (reflected, viewer);
		l *= ilength;

		if (l < 0) {
			b = 0;
		} else {
			l = l*l;
			l = l*l;
			b = l * 255;
			if (b > 255) {
				b = 255;
			}
		}

		*alphas = b;
	}
}

void RB_CalcLightGridColor(unsigned char* colors)
{
	int i;

	if (!backEnd.currentEntity) {
		for (i = 0; i < tess.numVertexes; i++) {
			colors[i * 4] = ((byte*)&backEnd.currentStaticModel->iGridLighting)[0];
			colors[i * 4 + 1] = ((byte*)&backEnd.currentStaticModel->iGridLighting)[1];
			colors[i * 4 + 2] = ((byte*)&backEnd.currentStaticModel->iGridLighting)[2];
			colors[i * 4 + 3] = ((byte*)&backEnd.currentStaticModel->iGridLighting)[3];
		}
	}
	else if (backEnd.currentEntity != &tr.worldEntity) {
		for (i = 0; i < tess.numVertexes; i++) {
			colors[i * 4] = ((byte*)&backEnd.currentEntity->iGridLighting)[0];
			colors[i * 4 + 1] = ((byte*)&backEnd.currentEntity->iGridLighting)[1];
			colors[i * 4 + 2] = ((byte*)&backEnd.currentEntity->iGridLighting)[2];
			colors[i * 4 + 3] = ((byte*)&backEnd.currentEntity->iGridLighting)[3];
		}
	}
	else {
		ri.Printf(PRINT_ALL, 
			"##### shader '%s' incorrectly uses rgbGen lightingGrid or lightingSpherical; was rgbGen vertex intended?\n",
			tess.shader->name);

		for (i = 0; i < tess.numVertexes; i++) {
			colors[i * 4] = 0xFF;
			colors[i * 4 + 1] = 0xFF;
			colors[i * 4 + 2] = 0xFF;
			colors[i * 4 + 3] = 0xFF;
		}
	}
}

void RB_CalcAlphaFromDotView(unsigned char* colors, float alphaMin, float alphaMax)
{
	int i;
	vec3_t viewInModel;

    for (i = 0; i < tess.numVertexes; i++, colors += 4) {
        union {
            float f;
            int i;
        } u;

		VectorCopy(tr.refdef.viewaxis[0], viewInModel);
		VectorNormalizeFast(viewInModel);

        u.f = DotProduct(viewInModel, tess.normal[i]);
        u.f = fabs(u.f);
        ri.Printf(PRINT_ALL, "normal: %f %f %f  dot: %f  i %d\n", tess.normal[i][0], tess.normal[i][1], tess.normal[i][2], u.f, i);

		u.f = ((alphaMax - alphaMin) * u.f + alphaMin) * 255.0;
        u.f = u.f - (float)(~((int)(u.f - 255.0) >> 31) & (int)(u.f - 255.0));

        u.i &= ~(u.i >> 31);
		colors[3] = u.i;
	}
}

void RB_CalcAlphaFromOneMinusDotView(unsigned char* colors, float alphaMin, float alphaMax)
{
	int i;
	vec3_t viewInModel;

    for (i = 0; i < tess.numVertexes; i++, colors += 4) {
        union {
            float f;
            int i;
        } u;

		VectorCopy(tr.refdef.viewaxis[0], viewInModel);
		VectorNormalizeFast(viewInModel);

        u.f = DotProduct(viewInModel, tess.normal[i]);
        u.f = fabs(u.f);
		u.f = 1.0 - u.f;
        ri.Printf(PRINT_ALL, "normal: %f %f %f  dot: %f  i %d\n", tess.normal[i][0], tess.normal[i][1], tess.normal[i][2], u.f, i);
        
        u.f = ((alphaMax - alphaMin) * u.f + alphaMin) * 255.0;
        u.f = u.f - (long double)(~((int)(u.f - 255.0) >> 31) & (int)(u.f - 255.0));

        u.i &= ~(u.i >> 31);
		colors[3] = u.i;
	}
}

/*
** RB_CalcDiffuseColor
**
** The basic vertex lighting calc
*/
void RB_CalcDiffuseColor( unsigned char *colors )
{
	int				i, j;
	float			*v, *normal;
	float			incoming;
	trRefEntity_t	*ent;
	int				ambientLightInt;
	vec3_t			ambientLight;
	vec3_t			lightDir;
	vec3_t			directedLight;
	int				numVertexes;
#if idppc_altivec
	vector unsigned char vSel = (vector unsigned char)(0x00, 0x00, 0x00, 0xff,
							   0x00, 0x00, 0x00, 0xff,
							   0x00, 0x00, 0x00, 0xff,
							   0x00, 0x00, 0x00, 0xff);
	vector float ambientLightVec;
	vector float directedLightVec;
	vector float lightDirVec;
	vector float normalVec0, normalVec1;
	vector float incomingVec0, incomingVec1, incomingVec2;
	vector float zero, jVec;
	vector signed int jVecInt;
	vector signed short jVecShort;
	vector unsigned char jVecChar, normalPerm;
#endif
	ent = backEnd.currentEntity;
	ambientLightInt = ent->ambientLightInt;
#if idppc_altivec
	// A lot of this could be simplified if we made sure
	// entities light info was 16-byte aligned.
	jVecChar = vec_lvsl(0, ent->ambientLight);
	ambientLightVec = vec_ld(0, (vector float *)ent->ambientLight);
	jVec = vec_ld(11, (vector float *)ent->ambientLight);
	ambientLightVec = vec_perm(ambientLightVec,jVec,jVecChar);

	jVecChar = vec_lvsl(0, ent->directedLight);
	directedLightVec = vec_ld(0,(vector float *)ent->directedLight);
	jVec = vec_ld(11,(vector float *)ent->directedLight);
	directedLightVec = vec_perm(directedLightVec,jVec,jVecChar);	 

	jVecChar = vec_lvsl(0, ent->lightDir);
	lightDirVec = vec_ld(0,(vector float *)ent->lightDir);
	jVec = vec_ld(11,(vector float *)ent->lightDir);
	lightDirVec = vec_perm(lightDirVec,jVec,jVecChar);	 

	zero = (vector float)vec_splat_s8(0);
	VectorCopy( ent->lightDir, lightDir );
#else
	VectorCopy( ent->ambientLight, ambientLight );
	VectorCopy( ent->directedLight, directedLight );
	VectorCopy( ent->lightDir, lightDir );
#endif

	v = tess.xyz[0];
	normal = tess.normal[0];

#if idppc_altivec
	normalPerm = vec_lvsl(0,normal);
#endif
	numVertexes = tess.numVertexes;
	for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) {
#if idppc_altivec
		normalVec0 = vec_ld(0,(vector float *)normal);
		normalVec1 = vec_ld(11,(vector float *)normal);
		normalVec0 = vec_perm(normalVec0,normalVec1,normalPerm);
		incomingVec0 = vec_madd(normalVec0, lightDirVec, zero);
		incomingVec1 = vec_sld(incomingVec0,incomingVec0,4);
		incomingVec2 = vec_add(incomingVec0,incomingVec1);
		incomingVec1 = vec_sld(incomingVec1,incomingVec1,4);
		incomingVec2 = vec_add(incomingVec2,incomingVec1);
		incomingVec0 = vec_splat(incomingVec2,0);
		incomingVec0 = vec_max(incomingVec0,zero);
		normalPerm = vec_lvsl(12,normal);
		jVec = vec_madd(incomingVec0, directedLightVec, ambientLightVec);
		jVecInt = vec_cts(jVec,0);	// RGBx
		jVecShort = vec_pack(jVecInt,jVecInt);		// RGBxRGBx
		jVecChar = vec_packsu(jVecShort,jVecShort);	// RGBxRGBxRGBxRGBx
		jVecChar = vec_sel(jVecChar,vSel,vSel);		// RGBARGBARGBARGBA replace alpha with 255
		vec_ste((vector unsigned int)jVecChar,0,(unsigned int *)&colors[i*4]);	// store color
#else
		incoming = DotProduct (normal, lightDir);
		if ( incoming <= 0 ) {
			*(int *)&colors[i*4] = ambientLightInt;
			continue;
		} 
		j = myftol( ambientLight[0] + incoming * directedLight[0] );
		if ( j > 255 ) {
			j = 255;
		}
		colors[i*4+0] = j;

		j = myftol( ambientLight[1] + incoming * directedLight[1] );
		if ( j > 255 ) {
			j = 255;
		}
		colors[i*4+1] = j;

		j = myftol( ambientLight[2] + incoming * directedLight[2] );
		if ( j > 255 ) {
			j = 255;
		}
		colors[i*4+2] = j;

		colors[i*4+3] = 255;
#endif
	}
}

