/* ============= Tess_SurfaceMDV ============= */ static void Tess_SurfaceMDV( mdvSurface_t *srf ) { int i, j; int numIndexes = 0; int numVertexes; mdvXyz_t *oldVert, *newVert; mdvNormal_t *oldNormal, *newNormal; mdvSt_t *st; srfTriangle_t *tri; float backlerp; float oldXyzScale, newXyzScale; GLimp_LogComment( "--- Tess_SurfaceMDV ---\n" ); if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { backlerp = 0; } else { backlerp = backEnd.currentEntity->e.backlerp; } newXyzScale = ( 1.0f - backlerp ); oldXyzScale = backlerp; Tess_CheckOverflow( srf->numVerts, srf->numTriangles * 3 ); numIndexes = srf->numTriangles * 3; for ( i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++ ) { tess.indexes[ tess.numIndexes + i * 3 + 0 ] = tess.numVertexes + tri->indexes[ 0 ]; tess.indexes[ tess.numIndexes + i * 3 + 1 ] = tess.numVertexes + tri->indexes[ 1 ]; tess.indexes[ tess.numIndexes + i * 3 + 2 ] = tess.numVertexes + tri->indexes[ 2 ]; } newVert = srf->verts + ( backEnd.currentEntity->e.frame * srf->numVerts ); oldVert = srf->verts + ( backEnd.currentEntity->e.oldframe * srf->numVerts ); newNormal = srf->normals + ( backEnd.currentEntity->e.frame * srf->numVerts ); oldNormal = srf->normals + ( backEnd.currentEntity->e.oldframe * srf->numVerts ); st = srf->st; numVertexes = srf->numVerts; for ( j = 0; j < numVertexes; j++, newVert++, oldVert++, st++ ) { vec3_t tmpVert; if ( backlerp == 0 ) { // just copy VectorCopy( newVert->xyz, tmpVert ); } else { // interpolate the xyz VectorScale( oldVert->xyz, oldXyzScale, tmpVert ); VectorMA( tmpVert, newXyzScale, newVert->xyz, tmpVert ); } tess.verts[ tess.numVertexes + j ].xyz[ 0 ] = tmpVert[ 0 ]; tess.verts[ tess.numVertexes + j ].xyz[ 1 ] = tmpVert[ 1 ]; tess.verts[ tess.numVertexes + j ].xyz[ 2 ] = tmpVert[ 2 ]; tess.verts[ tess.numVertexes + j ].texCoords[ 0 ] = floatToHalf( st->st[ 0 ] ); tess.verts[ tess.numVertexes + j ].texCoords[ 1 ] = floatToHalf( st->st[ 1 ] ); } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD; // calc tangent spaces if ( !tess.skipTangentSpaces ) { int i; float *v; const float *v0, *v1, *v2; const int16_t *t0, *t1, *t2; vec3_t tangent, *tangents; vec3_t binormal, *binormals; vec3_t *normals; glIndex_t *indices; tess.attribsSet |= ATTR_QTANGENT; tangents = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); binormals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); normals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); for ( i = 0; i < numVertexes; i++ ) { VectorClear( tangents[ i ] ); VectorClear( binormals[ i ] ); if ( backlerp == 0 ) { // just copy VectorCopy( newNormal->normal, normals[ i ] ); } else { // interpolate the xyz VectorScale( oldNormal->normal, oldXyzScale, normals[ i ] ); VectorMA( normals[ i ], newXyzScale, newNormal->normal, normals[ i ] ); VectorNormalizeFast( normals[ i ] ); } } for ( i = 0, indices = tess.indexes + tess.numIndexes; i < numIndexes; i += 3, indices += 3 ) { v0 = tess.verts[ indices[ 0 ] ].xyz; v1 = tess.verts[ indices[ 1 ] ].xyz; v2 = tess.verts[ indices[ 2 ] ].xyz; t0 = tess.verts[ indices[ 0 ] ].texCoords; t1 = tess.verts[ indices[ 1 ] ].texCoords; t2 = tess.verts[ indices[ 2 ] ].texCoords; R_CalcTangents( tangent, binormal, v0, v1, v2, t0, t1, t2 ); for ( j = 0; j < 3; j++ ) { v = tangents[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, tangent, v ); v = binormals[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, binormal, v ); } } for ( i = 0; i < numVertexes; i++ ) { R_TBNtoQtangents( tangents[ i ], binormals[ i ], normals[ i ], tess.verts[ numVertexes + i ].qtangents ); } ri.Hunk_FreeTempMemory( normals ); ri.Hunk_FreeTempMemory( binormals ); ri.Hunk_FreeTempMemory( tangents ); } tess.numIndexes += numIndexes; tess.numVertexes += numVertexes; }
/* ============== Tess_AddQuadStampExt2 ============== */ void Tess_AddQuadStampExt2( vec4_t quadVerts[ 4 ], const vec4_t color, float s1, float t1, float s2, float t2 ) { int i; vec3_t normal, tangent, binormal; int ndx; GLimp_LogComment( "--- Tess_AddQuadStampExt2 ---\n" ); Tess_CheckOverflow( 4, 6 ); ndx = tess.numVertexes; // triangle indexes for a simple quad tess.indexes[ tess.numIndexes ] = ndx; tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; VectorCopy( quadVerts[ 0 ], tess.verts[ ndx + 0 ].xyz ); VectorCopy( quadVerts[ 1 ], tess.verts[ ndx + 1 ].xyz ); VectorCopy( quadVerts[ 2 ], tess.verts[ ndx + 2 ].xyz ); VectorCopy( quadVerts[ 3 ], tess.verts[ ndx + 3 ].xyz ); tess.attribsSet |= ATTR_POSITION | ATTR_COLOR | ATTR_TEXCOORD | ATTR_QTANGENT; // constant normal all the way around vec2_t st[ 3 ] = { { s1, t1 }, { s2, t1 }, { s2, t2 } }; R_CalcFaceNormal( normal, quadVerts[ 0 ], quadVerts[ 1 ], quadVerts[ 2 ] ); R_CalcTangents( tangent, binormal, quadVerts[ 0 ], quadVerts[ 1 ], quadVerts[ 2 ], st[ 0 ], st[ 1 ], st[ 2 ] ); //if ( !calcNormals ) //{ // VectorNegate( backEnd.viewParms.orientation.axis[ 0 ], normal ); //} R_TBNtoQtangents( tangent, binormal, normal, tess.verts[ ndx ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 1 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 2 ].qtangents ); Vector4Copy( tess.verts[ ndx ].qtangents, tess.verts[ ndx + 3 ].qtangents ); // standard square texture coordinates tess.verts[ ndx ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 1 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 1 ].texCoords[ 1 ] = floatToHalf( t1 ); tess.verts[ ndx + 2 ].texCoords[ 0 ] = floatToHalf( s2 ); tess.verts[ ndx + 2 ].texCoords[ 1 ] = floatToHalf( t2 ); tess.verts[ ndx + 3 ].texCoords[ 0 ] = floatToHalf( s1 ); tess.verts[ ndx + 3 ].texCoords[ 1 ] = floatToHalf( t2 ); // constant color all the way around // should this be identity and let the shader specify from entity? u8vec4_t iColor; floatToUnorm8( color, iColor ); for ( i = 0; i < 4; i++ ) { Vector4Copy( iColor, tess.verts[ ndx + i ].color ); } tess.numVertexes += 4; tess.numIndexes += 6; }
/* ============= Tess_SurfacePolychain ============= */ static void Tess_SurfacePolychain( srfPoly_t *p ) { int i, j; int numVertexes; int numIndexes; GLimp_LogComment( "--- Tess_SurfacePolychain ---\n" ); Tess_CheckOverflow( p->numVerts, 3 * ( p->numVerts - 2 ) ); // fan triangles into the tess array numVertexes = 0; for ( i = 0; i < p->numVerts; i++ ) { VectorCopy( p->verts[ i ].xyz, tess.verts[ tess.numVertexes + i ].xyz ); tess.verts[ tess.numVertexes + i ].texCoords[ 0 ] = floatToHalf( p->verts[ i ].st[ 0 ] ); tess.verts[ tess.numVertexes + i ].texCoords[ 1 ] = floatToHalf( p->verts[ i ].st[ 1 ] ); Vector4Copy( p->verts[ i ].modulate, tess.verts[ tess.numVertexes + i ].color ); numVertexes++; } // generate fan indexes into the tess array numIndexes = 0; for ( i = 0; i < p->numVerts - 2; i++ ) { tess.indexes[ tess.numIndexes + i * 3 + 0 ] = tess.numVertexes; tess.indexes[ tess.numIndexes + i * 3 + 1 ] = tess.numVertexes + i + 1; tess.indexes[ tess.numIndexes + i * 3 + 2 ] = tess.numVertexes + i + 2; numIndexes += 3; } tess.attribsSet |= ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR; // calc tangent spaces if ( tess.surfaceShader->interactLight && !tess.skipTangentSpaces ) { int i; float *v; const float *v0, *v1, *v2; const int16_t *t0, *t1, *t2; vec3_t tangent, *tangents; vec3_t binormal, *binormals; vec3_t normal, *normals; glIndex_t *indices; tangents = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); binormals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); normals = (vec3_t *)ri.Hunk_AllocateTempMemory( numVertexes * sizeof( vec3_t ) ); for ( i = 0; i < numVertexes; i++ ) { VectorClear( tangents[ i ] ); VectorClear( binormals[ i ] ); VectorClear( normals[ i ] ); } for ( i = 0, indices = tess.indexes + tess.numIndexes; i < numIndexes; i += 3, indices += 3 ) { v0 = tess.verts[ indices[ 0 ] ].xyz; v1 = tess.verts[ indices[ 1 ] ].xyz; v2 = tess.verts[ indices[ 2 ] ].xyz; t0 = tess.verts[ indices[ 0 ] ].texCoords; t1 = tess.verts[ indices[ 1 ] ].texCoords; t2 = tess.verts[ indices[ 2 ] ].texCoords; R_CalcFaceNormal( normal, v0, v1, v2 ); R_CalcTangents( tangent, binormal, v0, v1, v2, t0, t1, t2 ); for ( j = 0; j < 3; j++ ) { v = tangents[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, tangent, v ); v = binormals[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, binormal, v ); v = normals[ indices[ j ] - tess.numVertexes ]; VectorAdd( v, normal, v ); } } for ( i = 0; i < numVertexes; i++ ) { VectorNormalizeFast( normals[ i ] ); R_TBNtoQtangents( tangents[ i ], binormals[ i ], normals[ i ], tess.verts[ tess.numVertexes + i ].qtangents ); } ri.Hunk_FreeTempMemory( normals ); ri.Hunk_FreeTempMemory( binormals ); ri.Hunk_FreeTempMemory( tangents ); tess.attribsSet |= ATTR_QTANGENT; } tess.numIndexes += numIndexes; tess.numVertexes += numVertexes; }
/* ================= MakeMeshNormals Handles all the complicated wrapping and degenerate cases ================= */ static void MakeMeshNormals( int width, int height, srfVert_t ctrl[ MAX_GRID_SIZE ][ MAX_GRID_SIZE ] ) { int i, j, k, dist; vec3_t tangent, binormal, normal; vec3_t sum, sumTangents, sumBinormals; int count; vec3_t base; vec3_t delta; int x, y; srfVert_t *dv; vec3_t around[ 8 ], temp; bool good[ 8 ]; vec2_t st[8]; bool wrapWidth, wrapHeight; float len; static int neighbors[ 8 ][ 2 ] = { { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 } }; wrapWidth = false; for ( i = 0; i < height; i++ ) { VectorSubtract( ctrl[ i ][ 0 ].xyz, ctrl[ i ][ width - 1 ].xyz, delta ); len = VectorLengthSquared( delta ); if ( len > 1.0 ) { break; } } if ( i == height ) { wrapWidth = true; } wrapHeight = false; for ( i = 0; i < width; i++ ) { VectorSubtract( ctrl[ 0 ][ i ].xyz, ctrl[ height - 1 ][ i ].xyz, delta ); len = VectorLengthSquared( delta ); if ( len > 1.0 ) { break; } } if ( i == width ) { wrapHeight = true; } for ( i = 0; i < width; i++ ) { for ( j = 0; j < height; j++ ) { count = 0; dv = &ctrl[ j ][ i ]; VectorCopy( dv->xyz, base ); for ( k = 0; k < 8; k++ ) { VectorClear( around[ k ] ); good[ k ] = false; for ( dist = 1; dist <= 3; dist++ ) { x = i + neighbors[ k ][ 0 ] * dist; y = j + neighbors[ k ][ 1 ] * dist; if ( wrapWidth ) { if ( x < 0 ) { x = width - 1 + x; } else if ( x >= width ) { x = 1 + x - width; } } if ( wrapHeight ) { if ( y < 0 ) { y = height - 1 + y; } else if ( y >= height ) { y = 1 + y - height; } } if ( x < 0 || x >= width || y < 0 || y >= height ) { break; // edge of patch } VectorSubtract( ctrl[ y ][ x ].xyz, base, temp ); if ( VectorNormalize2( temp, temp ) == 0 ) { continue; // degenerate edge, get more dist } else { good[ k ] = true; VectorCopy( temp, around[ k ] ); Vector2Copy( ctrl[ y ][ x ].st, st[ k ] ); break; // good edge } } } VectorClear( sum ); VectorClear( sumTangents ); VectorClear( sumBinormals ); for ( k = 0; k < 8; k++ ) { if ( !good[ k ] || !good[( k + 1 ) & 7 ] ) { continue; // didn't get two points } CrossProduct( around[( k + 1 ) & 7 ], around[ k ], normal ); if ( VectorNormalize2( normal, normal ) == 0 ) { continue; } R_CalcTangents( tangent, binormal, vec3_origin, around[ k ], around[ ( k + 1 ) & 7 ], dv->st, st[ k ], st[ ( k + 1 ) & 7 ] ); VectorAdd( normal, sum, sum ); VectorAdd( tangent, sumTangents, sumTangents ); VectorAdd( binormal, sumBinormals, sumBinormals ); count++; } if ( count == 0 ) { VectorSet( dv->normal, 0.0f, 0.0f, 1.0f ); } else { VectorNormalize2( sum, dv->normal ); } R_TBNtoQtangents( sumTangents, sumBinormals, dv->normal, dv->qtangent ); } } }