//----------------------------------------------------------------------------- // Torque Shader Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "util/quadTreeTracer.h" #include "platform/profiler.h" #include "core/frameAllocator.h" static F32 calcInterceptV(F32 vStart, F32 invDeltaV, F32 intercept) { return (intercept - vStart) * invDeltaV; } static F32 calcInterceptNone(F32, F32, F32) { return F32_MAX; } static F32 (*calcInterceptX)(F32, F32, F32); static F32 (*calcInterceptY)(F32, F32, F32); bool QuadTreeTracer::castRay(const Point3F &start, const Point3F &end, RayInfo *info) { PROFILE_START(QuadTreeTracer_castRay); // Do some precalculations we'll use for the rest of this routine. // Set up our intercept calculation methods. F32 invDeltaX; if(end.x == start.x) { calcInterceptX = calcInterceptNone; invDeltaX = 0; } else { invDeltaX = 1.f / (end.x - start.x); calcInterceptX = calcInterceptV; } F32 invDeltaY; if(end.y == start.y) { calcInterceptY = calcInterceptNone; invDeltaY = 0; } else { invDeltaY = 1.f / (end.y - start.y); calcInterceptY = calcInterceptV; } // Subdivide our space based on the size of the lowest level of the tree... const F32 invSize = 1.f / F32(BIT(mTreeDepth-1)); // Grab this off the frame allocator, we don't want to do a proper alloc // on every ray! FrameAllocatorMarker stackAlloc; RayStackNode *stack = (RayStackNode*)stackAlloc.alloc(sizeof(RayStackNode) * (mTreeDepth * 3 + 1)); U32 stackSize = 1; // Kick off the stack with the root node. stack[0].startT = 0; stack[0].endT = 1; stack[0].squarePos.set(0,0); stack[0].level = mTreeDepth - 1; //Con::printf("QuadTreeTracer::castRay(%x)", this); // Aright, now let's do some raycasting! while(stackSize--) { // Get the current node for easy access... RayStackNode *sn = stack + stackSize; const U32 level = sn->level; const F32 startT = sn->startT; const F32 endT = sn->endT; const Point2I squarePos = sn->squarePos; AssertFatal((startT >= 0.f) && (startT <= 1.f), "QuadTreeTracer::castRay - out of range startT on stack!"); AssertFatal((endT >= 0.f) && (endT <= 1.f), "QuadTreeTracer::castRay - out of range endT on stack!"); //Con::printf(" -- node(%d, %d @ %d), sT=%g, eT=%g", squarePos.x, squarePos.y, level, startT, endT); // Figure our start and end Z. const F32 startZ = startT * (end.z - start.z) + start.z; const F32 endZ = endT * (end.z - start.z) + start.z; // Ok, now let's see if we hit the lower bound const F32 squareMin = getSquareMin(level, squarePos); if(startZ < squareMin && endZ < squareMin) continue; //Nope, skip out. // Hmm, let's check the upper bound. const F32 squareMax = getSquareMax(level, squarePos); if(startZ > squareMax && endZ > squareMax) continue; //Nope, skip out. // We might be intersecting something... If we've hit // the tree depth let's deal with the leaf intersection. if(level == 0) { //Con::printf(" ++ check node(%d, %d @ %d), sT=%g, eT=%g", squarePos.x, squarePos.y, level, startT, endT); if(castLeafRay(squarePos, start, end, startT, endT, info)) { PROFILE_END(); return true; // We hit, tell 'em so! } continue; // Otherwise, keep looking. } else { // Ok, we have to push our children as we're an inner node. // First, figure out some widths... U32 squareSize = BIT(level); U32 subSqSize = BIT(level - 1); // Now, calculate intercepts so we know how to deal with this // situation... (intercept = position, int = t value for that pos) const F32 xIntercept = (squarePos.x + subSqSize) * invSize; F32 xInt = calcInterceptX(start.x, invDeltaX, xIntercept); const F32 yIntercept = (squarePos.y + subSqSize) * invSize; F32 yInt = calcInterceptY(start.y, invDeltaY, yIntercept); // Our starting position for this subray... const F32 startX = startT * (end.x - start.x) + start.x; const F32 startY = startT * (end.y - start.y) + start.y; // Deal with squares that might be "behind" the ray. if(xInt < startT) xInt = F32_MAX; if(yInt < startT) yInt = F32_MAX; // Do a little magic to calculate our next checks... const U32 x0 = (startX > xIntercept) * subSqSize; const U32 y0 = (startY > yIntercept) * subSqSize; const U32 x1 = subSqSize - x0; const U32 y1 = subSqSize - y0; //AssertFatal(x1 < 1000, "QuadTreeTracer::castRay - possible overflow in x1!"); //AssertFatal(y1 < 1000, "QuadTreeTracer::castRay - possible overflow in y1!"); const U32 nextLevel = level - 1; // Ok, now let's figure out what nodes, in what order, need to go // on the stack. We push things on in reverse order of processing. if(xInt > endT && yInt > endT) { stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize].level = nextLevel; stackSize++; } else if(xInt < yInt) { F32 nextIntersect = endT; if(yInt <= endT) { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = yInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; nextIntersect = yInt; stackSize++; } // Do middle two, order doesn't matter. stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y0); stack[stackSize].startT = xInt; stack[stackSize].endT = nextIntersect; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = xInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } else if(yInt < xInt) { F32 nextIntersect = endT; if(xInt <= endT) { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = xInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; nextIntersect = xInt; stackSize++; } stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y1); stack[stackSize].startT = yInt; stack[stackSize].endT = nextIntersect; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = yInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } else { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = xInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = xInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } } } // Nothing found, so give up. PROFILE_END(); return false; }