From 6a50d66d7d1c0df0b4f570df3ed5bbaae296e832 Mon Sep 17 00:00:00 2001 From: Joilnen Date: Sun, 17 Sep 2023 08:38:01 -0300 Subject: [PATCH] Terrain: range loop factoring (#2945) --- .../Terrain/src/OgreTerrainQuadTreeNode.cpp | 190 ++++++++---------- 1 file changed, 88 insertions(+), 102 deletions(-) diff --git a/Components/Terrain/src/OgreTerrainQuadTreeNode.cpp b/Components/Terrain/src/OgreTerrainQuadTreeNode.cpp index 85379fc2147..1093d6bc65d 100644 --- a/Components/Terrain/src/OgreTerrainQuadTreeNode.cpp +++ b/Components/Terrain/src/OgreTerrainQuadTreeNode.cpp @@ -45,8 +45,8 @@ namespace Ogre unsigned short TerrainQuadTreeNode::DELTA_BUFFER = 1; //--------------------------------------------------------------------- - TerrainQuadTreeNode::TerrainQuadTreeNode(Terrain* terrain, - TerrainQuadTreeNode* parent, uint16 xoff, uint16 yoff, uint16 size, + TerrainQuadTreeNode::TerrainQuadTreeNode(Terrain* terrain, + TerrainQuadTreeNode* parent, uint16 xoff, uint16 yoff, uint16 size, uint16 lod, uint16 depth) : mTerrain(terrain) , mParent(parent) @@ -111,11 +111,11 @@ namespace Ogre if (ownLod) sz = (uint16)(((sz - 1) * 0.5) + 1); } - + assert(sz == terrain->getMinBatchSize()); } - + // Local centre calculation // Because of pow2 + 1 there is always a middle point uint16 midoffset = (size - 1) / 2; @@ -184,15 +184,14 @@ namespace Ogre void TerrainQuadTreeNode::prepare(StreamSerialiser& stream) { // load LOD data we need - for (LodLevelList::iterator i = mLodLevels.begin(); i != mLodLevels.end(); ++i) + for (auto *l : mLodLevels) { - LodLevel* ll = *i; // only read 'calc' and then copy to final (separation is only for // real-time calculation // Basically this is what finaliseHeightDeltas does in calc path - stream.read(&ll->calcMaxHeightDelta); - ll->maxHeightDelta = ll->calcMaxHeightDelta; - ll->lastCFactor = 0; + stream.read(&l->calcMaxHeightDelta); + l->maxHeightDelta = l->calcMaxHeightDelta; + l->lastCFactor = 0; } if (!isLeaf()) @@ -214,11 +213,8 @@ namespace Ogre void TerrainQuadTreeNode::save(StreamSerialiser& stream) { // save LOD data we need - for (LodLevelList::iterator i = mLodLevels.begin(); i != mLodLevels.end(); ++i) - { - LodLevel* ll = *i; - stream.write(&ll->maxHeightDelta); - } + for (const auto *l : mLodLevels) + stream.write(&l->maxHeightDelta); if (!isLeaf()) { @@ -318,14 +314,14 @@ namespace Ogre // if the rect covers the whole node, reset the max height // this means that if you recalculate the deltas progressively, end up keeping - // a max height that's no longer the case (ie more conservative lod), - // but that's the price for not recaculating the whole node. If a + // a max height that's no longer the case (ie more conservative lod), + // but that's the price for not recaculating the whole node. If a // complete recalculation is required, just dirty the entire node. (or terrain) // Note we use the 'calc' field here to avoid interfering with any // ongoing LOD calculations (this can be in the background) - if (rect.left <= mOffsetX && rect.right > mBoundaryX + if (rect.left <= mOffsetX && rect.right > mBoundaryX && rect.top <= mOffsetY && rect.bottom > mBoundaryY) { for (LodLevelList::iterator i = mLodLevels.begin(); i != mLodLevels.end(); ++i) @@ -344,7 +340,7 @@ namespace Ogre //--------------------------------------------------------------------- void TerrainQuadTreeNode::notifyDelta(uint16 x, uint16 y, uint16 lod, Real delta) { - if (x >= mOffsetX && x < mBoundaryX + if (x >= mOffsetX && x < mBoundaryX && y >= mOffsetY && y < mBoundaryY) { // within our bounds, check it's our LOD level @@ -411,8 +407,8 @@ namespace Ogre { // the next LOD after this one should have a higher delta // otherwise it won't come into affect further back like it should! - mLodLevels[i+1]->calcMaxHeightDelta = - std::max(mLodLevels[i+1]->calcMaxHeightDelta, + mLodLevels[i+1]->calcMaxHeightDelta = + std::max(mLodLevels[i+1]->calcMaxHeightDelta, mLodLevels[i]->calcMaxHeightDelta * (Real)1.05); } @@ -440,19 +436,17 @@ namespace Ogre } // Self - for (LodLevelList::iterator i = mLodLevels.begin(); i != mLodLevels.end(); ++i) + for (auto *l : mLodLevels) { // copy from 'calc' area to runtime value - (*i)->maxHeightDelta = (*i)->calcMaxHeightDelta; + l->maxHeightDelta = l->calcMaxHeightDelta; // also trash stored cfactor - (*i)->lastCFactor = 0; + l->lastCFactor = 0; } - } - } //--------------------------------------------------------------------- - void TerrainQuadTreeNode::assignVertexData(uint16 treeDepthStart, + void TerrainQuadTreeNode::assignVertexData(uint16 treeDepthStart, uint16 treeDepthEnd, uint16 resolution, uint sz) { assert(treeDepthStart >= mDepth && "Should not be calling this"); @@ -480,25 +474,24 @@ namespace Ogre for (int i = 0; i < 4; ++i) mChildren[i]->assignVertexData(treeDepthStart, treeDepthEnd, resolution, sz); - + } } //--------------------------------------------------------------------- void TerrainQuadTreeNode::useAncestorVertexData(TerrainQuadTreeNode* owner, uint16 treeDepthEnd, uint16 resolution) { - mNodeWithVertexData = owner; + mNodeWithVertexData = owner; mVertexDataRecord = 0; if (!isLeaf() && treeDepthEnd > (mDepth + 1)) // treeDepthEnd is exclusive, and this is children { for (int i = 0; i < 4; ++i) mChildren[i]->useAncestorVertexData(owner, treeDepthEnd, resolution); - } } //--------------------------------------------------------------------- - void TerrainQuadTreeNode::updateVertexData(bool positions, bool deltas, + void TerrainQuadTreeNode::updateVertexData(bool positions, bool deltas, const Rect& rect, bool cpuData) { //Check that we really intersect the dirty rect. This avoid assertion errors further down the line. @@ -510,19 +503,19 @@ namespace Ogre { // update the GPU buffer directly // TODO: do we have no use for CPU vertex data after initial load? - // if so, destroy it to free RAM, this should be fast enough to + // if so, destroy it to free RAM, this should be fast enough to // to direct HardwareVertexBufferSharedPtr posbuf, deltabuf; VertexData* targetVertexData = mVertexDataRecord->cpuVertexData; if(!cpuData) { - if(mVertexDataRecord->gpuVertexData == NULL) + if(mVertexDataRecord->gpuVertexData == NULL) createGpuVertexData(); assert(mVertexDataRecord->gpuVertexData); targetVertexData = mVertexDataRecord->gpuVertexData; } - if (positions) + if (positions) posbuf = targetVertexData->vertexBufferBinding->getBuffer(POSITION_BUFFER); if (deltas) deltabuf = targetVertexData->vertexBufferBinding->getBuffer(DELTA_BUFFER); @@ -575,7 +568,7 @@ namespace Ogre // Vertex declaration size_t offset = 0; - + if (mTerrain->_getUseVertexCompression()) { // 16 bytes per vertex, shaders only @@ -589,10 +582,10 @@ namespace Ogre // float2(delta, deltaLODthreshold) dcl->addElement(DELTA_BUFFER, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES, 1).getSize(); } - else + else { // 28 bytes per vertex, compatibility - // POSITION + // POSITION // float3(x, y, z) offset += dcl->addElement(POSITION_BUFFER, offset, VET_FLOAT3, VES_POSITION).getSize(); // UV0 @@ -609,9 +602,9 @@ namespace Ogre size_t numVerts = baseNumVerts; // Now add space for skirts // Skirts will be rendered as copies of the edge vertices translated downwards - // Some people use one big fan with only 3 vertices at the bottom, + // Some people use one big fan with only 3 vertices at the bottom, // but this requires creating them much bigger that necessary, meaning - // more unnecessary overdraw, so we'll use more vertices + // more unnecessary overdraw, so we'll use more vertices // You need 2^levels + 1 rows of full resolution (max 129) vertex copies, plus // the same number of columns. There are common vertices at intersections uint16 levels = mVertexDataRecord->treeLevels; @@ -630,7 +623,7 @@ namespace Ogre mVertexDataRecord->cpuVertexData->vertexStart = 0; mVertexDataRecord->cpuVertexData->vertexCount = numVerts; - + Rect updateRect(mOffsetX, mOffsetY, mBoundaryX, mBoundaryY); updateVertexBuffer(posbuf, deltabuf, updateRect); bufbind->setBinding(POSITION_BUFFER, posbuf); @@ -642,7 +635,7 @@ namespace Ogre void TerrainQuadTreeNode::updateVertexBuffer(const HardwareVertexBufferPtr& posbuf, const HardwareVertexBufferPtr& deltabuf, const Rect& rect) { - assert (rect.left >= mOffsetX && rect.right <= mBoundaryX && + assert (rect.left >= mOffsetX && rect.right <= mBoundaryX && rect.top >= mOffsetY && rect.bottom <= mBoundaryY); // potentially reset our bounds depending on coverage of the update @@ -653,7 +646,7 @@ namespace Ogre long destOffsetX = rect.left <= mOffsetX ? 0 : (rect.left - mOffsetX) / inc; long destOffsetY = rect.top <= mOffsetY ? 0 : (rect.top - mOffsetY) / inc; // Fill the buffers - + HardwareBuffer::LockOptions lockMode; if (destOffsetX || destOffsetY || rect.width() < mSize || rect.height() < mSize) @@ -692,9 +685,9 @@ namespace Ogre pRowDeltaBuf += destOffsetY * destDeltaRowSkip + destOffsetX * deltabuf->getVertexSize(); } Vector3 pos; - + bool vcompress = mTerrain->_getUseVertexCompression(); - + for (uint16 y = rect.top; y < rect.bottom; y += inc) { const float* pHeight = pBaseHeight; @@ -711,10 +704,10 @@ namespace Ogre mergeIntoBounds(x, y, pos); // relative to local centre pos -= mLocalCentre; - + writePosVertex(vcompress, x, y, *pHeight, pos, uvScale, &pPosBuf); pHeight += inc; - + } @@ -724,14 +717,14 @@ namespace Ogre // we want delta to apply to LODs no higher than this value // at runtime this will be combined with a per-renderable parameter // to ensure we only apply morph to the correct LOD - writeDeltaVertex(vcompress, x, y, *pDelta, - (float)mTerrain->getLODLevelWhenVertexEliminated(x, y) - 1.0f, + writeDeltaVertex(vcompress, x, y, *pDelta, + (float)mTerrain->getLODLevelWhenVertexEliminated(x, y) - 1.0f, &pDeltaBuf); pDelta += inc; } - + } pBaseHeight += rowskip; pBaseDelta += rowskip; @@ -762,7 +755,7 @@ namespace Ogre if (posbuf) { // position dest buffer just after the main vertex data - pRowPosBuf = pRootPosBuf + posbuf->getVertexSize() + pRowPosBuf = pRootPosBuf + posbuf->getVertexSize() * mVertexDataRecord->size * mVertexDataRecord->size; // move it onwards to skip the skirts we don't need to update pRowPosBuf += destPosRowSkip * ((skirtStartY - mOffsetY) / skirtSpacing); @@ -771,7 +764,7 @@ namespace Ogre if (deltabuf) { // position dest buffer just after the main vertex data - pRowDeltaBuf = pRootDeltaBuf + deltabuf->getVertexSize() + pRowDeltaBuf = pRootDeltaBuf + deltabuf->getVertexSize() * mVertexDataRecord->size * mVertexDataRecord->size; // move it onwards to skip the skirts we don't need to update pRowDeltaBuf += destDeltaRowSkip * (skirtStartY - mOffsetY) / skirtSpacing; @@ -794,7 +787,7 @@ namespace Ogre pHeight += inc; - + } @@ -824,7 +817,7 @@ namespace Ogre if (posbuf) { // position dest buffer just after the main vertex data and skirt rows - pRowPosBuf = pRootPosBuf + posbuf->getVertexSize() + pRowPosBuf = pRootPosBuf + posbuf->getVertexSize() * mVertexDataRecord->size * mVertexDataRecord->size; // skip the row skirts pRowPosBuf += mVertexDataRecord->numSkirtRowsCols * mVertexDataRecord->size * posbuf->getVertexSize(); @@ -835,7 +828,7 @@ namespace Ogre if (deltabuf) { // Delta dest buffer just after the main vertex data and skirt rows - pRowDeltaBuf = pRootDeltaBuf + deltabuf->getVertexSize() + pRowDeltaBuf = pRootDeltaBuf + deltabuf->getVertexSize() * mVertexDataRecord->size * mVertexDataRecord->size; // skip the row skirts pRowDeltaBuf += mVertexDataRecord->numSkirtRowsCols * mVertexDataRecord->size * deltabuf->getVertexSize(); @@ -843,7 +836,7 @@ namespace Ogre pRowDeltaBuf += destDeltaRowSkip * (skirtStartX - mOffsetX) / skirtSpacing; pRowDeltaBuf += deltabuf->getVertexSize() * (skirtStartY - mOffsetY) / inc; } - + for (uint16 x = skirtStartX; x < rect.right; x += skirtSpacing) { float* pPosBuf = static_cast(static_cast(pRowPosBuf)); @@ -857,7 +850,7 @@ namespace Ogre // relative to local centre pos -= mLocalCentre; pos += skirtOffset; - + writePosVertex(vcompress, x, y, height - mTerrain->getSkirtSize(), pos, uvScale, &pPosBuf); } @@ -878,14 +871,14 @@ namespace Ogre posbuf->unlock(); if (deltabuf) deltabuf->unlock(); - + } //--------------------------------------------------------------------- - void TerrainQuadTreeNode::writePosVertex(bool compress, uint16 x, uint16 y, float height, + void TerrainQuadTreeNode::writePosVertex(bool compress, uint16 x, uint16 y, float height, const Vector3& pos, float uvScale, float** ppPos) { float* pPosBuf = *ppPos; - + if (compress) { short* pPosShort = static_cast(static_cast(pPosBuf)); @@ -895,22 +888,22 @@ namespace Ogre *pPosBuf++ = height; } - else + else { *pPosBuf++ = pos.x; *pPosBuf++ = pos.y; *pPosBuf++ = pos.z; - + // UVs - base UVs vary from 0 to 1, all other values // will be derived using scalings *pPosBuf++ = x * uvScale; *pPosBuf++ = 1.0f - (y * uvScale); } - + *ppPos = pPosBuf; } //--------------------------------------------------------------------- - void TerrainQuadTreeNode::writeDeltaVertex(bool compress, uint16 x, uint16 y, + void TerrainQuadTreeNode::writeDeltaVertex(bool compress, uint16 x, uint16 y, float delta, float deltaThresh, float** ppDelta) { *(*ppDelta)++ = delta; @@ -923,7 +916,7 @@ namespace Ogre // row / col in main vertex resolution uint16 row = mainIndex / vdr->size; uint16 col = mainIndex % vdr->size; - + // skrits are after main vertices, so skip them uint16 base = vdr->size * vdr->size; @@ -933,7 +926,7 @@ namespace Ogre // 2. column skirts // numSkirtRowsCols cols of resolution vertices each - // No offsets used here, this is an index into the current vertex data, + // No offsets used here, this is an index into the current vertex data, // which is already relative if (isCol) { @@ -946,7 +939,7 @@ namespace Ogre uint16 skirtNum = row / vdr->skirtRowColSkip; return base + vdr->size * skirtNum + col; } - + } //--------------------------------------------------------------------- void TerrainQuadTreeNode::destroyCpuVertexData() @@ -983,13 +976,13 @@ namespace Ogre uint16 vdatasizeOffsetX = (mOffsetX - mNodeWithVertexData->mOffsetX) / resolutionRatio; uint16 vdatasizeOffsetY = (mOffsetY - mNodeWithVertexData->mOffsetY) / resolutionRatio; - destData->indexBuffer = mTerrain->getGpuBufferAllocator()->getSharedIndexBuffer(batchSize, vdr->size, - vertexIncrement, vdatasizeOffsetX, vdatasizeOffsetY, + destData->indexBuffer = mTerrain->getGpuBufferAllocator()->getSharedIndexBuffer(batchSize, vdr->size, + vertexIncrement, vdatasizeOffsetX, vdatasizeOffsetY, vdr->numSkirtRowsCols, vdr->skirtRowColSkip); destData->indexStart = 0; destData->indexCount = destData->indexBuffer->getNumIndexes(); - // shared index buffer is pre-populated + // shared index buffer is pre-populated } //--------------------------------------------------------------------- void TerrainQuadTreeNode::createGpuVertexData() @@ -1005,9 +998,9 @@ namespace Ogre // copy vertex buffers // get new buffers HardwareVertexBufferSharedPtr destPosBuf, destDeltaBuf; - mTerrain->getGpuBufferAllocator()->allocateVertexBuffers(mTerrain, srcData->vertexCount, + mTerrain->getGpuBufferAllocator()->allocateVertexBuffers(mTerrain, srcData->vertexCount, destPosBuf, destDeltaBuf); - + // copy data destPosBuf->copyData(*srcData->vertexBufferBinding->getBuffer(POSITION_BUFFER)); destDeltaBuf->copyData(*srcData->vertexBufferBinding->getBuffer(DELTA_BUFFER)); @@ -1015,29 +1008,24 @@ namespace Ogre // set bindings destData->vertexBufferBinding->setBinding(POSITION_BUFFER, destPosBuf); destData->vertexBufferBinding->setBinding(DELTA_BUFFER, destDeltaBuf); - // Basic vertex info destData->vertexStart = srcData->vertexStart; destData->vertexCount = srcData->vertexCount; // Copy elements - const VertexDeclaration::VertexElementList elems = + const VertexDeclaration::VertexElementList elems = srcData->vertexDeclaration->getElements(); - VertexDeclaration::VertexElementList::const_iterator ei, eiend; - eiend = elems.end(); - for (ei = elems.begin(); ei != eiend; ++ei) - { + for (auto& e : elems) { destData->vertexDeclaration->addElement( - ei->getSource(), - ei->getOffset(), - ei->getType(), - ei->getSemantic(), - ei->getIndex() ); + e.getSource(), + e.getOffset(), + e.getType(), + e.getSemantic(), + e.getIndex() + ); } - // We don't need the CPU copy anymore destroyCpuVertexData(); } - } //--------------------------------------------------------------------- void TerrainQuadTreeNode::updateGpuVertexData() @@ -1058,7 +1046,7 @@ namespace Ogre { // Before we delete, free up the vertex buffers for someone else mTerrain->getGpuBufferAllocator()->freeVertexBuffers( - mVertexDataRecord->gpuVertexData->vertexBufferBinding->getBuffer(POSITION_BUFFER), + mVertexDataRecord->gpuVertexData->vertexBufferBinding->getBuffer(POSITION_BUFFER), mVertexDataRecord->gpuVertexData->vertexBufferBinding->getBuffer(DELTA_BUFFER)); OGRE_DELETE mVertexDataRecord->gpuVertexData; mVertexDataRecord->gpuVertexData = 0; @@ -1079,9 +1067,7 @@ namespace Ogre ll->gpuIndexData = OGRE_NEW IndexData(); populateIndexData(ll->batchSize, ll->gpuIndexData); } - } - } //--------------------------------------------------------------------- void TerrainQuadTreeNode::destroyGpuIndexData() @@ -1103,7 +1089,7 @@ namespace Ogre Vector3 localPos = pos - mLocalCentre; mAABB.merge(localPos); mBoundingRadius = std::max(mBoundingRadius, localPos.length()); - + if (!isLeaf()) { for (int i = 0; i < 4; ++i) @@ -1118,14 +1104,14 @@ namespace Ogre { mAABB.setNull(); mBoundingRadius = 0; - + if (!isLeaf()) { for (int i = 0; i < 4; ++i) mChildren[i]->resetBounds(rect); } - + } } //--------------------------------------------------------------------- @@ -1143,7 +1129,7 @@ namespace Ogre //--------------------------------------------------------------------- bool TerrainQuadTreeNode::pointIntersectsNode(long x, long y) { - return x >= mOffsetX && x < mBoundaryX && + return x >= mOffsetX && x < mBoundaryX && y >= mOffsetY && y < mBoundaryY; } //--------------------------------------------------------------------- @@ -1233,7 +1219,7 @@ namespace Ogre { // distance to tile centre dist = localPos.length(); - // deduct half the radius of the box, assume that on average the + // deduct half the radius of the box, assume that on average the // worst case is best approximated by this dist -= (mBoundingRadius * 0.5f); } @@ -1247,7 +1233,7 @@ namespace Ogre // For each LOD, the distance at which the LOD will transition *downwards* - // is given by + // is given by // distTransition = maxDelta * cFactor; uint lodLvl = 0; mCurrentLod = -1; @@ -1286,9 +1272,9 @@ namespace Ogre if (mTerrain->_getMorphRequired()) { // calculate the transition percentage - // we need a percentage of the total distance for just this LOD, + // we need a percentage of the total distance for just this LOD, // which means taking off the distance for the next higher LOD - // which is either the previous entry in the LOD list, + // which is either the previous entry in the LOD list, // or the largest of any children. In both cases these will // have been calculated before this point, since we process // children first. Distances at lower LODs are guaranteed @@ -1321,12 +1307,12 @@ namespace Ogre // Pass both the transition % and target LOD (GLOBAL current + 1) // this selectively applies the morph just to the - // vertices which would drop out at this LOD, even + // vertices which would drop out at this LOD, even // while using the single shared vertex data setCustomParameter(Terrain::LOD_MORPH_CUSTOM_PARAM, Vector4f(mLodTransition, mCurrentLod + mBaseLod + 1, 0, 0)); } - // since LODs are ordered from highest to lowest detail, + // since LODs are ordered from highest to lowest detail, // we can stop looking now break; } @@ -1335,14 +1321,14 @@ namespace Ogre } } - else + else { // we should not render ourself mCurrentLod = -1; - mSelfOrChildRendered = true; + mSelfOrChildRendered = true; if (childRenderedCount < 4) { - // only *some* children decided to render on their own, but either + // only *some* children decided to render on their own, but either // none or all need to render, so set the others manually to their lowest for (int i = 0; i < 4; ++i) { @@ -1405,7 +1391,7 @@ namespace Ogre } //--------------------------------------------------------------------- Technique* TerrainQuadTreeNode::getTechnique(void) const - { + { return getMaterial()->getBestTechnique(mMaterialLodIndex, this); } //--------------------------------------------------------------------- @@ -1424,9 +1410,9 @@ namespace Ogre // vertex data is generated in terrain space *xform = Matrix4::IDENTITY; xform->setTrans(mTerrain->getPosition()); - + } - else + else { // the vertex data is relative to the node that owns the vertex data *xform = mNodeWithVertexData->_getParentNodeFullTransform();