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 }