Coverage Report

Created: 2025-08-25 06:48

/src/ogre/Components/Terrain/src/OgreTerrainLodManager.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
(Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
#include "OgreRoot.h"
29
#include "OgreTerrainQuadTreeNode.h"
30
#include "OgreTerrainLodManager.h"
31
#include "OgreStreamSerialiser.h"
32
#include "OgreLogManager.h"
33
#include "OgreTerrain.h"
34
35
namespace Ogre
36
{
37
    const uint32 TerrainLodManager::TERRAINLODDATA_CHUNK_ID = StreamSerialiser::makeIdentifier("TLDA");
38
    const uint16 TerrainLodManager::TERRAINLODDATA_CHUNK_VERSION = 1;
39
40
    TerrainLodManager::TerrainLodManager(Terrain* t, DataStreamPtr& stream)
41
0
        : mTerrain(t)
42
0
    {
43
0
        init();
44
0
        mDataStream = stream;
45
0
        mStreamOffset = !mDataStream ? 0 : mDataStream->tell();
46
0
    }
47
48
    TerrainLodManager::TerrainLodManager(Terrain* t, const String& filename)
49
0
        : mTerrain(t), mStreamOffset(0)
50
0
    {
51
0
        init();
52
0
        open(filename);
53
0
    }
54
55
    void TerrainLodManager::open(const String& filename)
56
0
    {
57
0
        if(!filename.empty())
58
0
            mDataStream = Root::getSingleton().openFileStream(filename, mTerrain->_getDerivedResourceGroup());
59
0
    }
60
61
    void TerrainLodManager::close()
62
0
    {
63
0
        mDataStream.reset();
64
0
    }
65
66
    bool TerrainLodManager::isOpen() const
67
0
    {
68
0
        return mDataStream.get() != 0;
69
0
    }
70
71
    void TerrainLodManager::init()
72
0
    {
73
0
        mHighestLodPrepared = -1;
74
0
        mHighestLodLoaded = -1;
75
0
        mTargetLodLevel = -1;
76
0
        mIncreaseLodLevelInProgress = false;
77
0
        mLastRequestSynchronous = false;
78
0
        mLodInfoTable = 0;
79
0
    }
80
81
    TerrainLodManager::~TerrainLodManager()
82
0
    {
83
0
        waitForDerivedProcesses();
84
85
0
        if(mLodInfoTable)
86
0
            OGRE_FREE(mLodInfoTable,MEMCATEGORY_GENERAL);
87
0
    }
88
89
    WorkQueue::Response* TerrainLodManager::handleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ)
90
0
    {
91
0
        LoadLodRequest lreq = any_cast<LoadLodRequest>(req->getData());
92
        // read data from file into temporary height & delta buffer
93
0
        try {
94
0
            if(lreq.currentPreparedLod>lreq.requestedLod)
95
0
                readLodData(lreq.currentPreparedLod-1, lreq.requestedLod);
96
0
        } catch (Exception& e) {
97
0
            return OGRE_NEW WorkQueue::Response(req, false, Any(), e.getFullDescription());
98
0
        }
99
100
0
        int lastTreeStart = -1;
101
0
        for( int level=lreq.currentLoadedLod-1; level>=lreq.requestedLod; --level )
102
0
        {
103
0
            LodInfo& lodinfo = getLodInfo(level);
104
            // skip re-assign
105
0
            if(lastTreeStart != (int)lodinfo.treeStart)
106
0
            {
107
0
                mTerrain->getQuadTree()->assignVertexData(lodinfo.treeStart, lodinfo.treeEnd,
108
0
                        lodinfo.resolution, lodinfo.size);
109
0
                lastTreeStart = lodinfo.treeStart;
110
0
            }
111
0
        }
112
0
        return OGRE_NEW WorkQueue::Response(req, true, Any());
113
0
    }
114
115
    void TerrainLodManager::handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
116
0
    {
117
0
        const WorkQueue::Request* req = res->getRequest();
118
        // No response data, just request
119
0
        LoadLodRequest lreq = any_cast<LoadLodRequest>(req->getData());
120
121
0
        mIncreaseLodLevelInProgress = false;
122
123
0
        if (res->succeeded())
124
0
        {
125
            // no others update LOD status
126
0
            if(lreq.currentPreparedLod == mHighestLodPrepared && lreq.currentLoadedLod == mHighestLodLoaded )
127
0
            {
128
0
                if( lreq.requestedLod < mHighestLodPrepared )
129
0
                    mHighestLodPrepared = lreq.requestedLod;
130
131
0
                int lastTreeStart = -1;
132
0
                for( int level = mHighestLodLoaded-1; level>=lreq.requestedLod && level>=mTargetLodLevel; --level )
133
0
                {
134
0
                    LodInfo& lodinfo = getLodInfo(level);
135
                    // skip re-load
136
0
                    if(lastTreeStart != (int)lodinfo.treeStart)
137
0
                    {
138
0
                        mTerrain->getQuadTree()->load(lodinfo.treeStart, lodinfo.treeEnd);
139
0
                        lastTreeStart = lodinfo.treeStart;
140
0
                    }
141
0
                    --mHighestLodLoaded;
142
0
                }
143
0
            }
144
145
            // has streamed in new data, should update terrain
146
0
            if(lreq.currentPreparedLod>lreq.requestedLod)
147
0
            {
148
0
                mTerrain->dirty();
149
0
                mTerrain->updateGeometryWithoutNotifyNeighbours();
150
0
            }
151
152
            // there are new requests
153
0
            if(mHighestLodLoaded != mTargetLodLevel)
154
0
                updateToLodLevel(mTargetLodLevel,mLastRequestSynchronous);
155
0
        }
156
0
        else
157
0
        {
158
0
            LogManager::getSingleton().stream(LML_CRITICAL) << "Failed to prepare and load terrain LOD: " << res->getMessages();
159
0
        }
160
0
    }
161
    void TerrainLodManager::buildLodInfoTable()
162
0
    {
163
0
        uint16 numLodLevels = mTerrain->getNumLodLevels();
164
0
        mLodInfoTable = OGRE_ALLOC_T(LodInfo, numLodLevels, MEMCATEGORY_GENERAL);
165
166
0
        uint16 size = mTerrain->getSize();
167
0
        uint16 depth = mTerrain->mTreeDepth;
168
0
        uint16 prevdepth = depth;
169
0
        uint16 last = 0;
170
0
        uint16 currresolution = size;
171
0
        uint16 bakedresolution = size;
172
0
        uint16 targetSplits = (bakedresolution - 1) / (Terrain::TERRAIN_MAX_BATCH_SIZE - 1);
173
174
0
        int *lodDepth = OGRE_ALLOC_T(int, numLodLevels, MEMCATEGORY_GENERAL);
175
0
        for(int level=0; level<numLodLevels; level++)
176
0
            lodDepth[level] = (level < mTerrain->getNumLodLevelsPerLeaf()) ? depth-1 : numLodLevels-level-1;
177
178
0
        while(depth-- && targetSplits)
179
0
        {
180
0
            uint splits = 1 << depth;
181
0
            if (splits == targetSplits)
182
0
            {
183
0
                for(uint level=0; level<numLodLevels; level++)
184
0
                {
185
0
                    if (lodDepth[level] >= depth && lodDepth[level] < prevdepth)
186
0
                    {
187
0
                        mLodInfoTable[level].treeStart = depth;
188
0
                        mLodInfoTable[level].treeEnd = prevdepth;
189
0
                        mLodInfoTable[level].isLast = level == last+prevdepth-depth-static_cast<uint>(1);
190
0
                        mLodInfoTable[level].resolution = bakedresolution;
191
0
                        mLodInfoTable[level].size = ((bakedresolution-1) / splits) + 1;
192
                        // this lod info has been filled
193
0
                        lodDepth[level] = -1;
194
0
                    }
195
0
                }
196
                // next set to look for
197
0
                bakedresolution =  ((currresolution - 1) >> 1) + 1;
198
0
                targetSplits = (bakedresolution - 1) / (Terrain::TERRAIN_MAX_BATCH_SIZE - 1);
199
0
                prevdepth = depth;
200
0
            }
201
0
            currresolution = ((currresolution - 1) >> 1) + 1;
202
0
            last++;
203
0
        }
204
0
        for(int level=0; level<numLodLevels; level++)
205
0
        {
206
0
            if (lodDepth[level]>=0 && lodDepth[level]<=prevdepth)
207
0
            {
208
0
                mLodInfoTable[level].treeStart = 0;
209
0
                mLodInfoTable[level].treeEnd = 1;
210
0
                mLodInfoTable[level].isLast = level == last+prevdepth-depth-1;
211
0
                mLodInfoTable[level].resolution = bakedresolution;
212
0
                mLodInfoTable[level].size = bakedresolution;
213
0
            }
214
0
        }
215
0
        OGRE_FREE(lodDepth,MEMCATEGORY_GENERAL);
216
0
    }
217
218
    // data are reorganized from lowest lod level(mNumLodLevels-1) to highest(0)
219
    void TerrainLodManager::separateData(float* data, uint16 size, uint16 numLodLevels, LodsData& lods)
220
0
    {
221
0
        lods.resize(numLodLevels);
222
0
        for (int level = numLodLevels - 1; level >= 0; level--)
223
0
        {
224
0
            unsigned int inc = 1 << level;
225
0
            unsigned int prev = 1 << (level + 1);
226
227
0
            for (uint16 y = 0; y < size; y += inc)
228
0
            {
229
0
                for (uint16 x = 0; x < size-1; x += inc)
230
0
                    if ((level == numLodLevels - 1) || ((x % prev != 0) || (y % prev != 0)))
231
0
                        lods[level].push_back( data[y*size + x] );
232
0
                if ((level == numLodLevels -1) || (y % prev) != 0)
233
0
                    lods[level].push_back( data[y*size + size-1] );
234
0
                if (y+inc > size)
235
0
                    break;
236
0
            }
237
0
        }
238
0
    }
239
    //---------------------------------------------------------------------
240
    void TerrainLodManager::updateToLodLevel(int lodLevel, bool synchronous /* = false */)
241
0
    {
242
        //init
243
0
        if(mHighestLodPrepared==-1)
244
0
            mHighestLodPrepared = mTerrain->getNumLodLevels();
245
0
        if(mHighestLodLoaded==-1)
246
0
            mHighestLodLoaded = mTerrain->getNumLodLevels();
247
248
0
        lodLevel = mTerrain->getPositiveLodLevel(lodLevel);
249
250
0
        mTargetLodLevel = lodLevel;
251
0
        mLastRequestSynchronous = synchronous;
252
253
        // need loading
254
0
        if(mTargetLodLevel<mHighestLodLoaded)
255
0
        {
256
            // no task is running
257
0
            if (!mIncreaseLodLevelInProgress)
258
0
            {
259
0
                mIncreaseLodLevelInProgress = true;
260
0
                LoadLodRequest req(this,mHighestLodPrepared,mHighestLodLoaded,mTargetLodLevel);
261
262
0
                auto r = new WorkQueue::Request(0, 0, req, 0, 0);
263
0
                if(synchronous)
264
0
                {
265
0
                    auto res = handleRequest(r, NULL);
266
0
                    handleResponse(res, NULL);
267
0
                    delete res;
268
0
                }
269
0
                else
270
0
                {
271
0
                    Root::getSingleton().getWorkQueue()->addTask([this, r]() {
272
0
                        auto res = handleRequest(r, NULL);
273
0
                        Root::getSingleton().getWorkQueue()->addMainThreadTask([this, res]() {
274
0
                            handleResponse(res, NULL);
275
0
                            delete res;
276
0
                        });
277
0
                    });
278
0
                }
279
0
            }
280
0
            else if(synchronous)
281
0
                waitForDerivedProcesses();
282
0
        }
283
        // need unloading
284
0
        else if(mTargetLodLevel>mHighestLodLoaded)
285
0
        {
286
0
            for( int level=mHighestLodLoaded; level<mTargetLodLevel; level++ )
287
0
            {
288
0
                LodInfo& lod = getLodInfo(level);
289
0
                if(lod.isLast)
290
0
                {
291
0
                    mTerrain->getQuadTree()->unload(lod.treeStart, lod.treeEnd);
292
0
                    mHighestLodLoaded = level+1;
293
0
                }
294
0
            }
295
0
        }
296
0
    }
297
298
    // save each LOD level separately compressed so seek is possible
299
    void TerrainLodManager::saveLodData(StreamSerialiser& stream, Terrain* terrain)
300
0
    {
301
0
        uint16 numLodLevels = terrain->getNumLodLevels();
302
303
0
        LodsData lods;
304
0
        separateData(terrain->mHeightData, terrain->getSize(), numLodLevels, lods);
305
0
        separateData(terrain->mDeltaData, terrain->getSize(), numLodLevels, lods);
306
307
0
        for (int level = numLodLevels - 1; level >=0; level--)
308
0
        {
309
0
            stream.writeChunkBegin(TERRAINLODDATA_CHUNK_ID, TERRAINLODDATA_CHUNK_VERSION);
310
0
            stream.startDeflate();
311
0
            stream.write(&(lods[level][0]), lods[level].size());
312
0
            stream.stopDeflate();
313
0
            stream.writeChunkEnd(TERRAINLODDATA_CHUNK_ID);
314
0
        }
315
0
    }
316
317
    void TerrainLodManager::readLodData(uint16 lowerLodBound, uint16 higherLodBound)
318
0
    {
319
0
        if(!mDataStream) // No file to read from
320
0
            return;
321
322
0
        uint16 numLodLevels = mTerrain->getNumLodLevels();
323
0
        mDataStream->seek(mStreamOffset);
324
0
        StreamSerialiser stream(mDataStream);
325
326
0
        const StreamSerialiser::Chunk *mainChunk = stream.readChunkBegin(Terrain::TERRAIN_CHUNK_ID, Terrain::TERRAIN_CHUNK_VERSION);
327
328
0
        if(mainChunk->version > 1)
329
0
        {
330
            // skip the general information
331
0
            stream.readChunkBegin(Terrain::TERRAINGENERALINFO_CHUNK_ID, Terrain::TERRAINGENERALINFO_CHUNK_VERSION);
332
0
            stream.readChunkEnd(Terrain::TERRAINGENERALINFO_CHUNK_ID);
333
334
            // skip the previous lod data
335
0
            for(int skip=numLodLevels-1-lowerLodBound; skip>0; skip--)
336
0
            {
337
0
                stream.readChunkBegin(TERRAINLODDATA_CHUNK_ID, TERRAINLODDATA_CHUNK_VERSION);
338
0
                stream.readChunkEnd(TERRAINLODDATA_CHUNK_ID);
339
0
            }
340
341
            // uncompress
342
0
            uint maxSize = 2 * mTerrain->getGeoDataSizeAtLod(higherLodBound);
343
0
            float *lodData = OGRE_ALLOC_T(float, maxSize, MEMCATEGORY_GENERAL);
344
345
0
            for(int level=lowerLodBound; level>=higherLodBound; level-- )
346
0
            {
347
                // both height data and delta data
348
0
                uint dataSize = 2 * mTerrain->getGeoDataSizeAtLod(level);
349
350
                // reach and read the target lod data
351
0
                const StreamSerialiser::Chunk *c = stream.readChunkBegin(TERRAINLODDATA_CHUNK_ID,
352
0
                        TERRAINLODDATA_CHUNK_VERSION);
353
0
                stream.startDeflate(c->length);
354
0
                stream.read(lodData, dataSize);
355
0
                stream.stopDeflate();
356
0
                stream.readChunkEnd(TERRAINLODDATA_CHUNK_ID);
357
358
0
                fillBufferAtLod(level, lodData, dataSize);
359
0
            }
360
0
            stream.readChunkEnd(Terrain::TERRAIN_CHUNK_ID);
361
362
0
            OGRE_FREE(lodData, MEMCATEGORY_GENERAL);
363
0
        }
364
0
    }
365
    void TerrainLodManager::fillBufferAtLod(uint lodLevel, const float* data, uint dataSize )
366
0
    {
367
0
        unsigned int inc = 1 << lodLevel;
368
0
        unsigned int prev = 1 << (lodLevel + 1);
369
0
        uint16 numLodLevels = mTerrain->getNumLodLevels();
370
0
        uint16 size = mTerrain->getSize();
371
372
0
        const float* heightDataPtr = data;
373
0
        const float* deltaDataPtr = data+dataSize/2;
374
375
0
        for (uint16 y = 0; y < size; y += inc)
376
0
        {
377
0
            for (uint16 x = 0; x < size-1; x += inc)
378
0
                if ((lodLevel == numLodLevels - static_cast<uint>(1)) || (x % prev) || (y % prev))
379
0
                {
380
0
                    mTerrain->mHeightData[y*size + x] = *(heightDataPtr++);
381
0
                    mTerrain->mDeltaData[y*size + x] = *(deltaDataPtr++);
382
0
                }
383
0
            if ((lodLevel == numLodLevels - static_cast<uint>(1)) || (y % prev))
384
0
            {
385
0
                mTerrain->mHeightData[y*size + size-1] = *(heightDataPtr++);
386
0
                mTerrain->mDeltaData[y*size + size-1] = *(deltaDataPtr++);
387
0
            }
388
0
            if (y+inc > size)
389
0
                break;
390
0
        }
391
0
    }
392
    void TerrainLodManager::waitForDerivedProcesses()
393
0
    {
394
0
        while (mIncreaseLodLevelInProgress)
395
0
        {
396
            // we need to wait for this to finish
397
0
            OGRE_THREAD_SLEEP(50);
398
0
            Root::getSingleton().getWorkQueue()->processMainThreadTasks();
399
0
        }
400
0
    }
401
}