summaryrefslogtreecommitdiff
path: root/src/renderergl2/tr_shade_calc.cpp
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /src/renderergl2/tr_shade_calc.cpp
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'src/renderergl2/tr_shade_calc.cpp')
-rw-r--r--src/renderergl2/tr_shade_calc.cpp843
1 files changed, 843 insertions, 0 deletions
diff --git a/src/renderergl2/tr_shade_calc.cpp b/src/renderergl2/tr_shade_calc.cpp
new file mode 100644
index 0000000..182020b
--- /dev/null
+++ b/src/renderergl2/tr_shade_calc.cpp
@@ -0,0 +1,843 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous 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 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+// tr_shade_calc.c
+
+#include "tr_local.h"
+#if idppc_altivec && !defined(__APPLE__)
+#include <altivec.h>
+#endif
+
+
+#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ static_cast<int64_t>( ( ( (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'", 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;
+
+ table = TableForFunc( wf->func );
+
+ return WAVEVALUE( table, wf->base, wf->amplitude, wf->phase, wf->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_CalcStretchTexMatrix
+*/
+void RB_CalcStretchTexMatrix( const waveForm_t *wf, float *matrix )
+{
+ float p;
+
+ p = 1.0f / EvalWaveForm( wf );
+
+ matrix[0] = p; matrix[2] = 0; matrix[4] = 0.5f - 0.5f * p;
+ matrix[1] = 0; matrix[3] = p; matrix[5] = 0.5f - 0.5f * p;
+}
+
+/*
+====================================================================
+
+DEFORMATIONS
+
+====================================================================
+*/
+
+/*
+========================
+RB_CalcDeformVertexes
+
+========================
+*/
+void RB_CalcDeformVertexes( deformStage_t *ds )
+{
+ int i;
+ vec3_t offset;
+ float scale;
+ float *xyz = ( float * ) tess.xyz;
+ int16_t *normal = tess.normal[0];
+ float *table;
+
+ if ( ds->deformationWave.frequency == 0 )
+ {
+ scale = EvalWaveForm( &ds->deformationWave );
+
+ for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 )
+ {
+ 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 += 4 )
+ {
+ 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;
+ }
+ }
+}
+
+/*
+=========================
+RB_CalcDeformNormals
+
+Wiggle the normals for wavy environment mapping
+=========================
+*/
+void RB_CalcDeformNormals( deformStage_t *ds ) {
+ int i;
+ float scale;
+ float *xyz = ( float * ) tess.xyz;
+ int16_t *normal = tess.normal[0];
+
+ for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) {
+ 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(normal, fNormal);
+ }
+}
+
+/*
+========================
+RB_CalcBulgeVertexes
+
+========================
+*/
+void RB_CalcBulgeVertexes( deformStage_t *ds ) {
+ int i;
+ const float *st = ( const float * ) tess.texCoords[0];
+ float *xyz = ( float * ) tess.xyz;
+ int16_t *normal = tess.normal[0];
+
+ double now = backEnd.refdef.time * 0.001 * ds->bulgeSpeed;
+
+ for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 2, normal += 4 ) {
+ int64_t 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_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;
+ 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 );
+ }
+}
+
+/*
+==================
+GlobalVectorToLocal
+==================
+*/
+static void GlobalVectorToLocal( const vec3_t in, vec3_t out ) {
+ out[0] = DotProduct( in, backEnd.orientation.axis[0] );
+ out[1] = DotProduct( in, backEnd.orientation.axis[1] );
+ out[2] = DotProduct( in, backEnd.orientation.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;
+ tess.firstIndex = 0;
+
+ if ( backEnd.currentEntity != &tr.worldEntity ) {
+ GlobalVectorToLocal( backEnd.viewParms.orientation.axis[1], leftDir );
+ GlobalVectorToLocal( backEnd.viewParms.orientation.axis[2], upDir );
+ } else {
+ VectorCopy( backEnd.viewParms.orientation.axis[1], leftDir );
+ VectorCopy( backEnd.viewParms.orientation.axis[2], upDir );
+ }
+
+ for ( i = 0 ; i < oldVerts ; i+=4 ) {
+ vec4_t color;
+ // 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.currentEntity->e.nonNormalizedAxes ) {
+ float axisLength;
+ axisLength = VectorLength( backEnd.currentEntity->e.axis[0] );
+ if ( !axisLength ) {
+ axisLength = 0;
+ } else {
+ axisLength = 1.0f / axisLength;
+ }
+ VectorScale(left, axisLength, left);
+ VectorScale(up, axisLength, up);
+ }
+
+ VectorScale4(tess.color[i], 1.0f / 65535.0f, color);
+ RB_AddQuadStamp( mid, left, up, color );
+ }
+}
+
+
+/*
+=====================
+Autosprite2Deform
+
+Autosprite2 will pivot a rectangular quad along the center of its long axis
+=====================
+*/
+int edgeVerts[6][2] = {
+ { 0, 1 },
+ { 0, 2 },
+ { 0, 3 },
+ { 1, 2 },
+ { 1, 3 },
+ { 2, 3 }
+};
+
+static void Autosprite2Deform( void ) {
+ int i, j, k;
+ 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.currentEntity != &tr.worldEntity ) {
+ GlobalVectorToLocal( backEnd.viewParms.orientation.axis[0], forward );
+ } else {
+ VectorCopy( backEnd.viewParms.orientation.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 lengths[2];
+ int nums[2];
+ vec3_t mid[2];
+ vec3_t major, minor;
+ float *v1, *v2;
+
+ // find the midpoint
+ xyz = tess.xyz[i];
+
+ // identify the two shortest edges
+ nums[0] = nums[1] = 0;
+ lengths[0] = lengths[1] = 999999;
+
+ 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 < lengths[0] ) {
+ nums[1] = nums[0];
+ lengths[1] = lengths[0];
+ nums[0] = j;
+ lengths[0] = l;
+ } else if ( l < lengths[1] ) {
+ nums[1] = j;
+ lengths[1] = l;
+ }
+ }
+
+ for ( j = 0 ; j < 2 ; j++ ) {
+ v1 = xyz + 4 * edgeVerts[nums[j]][0];
+ v2 = xyz + 4 * edgeVerts[nums[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 );
+
+ // re-project the points
+ for ( j = 0 ; j < 2 ; j++ ) {
+ float l;
+
+ v1 = xyz + 4 * edgeVerts[nums[j]][0];
+ v2 = xyz + 4 * edgeVerts[nums[j]][1];
+
+ l = 0.5 * sqrt( lengths[j] );
+
+ // we need to see which direction this edge
+ // is used to determine direction of projection
+ for ( k = 0 ; k < 5 ; k++ ) {
+ if ( tess.indexes[ indexes + k ] == i + edgeVerts[nums[j]][0]
+ && tess.indexes[ indexes + k + 1 ] == i + edgeVerts[nums[j]][1] ) {
+ break;
+ }
+ }
+
+ if ( k == 5 ) {
+ VectorMA( mid[j], l, minor, v1 );
+ VectorMA( mid[j], -l, minor, v2 );
+ } else {
+ VectorMA( mid[j], -l, minor, v1 );
+ VectorMA( mid[j], l, minor, v2 );
+ }
+ }
+ }
+}
+
+
+/*
+=====================
+RB_DeformTessGeometry
+
+=====================
+*/
+void RB_DeformTessGeometry( void ) {
+ int i;
+ deformStage_t *ds;
+
+ if(!ShaderRequiresCPUDeforms(tess.shader))
+ {
+ // we don't need the following CPU deforms
+ return;
+ }
+
+ 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_PROJECTION_SHADOW:
+ RB_ProjectionShadowDeform();
+ break;
+ case DEFORM_AUTOSPRITE:
+ AutospriteDeform();
+ break;
+ case DEFORM_AUTOSPRITE2:
+ Autosprite2Deform();
+ break;
+ case DEFORM_TEXT0:
+ case DEFORM_TEXT1:
+ case DEFORM_TEXT2:
+ case DEFORM_TEXT3:
+ case DEFORM_TEXT4:
+ case DEFORM_TEXT5:
+ case DEFORM_TEXT6:
+ case DEFORM_TEXT7:
+ DeformText( backEnd.refdef.text[ds->deformation - DEFORM_TEXT0] );
+ break;
+ }
+ }
+}
+
+/*
+====================================================================
+
+COLORS
+
+====================================================================
+*/
+
+
+/*
+** RB_CalcWaveColorSingle
+*/
+float RB_CalcWaveColorSingle( const waveForm_t *wf )
+{
+ float glow;
+
+ 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;
+ }
+
+ return glow;
+}
+
+/*
+** RB_CalcWaveAlphaSingle
+*/
+float RB_CalcWaveAlphaSingle( const waveForm_t *wf )
+{
+ return EvalWaveFormClamped( wf );
+}
+
+/*
+** RB_CalcModulateColorsByFog
+*/
+void RB_CalcModulateColorsByFog( unsigned char *colors ) {
+ int i;
+ float texCoords[SHADER_MAX_VERTEXES][2] = {{0.0f}};
+
+ // calculate texcoords so we can derive density
+ // this is not wasted, because it would only have
+ // been previously called if the surface was opaque
+ RB_CalcFogTexCoords( texCoords[0] );
+
+ for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) {
+ float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] );
+ colors[0] *= f;
+ colors[1] *= f;
+ colors[2] *= f;
+ }
+}
+
+
+/*
+====================================================================
+
+TEX COORDS
+
+====================================================================
+*/
+
+/*
+========================
+RB_CalcFogTexCoords
+
+To do the clipped fog plane really correctly, we should use
+projected textures, but I don't trust the drivers and it
+doesn't fit our shader data.
+========================
+*/
+void RB_CalcFogTexCoords( float *st ) {
+ int i;
+ float *v;
+ float s, t;
+ float eyeT;
+ bool eyeOutside;
+ fog_t *fog;
+ vec3_t local;
+ vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0};
+
+ fog = tr.world->fogs + tess.fogNum;
+
+ // all fogging distance is based on world Z units
+ VectorSubtract( backEnd.orientation.origin, backEnd.viewParms.orientation.origin, local );
+ fogDistanceVector[0] = -backEnd.orientation.modelMatrix[2];
+ fogDistanceVector[1] = -backEnd.orientation.modelMatrix[6];
+ fogDistanceVector[2] = -backEnd.orientation.modelMatrix[10];
+ fogDistanceVector[3] = DotProduct( local, backEnd.viewParms.orientation.axis[0] );
+
+ // scale the fog vectors based on the fog's thickness
+ fogDistanceVector[0] *= fog->tcScale;
+ fogDistanceVector[1] *= fog->tcScale;
+ fogDistanceVector[2] *= fog->tcScale;
+ fogDistanceVector[3] *= fog->tcScale;
+
+ // rotate the gradient vector for this orientation
+ if ( fog->hasSurface ) {
+ fogDepthVector[0] = fog->surface[0] * backEnd.orientation.axis[0][0] +
+ fog->surface[1] * backEnd.orientation.axis[0][1] + fog->surface[2] * backEnd.orientation.axis[0][2];
+ fogDepthVector[1] = fog->surface[0] * backEnd.orientation.axis[1][0] +
+ fog->surface[1] * backEnd.orientation.axis[1][1] + fog->surface[2] * backEnd.orientation.axis[1][2];
+ fogDepthVector[2] = fog->surface[0] * backEnd.orientation.axis[2][0] +
+ fog->surface[1] * backEnd.orientation.axis[2][1] + fog->surface[2] * backEnd.orientation.axis[2][2];
+ fogDepthVector[3] = -fog->surface[3] + DotProduct( backEnd.orientation.origin, fog->surface );
+
+ eyeT = DotProduct( backEnd.orientation.viewOrigin, fogDepthVector ) + fogDepthVector[3];
+ } else {
+ eyeT = 1; // non-surface fog always has eye inside
+ }
+
+ // see if the viewpoint is outside
+ // this is needed for clipping distance even for constant fog
+
+ if ( eyeT < 0 ) {
+ eyeOutside = true;
+ } else {
+ eyeOutside = false;
+ }
+
+ fogDistanceVector[3] += 1.0/512;
+
+ // calculate density for each point
+ for (i = 0, v = tess.xyz[0] ; i < tess.numVertexes ; i++, v += 4) {
+ // calculate the length in fog
+ s = DotProduct( v, fogDistanceVector ) + fogDistanceVector[3];
+ t = DotProduct( v, fogDepthVector ) + fogDepthVector[3];
+
+ // partially clipped fogs use the T axis
+ if ( eyeOutside ) {
+ if ( t < 1.0 ) {
+ t = 1.0/32; // point is outside, so no fogging
+ } else {
+ t = 1.0/32 + 30.0/32 * t / ( t - eyeT ); // cut the distance at the fog plane
+ }
+ } else {
+ if ( t < 0 ) {
+ t = 1.0/32; // point is outside, so no fogging
+ } else {
+ t = 31.0/32;
+ }
+ }
+
+ st[0] = s;
+ st[1] = t;
+ st += 2;
+ }
+}
+
+/*
+** RB_CalcTurbulentFactors
+*/
+void RB_CalcTurbulentFactors( const waveForm_t *wf, float *amplitude, float *now )
+{
+ *now = wf->phase + tess.shaderTime * wf->frequency;
+ *amplitude = wf->amplitude;
+}
+
+/*
+** RB_CalcScaleTexMatrix
+*/
+void RB_CalcScaleTexMatrix( const float scale[2], float *matrix )
+{
+ matrix[0] = scale[0]; matrix[2] = 0.0f; matrix[4] = 0.0f;
+ matrix[1] = 0.0f; matrix[3] = scale[1]; matrix[5] = 0.0f;
+}
+
+/*
+** RB_CalcScrollTexMatrix
+*/
+void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix )
+{
+ double timeScale = tess.shaderTime;
+ double adjustedScrollS = scrollSpeed[0] * timeScale;
+ double adjustedScrollT = scrollSpeed[1] * timeScale;
+
+ // clamp so coordinates don't continuously get larger, causing problems
+ // with hardware limits
+ adjustedScrollS -= floor( adjustedScrollS );
+ adjustedScrollT -= floor( adjustedScrollT );
+
+ matrix[0] = 1.0f; matrix[2] = 0.0f; matrix[4] = adjustedScrollS;
+ matrix[1] = 0.0f; matrix[3] = 1.0f; matrix[5] = adjustedScrollT;
+}
+
+/*
+** RB_CalcTransformTexMatrix
+*/
+void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix )
+{
+ matrix[0] = tmi->matrix[0][0]; matrix[2] = tmi->matrix[1][0]; matrix[4] = tmi->translate[0];
+ matrix[1] = tmi->matrix[0][1]; matrix[3] = tmi->matrix[1][1]; matrix[5] = tmi->translate[1];
+}
+
+/*
+** RB_CalcRotateTexMatrix
+*/
+void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix )
+{
+ double timeScale = tess.shaderTime;
+ double degs;
+ float sinValue, cosValue;
+
+ degs = -degsPerSecond * timeScale;
+ int i = degs * ( FUNCTABLE_SIZE / 360.0f );
+
+ sinValue = tr.sinTable[ i & FUNCTABLE_MASK ];
+ cosValue = tr.sinTable[ ( i + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ];
+
+ matrix[0] = cosValue; matrix[2] = -sinValue; matrix[4] = 0.5 - 0.5 * cosValue + 0.5 * sinValue;
+ matrix[1] = sinValue; matrix[3] = cosValue; matrix[5] = 0.5 - 0.5 * sinValue - 0.5 * cosValue;
+}
+
+bool ShaderRequiresCPUDeforms(const shader_t * shader)
+{
+ if(shader->numDeforms)
+ {
+ const deformStage_t *ds = &shader->deforms[0];
+
+ if (shader->numDeforms > 1)
+ return true;
+
+ switch (ds->deformation)
+ {
+ case DEFORM_WAVE:
+ case DEFORM_BULGE:
+ // need CPU deforms at high level-times to avoid floating point percision loss
+ return ( backEnd.refdef.floatTime != (float)backEnd.refdef.floatTime );
+
+ default:
+ return true;
+ }
+ }
+
+ return false;
+}