aas_gsubdiv.c (21784B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 23 #include "qbsp.h" 24 #include "../botlib/aasfile.h" 25 #include "aas_create.h" 26 #include "aas_store.h" 27 #include "aas_cfg.h" 28 29 #define FACECLIP_EPSILON 0.2 30 #define FACE_EPSILON 1.0 31 32 int numgravitationalsubdivisions = 0; 33 int numladdersubdivisions = 0; 34 35 //NOTE: only do gravitational subdivision BEFORE area merging!!!!!!! 36 // because the bsp tree isn't refreshes like with ladder subdivision 37 38 //=========================================================================== 39 // NOTE: the original face is invalid after splitting 40 // 41 // Parameter: - 42 // Returns: - 43 // Changes Globals: - 44 //=========================================================================== 45 void AAS_SplitFace(tmp_face_t *face, vec3_t normal, float dist, 46 tmp_face_t **frontface, tmp_face_t **backface) 47 { 48 winding_t *frontw, *backw; 49 50 // 51 *frontface = *backface = NULL; 52 53 ClipWindingEpsilon(face->winding, normal, dist, FACECLIP_EPSILON, &frontw, &backw); 54 55 #ifdef DEBUG 56 // 57 if (frontw) 58 { 59 if (WindingIsTiny(frontw)) 60 { 61 Log_Write("AAS_SplitFace: tiny back face\r\n"); 62 FreeWinding(frontw); 63 frontw = NULL; 64 } //end if 65 } //end if 66 if (backw) 67 { 68 if (WindingIsTiny(backw)) 69 { 70 Log_Write("AAS_SplitFace: tiny back face\r\n"); 71 FreeWinding(backw); 72 backw = NULL; 73 } //end if 74 } //end if 75 #endif //DEBUG 76 //if the winding was split 77 if (frontw) 78 { 79 //check bounds 80 (*frontface) = AAS_AllocTmpFace(); 81 (*frontface)->planenum = face->planenum; 82 (*frontface)->winding = frontw; 83 (*frontface)->faceflags = face->faceflags; 84 } //end if 85 if (backw) 86 { 87 //check bounds 88 (*backface) = AAS_AllocTmpFace(); 89 (*backface)->planenum = face->planenum; 90 (*backface)->winding = backw; 91 (*backface)->faceflags = face->faceflags; 92 } //end if 93 } //end of the function AAS_SplitFace 94 //=========================================================================== 95 // 96 // Parameter: - 97 // Returns: - 98 // Changes Globals: - 99 //=========================================================================== 100 winding_t *AAS_SplitWinding(tmp_area_t *tmparea, int planenum) 101 { 102 tmp_face_t *face; 103 plane_t *plane; 104 int side; 105 winding_t *splitwinding; 106 107 // 108 plane = &mapplanes[planenum]; 109 //create a split winding, first base winding for plane 110 splitwinding = BaseWindingForPlane(plane->normal, plane->dist); 111 //chop with all the faces of the area 112 for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side]) 113 { 114 //side of the face the original area was on 115 side = face->frontarea != tmparea; 116 plane = &mapplanes[face->planenum ^ side]; 117 ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON); 118 } //end for 119 return splitwinding; 120 } //end of the function AAS_SplitWinding 121 //=========================================================================== 122 // 123 // Parameter: - 124 // Returns: - 125 // Changes Globals: - 126 //=========================================================================== 127 int AAS_TestSplitPlane(tmp_area_t *tmparea, vec3_t normal, float dist, 128 int *facesplits, int *groundsplits, int *epsilonfaces) 129 { 130 int j, side, front, back, planenum; 131 float d, d_front, d_back; 132 tmp_face_t *face; 133 winding_t *w; 134 135 *facesplits = *groundsplits = *epsilonfaces = 0; 136 137 planenum = FindFloatPlane(normal, dist); 138 139 w = AAS_SplitWinding(tmparea, planenum); 140 if (!w) return false; 141 FreeWinding(w); 142 // 143 for (face = tmparea->tmpfaces; face; face = face->next[side]) 144 { 145 //side of the face the area is on 146 side = face->frontarea != tmparea; 147 148 if ((face->planenum & ~1) == (planenum & ~1)) 149 { 150 Log_Print("AAS_TestSplitPlane: tried face plane as splitter\n"); 151 return false; 152 } //end if 153 w = face->winding; 154 //reset distance at front and back side of plane 155 d_front = d_back = 0; 156 //reset front and back flags 157 front = back = 0; 158 for (j = 0; j < w->numpoints; j++) 159 { 160 d = DotProduct(w->p[j], normal) - dist; 161 if (d > d_front) d_front = d; 162 if (d < d_back) d_back = d; 163 164 if (d > 0.4) // PLANESIDE_EPSILON) 165 front = 1; 166 if (d < -0.4) // PLANESIDE_EPSILON) 167 back = 1; 168 } //end for 169 //check for an epsilon face 170 if ( (d_front > FACECLIP_EPSILON && d_front < FACE_EPSILON) 171 || (d_back < -FACECLIP_EPSILON && d_back > -FACE_EPSILON) ) 172 { 173 (*epsilonfaces)++; 174 } //end if 175 //if the face has points at both sides of the plane 176 if (front && back) 177 { 178 (*facesplits)++; 179 if (face->faceflags & FACE_GROUND) 180 { 181 (*groundsplits)++; 182 } //end if 183 } //end if 184 } //end for 185 return true; 186 } //end of the function AAS_TestSplitPlane 187 //=========================================================================== 188 // 189 // Parameter: - 190 // Returns: - 191 // Changes Globals: - 192 //=========================================================================== 193 void AAS_SplitArea(tmp_area_t *tmparea, int planenum, tmp_area_t **frontarea, tmp_area_t **backarea) 194 { 195 int side; 196 tmp_area_t *facefrontarea, *facebackarea, *faceotherarea; 197 tmp_face_t *face, *frontface, *backface, *splitface, *nextface; 198 winding_t *splitwinding; 199 plane_t *splitplane; 200 201 /* 202 #ifdef AW_DEBUG 203 int facesplits, groundsplits, epsilonface; 204 Log_Print("\n----------------------\n"); 205 Log_Print("splitting area %d\n", areanum); 206 Log_Print("with normal = \'%f %f %f\', dist = %f\n", normal[0], normal[1], normal[2], dist); 207 AAS_TestSplitPlane(areanum, normal, dist, 208 &facesplits, &groundsplits, &epsilonface); 209 Log_Print("face splits = %d\nground splits = %d\n", facesplits, groundsplits); 210 if (epsilonface) Log_Print("aaahh epsilon face\n"); 211 #endif //AW_DEBUG*/ 212 //the original area 213 214 AAS_FlipAreaFaces(tmparea); 215 AAS_CheckArea(tmparea); 216 // 217 splitplane = &mapplanes[planenum]; 218 /* //create a split winding, first base winding for plane 219 splitwinding = BaseWindingForPlane(splitplane->normal, splitplane->dist); 220 //chop with all the faces of the area 221 for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side]) 222 { 223 //side of the face the original area was on 224 side = face->frontarea != tmparea->areanum; 225 plane = &mapplanes[face->planenum ^ side]; 226 ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON); 227 } //end for*/ 228 splitwinding = AAS_SplitWinding(tmparea, planenum); 229 if (!splitwinding) 230 { 231 /* 232 #ifdef DEBUG 233 AAS_TestSplitPlane(areanum, normal, dist, 234 &facesplits, &groundsplits, &epsilonface); 235 Log_Print("\nface splits = %d\nground splits = %d\n", facesplits, groundsplits); 236 if (epsilonface) Log_Print("aaahh epsilon face\n"); 237 #endif //DEBUG*/ 238 Error("AAS_SplitArea: no split winding when splitting area %d\n", tmparea->areanum); 239 } //end if 240 //create a split face 241 splitface = AAS_AllocTmpFace(); 242 //get the map plane 243 splitface->planenum = planenum; 244 //store the split winding 245 splitface->winding = splitwinding; 246 //the new front area 247 (*frontarea) = AAS_AllocTmpArea(); 248 (*frontarea)->presencetype = tmparea->presencetype; 249 (*frontarea)->contents = tmparea->contents; 250 (*frontarea)->modelnum = tmparea->modelnum; 251 (*frontarea)->tmpfaces = NULL; 252 //the new back area 253 (*backarea) = AAS_AllocTmpArea(); 254 (*backarea)->presencetype = tmparea->presencetype; 255 (*backarea)->contents = tmparea->contents; 256 (*backarea)->modelnum = tmparea->modelnum; 257 (*backarea)->tmpfaces = NULL; 258 //add the split face to the new areas 259 AAS_AddFaceSideToArea(splitface, 0, (*frontarea)); 260 AAS_AddFaceSideToArea(splitface, 1, (*backarea)); 261 262 //split all the faces of the original area 263 for (face = tmparea->tmpfaces; face; face = nextface) 264 { 265 //side of the face the original area was on 266 side = face->frontarea != tmparea; 267 //next face of the original area 268 nextface = face->next[side]; 269 //front area of the face 270 facefrontarea = face->frontarea; 271 //back area of the face 272 facebackarea = face->backarea; 273 //remove the face from both the front and back areas 274 if (facefrontarea) AAS_RemoveFaceFromArea(face, facefrontarea); 275 if (facebackarea) AAS_RemoveFaceFromArea(face, facebackarea); 276 //split the face 277 AAS_SplitFace(face, splitplane->normal, splitplane->dist, &frontface, &backface); 278 //free the original face 279 AAS_FreeTmpFace(face); 280 //get the number of the area at the other side of the face 281 if (side) faceotherarea = facefrontarea; 282 else faceotherarea = facebackarea; 283 //if there is an area at the other side of the original face 284 if (faceotherarea) 285 { 286 if (frontface) AAS_AddFaceSideToArea(frontface, !side, faceotherarea); 287 if (backface) AAS_AddFaceSideToArea(backface, !side, faceotherarea); 288 } //end if 289 //add the front and back part left after splitting the original face to the new areas 290 if (frontface) AAS_AddFaceSideToArea(frontface, side, (*frontarea)); 291 if (backface) AAS_AddFaceSideToArea(backface, side, (*backarea)); 292 } //end for 293 294 if (!(*frontarea)->tmpfaces) Log_Print("AAS_SplitArea: front area without faces\n"); 295 if (!(*backarea)->tmpfaces) Log_Print("AAS_SplitArea: back area without faces\n"); 296 297 tmparea->invalid = true; 298 /* 299 #ifdef AW_DEBUG 300 for (i = 0, face = frontarea->tmpfaces; face; face = face->next[side]) 301 { 302 side = face->frontarea != frontarea->areanum; 303 i++; 304 } //end for 305 Log_Print("created front area %d with %d faces\n", frontarea->areanum, i); 306 307 for (i = 0, face = backarea->tmpfaces; face; face = face->next[side]) 308 { 309 side = face->frontarea != backarea->areanum; 310 i++; 311 } //end for 312 Log_Print("created back area %d with %d faces\n", backarea->areanum, i); 313 #endif //AW_DEBUG*/ 314 315 AAS_FlipAreaFaces((*frontarea)); 316 AAS_FlipAreaFaces((*backarea)); 317 // 318 AAS_CheckArea((*frontarea)); 319 AAS_CheckArea((*backarea)); 320 } //end of the function AAS_SplitArea 321 //=========================================================================== 322 // 323 // Parameter: - 324 // Returns: - 325 // Changes Globals: - 326 //=========================================================================== 327 int AAS_FindBestAreaSplitPlane(tmp_area_t *tmparea, vec3_t normal, float *dist) 328 { 329 int side1, side2; 330 int foundsplitter, facesplits, groundsplits, epsilonfaces, bestepsilonfaces; 331 float bestvalue, value; 332 tmp_face_t *face1, *face2; 333 vec3_t tmpnormal, invgravity; 334 float tmpdist; 335 336 //get inverse of gravity direction 337 VectorCopy(cfg.phys_gravitydirection, invgravity); 338 VectorInverse(invgravity); 339 340 foundsplitter = false; 341 bestvalue = -999999; 342 bestepsilonfaces = 0; 343 // 344 #ifdef AW_DEBUG 345 Log_Print("finding split plane for area %d\n", tmparea->areanum); 346 #endif //AW_DEBUG 347 for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) 348 { 349 //side of the face the area is on 350 side1 = face1->frontarea != tmparea; 351 // 352 if (WindingIsTiny(face1->winding)) 353 { 354 Log_Write("gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum); 355 continue; 356 } //end if 357 //if the face isn't a gap or ground there's no split edge 358 if (!(face1->faceflags & FACE_GROUND) && !AAS_GapFace(face1, side1)) continue; 359 // 360 for (face2 = face1->next[side1]; face2; face2 = face2->next[side2]) 361 { 362 //side of the face the area is on 363 side2 = face2->frontarea != tmparea; 364 // 365 if (WindingIsTiny(face1->winding)) 366 { 367 Log_Write("gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum); 368 continue; 369 } //end if 370 //if the face isn't a gap or ground there's no split edge 371 if (!(face2->faceflags & FACE_GROUND) && !AAS_GapFace(face2, side2)) continue; 372 //only split between gaps and ground 373 if (!(((face1->faceflags & FACE_GROUND) && AAS_GapFace(face2, side2)) || 374 ((face2->faceflags & FACE_GROUND) && AAS_GapFace(face1, side1)))) continue; 375 //find a plane seperating the windings of the faces 376 if (!FindPlaneSeperatingWindings(face1->winding, face2->winding, invgravity, 377 tmpnormal, &tmpdist)) continue; 378 #ifdef AW_DEBUG 379 Log_Print("normal = \'%f %f %f\', dist = %f\n", 380 tmpnormal[0], tmpnormal[1], tmpnormal[2], tmpdist); 381 #endif //AW_DEBUG 382 //get metrics for this vertical plane 383 if (!AAS_TestSplitPlane(tmparea, tmpnormal, tmpdist, 384 &facesplits, &groundsplits, &epsilonfaces)) 385 { 386 continue; 387 } //end if 388 #ifdef AW_DEBUG 389 Log_Print("face splits = %d\nground splits = %d\n", 390 facesplits, groundsplits); 391 #endif //AW_DEBUG 392 value = 100 - facesplits - 2 * groundsplits; 393 //avoid epsilon faces 394 value += epsilonfaces * -1000; 395 if (value > bestvalue) 396 { 397 VectorCopy(tmpnormal, normal); 398 *dist = tmpdist; 399 bestvalue = value; 400 bestepsilonfaces = epsilonfaces; 401 foundsplitter = true; 402 } //end if 403 } //end for 404 } //end for 405 if (bestepsilonfaces) 406 { 407 Log_Write("found %d epsilon faces trying to split area %d\r\n", 408 epsilonfaces, tmparea->areanum); 409 } //end else 410 return foundsplitter; 411 } //end of the function AAS_FindBestAreaSplitPlane 412 //=========================================================================== 413 // 414 // Parameter: - 415 // Returns: - 416 // Changes Globals: - 417 //=========================================================================== 418 tmp_node_t *AAS_SubdivideArea_r(tmp_node_t *tmpnode) 419 { 420 int planenum; 421 tmp_area_t *frontarea, *backarea; 422 tmp_node_t *tmpnode1, *tmpnode2; 423 vec3_t normal; 424 float dist; 425 426 if (AAS_FindBestAreaSplitPlane(tmpnode->tmparea, normal, &dist)) 427 { 428 qprintf("\r%6d", ++numgravitationalsubdivisions); 429 // 430 planenum = FindFloatPlane(normal, dist); 431 //split the area 432 AAS_SplitArea(tmpnode->tmparea, planenum, &frontarea, &backarea); 433 // 434 tmpnode->tmparea = NULL; 435 tmpnode->planenum = FindFloatPlane(normal, dist); 436 // 437 tmpnode1 = AAS_AllocTmpNode(); 438 tmpnode1->planenum = 0; 439 tmpnode1->tmparea = frontarea; 440 // 441 tmpnode2 = AAS_AllocTmpNode(); 442 tmpnode2->planenum = 0; 443 tmpnode2->tmparea = backarea; 444 //subdivide the areas created by splitting recursively 445 tmpnode->children[0] = AAS_SubdivideArea_r(tmpnode1); 446 tmpnode->children[1] = AAS_SubdivideArea_r(tmpnode2); 447 } //end if 448 return tmpnode; 449 } //end of the function AAS_SubdivideArea_r 450 //=========================================================================== 451 // 452 // Parameter: - 453 // Returns: - 454 // Changes Globals: - 455 //=========================================================================== 456 tmp_node_t *AAS_GravitationalSubdivision_r(tmp_node_t *tmpnode) 457 { 458 //if this is a solid leaf 459 if (!tmpnode) return NULL; 460 //negative so it's an area 461 if (tmpnode->tmparea) return AAS_SubdivideArea_r(tmpnode); 462 //do the children recursively 463 tmpnode->children[0] = AAS_GravitationalSubdivision_r(tmpnode->children[0]); 464 tmpnode->children[1] = AAS_GravitationalSubdivision_r(tmpnode->children[1]); 465 return tmpnode; 466 } //end of the function AAS_GravitationalSubdivision_r 467 //=========================================================================== 468 // NOTE: merge faces and melt edges first 469 // 470 // Parameter: - 471 // Returns: - 472 // Changes Globals: - 473 //=========================================================================== 474 void AAS_GravitationalSubdivision(void) 475 { 476 Log_Write("AAS_GravitationalSubdivision\r\n"); 477 numgravitationalsubdivisions = 0; 478 qprintf("%6i gravitational subdivisions", numgravitationalsubdivisions); 479 //start with the head node 480 AAS_GravitationalSubdivision_r(tmpaasworld.nodes); 481 qprintf("\n"); 482 Log_Write("%6i gravitational subdivisions\r\n", numgravitationalsubdivisions); 483 } //end of the function AAS_GravitationalSubdivision 484 //=========================================================================== 485 // 486 // Parameter: - 487 // Returns: - 488 // Changes Globals: - 489 //=========================================================================== 490 tmp_node_t *AAS_RefreshLadderSubdividedTree_r(tmp_node_t *tmpnode, tmp_area_t *tmparea, 491 tmp_node_t *tmpnode1, tmp_node_t *tmpnode2, int planenum) 492 { 493 //if this is a solid leaf 494 if (!tmpnode) return NULL; 495 //negative so it's an area 496 if (tmpnode->tmparea) 497 { 498 if (tmpnode->tmparea == tmparea) 499 { 500 tmpnode->tmparea = NULL; 501 tmpnode->planenum = planenum; 502 tmpnode->children[0] = tmpnode1; 503 tmpnode->children[1] = tmpnode2; 504 } //end if 505 return tmpnode; 506 } //end if 507 //do the children recursively 508 tmpnode->children[0] = AAS_RefreshLadderSubdividedTree_r(tmpnode->children[0], 509 tmparea, tmpnode1, tmpnode2, planenum); 510 tmpnode->children[1] = AAS_RefreshLadderSubdividedTree_r(tmpnode->children[1], 511 tmparea, tmpnode1, tmpnode2, planenum); 512 return tmpnode; 513 } //end of the function AAS_RefreshLadderSubdividedTree_r 514 //=========================================================================== 515 // find an area with ladder faces and ground faces that are not connected 516 // split the area with a horizontal plane at the lowest vertex of all 517 // ladder faces in the area 518 // 519 // Parameter: - 520 // Returns: - 521 // Changes Globals: - 522 //=========================================================================== 523 tmp_node_t *AAS_LadderSubdivideArea_r(tmp_node_t *tmpnode) 524 { 525 int side1, i, planenum; 526 int foundladderface, foundgroundface; 527 float dist; 528 tmp_area_t *tmparea, *frontarea, *backarea; 529 tmp_face_t *face1; 530 tmp_node_t *tmpnode1, *tmpnode2; 531 vec3_t lowestpoint, normal = {0, 0, 1}; 532 plane_t *plane; 533 winding_t *w; 534 535 tmparea = tmpnode->tmparea; 536 //skip areas with a liquid 537 if (tmparea->contents & (AREACONTENTS_WATER 538 | AREACONTENTS_LAVA 539 | AREACONTENTS_SLIME)) return tmpnode; 540 //must be possible to stand in the area 541 if (!(tmparea->presencetype & PRESENCE_NORMAL)) return tmpnode; 542 // 543 foundladderface = false; 544 foundgroundface = false; 545 lowestpoint[2] = 99999; 546 // 547 for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) 548 { 549 //side of the face the area is on 550 side1 = face1->frontarea != tmparea; 551 //if the face is a ladder face 552 if (face1->faceflags & FACE_LADDER) 553 { 554 plane = &mapplanes[face1->planenum]; 555 //the ladder face plane should be pretty much vertical 556 if (DotProduct(plane->normal, normal) > -0.1) 557 { 558 foundladderface = true; 559 //find lowest point 560 for (i = 0; i < face1->winding->numpoints; i++) 561 { 562 if (face1->winding->p[i][2] < lowestpoint[2]) 563 { 564 VectorCopy(face1->winding->p[i], lowestpoint); 565 } //end if 566 } //end for 567 } //end if 568 } //end if 569 else if (face1->faceflags & FACE_GROUND) 570 { 571 foundgroundface = true; 572 } //end else if 573 } //end for 574 // 575 if ((!foundladderface) || (!foundgroundface)) return tmpnode; 576 // 577 for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1]) 578 { 579 //side of the face the area is on 580 side1 = face1->frontarea != tmparea; 581 //if the face isn't a ground face 582 if (!(face1->faceflags & FACE_GROUND)) continue; 583 //the ground plane 584 plane = &mapplanes[face1->planenum]; 585 //get the difference between the ground plane and the lowest point 586 dist = DotProduct(plane->normal, lowestpoint) - plane->dist; 587 //if the lowest point is very near one of the ground planes 588 if (dist > -1 && dist < 1) 589 { 590 return tmpnode; 591 } //end if 592 } //end for 593 // 594 dist = DotProduct(normal, lowestpoint); 595 planenum = FindFloatPlane(normal, dist); 596 // 597 w = AAS_SplitWinding(tmparea, planenum); 598 if (!w) return tmpnode; 599 FreeWinding(w); 600 //split the area with a horizontal plane through the lowest point 601 qprintf("\r%6d", ++numladdersubdivisions); 602 // 603 AAS_SplitArea(tmparea, planenum, &frontarea, &backarea); 604 // 605 tmpnode->tmparea = NULL; 606 tmpnode->planenum = planenum; 607 // 608 tmpnode1 = AAS_AllocTmpNode(); 609 tmpnode1->planenum = 0; 610 tmpnode1->tmparea = frontarea; 611 // 612 tmpnode2 = AAS_AllocTmpNode(); 613 tmpnode2->planenum = 0; 614 tmpnode2->tmparea = backarea; 615 //subdivide the areas created by splitting recursively 616 tmpnode->children[0] = AAS_LadderSubdivideArea_r(tmpnode1); 617 tmpnode->children[1] = AAS_LadderSubdivideArea_r(tmpnode2); 618 //refresh the tree 619 AAS_RefreshLadderSubdividedTree_r(tmpaasworld.nodes, tmparea, tmpnode1, tmpnode2, planenum); 620 // 621 return tmpnode; 622 } //end of the function AAS_LadderSubdivideArea_r 623 //=========================================================================== 624 // 625 // Parameter: - 626 // Returns: - 627 // Changes Globals: - 628 //=========================================================================== 629 tmp_node_t *AAS_LadderSubdivision_r(tmp_node_t *tmpnode) 630 { 631 //if this is a solid leaf 632 if (!tmpnode) return 0; 633 //negative so it's an area 634 if (tmpnode->tmparea) return AAS_LadderSubdivideArea_r(tmpnode); 635 //do the children recursively 636 tmpnode->children[0] = AAS_LadderSubdivision_r(tmpnode->children[0]); 637 tmpnode->children[1] = AAS_LadderSubdivision_r(tmpnode->children[1]); 638 return tmpnode; 639 } //end of the function AAS_LadderSubdivision_r 640 //=========================================================================== 641 // 642 // Parameter: - 643 // Returns: - 644 // Changes Globals: - 645 //=========================================================================== 646 void AAS_LadderSubdivision(void) 647 { 648 Log_Write("AAS_LadderSubdivision\r\n"); 649 numladdersubdivisions = 0; 650 qprintf("%6i ladder subdivisions", numladdersubdivisions); 651 //start with the head node 652 AAS_LadderSubdivision_r(tmpaasworld.nodes); 653 // 654 qprintf("\n"); 655 Log_Write("%6i ladder subdivisions\r\n", numladdersubdivisions); 656 } //end of the function AAS_LadderSubdivision