void ClipMap::calculateModuloDeltaBounds(const RectI &oldData, const RectI &newData, RectI *outRects, S32 *outRectCount) { // Sanity checking. /*AssertFatal(oldData.point.x >= 0 && oldData.point.y >= 0 && oldData.isValidRect(), "ClipMap::calculateModuloDeltaBounds - negative oldData origin or bad rect!"); AssertFatal(newData.point.x >= 0 && newData.point.y >= 0 && newData.isValidRect(), "ClipMap::calculateModuloDeltaBounds - negative newData origin or bad rect!");*/ AssertFatal(newData.extent == oldData.extent, "ClipMap::calculateModuloDeltaBounts - mismatching extents, can only work with matching extents!"); // Easiest case - if they're the same then do nothing. if(oldData.point == newData.point) { *outRectCount = 0; return; } // Easy case - if there's no overlap then it's all new! if(!oldData.overlaps(newData)) { // Clip out to return buffer, and we're done. clipAgainstGrid(mClipMapSize, newData, outRectCount, outRects); return; } // Calculate some useful values for both X and Y. Delta is used a lot // in determining bounds, and the boundary values are important for // determining where to start copying new data in. const S32 xDelta = newData.point.x - oldData.point.x; const S32 yDelta = newData.point.y - oldData.point.y; const S32 xBoundary = (oldData.point.x + oldData.extent.x) % mClipMapSize; const S32 yBoundary = (oldData.point.y + oldData.extent.y) % mClipMapSize; AssertFatal(xBoundary % mClipMapSize == oldData.point.x % mClipMapSize, "ClipMap::calculateModuleDeltaBounds - we assume that left and " "right of the dataset are identical (ie, it's periodical on size of clipmap!) (x)"); AssertFatal(yBoundary % mClipMapSize == oldData.point.y % mClipMapSize, "ClipMap::calculateModuleDeltaBounds - we assume that left and " "right of the dataset are identical (ie, it's periodical on size of clipmap!) (y)"); // Now, let's build up our rects. We have one rect if we are moving // on the X or Y axis, two if both. We dealt with the no-move case // previously. if(xDelta == 0) { // Moving on Y! So generate and store clipped results. RectI yRect; if(yDelta < 0) { // We need to generate the box from right of old to right of new. yRect.point = newData.point; yRect.extent.x = mClipMapSize; yRect.extent.y = -yDelta; } else { // We need to generate the box from left of old to left of new. yRect.point.x = newData.point.x; // Doesn't matter which rect we get this from. yRect.point.y = (oldData.point.y + oldData.extent.y); yRect.extent.x = mClipMapSize; yRect.extent.y = yDelta; } // Clip out to return buffer, and we're done. clipAgainstGrid(mClipMapSize, yRect, outRectCount, outRects); return; } else if(yDelta == 0) { // Moving on X! So generate and store clipped results. RectI xRect; if(xDelta < 0) { // We need to generate the box from right of old to right of new. xRect.point = newData.point; xRect.extent.x = -xDelta; xRect.extent.y = mClipMapSize; } else { // We need to generate the box from left of old to left of new. xRect.point.x = (oldData.point.x + oldData.extent.x); xRect.point.y = newData.point.y; // Doesn't matter which rect we get this from. xRect.extent.x = xDelta; xRect.extent.y = mClipMapSize; } // Clip out to return buffer, and we're done. clipAgainstGrid(mClipMapSize, xRect, outRectCount, outRects); return; } else { // Both! We have an L shape. So let's do the bulk of it in one rect, // and the remainder in the other. We'll choose X as the dominant axis. // // a-----b---------c going from e to a. // | | | // | | | // d-----e---------f So the dominant rect is abgh and the passive // | | | rect is bcef. Obviously depending on delta we // | | | have to switch things around a bit. // | | | y+ ^ // | | | | // g-----h---------i x+-> | RectI xRect, yRect; if(xDelta < 0) { // Case in the diagram. xRect.point = newData.point; xRect.extent.x = -xDelta; xRect.extent.y = mClipMapSize; // Set up what of yRect we know, too. yRect.point.x = xRect.point.x + xRect.extent.x; yRect.extent.x = mClipMapSize - mAbs(xDelta); } else { // Opposite of case in diagram! xRect.point.x = oldData.point.x + oldData.extent.x; xRect.point.y = newData.point.y; xRect.extent.x = xDelta; xRect.extent.y = mClipMapSize; // Set up what of yRect we know, too. yRect.point.x = (xRect.point.x + xRect.extent.x )- mClipMapSize; yRect.extent.x = mClipMapSize - xRect.extent.x; } if(yDelta < 0) { // Case in the diagram. yRect.point.y = newData.point.y; yRect.extent.y = -yDelta; } else { // Opposite of case in diagram! yRect.point.y = oldData.point.y + oldData.extent.y; yRect.extent.y = yDelta; } // Make sure we don't overlap. AssertFatal(!yRect.overlaps(xRect), "ClipMap::calculateModuloDeltaBounds - have overlap in result rects!"); // Ok, now run them through the clipper. S32 firstCount; clipAgainstGrid(mClipMapSize, xRect, &firstCount, outRects); clipAgainstGrid(mClipMapSize, yRect, outRectCount, outRects + firstCount); *outRectCount += firstCount; // All done! return; } }
void TerrCell::updateGrid( const RectI &gridRect, bool opacityOnly ) { PROFILE_SCOPE( TerrCell_UpdateGrid ); // If we have a VB... then update it. if ( mVertexBuffer.isValid() && !opacityOnly ) _updateVertexBuffer(); // Update our PB, if any _updatePrimitiveBuffer(); // If we don't have children... then we're // a leaf at the bottom of the cell quadtree // and we should just update our bounds. if ( !mChildren[0] ) { if ( !opacityOnly ) _updateBounds(); _updateMaterials(); return; } // Otherwise, we must call updateGrid on our children // and then update our bounds/materials AFTER to contain them. mMaterials = 0; for ( U32 i = 0; i < 4; i++ ) { TerrCell *cell = mChildren[i]; // The overlap test doesn't hit shared edges // so grow it a bit when we create it. const RectI cellRect( cell->mPoint.x - 1, cell->mPoint.y - 1, cell->mSize + 2, cell->mSize + 2 ); // We do an overlap and containment test as it // properly handles zero sized rects. if ( cellRect.contains( gridRect ) || cellRect.overlaps( gridRect ) ) cell->updateGrid( gridRect, opacityOnly ); // Update the bounds from our children. if ( !opacityOnly ) { if ( i == 0 ) mBounds = mChildren[i]->getBounds(); else mBounds.intersect( mChildren[i]->getBounds() ); mRadius = mBounds.len() * 0.5f; } // Update the material flags. mMaterials |= mChildren[i]->getMaterials(); } if ( mMaterial ) mMaterial->init( mTerrain, mMaterials ); }