DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Surface_Patch.cpp (18312B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../precompiled.h"
     31 
     32 /*
     33 =================
     34 idSurface_Patch::SetSize
     35 =================
     36 */
     37 void idSurface_Patch::SetSize( int patchWidth, int patchHeight ) {
     38 	if ( patchWidth < 1 || patchWidth > maxWidth ) {
     39 		idLib::common->FatalError("idSurface_Patch::SetSize: invalid patchWidth");
     40 	}
     41 	if ( patchHeight < 1 || patchHeight > maxHeight ) {
     42 		idLib::common->FatalError("idSurface_Patch::SetSize: invalid patchHeight");
     43 	}
     44 	width = patchWidth;
     45 	height = patchHeight;
     46 	verts.SetNum( width * height );
     47 }
     48 
     49 /*
     50 =================
     51 idSurface_Patch::PutOnCurve
     52 
     53 Expects an expanded patch.
     54 =================
     55 */
     56 void idSurface_Patch::PutOnCurve() {
     57 	int i, j;
     58 	idDrawVert prev, next;
     59 
     60 	assert( expanded == true );
     61 	// put all the approximating points on the curve
     62 	for ( i = 0; i < width; i++ ) {
     63 		for ( j = 1; j < height; j += 2 ) {
     64 			LerpVert( verts[j*maxWidth+i], verts[(j+1)*maxWidth+i], prev );
     65 			LerpVert( verts[j*maxWidth+i], verts[(j-1)*maxWidth+i], next );
     66 			LerpVert( prev, next, verts[j*maxWidth+i] );
     67 		}
     68 	}
     69 
     70 	for ( j = 0; j < height; j++ ) {
     71 		for ( i = 1; i < width; i += 2 ) {
     72 			LerpVert( verts[j*maxWidth+i], verts[j*maxWidth+i+1], prev );
     73 			LerpVert( verts[j*maxWidth+i], verts[j*maxWidth+i-1], next );
     74 			LerpVert( prev, next, verts[j*maxWidth+i] );
     75 		}
     76 	}
     77 }
     78 
     79 /*
     80 ================
     81 idSurface_Patch::ProjectPointOntoVector
     82 ================
     83 */
     84 void idSurface_Patch::ProjectPointOntoVector( const idVec3 &point, const idVec3 &vStart, const idVec3 &vEnd, idVec3 &vProj ) {
     85 	idVec3 pVec, vec;
     86 
     87 	pVec = point - vStart;
     88 	vec = vEnd - vStart;
     89 	vec.Normalize();
     90 	// project onto the directional vector for this segment
     91 	vProj = vStart + (pVec * vec) * vec;
     92 }
     93 
     94 /*
     95 ================
     96 idSurface_Patch::RemoveLinearColumnsRows
     97 
     98 Expects an expanded patch.
     99 ================
    100 */
    101 void idSurface_Patch::RemoveLinearColumnsRows() {
    102 	int i, j, k;
    103 	float len, maxLength;
    104 	idVec3 proj, dir;
    105 
    106 	assert( expanded == true );
    107 	for ( j = 1; j < width - 1; j++ ) {
    108 		maxLength = 0;
    109 		for ( i = 0; i < height; i++ ) {
    110 			idSurface_Patch::ProjectPointOntoVector( verts[i*maxWidth + j].xyz,
    111 									verts[i*maxWidth + j-1].xyz, verts[i*maxWidth + j+1].xyz, proj);
    112 			dir = verts[i*maxWidth + j].xyz - proj;
    113 			len = dir.LengthSqr();
    114 			if ( len > maxLength ) {
    115 				maxLength = len;
    116 			}
    117 		}
    118 		if ( maxLength < Square( 0.2f ) ) {
    119 			width--;
    120 			for ( i = 0; i < height; i++ ) {
    121 				for ( k = j; k < width; k++ ) {
    122 					verts[i*maxWidth + k] = verts[i*maxWidth + k+1];
    123 				}
    124 			}
    125 			j--;
    126 		}
    127 	}
    128 	for ( j = 1; j < height - 1; j++ ) {
    129 		maxLength = 0;
    130 		for ( i = 0; i < width; i++ ) {
    131 			idSurface_Patch::ProjectPointOntoVector( verts[j*maxWidth + i].xyz,
    132 									verts[(j-1)*maxWidth + i].xyz, verts[(j+1)*maxWidth + i].xyz, proj);
    133 			dir = verts[j*maxWidth + i].xyz - proj;
    134 			len = dir.LengthSqr();
    135 			if ( len > maxLength ) {
    136 				maxLength = len;
    137 			}
    138 		}
    139 		if ( maxLength < Square( 0.2f ) ) {
    140 			height--;
    141 			for ( i = 0; i < width; i++ ) {
    142 				for ( k = j; k < height; k++ ) {
    143 					verts[k*maxWidth + i] = verts[(k+1)*maxWidth + i];
    144 				}
    145 			}
    146 			j--;
    147 		}
    148 	}
    149 }
    150 
    151 /*
    152 ================
    153 idSurface_Patch::ResizeExpanded
    154 ================
    155 */
    156 void idSurface_Patch::ResizeExpanded( int newHeight, int newWidth ) {
    157 	int i, j;
    158 
    159 	assert( expanded == true );
    160 	if ( newHeight <= maxHeight && newWidth <= maxWidth ) {
    161 		return;
    162 	}
    163 	if ( newHeight * newWidth > maxHeight * maxWidth ) {
    164 		verts.SetNum( newHeight * newWidth );
    165 	}
    166 	// space out verts for new height and width
    167 	for ( j = maxHeight-1; j >= 0; j-- ) {
    168 		for ( i = maxWidth-1; i >= 0; i-- ) {
    169 			verts[j*newWidth + i] = verts[j*maxWidth + i];
    170 		}
    171 	}
    172 	maxHeight = newHeight;
    173 	maxWidth = newWidth;
    174 }
    175 
    176 /*
    177 ================
    178 idSurface_Patch::Collapse
    179 ================
    180 */
    181 void idSurface_Patch::Collapse() {
    182 	int i, j;
    183 
    184 	if ( !expanded ) {
    185 		idLib::common->FatalError("idSurface_Patch::Collapse: patch not expanded");
    186 	}
    187 	expanded = false;
    188 	if ( width != maxWidth ) {
    189 		for ( j = 0; j < height; j++ ) {
    190 			for ( i = 0; i < width; i++ ) {
    191 				verts[j*width + i] = verts[j*maxWidth + i];
    192 			}
    193 		}
    194 	}
    195 	verts.SetNum( width * height );
    196 }
    197 
    198 /*
    199 ================
    200 idSurface_Patch::Expand
    201 ================
    202 */
    203 void idSurface_Patch::Expand() {
    204 	int i, j;
    205 
    206 	if ( expanded ) {
    207 		idLib::common->FatalError("idSurface_Patch::Expand: patch alread expanded");
    208 	}
    209 	expanded = true;
    210 	verts.SetNum( maxWidth * maxHeight );
    211 	if ( width != maxWidth ) {
    212 		for ( j = height-1; j >= 0; j-- ) {
    213 			for ( i = width-1; i >= 0; i-- ) {
    214 				verts[j*maxWidth + i] = verts[j*width + i];
    215 			}
    216 		}
    217 	}
    218 }
    219 
    220 /*
    221 ============
    222 idSurface_Patch::LerpVert
    223 ============
    224 */
    225 void idSurface_Patch::LerpVert( const idDrawVert &a, const idDrawVert &b, idDrawVert &out ) const {
    226 	out.xyz[0] = 0.5f * ( a.xyz[0] + b.xyz[0] );
    227 	out.xyz[1] = 0.5f * ( a.xyz[1] + b.xyz[1] );
    228 	out.xyz[2] = 0.5f * ( a.xyz[2] + b.xyz[2] );
    229 	out.SetNormal( ( a.GetNormal() + b.GetNormal() ) * 0.5f );
    230 	out.SetTexCoord( ( a.GetTexCoord() + b.GetTexCoord() ) * 0.5f );
    231 }
    232 
    233 /*
    234 =================
    235 idSurface_Patch::GenerateNormals
    236 
    237 Handles all the complicated wrapping and degenerate cases
    238 Expects a Not expanded patch.
    239 =================
    240 */
    241 #define	COPLANAR_EPSILON	0.1f
    242 
    243 void idSurface_Patch::GenerateNormals() {
    244 	int			i, j, k, dist;
    245 	idVec3		norm;
    246 	idVec3		sum;
    247 	int			count;
    248 	idVec3		base;
    249 	idVec3		delta;
    250 	int			x, y;
    251 	idVec3		around[8], temp;
    252 	bool		good[8];
    253 	bool		wrapWidth, wrapHeight;
    254 	static int	neighbors[8][2] = {
    255 		{0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
    256 	};
    257 
    258 	assert( expanded == false );
    259 
    260 	//
    261 	// if all points are coplanar, set all normals to that plane
    262 	//
    263 	idVec3		extent[3];
    264 	float		offset;
    265 	
    266 	extent[0] = verts[width - 1].xyz - verts[0].xyz;
    267 	extent[1] = verts[(height-1) * width + width - 1].xyz - verts[0].xyz;
    268 	extent[2] = verts[(height-1) * width].xyz - verts[0].xyz;
    269 
    270 	norm = extent[0].Cross( extent[1] );
    271 	if ( norm.LengthSqr() == 0.0f ) {
    272 		norm = extent[0].Cross( extent[2] );
    273 		if ( norm.LengthSqr() == 0.0f ) {
    274 			norm = extent[1].Cross( extent[2] );
    275 		}
    276 	}
    277 
    278 	// wrapped patched may not get a valid normal here
    279 	if ( norm.Normalize() != 0.0f ) {
    280 
    281 		offset = verts[0].xyz * norm;
    282 		for ( i = 1; i < width * height; i++ ) {
    283 			float d = verts[i].xyz * norm;
    284 			if ( idMath::Fabs( d - offset ) > COPLANAR_EPSILON ) {
    285 				break;
    286 			}
    287 		}
    288 
    289 		if ( i == width * height ) {
    290 			// all are coplanar
    291 			for ( i = 0; i < width * height; i++ ) {
    292 				verts[i].SetNormal( norm );
    293 			}
    294 			return;
    295 		}
    296 	}
    297 
    298 	// check for wrapped edge cases, which should smooth across themselves
    299 	wrapWidth = false;
    300 	for ( i = 0; i < height; i++ ) {
    301 		delta = verts[i * width].xyz - verts[i * width + width-1].xyz;
    302 		if ( delta.LengthSqr() > Square( 1.0f ) ) {
    303 			break;
    304 		}
    305 	}
    306 	if ( i == height ) {
    307 		wrapWidth = true;
    308 	}
    309 
    310 	wrapHeight = false;
    311 	for ( i = 0; i < width; i++ ) {
    312 		delta = verts[i].xyz - verts[(height-1) * width + i].xyz;
    313 		if ( delta.LengthSqr() > Square( 1.0f ) ) {
    314 			break;
    315 		}
    316 	}
    317 	if ( i == width ) {
    318 		wrapHeight = true;
    319 	}
    320 
    321 	for ( i = 0; i < width; i++ ) {
    322 		for ( j = 0; j < height; j++ ) {
    323 			count = 0;
    324 			base = verts[j * width + i].xyz;
    325 			for ( k = 0; k < 8; k++ ) {
    326 				around[k] = vec3_origin;
    327 				good[k] = false;
    328 
    329 				for ( dist = 1; dist <= 3; dist++ ) {
    330 					x = i + neighbors[k][0] * dist;
    331 					y = j + neighbors[k][1] * dist;
    332 					if ( wrapWidth ) {
    333 						if ( x < 0 ) {
    334 							x = width - 1 + x;
    335 						} else if ( x >= width ) {
    336 							x = 1 + x - width;
    337 						}
    338 					}
    339 					if ( wrapHeight ) {
    340 						if ( y < 0 ) {
    341 							y = height - 1 + y;
    342 						} else if ( y >= height ) {
    343 							y = 1 + y - height;
    344 						}
    345 					}
    346 
    347 					if ( x < 0 || x >= width || y < 0 || y >= height ) {
    348 						break;					// edge of patch
    349 					}
    350 					temp = verts[y * width + x].xyz - base;
    351 					if ( temp.Normalize() == 0.0f ) {
    352 						continue;				// degenerate edge, get more dist
    353 					} else {
    354 						good[k] = true;
    355 						around[k] = temp;
    356 						break;					// good edge
    357 					}
    358 				}
    359 			}
    360 
    361 			sum = vec3_origin;
    362 			for ( k = 0; k < 8; k++ ) {
    363 				if ( !good[k] || !good[(k+1)&7] ) {
    364 					continue;	// didn't get two points
    365 				}
    366 				norm = around[(k+1)&7].Cross( around[k] );
    367 				if ( norm.Normalize() == 0.0f ) {
    368 					continue;
    369 				}
    370 				sum += norm;
    371 				count++;
    372 			}
    373 			if ( count == 0 ) {
    374 				//idLib::common->Printf("bad normal\n");
    375 				count = 1;
    376 			}
    377 			sum.Normalize();
    378 			verts[j * width + i].SetNormal( sum );
    379 		}
    380 	}
    381 }
    382 
    383 /*
    384 =================
    385 idSurface_Patch::GenerateIndexes
    386 =================
    387 */
    388 void idSurface_Patch::GenerateIndexes() {
    389 	int i, j, v1, v2, v3, v4, index;
    390 
    391 	indexes.SetNum( (width-1) * (height-1) * 2 * 3 );
    392 	index = 0;
    393 	for ( i = 0; i < width - 1; i++ ) {
    394 		for ( j = 0; j < height - 1; j++ ) {
    395 			v1 = j * width + i;
    396 			v2 = v1 + 1;
    397 			v3 = v1 + width + 1;
    398 			v4 = v1 + width;
    399 			indexes[index++] = v1;
    400 			indexes[index++] = v3;
    401 			indexes[index++] = v2;
    402 			indexes[index++] = v1;
    403 			indexes[index++] = v4;
    404 			indexes[index++] = v3;
    405 		}
    406 	}
    407 
    408 	GenerateEdgeIndexes();
    409 }
    410 
    411 /*
    412 ===============
    413 idSurface_Patch::SampleSinglePatchPoint
    414 ===============
    415 */
    416 void idSurface_Patch::SampleSinglePatchPoint( const idDrawVert ctrl[3][3], float u, float v, idDrawVert *out ) const {
    417 	float	vCtrl[3][8];
    418 	int		vPoint;
    419 	int		axis;
    420 
    421 	// find the control points for the v coordinate
    422 	for ( vPoint = 0; vPoint < 3; vPoint++ ) {
    423 		for ( axis = 0; axis < 8; axis++ ) {
    424 			float a, b, c;
    425 			float qA, qB, qC;
    426 			if ( axis < 3 ) {
    427 				a = ctrl[0][vPoint].xyz[axis];
    428 				b = ctrl[1][vPoint].xyz[axis];
    429 				c = ctrl[2][vPoint].xyz[axis];
    430 			} else if ( axis < 6 ) {
    431 				a = ctrl[0][vPoint].GetNormal()[axis-3];
    432 				b = ctrl[1][vPoint].GetNormal()[axis-3];
    433 				c = ctrl[2][vPoint].GetNormal()[axis-3];
    434 			} else {
    435 				a = ctrl[0][vPoint].GetTexCoord()[axis-6];
    436 				b = ctrl[1][vPoint].GetTexCoord()[axis-6];
    437 				c = ctrl[2][vPoint].GetTexCoord()[axis-6];
    438 			}
    439 			qA = a - 2.0f * b + c;
    440 			qB = 2.0f * b - 2.0f * a;
    441 			qC = a;
    442 			vCtrl[vPoint][axis] = qA * u * u + qB * u + qC;
    443 		}
    444 	}
    445 
    446 	// interpolate the v value
    447 	for ( axis = 0; axis < 8; axis++ ) {
    448 		float a, b, c;
    449 		float qA, qB, qC;
    450 
    451 		a = vCtrl[0][axis];
    452 		b = vCtrl[1][axis];
    453 		c = vCtrl[2][axis];
    454 		qA = a - 2.0f * b + c;
    455 		qB = 2.0f * b - 2.0f * a;
    456 		qC = a;
    457 
    458 		if ( axis < 3 ) {
    459 			out->xyz[axis] = qA * v * v + qB * v + qC;
    460 		} else if ( axis < 6 ) {
    461 			idVec3 tempNormal = out->GetNormal();
    462 			tempNormal[axis-3] = qA * v * v + qB * v + qC;
    463 			out->SetNormal( tempNormal );
    464 			//out->normal[axis-3] = qA * v * v + qB * v + qC;
    465 		} else {
    466 			idVec2 tempST = out->GetTexCoord();
    467 			tempST[axis-6] = qA * v * v + qB * v + qC;
    468 			out->SetTexCoord( tempST );
    469 		}
    470 	}
    471 }
    472 
    473 /*
    474 ===================
    475 idSurface_Patch::SampleSinglePatch
    476 ===================
    477 */
    478 void idSurface_Patch::SampleSinglePatch( const idDrawVert ctrl[3][3], int baseCol, int baseRow, int width, int horzSub, int vertSub, idDrawVert *outVerts ) const {
    479 	int		i, j;
    480 	float	u, v;
    481 
    482 	horzSub++;
    483 	vertSub++;
    484 	for ( i = 0; i < horzSub; i++ ) {
    485 		for ( j = 0; j < vertSub; j++ ) {
    486 			u = (float) i / ( horzSub - 1 );
    487 			v = (float) j / ( vertSub - 1 );
    488 			SampleSinglePatchPoint( ctrl, u, v, &outVerts[((baseRow + j) * width) + i + baseCol] );
    489 		}
    490 	}
    491 }
    492 
    493 /*
    494 =================
    495 idSurface_Patch::SubdivideExplicit
    496 =================
    497 */
    498 void idSurface_Patch::SubdivideExplicit( int horzSubdivisions, int vertSubdivisions, bool genNormals, bool removeLinear ) {
    499 	int i, j, k, l;
    500 	idDrawVert sample[3][3];
    501 	int outWidth = ((width - 1) / 2 * horzSubdivisions) + 1;
    502 	int outHeight = ((height - 1) / 2 * vertSubdivisions) + 1;
    503 	idDrawVert *dv = new (TAG_IDLIB_SURFACE) idDrawVert[ outWidth * outHeight ];
    504 
    505 	// generate normals for the control mesh
    506 	if ( genNormals ) {
    507 		GenerateNormals();
    508 	}
    509 
    510 	int baseCol = 0;
    511 	for ( i = 0; i + 2 < width; i += 2 ) {
    512 		int baseRow = 0;
    513 		for ( j = 0; j + 2 < height; j += 2 ) {
    514 			for ( k = 0; k < 3; k++ ) {
    515 				for ( l = 0; l < 3; l++ ) {
    516 					sample[k][l] = verts[ ((j + l) * width) + i + k ];
    517 				}
    518 			}
    519 			SampleSinglePatch( sample, baseCol, baseRow, outWidth, horzSubdivisions, vertSubdivisions, dv );
    520 			baseRow += vertSubdivisions;
    521 		}
    522 		baseCol += horzSubdivisions;
    523 	}
    524 	verts.SetNum( outWidth * outHeight );
    525 	for ( i = 0; i < outWidth * outHeight; i++ ) {
    526 		verts[i] = dv[i];
    527 	}
    528 
    529 	delete[] dv;
    530 
    531 	width = maxWidth = outWidth;
    532 	height = maxHeight = outHeight;
    533 	expanded = false;
    534 
    535 	if ( removeLinear ) {
    536 		Expand();
    537 		RemoveLinearColumnsRows();
    538 		Collapse();
    539 	}
    540 
    541 	// normalize all the lerped normals
    542 	if ( genNormals ) {
    543 		idVec3 tempNormal;
    544 		for ( i = 0; i < width * height; i++ ) {
    545 			tempNormal= verts[i].GetNormal();
    546 			tempNormal.Normalize();
    547 			verts[i].SetNormal( tempNormal );
    548 		}
    549 	}
    550 
    551 	GenerateIndexes();
    552 }
    553 
    554 /*
    555 =================
    556 idSurface_Patch::Subdivide
    557 =================
    558 */
    559 void idSurface_Patch::Subdivide( float maxHorizontalError, float maxVerticalError, float maxLength, bool genNormals ) {
    560 	int			i, j, k, l;
    561 	idDrawVert	prev, next, mid;
    562 	idVec3		prevxyz, nextxyz, midxyz;
    563 	idVec3		delta;
    564 	float		maxHorizontalErrorSqr, maxVerticalErrorSqr, maxLengthSqr;
    565 
    566 	// generate normals for the control mesh
    567 	if ( genNormals ) {
    568 		GenerateNormals();
    569 	}
    570 
    571 	maxHorizontalErrorSqr = Square( maxHorizontalError );
    572 	maxVerticalErrorSqr = Square( maxVerticalError );
    573 	maxLengthSqr = Square( maxLength );
    574 
    575 	Expand();
    576 
    577 	// horizontal subdivisions
    578 	for ( j = 0; j + 2 < width; j += 2 ) {
    579 		// check subdivided midpoints against control points
    580 		for ( i = 0; i < height; i++ ) {
    581 			for ( l = 0; l < 3; l++ ) {
    582 				prevxyz[l] = verts[i*maxWidth + j+1].xyz[l] - verts[i*maxWidth + j  ].xyz[l];
    583 				nextxyz[l] = verts[i*maxWidth + j+2].xyz[l] - verts[i*maxWidth + j+1].xyz[l];
    584 				midxyz[l] = (verts[i*maxWidth + j  ].xyz[l] + verts[i*maxWidth + j+1].xyz[l] * 2.0f +
    585 														   verts[i*maxWidth + j+2].xyz[l] ) * 0.25f;
    586 			}
    587 
    588 			if ( maxLength > 0.0f ) {
    589 				// if the span length is too long, force a subdivision
    590 				if ( prevxyz.LengthSqr() > maxLengthSqr || nextxyz.LengthSqr() > maxLengthSqr ) {
    591 					break;
    592 				}
    593 			}
    594 			// see if this midpoint is off far enough to subdivide
    595 			delta = verts[i*maxWidth + j+1].xyz - midxyz;
    596 			if ( delta.LengthSqr() > maxHorizontalErrorSqr ) {
    597 				break;
    598 			}
    599 		}
    600 
    601 		if ( i == height ) {
    602 			continue;	// didn't need subdivision
    603 		}
    604 
    605 		if ( width + 2 >= maxWidth ) {
    606 			ResizeExpanded( maxHeight, maxWidth + 4 );
    607 		}
    608 
    609 		// insert two columns and replace the peak
    610 		width += 2;
    611 
    612 		for ( i = 0; i < height; i++ ) {
    613 			idSurface_Patch::LerpVert( verts[i*maxWidth + j  ], verts[i*maxWidth + j+1], prev );
    614 			idSurface_Patch::LerpVert( verts[i*maxWidth + j+1], verts[i*maxWidth + j+2], next );
    615 			idSurface_Patch::LerpVert( prev, next, mid );
    616 
    617 			for ( k = width - 1; k > j + 3; k-- ) {
    618 				verts[i*maxWidth + k] = verts[i*maxWidth + k-2];
    619 			}
    620 			verts[i*maxWidth + j+1] = prev;
    621 			verts[i*maxWidth + j+2] = mid;
    622 			verts[i*maxWidth + j+3] = next;
    623 		}
    624 
    625 		// back up and recheck this set again, it may need more subdivision
    626 		j -= 2;
    627 	}
    628 
    629 	// vertical subdivisions
    630 	for ( j = 0; j + 2 < height; j += 2 ) {
    631 		// check subdivided midpoints against control points
    632 		for ( i = 0; i < width; i++ ) {
    633 			for ( l = 0; l < 3; l++ ) {
    634 				prevxyz[l] = verts[(j+1)*maxWidth + i].xyz[l] - verts[j*maxWidth + i].xyz[l];
    635 				nextxyz[l] = verts[(j+2)*maxWidth + i].xyz[l] - verts[(j+1)*maxWidth + i].xyz[l];
    636 				midxyz[l] = (verts[j*maxWidth + i].xyz[l] + verts[(j+1)*maxWidth + i].xyz[l] * 2.0f +
    637 														verts[(j+2)*maxWidth + i].xyz[l] ) * 0.25f;
    638 			}
    639 
    640 			if ( maxLength > 0.0f ) {
    641 				// if the span length is too long, force a subdivision
    642 				if ( prevxyz.LengthSqr() > maxLengthSqr || nextxyz.LengthSqr() > maxLengthSqr ) {
    643 					break;
    644 				}
    645 			}
    646 			// see if this midpoint is off far enough to subdivide
    647 			delta = verts[(j+1)*maxWidth + i].xyz - midxyz;
    648 			if ( delta.LengthSqr() > maxVerticalErrorSqr ) {
    649 				break;
    650 			}
    651 		}
    652 
    653 		if ( i == width ) {
    654 			continue;	// didn't need subdivision
    655 		}
    656 
    657 		if ( height + 2 >= maxHeight ) {
    658 			ResizeExpanded( maxHeight + 4, maxWidth );
    659 		}
    660 
    661 		// insert two columns and replace the peak
    662 		height += 2;
    663 
    664 		for ( i = 0; i < width; i++ ) {
    665 			LerpVert( verts[j*maxWidth + i], verts[(j+1)*maxWidth + i], prev );
    666 			LerpVert( verts[(j+1)*maxWidth + i], verts[(j+2)*maxWidth + i], next );
    667 			LerpVert( prev, next, mid );
    668 
    669 			for ( k = height - 1; k > j + 3; k-- ) {
    670 				verts[k*maxWidth + i] = verts[(k-2)*maxWidth + i];
    671 			}
    672 			verts[(j+1)*maxWidth + i] = prev;
    673 			verts[(j+2)*maxWidth + i] = mid;
    674 			verts[(j+3)*maxWidth + i] = next;
    675 		}
    676 
    677 		// back up and recheck this set again, it may need more subdivision
    678 		j -= 2;
    679 	}
    680 
    681 	PutOnCurve();
    682 
    683 	RemoveLinearColumnsRows();
    684 
    685 	Collapse();
    686 
    687 	// normalize all the lerped normals
    688 	if ( genNormals ) {
    689 		idVec3 tempNormal;
    690 		for ( i = 0; i < width * height; i++ ) {
    691 			tempNormal = verts[i].GetNormal();
    692 			tempNormal.Normalize();
    693 			verts[i].SetNormal( tempNormal );
    694 		}
    695 	}
    696 
    697 	GenerateIndexes();
    698 }