00001 /* 00002 File: RT_Grid.cc 00003 00004 Function: Defines a hierarchical grid class for ray-tracing 00005 optimisation 00006 00007 Author: Andrew Willmott, ajw+@cs.cmu.edu 00008 */ 00009 00060 #include "RT_Grid.h" 00061 #include "RT_RayStats.h" 00062 #include "FCompare.h" 00063 #ifdef RT_VIS 00064 #include "gcl/Renderer.h" 00065 #endif 00066 00067 #define RT_SetLock(x) 00068 #define RT_UnsetLock(x) 00069 #define RT_Lock Int 00070 00071 #define DBG_COUT if (0) cerr 00072 00073 00074 // --- Globals ---------------------------------------------------------------- 00075 00076 00077 Int RT_Grid::sStamp = 1; 00078 Int RT_Grid::sTag = 0x00000001; 00079 // By default, the first group is enabled. 00080 Int RT_Grid::sMem = 0; 00081 Int RT_Grid::sTriMem = 0; 00082 Int RT_Grid::sGenMem = 0; 00083 Int RT_Grid::sPointMem = 0; 00084 Int RT_Grid::sMemLimit = 0; 00085 00086 Int RT_Grid::sMaxCells = 40; 00087 // Maximum cells along one side of a side 00088 Int RT_Grid::sMaxCellPrims = RT_Grid::sMaxCells; 00089 // Need this many prims in a cell before 00090 // considering them as subgrid candidates. 00091 00092 GCLReal RT_Grid::sPushSizeLimit = 0.05; 00093 // A primitive must be smaller than this 00094 // fraction of a cell for it to be a subgrid 00095 // candidate. 00096 GCLReal RT_Grid::sMaxCellRatio = 0.20; 00097 // A subgrid's cell size must be this much 00098 // smaller than its parent's. 00099 GCLReal RT_Grid::sMinDensity = 0.05; 00100 // A grid must have at least this primitive 00101 // density to be considered worthwhile creating 00102 // it. 00103 GCLReal RT_Grid::sMaxExpand = 3.00; 00104 // The maximum amount to expand a subgrid in 00105 // order to merge it with another. 00106 GCLReal RT_Grid::sEpsilon = 1e-15; 00107 // Nearness epsilon. For intersection 00108 // tests where a casting primitive isn't 00109 // provided, this helps avoid self-shadowing / 00110 // intersection. 00111 00112 00113 // --- Internal data structures ----------------------------------------------- 00114 00115 00116 struct PrimEntry 00117 { 00118 union 00119 { 00120 RT_Prim* prim; 00121 RT_Grid* grid; 00122 }; 00123 PrimEntry* next; 00124 }; 00125 00126 typedef PrimEntry* PrimEntryPtr; 00127 00128 // We assume no memory is malloc'd at $8. 00129 00130 const PrimEntryPtr PRIM_GRID_TAG = (PrimEntry*) 0x8; 00131 00132 struct PrimListEntry 00133 { 00134 PrimEntry** list; 00135 PrimListEntry* next; 00136 }; 00137 00138 00139 // --- GridStore methods ------------------------------------------------------ 00140 00141 00142 Void GridStore::Init(Int size) 00143 { 00144 Int spill; 00145 Int i; 00146 00147 spill = size & PAGE_PTR_MASK; // size of last page 00148 if (spill == 0) 00149 spill = PAGE_PTR_SIZE; 00150 00151 numPages = ((size - 1) >> PAGE_PTR_BITS) + 1; 00152 pages = RT_ArrayAlloc(PrimEntry**, numPages); 00153 mem = sizeof(PrimEntry**) * numPages; 00154 00155 for (i = 0; i < numPages - 1; i++) 00156 { 00157 pages[i] = RT_ArrayAllocClr(PrimEntry*, PAGE_PTR_SIZE); 00158 Assert(pages[i] != 0, "Out of memory"); 00159 mem += sizeof(PrimEntry*) * PAGE_PTR_SIZE; 00160 } 00161 00162 pages[numPages - 1] = RT_ArrayAllocClr(PrimEntry*, spill); 00163 mem += sizeof(PrimEntry*) * spill; 00164 } 00165 00166 Void GridStore::Free() 00167 { 00168 Int i; 00169 00170 if (pages) 00171 { 00172 for (i = 0; i < numPages; i++) 00173 RT_ArrayFree(pages[i]); 00174 00175 RT_ArrayFree(pages); 00176 } 00177 } 00178 00179 // --- Useful #defines -------------------------------------------------------- 00180 00181 00182 #ifdef RT_USE_FC 00183 #define FEQG(a,b) F_EQG10(a,b) 00184 #define FSGN(x) ((F_EQ10((x), 0)) ? 0 : (((x) < 0) ? -1 : 1)) 00185 #else 00186 #define FEQG(a, b) (b >= a) 00187 // the epsilon is necessary here so that planes that just touch grid cells 00188 // get added to them... old old bug. 00189 #define FSGN(x) (((x) > sEpsilon ) ? 1 : (((x) < -sEpsilon) ? -1 : 0)) 00190 #endif 00191 00192 // --- RT_Grid methods -------------------------------------------------------- 00193 00194 00195 Void RT_Grid::MasterInit() 00196 { 00197 sMem = 0; 00198 sTriMem = 0; 00199 sGenMem = 0; 00200 sPointMem = 0; 00201 Init(); 00202 } 00203 00204 Void RT_Grid::Init() 00205 { 00206 totalPrims = 0; 00207 dirty = false; 00208 level = 0; 00209 00210 max.MakeBlock(-vl_inf); 00211 min.MakeBlock(vl_inf); 00212 00213 totalCells = 0; 00214 addPrims = 0; 00215 addObjs = 0; 00216 addedObjs = 0; 00217 subGrids = 0; 00218 next = 0; 00219 hitPrim = 0; 00220 hitT = 0.0; 00221 stamp = 0; 00222 parent = 0; 00223 00224 sMem += sizeof(RT_Grid); 00225 } 00226 00227 Void RT_Grid::Free() 00228 // Delete subfields of the grid. 00229 { 00230 RT_Grid *subGrid, *nextGrid = 0; 00231 Int i; 00232 PrimEntry *entry, *nextEntry = 0; 00233 PrimListEntry *tlEntry, *tlNextEntry = 0; 00234 ObjEntry *objEntry, *objNextEntry = 0; 00235 00236 // Delete subgrids 00237 00238 for (subGrid = subGrids; subGrid; subGrid = nextGrid) 00239 { 00240 nextGrid = subGrid->next; 00241 subGrid->Free(); 00242 RT_Free(subGrid); 00243 } 00244 00245 // Delete the grid storage 00246 00247 if (totalCells > 0) 00248 { 00249 for (i = 0; i < totalCells; i++) 00250 for (entry = grid[i]; entry && entry != PRIM_GRID_TAG; 00251 entry = nextEntry) 00252 { 00253 nextEntry = entry->next; 00254 RT_Free(entry); 00255 } 00256 00257 grid.Free(); 00258 } 00259 00260 for (tlEntry = addPrims; tlEntry; tlEntry = tlNextEntry) 00261 { 00262 tlNextEntry = tlEntry->next; 00263 RT_Free(tlEntry); 00264 } 00265 00266 for (objEntry = addObjs; objEntry; objEntry = objNextEntry) 00267 { 00268 objNextEntry = objEntry->next; 00269 RT_Free(objEntry); 00270 } 00271 00272 for (objEntry = addedObjs; objEntry; objEntry = objNextEntry) 00273 { 00274 objNextEntry = objEntry->next; 00275 RT_Free(objEntry); 00276 } 00277 } 00278 00279 Void RT_Grid::AddObject(RT_Object *object) 00280 { 00281 ObjEntry* newEntry; 00282 00283 UpdateBounds(object); 00284 00285 newEntry = RT_Alloc(ObjEntry); 00286 sMem += sizeof(ObjEntry); 00287 00288 newEntry->object = object; 00289 newEntry->next = addObjs; 00290 addObjs = newEntry; 00291 00292 dirty = true; 00293 } 00294 00295 RT_Lock *gGridLock = 0; 00296 00297 Void RT_Grid::PrepareGrid() 00298 // Prepare grid for raytracing. 00299 { 00300 ObjEntry *entry, *nextEntry; 00301 Int j; 00302 00303 if (gGridLock) 00304 RT_SetLock(*gGridLock); 00305 00306 if (!dirty) 00307 { 00308 // Another process has Prepare()'d this grid while we were 00309 // waiting for the lock... 00310 00311 if (gGridLock) 00312 RT_UnsetLock(*gGridLock); 00313 return; 00314 } 00315 00316 if (addObjs) 00317 { 00318 RT_Tri* tri; 00319 RT_GenPtr* gen; 00320 00321 SetupGrid(); 00322 00323 // Add object triangles 00324 00325 for (entry = addObjs; entry; entry = nextEntry) 00326 { 00327 nextEntry = entry->next; 00328 00329 tri = entry->object->tris; 00330 for (j = 0; j < entry->object->numTris; j++, tri++) 00331 AddTriangle(tri); 00332 totalPrims += entry->object->numTris; 00333 00334 gen = entry->object->gens; 00335 for (j = 0; j < entry->object->numGens; j++, gen++) 00336 AddGeneric(*gen); 00337 totalPrims += entry->object->numGens; 00338 00339 sTriMem += sizeof(RT_Tri) * entry->object->numTris; 00340 sGenMem += sizeof(RT_GenPtr) * entry->object->numGens; 00341 sPointMem += entry->object->numPoints * sizeof(Point); 00342 00343 if (nextEntry == 0) 00344 entry->next = addedObjs; 00345 } 00346 00347 // transfer objects to the added objects list 00348 00349 addedObjs = addObjs; 00350 addObjs = 0; 00351 } 00352 00353 // Now, create nested grids. 00354 00355 dirty = false; 00356 00357 if (sMemLimit && sMem >= sMemLimit) 00358 { 00359 cerr << "*** out of memory: aborting grid creation... >" 00360 << level << endl; 00361 cerr << "*** memory = " << sMem << " bytes." << endl; 00362 if (gGridLock) 00363 RT_UnsetLock(*gGridLock); 00364 return; 00365 } 00366 00367 CreateNestedGrids(); 00368 00369 if (gGridLock) 00370 RT_UnsetLock(*gGridLock); 00371 } 00372 00373 00374 Void RT_Grid::UpdateBounds(Point& cellMin, Point& cellMax) 00375 { 00376 FindMinElts(cellMin, min, min); 00377 FindMaxElts(cellMax, max, max); 00378 } 00379 00380 Void RT_Grid::UpdateBounds(RT_Object *object) 00381 { 00382 Int i; 00383 Point* points = object->points; 00384 00385 for (i = 0; i < object->numPoints; i++) 00386 ::UpdateBounds(points[i], min, max); 00387 00388 for (i = 0; i < object->numGens; i++) 00389 object->gens[i]->UpdateBounds(min, max); 00390 } 00391 00392 Void RT_Grid::SetupGrid() 00393 { 00394 GCLReal maxSpan; 00395 Vector centre, span; 00396 Int i; 00397 00398 // Calculate the boundaries for the grid. We already know 00399 // the world-space boundaries for the object. 00400 00401 // XXX fix more elegantly 00402 // expand the box a little so we don't suffer from numerical 00403 // problems for surfaces lying along one of the sides of the grid 00404 const GCLReal kGridFudge = 1e-15; 00405 Vector fudge; 00406 00407 max += fudge.MakeBlock(kGridFudge); 00408 min -= fudge; 00409 00410 span = max - min; 00411 maxSpan = MaxElt(span); 00412 cellSize = maxSpan / sMaxCells; 00413 areaLimit = sPushSizeLimit * sqr(cellSize); 00414 00415 // Calculate how many grid cells the object will have in each 00416 // direction. We've already assigned the widest direction a 00417 // preset number of grid cells and the narrower directions fewer 00418 // cells. 00419 00420 for (i = 0; i < 3; i++) 00421 { 00422 numCells[i] = (Int) ceil(sMaxCells * span[i] / maxSpan); 00423 00424 // Ensure at least 1 cell in all directions 00425 if (numCells[i] == 0) 00426 numCells[i] = 1; 00427 00428 max[i] = min[i] + numCells[i] * cellSize; 00429 } 00430 00431 // Allocate a one-dimensional array for the grid. 00432 00433 totalCells = numCells[0] * numCells[1] * numCells[2]; 00434 xSpan = numCells[1] * numCells[2]; 00435 ySpan = numCells[2]; 00436 00437 grid.Init(totalCells); 00438 sMem += (Int) grid.MemoryUsage(); 00439 } 00440 00441 00442 Void RT_Grid::AddGeneric(RT_GenPtr gen) 00443 // Add a generic object to this grid. 00444 { 00445 Vector tMin, tMax; 00446 Vector cellMin, vertex; 00447 Int i; 00448 Int x, y, z, cellNum, cellsAdded; 00449 PrimEntry **last, *entry, *newEntry; 00450 Int start[3], end[3]; 00451 00452 // We want to enter the polygon into all of the grid cells 00453 // it intersects. We first limit our test of grid cells to 00454 // those in which the polygon's bounding box resides. 00455 00456 gen->FindBounds(tMin, tMax); 00457 00458 // Find the equivalent cell bounds 00459 00460 for (i = 0; i < 3; i++) 00461 { 00462 start[i] = (Int) ((tMin[i] - min[i]) / cellSize); 00463 end[i] = (Int) ((tMax[i] - min[i]) / cellSize); 00464 00465 // Crop to the grid 00466 00467 if (end[i] >= numCells[i]) 00468 end[i] = numCells[i] - 1; 00469 else if (end[i] < 0) 00470 end[i] = 0; 00471 00472 if (start[i] >= numCells[i]) 00473 start[i] = numCells[i] - 1; 00474 else if (start[i] < 0) 00475 start[i] = 0; 00476 } 00477 00478 // Finally, the generic is added to every grid cell in its bounding box 00479 00480 cellsAdded = 0; 00481 for (x = start[0]; x <= end[0]; x++) 00482 for (y = start[1]; y <= end[1]; y++) 00483 for (z = start[2]; z <= end[2]; z++) 00484 { 00485 cellsAdded++; 00486 newEntry = RT_Alloc(PrimEntry); 00487 sMem += sizeof(PrimEntry); 00488 cellNum = x * xSpan + y * ySpan + z; 00489 00490 newEntry->prim = gen; 00491 00492 // Insert gen in order of (equivalent) area... 00493 00494 last = &(grid[cellNum]); // the last entry we looked at 00495 00496 for (entry = *last; entry; entry = entry->next) 00497 { 00498 // Only bother sorting prims < areaLimit 00499 00500 if (entry->prim->area <= gen->area || 00501 entry->prim->area < areaLimit) 00502 break; 00503 00504 last = &(entry->next); 00505 } 00506 00507 *last = newEntry; 00508 newEntry->next = entry; 00509 } 00510 00511 #ifdef RT_OPAC 00512 gen->cells = cellsAdded; 00513 #endif 00514 } 00515 00516 00517 Void RT_Grid::AddTriangle(RT_Tri *tri) 00518 // Add a triangle to this grid. 00519 { 00520 Vector tMin, tMax; 00521 Vector cellMin, vertex; 00522 Point *p1, *p2, *p3; 00523 Int i; 00524 Int x, y, z, sign, cellNum, cellsAdded; 00525 PrimEntry **last, *entry, *newEntry; 00526 Point* points = tri->object->points; 00527 Int start[3], end[3]; 00528 GCLReal temp; 00529 00530 // We want to enter the polygon into all of the grid cells 00531 // it intersects. We first limit our test of grid cells to 00532 // those in which the polygon's bounding box resides. 00533 00534 dirty = true; 00535 p1 = points + tri->v[0]; 00536 p2 = points + tri->v[1]; 00537 p3 = points + tri->v[2]; 00538 00539 FindMaxElts(*p1, *p2, tMax); 00540 FindMaxElts(*p3, tMax, tMax); 00541 00542 FindMinElts(*p1, *p2, tMin); 00543 FindMinElts(*p3, tMin, tMin); 00544 00545 // Find the equivalent cell bounds 00546 00547 for (i = 0; i < 3; i++) 00548 { 00549 start[i] = (Int) ((tMin[i] - min[i]) / cellSize); 00550 end[i] = (Int) ((tMax[i] - min[i]) / cellSize); 00551 00552 // Crop to the grid 00553 00554 if (end[i] >= numCells[i]) 00555 end[i] = numCells[i] - 1; 00556 else if (end[i] < 0) 00557 end[i] = 0; 00558 00559 if (start[i] >= numCells[i]) 00560 start[i] = numCells[i] - 1; 00561 else if (start[i] < 0) 00562 start[i] = 0; 00563 } 00564 00565 // Finally, the triangle is added to every grid cell in the bounding box 00566 // which straddles the plane of the triangle. 00567 00568 cellsAdded = 0; 00569 for (x = start[0]; x <= end[0]; x++) 00570 for (y = start[1]; y <= end[1]; y++) 00571 for (z = start[2]; z <= end[2]; z++) 00572 { 00573 // We test each vertex of the cell by plugging it into the 00574 // triangle's plane equation. If a vertex has a different 00575 // sign from the preceeding vertices, we know the plane 00576 // straddles this cube, and we add the triangle to the 00577 // cell. A result of zero means the plane touches the vertex, 00578 // which is also grounds for adding the triangle. 00579 00580 cellMin = min + Vector(x, y, z) * cellSize; 00581 cellNum = x * xSpan + y * ySpan + z; 00582 temp = dot(tri->normal, cellMin) + tri->d; 00583 sign = FSGN(temp); 00584 00585 if (sign == 0) // The first sign is zero: we can stop. 00586 i = 1; 00587 else 00588 for (i = 1; i < 8; i++) 00589 { 00590 vertex = cellMin; 00591 00592 if (i & 1) vertex[0] += cellSize; 00593 if (i & 2) vertex[1] += cellSize; 00594 if (i & 4) vertex[2] += cellSize; 00595 00596 temp = dot(tri->normal, vertex) + tri->d; 00597 00598 if (sign != FSGN(temp)) 00599 break; 00600 } 00601 00602 // Add the triangle if we have a straddle 00603 00604 if (i < 8) 00605 { 00606 cellsAdded++; 00607 newEntry = RT_Alloc(PrimEntry); 00608 sMem += sizeof(PrimEntry); 00609 00610 newEntry->prim = tri; 00611 00612 // Insert tri in order of area... 00613 00614 last = &(grid[cellNum]); // the last entry we looked at 00615 00616 for (entry = *last; entry; entry = entry->next) 00617 { 00618 // Only bother sorting tris < areaLimit 00619 00620 if (entry->prim->area <= tri->area || 00621 entry->prim->area < areaLimit) 00622 break; 00623 00624 last = &(entry->next); 00625 } 00626 00627 *last = newEntry; 00628 newEntry->next = entry; 00629 } 00630 } 00631 00632 #ifdef RT_OPAC 00633 tri->cells = cellsAdded; 00634 #endif 00635 } 00636 00637 Void RT_Grid::RayClampEntryPoint(Int index, Bool backEntry[3], 00638 Vector& gridPoint, Int cell[3]) 00639 { 00640 if (backEntry[index]) 00641 { 00642 // If we're entering from the back end, we're entering the 00643 // largest numbered cell in this direction. 00644 00645 cell[index] = numCells[index] - 1; 00646 gridPoint[index] = (GCLReal) numCells[index]; 00647 } 00648 else 00649 { 00650 // From the front, it's the smallest, which is conveniently zero. 00651 00652 cell[index] = 0; 00653 gridPoint[index] = 0.0; 00654 } 00655 } 00656 00657 00658 Bool RT_Grid::FindGridStart( 00659 00660 const Point& start, 00661 const Vector& direction, 00662 Int cell[3], 00663 Int increment[3], 00664 Int limit[3], 00665 GCLReal& tRay, 00666 // start of ray in grid 00667 Vector& tDelta, 00668 // delta t for a step of size cellSize in each direction 00669 Vector& tMax 00670 // delta t to the next grid wall in each direction 00671 ) 00672 { 00673 Int i; 00674 Vector gridPoint; 00675 Vector in, out; 00676 Bool backEntry[3]; 00677 Point point; 00678 00679 gridPoint = (start - min) / cellSize; 00680 00681 if (0.0 <= gridPoint[0] && gridPoint[0] <= numCells[0] && 00682 0.0 <= gridPoint[1] && gridPoint[1] <= numCells[1] && 00683 0.0 <= gridPoint[2] && gridPoint[2] <= numCells[2]) 00684 { 00685 // We are starting inside the grid: find which cell 00686 00687 tRay = 0.0; 00688 00689 for (i = 0; i < 3; i++) 00690 { 00691 cell[i] = (Int) floor(gridPoint[i]); 00692 00693 if (cell[i] == numCells[i]) 00694 cell[i]--; 00695 } 00696 } 00697 else 00698 { 00699 // Otherwise, we have to compute the entry point 00700 // into the grid. 00701 00702 for (i = 0; i < 3; i++) 00703 { 00704 // Avoid the divide by zero. This would occur if the ray 00705 // has no component in one or two directions. 00706 00707 if (abs(direction[i]) > (GCLReal) 0.0) 00708 { 00709 in[i] = (min[i] - start[i]) / direction[i]; 00710 out[i] = (max[i] - start[i]) / direction[i]; 00711 } 00712 else 00713 { 00714 in[i] = -vl_inf; 00715 out[i] = vl_inf; 00716 } 00717 00718 // We're really concerned only with the entry point of the ray 00719 // into the grid. Thus, if we've got our entry and exit points 00720 // backwards, then correct the entry point. 00721 00722 if (backEntry[i] = (in[i] > out[i])) 00723 in[i] = out[i]; 00724 } 00725 00726 // The actual entry point into the grid occurs at the maximum of the 00727 // just-computed entry values (i.e., we enter the grid when we've 00728 // crossed the X _and_ the Y _and_ the Z planes of the Fujimoto 00729 // grid. 00730 00731 tRay = MaxElt(in); 00732 point = start + tRay * direction; 00733 00734 // Compute the entry point in terms of grid space. (I.e., in 00735 // what cell do we enter the grid?) 00736 00737 gridPoint = (point - min) / cellSize; 00738 00739 for (i = 0; i < 3; i++) 00740 cell[i] = (Int) floor(gridPoint[i]); 00741 00742 // the cell direction corresponding to the maximum component(s) 00743 // of 'in' will lie along the boundary of the grid. To 00744 // prevent spilling over the boundary due to round-off error, we clamp 00745 // the direction(s) back to the grid boundary. 00746 00747 if (in[0] > in[1]) 00748 { 00749 if (in[0] >= in[2]) 00750 { 00751 RayClampEntryPoint(0, backEntry, gridPoint, cell); 00752 00753 if (in[0] == in[2]) 00754 RayClampEntryPoint(2, backEntry, gridPoint, cell); 00755 } 00756 else 00757 RayClampEntryPoint(2, backEntry, gridPoint, cell); 00758 } 00759 else if (in[0] == in[1]) 00760 { 00761 if (in[2] >= in[0]) 00762 { 00763 RayClampEntryPoint(2, backEntry, gridPoint, cell); 00764 00765 if (in[2] == in[0]) 00766 { 00767 RayClampEntryPoint(0, backEntry, gridPoint, cell); 00768 RayClampEntryPoint(1, backEntry, gridPoint, cell); 00769 } 00770 } 00771 else 00772 { 00773 RayClampEntryPoint(0, backEntry, gridPoint, cell); 00774 RayClampEntryPoint(1, backEntry, gridPoint, cell); 00775 } 00776 } 00777 else 00778 { 00779 if (in[1] >= in[2]) 00780 { 00781 RayClampEntryPoint(1, backEntry, gridPoint, cell); 00782 00783 if (in[1] == in[2]) 00784 RayClampEntryPoint(2, backEntry, gridPoint, cell); 00785 } 00786 else 00787 RayClampEntryPoint(2, backEntry, gridPoint, cell); 00788 } 00789 00790 // If the ray completely misses the Fujimoto grid, then we 00791 // of course didn't hit anything. 00792 00793 for (i = 0; i < 3; i++) 00794 if (cell[i] < 0 || numCells[i] <= cell[i]) 00795 return(false); 00796 } 00797 00798 // Calculate auxillary info used for traversing the grid 00799 00800 for (i = 0; i < 3; i++) 00801 { 00802 GCLReal scale = cellSize; 00803 00804 // The ray that goes through the Fujimoto grid is parameterized, 00805 // i.e., the ray is of the form start + t * direction, where t is 00806 // the parameter. For any given direction (i.e. X, Y, or Z), 00807 // the amount that t changes as the ray crosses to the next cell 00808 // in the same direction is fixed. Calculate what that change in 00809 // t is and store the information. We're going to use this 00810 // information to help us traverse the grid. 00811 00812 tDelta[i] = (abs(direction[i]) == (GCLReal) 0.0) ? 00813 0.0 : scale / abs(direction[i]); 00814 00815 // Note the sign of the direction vector of the ray. 00816 00817 increment[i] = FSGN(direction[i]); 00818 limit[i] = (increment[i] < 0) ? -1 : numCells[i]; 00819 00820 // Make a note of what the value of the ray parameter t will 00821 // be when we cross over the next orthogonal plane in this 00822 // direction. (Of course, if the direction vector has no 00823 // component in this direction, then we'll _never_ reach the 00824 // next grid cell in this direction.) 00825 00826 if (increment[i] == 0) 00827 tMax[i] = vl_inf; 00828 else if (increment[i] == 1) 00829 tMax[i] = tRay + tDelta[i] * (cell[i] + 1 - gridPoint[i]); 00830 else 00831 { 00832 // The the grid point is integral, i.e. if it lies exactly on 00833 // one of the orthogonal planes, then the next movement will 00834 // cross the entire cell. 00835 00836 if (gridPoint[i] == floor(gridPoint[i])) 00837 tMax[i] = tRay + tDelta[i]; 00838 else 00839 tMax[i] = tRay + tDelta[i] * 00840 (gridPoint[i] - floor(gridPoint[i])); 00841 } 00842 } 00843 00844 return(tDelta[0] != 0.0 || tDelta[1] != 0.0 || tDelta[2] != 0); 00845 } 00846 00847 00848 // --- Intersection methods --------------------------------------------------- 00849 00850 00851 Bool RT_Grid::IntersectionExists( 00852 const Point& start, 00853 const Point& end, 00854 RT_Prim* origPrim, 00855 RayStats* stats 00856 ) 00857 // Finds out if any intersection occurs between start and end. Ignores 00858 // intersections with origPrim, if it is not nil. 00859 { 00860 Vector direction = end - start; 00861 Vector reflectPoint; 00862 GCLReal t, tRay; 00863 Int i, increment[3], limit[3]; 00864 Vector tDelta, tMax; 00865 Int nextCellDir; 00866 Int cell[3]; 00867 PrimEntry* entry; 00868 00869 if (stats) stats->rays++; 00870 00871 if (sqrlen(direction) < 1e-15) 00872 return(false); 00873 00874 if (dirty) 00875 PrepareGrid(); 00876 00877 if (level == 0) 00878 { 00879 // just starting off: ignore all previously stamped subgrids. 00880 RT_Grid::IncTime(); 00881 RT_Prim::IncTime(); 00882 } 00883 else if (IsStamped()) 00884 { 00885 // return cached result 00886 if (stats) stats->d2++; 00887 return(hitPrim != 0); 00888 } 00889 else 00890 { 00891 #ifdef RT_GRID_CACHE 00892 // ensure result is cached 00893 Stamp(); 00894 #endif 00895 } 00896 00897 if (stats) stats->grids++; 00898 hitPrim = 0; 00899 00900 // Figure out grid parameters, and return if we completely miss the grid 00901 if (!FindGridStart(start, direction, cell, increment, limit, tRay, 00902 tDelta, tMax)) 00903 return(false); 00904 00905 // Traverse the grid... 00906 00907 for (i = 0; i < 256; i++) 00908 { 00909 // We are only after a hit, not the closest hit, so as soon as we get 00910 // one, we can return. 00911 if (stats) stats->voxels++; 00912 00913 entry = grid[cell[0] * xSpan + cell[1] * ySpan + cell[2]]; 00914 00915 for (; entry; entry = entry->next) 00916 { 00917 if (stats) stats->polysTested++; 00918 00919 if (entry->next == PRIM_GRID_TAG) 00920 { 00921 // If this is a subgrid entry, call the subgrid... 00922 00923 if (entry->grid->IntersectionExists(start, end, 00924 origPrim, stats)) 00925 { 00926 hitPrim = entry->grid->hitPrim; 00927 hitT = entry->grid->hitT; 00928 return(true); 00929 } 00930 00931 break; // a grid entry is always last. 00932 } 00933 00934 if (entry->prim == origPrim) 00935 continue; 00936 if ((entry->prim->flags & TRI_INACTIVE) || !(sTag & entry->prim->object->tag)) 00937 continue; 00938 #ifdef RT_SUPPORT_GENS 00939 if (entry->prim->primType == rt_gen) 00940 { 00941 RT_GenPtr gen = *(RT_GenPtr*) entry->prim; 00942 00943 if (!gen->IsStamped()) 00944 gen->Intersect(start, direction, 0, 1); 00945 00946 if (gen->IsTrue(GEN_HIT)) 00947 { 00948 hitPrim = gen; 00949 hitT = gen->hitT; 00950 DBG_COUT << "hit generic at " << hitT << endl; 00951 return(true); 00952 } 00953 } 00954 else if (entry->prim->primType == rt_triangle) 00955 #else 00956 if(1) 00957 #endif 00958 { 00959 RT_Tri* thisTri = (RT_Tri*) entry->prim; 00960 00961 // Find ray intersection with tri's plane. Bail if there 00962 // isn't one. 00963 GCLReal normDotDirection = dot(thisTri->normal, direction); 00964 00965 if (thisTri->flags & TRI_2SIDED_V) 00966 { 00967 if (normDotDirection == 0.0) 00968 continue; 00969 } 00970 else 00971 if (normDotDirection >= 0.0) 00972 continue; 00973 00974 t = (thisTri->d + dot(thisTri->normal, start)) / 00975 -normDotDirection; 00976 00977 // Bail if the intersection occurs before 'start' or after 00978 // 'end'. the epsilon here accounts for self-shadowing. 00979 if (t <= sEpsilon || t >= (1.0 - sEpsilon)) 00980 continue; 00981 00982 // Okay, let's do the actual test. If we've hit the tri 00983 // itself, we can return. 00984 00985 if (stats) stats->intersections++; 00986 00987 reflectPoint = start + t * direction; 00988 thisTri->PointIsInside(reflectPoint); 00989 00990 if (thisTri->IsTrue(TRI_HIT)) 00991 { 00992 if (stats) stats->goodHits++; 00993 hitPrim = thisTri; 00994 hitT = t; 00995 return(true); 00996 } 00997 } 00998 else 00999 DBG_COUT << "unknown primitive type: " << 01000 entry->prim->primType << endl; 01001 } 01002 01003 nextCellDir = MinEltIndex(tMax); 01004 cell[nextCellDir] += increment[nextCellDir]; 01005 01006 // If we've exited the grid, then bail -- we didn't find any 01007 // intersections. 01008 if (cell[nextCellDir] == limit[nextCellDir]) 01009 return(false); 01010 else 01011 tMax[nextCellDir] += tDelta[nextCellDir]; 01012 } 01013 01014 #ifdef DEBUG 01015 cerr << "*** STUCK IN GRID: tMax: " << tMax << tDelta << " for " << start << end << endl; 01016 #endif 01017 } 01018 01019 RT_Prim *RT_Grid::ClosestIntersection( 01020 const Point& start, // Start of the ray 01021 const Vector& direction, // direction of the same 01022 RT_Prim* origPrim, // originating primitive 01023 RayStats* stats // ray statistics. 01024 ) 01025 // Finds the closest intersection to the start point. Ignores 01026 // intersections with origPrim, if it is not nil. 01027 { 01028 Vector reflectPoint; 01029 Int increment[3], limit[3]; 01030 GCLReal tRayNext, tRayLast, t, tMin; 01031 Vector tDelta, tMax; 01032 Int nextCellDir; 01033 Int cell[3]; 01034 PrimEntry* entry; 01035 01036 if (stats) stats->rays++; 01037 01038 if (dirty) 01039 PrepareGrid(); 01040 01041 if (level == 0) 01042 { 01043 // just starting off: ignore all previously stamped subgrids + prims 01044 DBG_COUT << "clearing stamps" << endl; 01045 RT_Prim::IncTime(); 01046 RT_Grid::IncTime(); 01047 } 01048 else if (IsStamped()) 01049 { 01050 // return cached result 01051 DBG_COUT << "returning cached result" << endl; 01052 if (stats) stats->d2++; 01053 return(hitPrim); 01054 } 01055 else 01056 { 01057 #ifdef RT_GRID_CACHE 01058 // ensure result is cached 01059 Stamp(); 01060 #endif 01061 } 01062 01063 if (stats) stats->grids++; 01064 hitPrim = 0; 01065 01066 // Figure out grid's parameters, and return if we completely miss its 01067 // bounding box. 01068 if (!FindGridStart(start, direction, cell, increment, limit, tRayNext, 01069 tDelta, tMax)) 01070 return(hitPrim); 01071 01072 // Traverse the grid & keep track of the closest intersection found so far. 01073 // tRayLast and tRayNext bracket the t values of the part of the ray that 01074 // passes through the current cell. We're only interested in hits that 01075 // occur within these values. 01076 tRayLast = Max(sEpsilon, tRayNext); 01077 01078 for (;;) 01079 { 01080 // We have to check all the triangles within this cell, since they 01081 // are in no particular order with respect to the ray. 01082 if (stats) stats->voxels++; 01083 01084 entry = grid[cell[0] * xSpan + cell[1] * ySpan + cell[2]]; 01085 nextCellDir = MinEltIndex(tMax); // the next cell after this... 01086 tRayNext = tMax[nextCellDir]; // t at which we leave this cell 01087 01088 // Update record of delta t to the next cell in each direction. 01089 cell[nextCellDir] += increment[nextCellDir]; 01090 tMax[nextCellDir] += tDelta[nextCellDir]; 01091 01092 // we're only interested in hits before tMin 01093 if (cell[nextCellDir] == limit[nextCellDir]) 01094 tMin = vl_inf; 01095 else 01096 tMin = tRayNext; 01097 01098 DBG_COUT << "searching " << tRayLast << " - " << tRayNext << endl; 01099 01100 // Iterate through the primitives in this cell... 01101 for (; entry; entry = entry->next) 01102 { 01103 DBG_COUT << "checking cell prim" << endl; 01104 if (entry->next == PRIM_GRID_TAG) 01105 { 01106 // If this is a subgrid entry 01107 if (entry->grid->ClosestIntersection(start, direction, 01108 origPrim, stats)) 01109 { 01110 t = entry->grid->hitT; 01111 01112 if (FEQG(tRayLast, t) && FEQG(t, tMin)) 01113 { 01114 hitPrim = entry->grid->hitPrim; 01115 hitT = t; 01116 tMin = t; 01117 DBG_COUT << "hit grid at " << t << endl; 01118 } 01119 } 01120 break; // RT_Grid is always last. 01121 } 01122 01123 if (entry->prim == origPrim) 01124 continue; 01125 if ((entry->prim->flags & TRI_INACTIVE) || !(sTag & entry->prim->object->tag)) 01126 continue; 01127 01128 DBG_COUT << "intersecting" << endl; 01129 #ifdef RT_SUPPORT_GENS 01130 if (entry->prim->primType == rt_gen) 01131 { 01132 RT_GenPtr gen = *(RT_GenPtr*) entry->prim; 01133 01134 if (!gen->IsStamped()) 01135 gen->Intersect(start, direction, tRayLast, tMin); 01136 01137 if (gen->IsTrue(GEN_HIT)) 01138 { 01139 tMin = gen->hitT; 01140 hitT = tMin; 01141 t = hitT; 01142 hitPrim = gen; 01143 if (stats) stats->hits++; 01144 DBG_COUT << "hit gen at " << hitT << endl; 01145 } 01146 01147 } 01148 else if (entry->prim->primType == rt_triangle) 01149 #else 01150 if(1) 01151 #endif 01152 { 01153 RT_Tri* thisTri = (RT_Tri*) entry->prim; 01154 01155 if (stats) stats->polysTested++; 01156 01157 // Find ray intersection with tri's plane. 01158 // Bail if there isn't one. 01159 GCLReal normDotDirection = dot(thisTri->normal, direction); 01160 01161 if (thisTri->flags & TRI_2SIDED_R) 01162 { 01163 if (normDotDirection == 0.0) 01164 continue; 01165 } 01166 else 01167 if (normDotDirection >= 0.0) 01168 continue; 01169 01170 t = (thisTri->d + dot(thisTri->normal, start)) / 01171 -normDotDirection; 01172 01173 // Bail if it occurs before start of the cell, or after tMin 01174 if ((!FEQG(tRayLast, t) || !FEQG(t, tMin))) 01175 { 01176 DBG_COUT << "hit outside cell" << endl; 01177 continue; 01178 } 01179 // Okay, lets do the actual test. If we've hit the tri itself, 01180 // we can return a hit after we've finished this voxel. 01181 if (stats) stats->intersections++; 01182 reflectPoint = start + t * direction; 01183 01184 if (!thisTri->IsStamped()) 01185 thisTri->PointIsInside(reflectPoint); 01186 01187 if (thisTri->IsTrue(TRI_HIT)) 01188 { 01189 tMin = t; 01190 hitT = t; 01191 #ifdef RT_GRID_SHADE_DEBUG 01192 thisTri->id = level; 01193 #endif 01194 hitPrim = thisTri; 01195 if (stats) stats->hits++; 01196 DBG_COUT << "hit tri at " << t << endl; 01197 } 01198 } 01199 else 01200 DBG_COUT << "unknown prim type: " << entry->prim->primType 01201 << endl; 01202 } 01203 01204 if (hitPrim) 01205 { 01206 // We hit something inside this cell. Mission over, let's go home... 01207 if (stats) stats->goodHits++; 01208 return(hitPrim); 01209 } 01210 01211 // If we've exited the grid, then bail--we didn't find any 01212 // intersections. 01213 01214 if (cell[nextCellDir] == limit[nextCellDir]) 01215 return(hitPrim); 01216 01217 tRayLast = tRayNext; 01218 } 01219 } 01220 01221 01222 // --- Nested grid setup ------------------------------------------------------ 01223 01224 01225 Void RT_Grid::CreateNestedGrids() 01226 // Finds cells with too many primitives in them, and creates new grids to hold 01227 // them. 01228 { 01229 Int x, y, z, count; 01230 PrimEntry *entry, *startEntry, **pushStart; 01231 RT_Grid *subGrid, *bestGrid; 01232 PrimListEntry *newPrimListEntry; 01233 Vector cellMin, cellMax, bmin, bmax; 01234 GCLReal newBVol, expRatio; 01235 GCLReal minRatio; 01236 Vector cellSpan, primsMin, primsMax, primsSpan; 01237 01238 // Loop over the entire grid. 01239 01240 for (x = 0; x < numCells[0]; x++) 01241 for (y = 0; y < numCells[1]; y++) 01242 for (z = 0; z < numCells[2]; z++) 01243 { 01244 count = 0; 01245 startEntry = grid[x * xSpan + y * ySpan + z]; 01246 01247 // + Don't push prims larger than areaLimit... 01248 // This limit exists for two reasons: if a primitive is very 01249 // large compared to its grid, it will create far too many 01250 // prim entries, plus there's also not much gain in pushing 01251 // a primitive that's already on the order of the size 01252 // of a voxel. 01253 01254 // find start of pushable prims (the entries are sorted 01255 // by area) 01256 pushStart = &(grid[x * xSpan + y * ySpan + z]); 01257 for (entry = startEntry; entry; entry = entry->next) 01258 if (entry->prim->area < areaLimit) 01259 break; 01260 else 01261 pushStart = &(entry->next); 01262 01263 // Count pushable prims 01264 for (entry = *pushStart; entry; entry = entry->next) 01265 count++; 01266 01267 // Add this cell's prims to a subgrid if there are too many of 01268 // them 01269 if (count > sMaxCellPrims * (level + 1)) 01270 { 01271 // Find the bounds of the prims 01272 01273 cellMin = min + Vector(x, y, z) * cellSize; 01274 cellMax.MakeBlock(cellSize); 01275 cellMax += cellMin; 01276 primsMin.MakeBlock(+vl_inf); 01277 primsMax.MakeBlock(-vl_inf); 01278 01279 for (entry = *pushStart; entry; entry = entry->next) 01280 { 01281 if (entry->prim->primType == rt_triangle) 01282 ((RT_Tri*) entry->prim)-> 01283 UpdateBounds(primsMin, primsMax); 01284 else if (entry->prim->primType == rt_gen) 01285 (*(RT_GenPtr*) entry->prim)-> 01286 UpdateBounds(primsMin, primsMax); 01287 else 01288 DBG_COUT << "illegal prim type" << endl; 01289 } 01290 01291 // Trim the bounds to the boundaries of the cell 01292 01293 FindMaxElts(primsMin, cellMin, primsMin); 01294 FindMinElts(primsMax, cellMax, primsMax); 01295 01296 // We need a new grid. See whether we can merge with one of 01297 // the already-created subgrids. 01298 01299 minRatio = sMaxExpand; 01300 bestGrid = 0; 01301 primsSpan = primsMax - primsMin; 01302 newBVol = primsSpan[0] * primsSpan[1] * primsSpan[2]; 01303 01304 for (subGrid = subGrids; subGrid; subGrid = subGrid->next) 01305 { 01306 GCLReal cellRatio; 01307 01308 // Find the bounds of the putatative new grid. 01309 01310 FindMinElts(subGrid->min, primsMin, bmin); 01311 FindMaxElts(subGrid->max, primsMax, bmax); 01312 01313 // We want to ensure the cellSize of subgrids doesn't 01314 // get too large... so we find the ratio of the cell 01315 // size of the new grid to the parent (cellRatio) 01316 // and the increase in volume over the old grid. 01317 01318 cellRatio = MaxElt(bmax - bmin) / 01319 (sMaxCells * cellSize); 01320 expRatio = BoxVol(bmin, bmax) / 01321 (BoxVol(subGrid->min, subGrid->max) + newBVol); 01322 01323 // If both are acceptable, this is our new best grid 01324 01325 if (expRatio < minRatio && cellRatio < sMaxCellRatio) 01326 { 01327 minRatio = expRatio; 01328 bestGrid = subGrid; 01329 } 01330 } 01331 01332 if (!bestGrid) 01333 { 01334 // We need a brand-spanking-new grid if we didn't 01335 // find one to merge with. 01336 01337 bestGrid = RT_Alloc(RT_Grid); 01338 01339 bestGrid->Init(); 01340 bestGrid->parent = this; 01341 bestGrid->level = level + 1; 01342 bestGrid->next = subGrids; 01343 bestGrid->dirty = true; 01344 subGrids = bestGrid; 01345 } 01346 01347 // Add a pointer to the pushable prims to the prim list of 01348 // the subgrid 01349 01350 newPrimListEntry = RT_Alloc(PrimListEntry); 01351 sMem += sizeof(PrimListEntry); 01352 01353 newPrimListEntry->list = pushStart; 01354 newPrimListEntry->next = bestGrid->addPrims; 01355 bestGrid->addPrims = newPrimListEntry; 01356 01357 bestGrid->UpdateBounds(primsMin, primsMax); 01358 bestGrid->totalPrims += count; 01359 } 01360 } 01361 01362 CullSubGrids(); 01363 PushPrimitives(); 01364 } 01365 01366 Void RT_Grid::CullSubGrids() 01367 // Cull subgrids that aren't worth the extra space. 01368 { 01369 GCLReal r; 01370 Int estCellTotal; 01371 Vector span; 01372 RT_Grid* subGrid; 01373 RT_Grid** last; 01374 01375 last = &subGrids; 01376 01377 for (subGrid = *last; subGrid; subGrid = *last) 01378 { 01379 span = subGrid->max - subGrid->min; 01380 r = sMaxCells / MaxElt(span); 01381 estCellTotal = Int(r * r * r * GCLReal(span[0] * span[1] * span[2])); 01382 01383 if (subGrid->totalPrims / GCLReal(estCellTotal) < 01384 sMinDensity * (1 - level / 2.0)) 01385 { 01386 // Delete the subgrid from the list. 01387 01388 *last = subGrid->next; 01389 subGrid->Free(); 01390 RT_Free(subGrid); 01391 } 01392 else 01393 last = &(subGrid->next); 01394 } 01395 } 01396 01397 Void RT_Grid::PushPrimitives() 01398 // Shift primitives down to the grids created by CreateNestedGrids routine... 01399 { 01400 PrimEntry *entry, *nextEntry; 01401 RT_Grid *subGrid; 01402 PrimListEntry *primList; 01403 01404 for (subGrid = subGrids; subGrid; subGrid = subGrid->next) 01405 { 01406 // Create the grids! 01407 subGrid->SetupGrid(); 01408 subGrid->dirty = true; 01409 RT_Prim::IncTime(); 01410 01411 // Push the primitives! 01412 for (primList = subGrid->addPrims; primList; primList = primList->next) 01413 { 01414 // Each primList is a list of primitives pushed to this grid 01415 // from a particular cell. 01416 01417 for (entry = *(primList->list); entry; entry = nextEntry) 01418 { 01419 nextEntry = entry->next; 01420 01421 // Add primitives, avoiding duplicates 01422 01423 if (!entry->prim->IsStamped()) 01424 { 01425 if (entry->prim->primType == rt_triangle) 01426 subGrid->AddTriangle((RT_Tri*) entry->prim); 01427 else if (entry->prim->primType == rt_gen) 01428 subGrid->AddGeneric(*(RT_GenPtr*) entry->prim); 01429 else 01430 DBG_COUT << "illegal prim type" << endl; 01431 01432 entry->prim->Stamp(); 01433 } 01434 else 01435 totalPrims--; 01436 01437 RT_Free(entry); 01438 sMem -= sizeof(PrimEntry); 01439 } 01440 01441 // Remove the pushed primitives from the cell list, and replace 01442 // them with a grid entry. 01443 01444 *primList->list = RT_Alloc(PrimEntry); 01445 sMem += sizeof(PrimEntry); 01446 (*primList->list)->next = PRIM_GRID_TAG; 01447 (*primList->list)->grid = subGrid; 01448 } 01449 01450 addPrims = 0; 01451 } 01452 } 01453 01454 01455 // --- Support routines ------------------------------------------------------- 01456 01457 01458 static Void Indent(ostream &s, Int n) 01459 { 01460 Int i; 01461 01462 for (i = 0; i < n; i++) 01463 s << ' '; 01464 } 01465 01466 Void RT_Grid::HierPrint(ostream &s, Int indent) 01467 { 01468 RT_Grid* subGrid; 01469 01470 Indent(s, indent); 01471 s << totalPrims << " prims, bbox: " << min << ' ' << max << endl; 01472 01473 for(subGrid = subGrids; subGrid; subGrid = subGrid->next) 01474 subGrid->HierPrint(s, indent + 2); 01475 } 01476 01477 01478 // --- Memory methods --------------------------------------------------------- 01479 01480 01481 Void RT_Grid::SetMemLimit(Int maxKbs) 01482 { 01483 sMemLimit = 1024 * maxKbs; 01484 } 01485 01486 GCLReal RT_Grid::GridMemoryUsage() 01487 { 01488 return(sMem / 1024.0); 01489 } 01490 01491 GCLReal RT_Grid::MemoryUsage() 01492 { 01493 return((sMem + sTriMem + sGenMem + sPointMem) / 1024.0); 01494 } 01495 01496 Void RT_Grid::DumpMemoryUsage() 01497 { 01498 RT_Grid *sgrid; 01499 01500 Int numGrids = 0; 01501 for (sgrid = subGrids; sgrid; sgrid = sgrid->next) 01502 numGrids++; 01503 01504 cout << "Sub grids: " << numGrids << endl; 01505 cout << "Grid pages: " << grid.NumPages() << endl; 01506 cout << "Grid memory usage: " << GridMemoryUsage() << "k" << endl; 01507 cout << "Triangle memory: " << sTriMem / 1024.0 << "k" << endl; 01508 cout << "Generic memory: " << sGenMem / 1024.0 << "k" << endl; 01509 cout << "Points memory: " << sPointMem / 1024.0 << "k" << endl; 01510 } 01511 01512 #ifdef RT_VIS 01513 01514 // --- Visualisation ---------------------------------------------------------- 01515 01516 #include "gcl/Draw.h" 01517 01518 Void RT_Grid::Draw(Renderer &r, Int level) 01519 { 01520 r.C(Colour4(HSVCol(hsvYellow + 60 * level, 1, 1), 0.2)); 01521 FrameBox(r, min, max); 01522 r.C(Colour4(HSVCol(hsvOrange + 60 * level, 1, 1), 0.2)); 01523 01524 Vector d1, d2; 01525 Point p; 01526 Int x, y, z; 01527 01528 d1 = max - min; 01529 d2 = d1; 01530 d2[0] /= numCells[0]; 01531 d2[1] /= numCells[1]; 01532 d2[2] /= numCells[2]; 01533 01534 for (x = 0; x < numCells[0]; x++) 01535 for (y = 0; y < numCells[1]; y++) 01536 for (z = 0; z < numCells[2]; z++) 01537 { 01538 if (grid[x * xSpan + y * ySpan + z]) 01539 { 01540 p = min + Vector(x, y, z) * d2; 01541 FrameBox(r, p, p + d2); 01542 } 01543 } 01544 01545 // Draw subgrids. 01546 RT_Grid *sgrid; 01547 level++; 01548 01549 for (sgrid = subGrids; sgrid; sgrid = sgrid->next) 01550 sgrid->Draw(r, level); 01551 } 01552 01553 #endif