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

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

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

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

		R_VaoUnpackNormal(fNormal, *normal);

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

		scale = tr.sinTable[ off & FUNCTABLE_MASK ] * ds->bulgeHeight;

		xyz[0] += fNormal[0] * scale;
		xyz[1] += fNormal[1] * scale;
		xyz[2] += fNormal[2] * scale;
	}
}
/*
=========================
RB_CalcDeformNormals

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

	for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal++ ) {
		vec3_t fNormal;

		R_VaoUnpackNormal(fNormal, *normal);

		scale = 0.98f;
		scale = R_NoiseGet4f( xyz[0] * scale, xyz[1] * scale, xyz[2] * scale,
							  tess.shaderTime * ds->deformationWave.frequency );
		fNormal[ 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 );
		fNormal[ 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 );
		fNormal[ 2 ] += ds->deformationWave.amplitude * scale;

		VectorNormalizeFast( fNormal );

		R_VaoPackNormal((byte *)normal, fNormal);
	}
}
/*
========================
RB_CalcDeformVertexes

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

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

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

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

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

			R_VaoUnpackNormal(offset, *normal);

			xyz[0] += offset[0] * scale;
			xyz[1] += offset[1] * scale;
			xyz[2] += offset[2] * scale;
		}
	}
}
/*
=============
DeformText

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

	height[0] = 0;
	height[1] = 0;
	height[2] = -1;

	R_VaoUnpackNormal(fNormal, tess.normal[0]);
	CrossProduct( fNormal, 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;
	tess.firstIndex = 0;

	color[0] = color[1] = color[2] = color[3] = 1.0f;

	// 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 );
	}
/*
========================
RB_CalcDeformVertexes

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

	// Ridah
	if ( ds->deformationWave.frequency < 0 ) {
		qboolean inverse = qfalse;
		vec3_t worldUp;
		//static vec3_t up = {0,0,1};

		if ( VectorCompare( backEnd.currentEntity->e.fireRiseDir, vec3_origin ) ) {
			VectorSet( backEnd.currentEntity->e.fireRiseDir, 0, 0, 1 );
		}

		// get the world up vector in local coordinates
		if ( backEnd.currentEntity->e.hModel ) {  // world surfaces dont have an axis
			VectorRotate( backEnd.currentEntity->e.fireRiseDir, backEnd.currentEntity->e.axis, worldUp );
		} else {
			VectorCopy( backEnd.currentEntity->e.fireRiseDir, worldUp );
		}
		// don't go so far if sideways, since they must be moving
		VectorScale( worldUp, 0.4 + 0.6 * fabs( backEnd.currentEntity->e.fireRiseDir[2] ), worldUp );

		ds->deformationWave.frequency *= -1;
		if ( ds->deformationWave.frequency > 999 ) {  // hack for negative Z deformation (ack)
			inverse = qtrue;
			ds->deformationWave.frequency -= 999;
		}

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

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

			R_VaoUnpackNormal(fNormal, *normal);

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

			dot = DotProduct( worldUp, fNormal );

			if ( dot * scale > 0 ) {
				if ( inverse ) {
					scale *= -1;
				}
				VectorMA( xyz, dot * scale, worldUp, xyz );
			}
		}

		if ( inverse ) {
			ds->deformationWave.frequency += 999;
		}
		ds->deformationWave.frequency *= -1;
	}
	// done.
	else if ( ds->deformationWave.frequency == 0 ) {
		scale = EvalWaveForm( &ds->deformationWave );

		for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal++ )
		{
			R_VaoUnpackNormal(offset, *normal);

			xyz[0] += offset[0] * scale;
			xyz[1] += offset[1] * scale;
			xyz[2] += offset[2] * scale;
		}
	} else
	{
		table = TableForFunc( ds->deformationWave.func );

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

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

			R_VaoUnpackNormal(offset, *normal);

			xyz[0] += offset[0] * scale;
			xyz[1] += offset[1] * scale;
			xyz[2] += offset[2] * scale;
		}
	}
}