Cogs.Core
ClipmapManager.cpp
1#include "ClipmapManager.h"
2
3#include "ClipmapUpdate.h"
4#include "ClipmapParameterCalculation.h"
5#include "ClipmapDebug.h"
6#include "Effects.h"
7#include "ExtentCalculation.h"
8#include "Raster/RasterSource.h"
9
10#include "Rendering/IGraphicsDevice.h"
11#include "Rendering/IContext.h"
12#include "Rendering/CommandGroupAnnotation.h"
13
14#include "Foundation/Logging/Logger.h"
15
16#include <cmath>
17#include <thread>
18
19namespace
20{
21 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ClipmapManager");
22
23 const bool enableMipMapping = false;
24}
25
26Cogs::ClipmapManager::ClipmapManager()
27{
28 std::memset(&metrics, 0, sizeof(TerrainMetrics));
29}
30
31Cogs::ClipmapManager::~ClipmapManager()
32{
33 detachResponseHandlers();
34
35 clipmapRenderer.cleanup(device);
36}
37
38void Cogs::ClipmapManager::setRasterSources(const RasterSourcePtr & terrain, const std::vector<RasterSourcePtr> & imagery)
39{
40 LOG_DEBUG(logger, "Setting raster sources...");
41
42 detachResponseHandlers();
43
44 terrainSource.setup(terrain);
45
46 imagerySources.resize(imagery.size());
47 imageryEnabled.resize(imagerySources.size(), true);
48
49 for (size_t i = 0; i < imagerySources.size(); ++i) {
50 imagerySources[i].setup(imagery[i]);
51 }
52
53 sourcesNeedUpdate = true;
54 effectsNeedUpdate = true;
55}
56
57void Cogs::ClipmapManager::setClipmapParameters(const ClipmapParameters * parameters)
58{
59 LOG_DEBUG(logger, "Setting clipmap parameters...");
60
61 // We need to rebuild effects for different normal computation policies.
62 effectsNeedUpdate |= parameters->precomputeNormals != this->parameters.precomputeNormals;
63
64 this->parameters = *parameters;
65
66 sourcesNeedUpdate = true;
67 geometryNeedsUpdate = true;
68}
69
70void Cogs::ClipmapManager::setCustomParameters(const unsigned char * data, int count)
71{
72 clipmapRenderer.setCustomParameters(data, count);
73}
74
75void Cogs::ClipmapManager::initialize(IGraphicsDevice * device)
76{
77 this->device = device;
78
79 Terrain::EffectLoader::setExtension(".hlsl"); // Seems to only have .hlsl shaders now
80
81 //First time we render depth for this camera, setup the state for it:
82 clipmapRenderer.raypickDepthData.emplace_back();
83 clipmapRenderer.raypickDepthData.back().initialize(device, 512, 512);
84
85 // Init subsystems
86 clipmapRenderer.initializeTextures(device->getTextures());
87 clipmapRenderer.initializeRasterizerStates(device->getRenderTargets());
88 clipmapRenderer.initializeRenderTargets(device->getRenderTargets());
89
90 oceanRenderer.initialize(&clipmapRenderer, device);
91 oceanRenderer.initializeTextures(device->getTextures(), device->getRenderTargets());
92
93 depthQuery.initialize(device, &clipmapRenderer);
94
95 updater.initialize(device, &normalUpdater);
96 normalUpdater.initialize(device);
97
98 initialized = true;
99}
100
101void Cogs::ClipmapManager::initializeGeometry()
102{
103 const size_t clipmapPosts = this->parameters.clipmapPosts;
104 const size_t clipmapSegments = clipmapPosts - 1;
105 const size_t fillPatchPosts = (clipmapPosts + 1) / 4; // M
106 const size_t fillPatchSegments = fillPatchPosts - 1;
107
108 if (clipmapGeometry.initialized) {
109 LOG_DEBUG(logger, "Releasing geometry...");
110
111 Cogs::releaseGeometry(clipmapGeometry, device->getBuffers());
112 }
113
114 LOG_DEBUG(logger, "Initializing geometry...");
115
116 Cogs::initializeGeometry(clipmapGeometry, device->getBuffers(), fillPatchPosts, fillPatchSegments, clipmapSegments, clipmapPosts);
117
118 clipmapState.fillPatchPosts = fillPatchPosts;
119 clipmapState.fillPatchSegments = fillPatchSegments;
120
121 clipmapState.clipmapPosts = clipmapPosts;
122 clipmapState.clipmapSegments = clipmapSegments;
123
124 clipmapState.oneOverBlendedRegionSize = (float)(10.0 / clipmapPosts);
125 clipmapState.unblendedRegionSize = (float)(clipmapSegments / 2 - clipmapPosts / 10.0 - 1);
126}
127
128void Cogs::ClipmapManager::initializeLevels()
129{
130 CommandGroupAnnotation preGroup(device->getImmediateContext(), "Terrain::initializeLevels");
131
132 const size_t numTerrainLevels = terrainSource->getLevels().size();
133 const size_t numLevels = numTerrainLevels;
134
135 cleanupLevels();
136
137 if (!numLevels) {
138 LOG_ERROR(logger, "No valid terrain levels found in raster source.");
139
140 return;
141 }
142
143 this->imagery.resize(imagerySources.size());
144
145 for (size_t i = 0; i < imagerySources.size(); ++i) {
146
147 this->imagery[i].resize(numLevels);
148 }
149
150 this->terrainLevels.resize(numLevels);
151
152 if (parameters.precomputeNormals) {
153 this->normalLevels.resize(numLevels);
154 }
155
156 const size_t clipmapPosts = this->parameters.clipmapPosts;
157
158 for (size_t i = 0; i < numLevels; ++i) {
159 ClipmapLevel & terrainLevel = terrainLevels[i];
160
161 RasterLevel & terrainRasterLevel = terrainSource->getLevels()[i < numTerrainLevels ? i : numTerrainLevels - 1];
162
163 terrainLevel.index = i;
164
165 terrainLevel.rasterLevel = &terrainRasterLevel;
166 terrainLevel.renderTexture = createTexture(clipmapPosts, clipmapPosts, TextureFormat::R32_FLOAT);
167 terrainLevel.coarserLevel = i == 0 ? &terrainLevel : &terrainLevels[i - 1];
168 terrainLevel.finerLevel = i == (numLevels - 1) ? nullptr : &terrainLevels[i + 1];
169
170 if (parameters.precomputeNormals) {
171 ClipmapLevel & normalLevel = normalLevels[i];
172
173 normalLevel.renderTexture = createTexture(clipmapPosts, clipmapPosts, TextureFormat::R16G16B16A16_FLOAT);
174 normalLevel.coarserLevel = i == 0 ? &normalLevel : &normalLevels[i - 1];
175 normalLevel.finerLevel = i == (numLevels - 1) ? nullptr : &normalLevels[i + 1];
176
177 terrainLevel.normalLevel = &normalLevel;
178 }
179
180 for (size_t j = 0; j < imagerySources.size(); ++j) {
181 std::vector<ClipmapLevel> & imageryLevels = imagery[j];
182
183 ClipmapLevel & imageryLevel = imageryLevels[i];
184
185 imageryLevel.index = i;
186
187 imageryLevel.coarserLevel = i == 0 ? &imageryLevel : &imageryLevels[i - 1];
188 imageryLevel.finerLevel = i == (numLevels - 1) ? nullptr : &imageryLevels[i + 1];
189
190 // Find the first imagery level that meets our resolution needs.
191 double longitudeResRequired = terrainRasterLevel.getPostDeltaLongitude() / parameters.imageryResolutionFactor[j];
192 double latitudeResRequired = terrainRasterLevel.getPostDeltaLatitude() / parameters.imageryResolutionFactor[j];
193
194 RasterLevel * imageryRasterLevel = nullptr;
195
196 auto & levels = imagerySources[j]->getLevels();
197
198 for (size_t k = 0; k < levels.size(); ++k) {
199 imageryRasterLevel = &levels[k];
200
201 if (imageryRasterLevel->getPostDeltaLongitude() <= longitudeResRequired && imageryRasterLevel->getPostDeltaLatitude() <= latitudeResRequired) {
202 if (k < levels.size() - parameters.imageryIndexOffset[j] && i > 0) {
203 imageryRasterLevel = &levels[k + parameters.imageryIndexOffset[j]];
204 }
205 break;
206 }
207 }
208
209 const int imageryWidth = (int)std::ceil(clipmapPosts * terrainRasterLevel.getPostDeltaLongitude() / imageryRasterLevel->getPostDeltaLongitude());
210 const int imageryHeight = (int)std::ceil(clipmapPosts * terrainRasterLevel.getPostDeltaLatitude() / imageryRasterLevel->getPostDeltaLatitude());
211
212 imageryLevel.rasterLevel = imageryRasterLevel;
213 imageryLevel.renderTexture = createTexture(imageryWidth, imageryHeight, imagerySources[j]->format, enableMipMapping);
214 }
215 }
216
217 initializeBackgroundLevels();
218}
219
220void Cogs::ClipmapManager::initializeBackgroundLevels()
221{
222 auto findSize = [](const int posts) -> int {
223 if (posts <= 2048) return posts / 2;
224 if (posts <= 4096) return posts / 2;
225 else return 2048;
226 };
227
228 RasterLevel & terrainRasterLevel = terrainSource->getLevels()[0];
229
230 backgroundLevel = ClipmapLevel();
231
232 backgroundLevel.index = 0;
233 backgroundLevel.background = true;
234 backgroundLevel.rasterLevel = &terrainRasterLevel;
235
236 glm::vec4 clearColor{ 0 };
237 const auto noData = terrainRasterLevel.getNoData();
238 if (!std::isnan(noData)) {
239 clearColor = glm::vec4{ noData };
240 }
241
242 backgroundLevel.renderTexture = createTexture(findSize(terrainRasterLevel.getLongitudePosts()), findSize(terrainRasterLevel.getLatitudePosts()), TextureFormat::R32_FLOAT, false, clearColor);
243 backgroundLevel.coarserLevel = &backgroundLevel;
244 backgroundLevel.finerLevel = &terrainLevels[0];
245 terrainLevels[0].coarserLevel = &backgroundLevel;
246
247 if (parameters.precomputeNormals) {
248 backgroundNormalLevel = ClipmapLevel();
249
250 backgroundNormalLevel.index = 0;
251 backgroundNormalLevel.background = true;
252 backgroundNormalLevel.renderTexture = createTexture(backgroundLevel.renderTexture.width, backgroundLevel.renderTexture.height, TextureFormat::R16G16B16A16_FLOAT);
253 backgroundNormalLevel.coarserLevel = &backgroundNormalLevel;
254 backgroundNormalLevel.finerLevel = &normalLevels[0];
255
256 backgroundLevel.normalLevel = &backgroundNormalLevel;
257 }
258
259 backgroundImagery.clear();
260 backgroundImagery.resize(imagery.size());
261
262 for (size_t i = 0; i < imagerySources.size(); ++i) {
263 std::vector<ClipmapLevel> & imageryLevels = imagery[i];
264
265 backgroundImagery[i].index = 0;
266 backgroundImagery[i].background = true;
267
268 backgroundImagery[i].coarserLevel = &backgroundImagery[i];
269 backgroundImagery[i].finerLevel = &imageryLevels[0];
270 imageryLevels[0].coarserLevel = &backgroundImagery[i];
271
272 const ClipmapLevel & imageryLevelZero = imagery[i][0];
273 const auto rasterLevel = imageryLevelZero.rasterLevel;
274
275 const int imageryWidth = findSize(rasterLevel->getLongitudePosts());
276 const int imageryHeight = findSize(rasterLevel->getLatitudePosts());
277
278 backgroundImagery[i].rasterLevel = imageryLevelZero.rasterLevel;
279 backgroundImagery[i].renderTexture = createTexture(imageryWidth * 2, imageryHeight * 2, imageryLevelZero.rasterLevel->getSource()->format, enableMipMapping);
280 }
281}
282
283void Cogs::ClipmapManager::cleanupLevels()
284{
285 auto clearLevels = [&](std::vector<ClipmapLevel> & levels) {
286 for (auto & level : levels) {
287 destroyTexture(level.renderTexture);
288 }
289
290 levels.clear();
291 };
292
293 for (auto & imageryLayer : imagery) {
294 clearLevels(imageryLayer);
295 }
296
297 clearLevels(terrainLevels);
298 clearLevels(normalLevels);
299
300 // Background needs update on first render.
301 backgroundNeedsUpdate = true;
302
303 if (HandleIsValid(backgroundLevel.renderTexture.handle)) {
304 destroyTexture(backgroundLevel.renderTexture);
305
306 if (HandleIsValid(backgroundNormalLevel.renderTexture.handle)) {
307 destroyTexture(backgroundNormalLevel.renderTexture);
308 }
309
310 for (auto & backgroundImageryLayer : backgroundImagery) {
311 destroyTexture(backgroundImageryLayer.renderTexture);
312 }
313 }
314}
315
316void Cogs::ClipmapManager::detachResponseHandlers()
317{
318 if (!terrainSource.isReady()) return;
319
320 terrainSource.teardown();
321
322 for (auto & imagerySource : imagerySources) {
323 imagerySource.teardown();
324 }
325}
326
327void Cogs::ClipmapManager::update()
328{
329 CommandGroupAnnotation preGroup(device->getImmediateContext(), "Terrain::update");
330
331 needsUpdate = false;
332
333 if (geometryNeedsUpdate) {
334 initializeGeometry();
335
336 geometryNeedsUpdate = false;
337 }
338
339 if (sourcesNeedUpdate && terrainSource.isReady()) {
340 updateSharedDevice(device);
341
342 initializeLevels();
343
344 preloadLevelZeroTiles();
345
346 sourcesNeedUpdate = false;
347 }
348
349 if (oceanActive) {
350 oceanRenderer.update();
351 }
352}
353
354size_t Cogs::ClipmapManager::preRender(RenderContext & renderContext)
355{
356 if (renderContext.oceanEnabled) {
357 oceanActive = true;
358 }
359
360 CommandGroupAnnotation preGroup(renderContext.context, "Terrain::preRender");
361
362 if (!terrainLevels.size()) {
363 LOG_ERROR(logger, "No terrain levels set. Pre render not possible.");
364
365 return 0;
366 }
367
368 if (!updateOptions.enableUpdate) {
369 renderContext.cullVolume.frustum = previousContext.cullVolume.frustum;
370 renderContext.cullOrigin = previousContext.cullOrigin;
371 }
372
373 updater.setupRenderingState(renderContext);
374
375 const size_t maxLevel = calculateCenterAndMaxLevel(renderContext);
376
377 ClipmapLevel & firstLevel = terrainLevels[terrainLevels.size() - 1];
378
379 calculateNextExtent(clipmapState.clipmapCenter.x, clipmapState.clipmapCenter.y, clipmapState, firstLevel);
380
381 for (size_t i = 0; i < imagerySources.size(); ++i) {
382 ClipmapLevel & firstImagery = imagery[i][terrainLevels.size() - 1];
383
384 if (imageryEnabled[i]) {
385 updateImageryExtent(firstImagery, firstLevel);
386 }
387 }
388
389 updateOriginInTextures(firstLevel);
390
391 for (size_t i = 0; i < imagerySources.size(); ++i) {
392 if (imageryEnabled[i]) {
393 updateOriginInTextures(imagery[i][terrainLevels.size() - 1]);
394 }
395 }
396
397 for (int i = static_cast<int>(terrainLevels.size() - 2); i >= 0; --i) {
398 ClipmapLevel & terrainLevel = terrainLevels[i];
399
400 calculateLevelNextExtent(terrainLevel, *terrainLevel.finerLevel, clipmapState);
401 updateOriginInTextures(terrainLevel);
402
403 for (size_t j = 0; j < imagerySources.size(); ++j) {
404 ClipmapLevel & imageryLevel = imagery[j][i];
405
406 updateImageryExtent(imageryLevel, terrainLevel);
407 updateOriginInTextures(imageryLevel);
408 }
409 }
410
411 if (updater.applyNewData(renderContext, worldOptions, terrainSource, terrainLevels, maxLevel) != 0) {
412 touch();
413 }
414
415 for (size_t i = 0; i < imagerySources.size(); ++i) {
416 if (imageryEnabled[i]) {
417 if (updater.applyNewData(renderContext, worldOptions, imagerySources[i], imagery[i], maxLevel) != 0) {
418 touch();
419 }
420 }
421 }
422
423 requestTileLoads(renderContext, maxLevel);
424
425 updateClipmapLevels(renderContext, worldOptions, maxLevel);
426
427 updateBackgroundLevel(renderContext, worldOptions);
428
429 // Restore the context to its original state
430 renderContext.context->setRenderTarget(renderContext.renderTarget, renderContext.depthStencilTarget);
431 renderContext.context->setViewport(renderContext.viewportX, renderContext.viewportY, renderContext.width, renderContext.height);
432 renderContext.context->setDepthStencilState(DepthStencilStateHandle::NoHandle);
433
434 if (oceanActive) {
435 oceanRenderer.preRender(renderContext);
436 }
437
438 // Restore the context to its original state
439 renderContext.context->setRenderTarget(renderContext.renderTarget, renderContext.depthStencilTarget);
440 renderContext.context->setViewport(renderContext.viewportX, renderContext.viewportY, renderContext.width, renderContext.height);
441 renderContext.context->setDepthStencilState(DepthStencilStateHandle::NoHandle);
442
443 updateOptions.invalidateNormals = false;
444
445 if(renderContext.reverseDepth != clipmapRenderer.reverseDepth){
446 if (renderContext.reverseDepth) {
447 clipmapRenderer.reverseDepth = true;
448 clipmapRenderer.initializeRasterizerStates(device->getRenderTargets());
449 }
450 else {
451 clipmapRenderer.reverseDepth = false;
452 clipmapRenderer.initializeRasterizerStates(device->getRenderTargets());
453 }
454 }
455
456 return maxLevel;
457}
458
459void Cogs::ClipmapManager::renderDepth(RenderContext & renderContext, size_t maxLevel)
460{
461 if (!updateOptions.enableUpdate) {
462 renderContext.cullVolume.frustum = previousContext.cullVolume.frustum;
463 renderContext.cullOrigin = previousContext.cullOrigin;
464 }
465
466 auto & permutationRendererData = clipmapRenderer.getPermutationDependentClipmapRendererData(renderContext.permutation);
467 auto & permutationRenderContextData = renderContext.getPermutationDependentRenderContextData(renderContext.permutation);
468 if (HandleIsValid(permutationRenderContextData.clipmapEffect) && permutationRenderContextData.clipmapEffect != permutationRendererData.clipmapEffect) {
469 permutationRendererData.clipmapEffect = permutationRenderContextData.clipmapEffect;
470 permutationRendererData.clipmapDepthEffect = permutationRenderContextData.clipmapDepthEffect;
471
472 clipmapRenderer.initializeEffects(device->getBuffers(), device->getEffects(), imagerySources.size(), renderContext.permutation);
473
474 effectsNeedUpdate = false;
475 }
476
477 if (!terrainLevels.size()) {
478 LOG_ERROR(logger, "No terrain levels set. Render not possible.");
479
480 return;
481 }
482
483 if (renderContext.renderPass == TerrainRenderPass::Reflection) maxLevel = maxLevel > 2 ? maxLevel - 2 : maxLevel;
484
485 if (renderOptions.enableMipMapping) {
486 updateMipmaps(maxLevel, renderContext.device);
487 }
488
489 GlobalParameters globalParameters;
490
491 calculateGlobalParameters(terrainLevels[0], renderContext, worldOptions, clipmapState, globalParameters);
492
493 std::vector<LevelParameters> parameters(maxLevel + 1);
494 std::vector<ImageryParameters> imageryParameters(maxLevel + 1);
495 LevelParameters backgroundParameters;
496 ImageryParameters backgroundImageryParameters;
497
498 // Camera center in map coordinates.
499 auto & worldScale = worldOptions.worldScale;
500 Vector3d scaledCenter = Vector3d(renderContext.center[0] * (1.0 / worldScale[0]), renderContext.center[1] * (1.0 / worldScale[1]), renderContext.center[2]);
501
502 for (int i = static_cast<int>(maxLevel); i >= 0; --i) {
503 ClipmapLevel & terrainLevel = terrainLevels[i];
504
505 parameters[i].levelIndex = i;
506
507 std::vector<const ClipmapLevel *> imageryLevels(imagerySources.size());
508
509 for (size_t j = 0; j < imagerySources.size(); ++j) {
510 imageryLevels[j] = &imagery[j][i];
511 }
512
513 calculateLevelParameters(
514 terrainLevel,
515 imageryLevels,
516 parameters[i],
517 imageryParameters[i],
518 scaledCenter,
519 worldOptions.enableNoData);
520
521 parameters[i].useBlendRegions = renderOptions.enableBlending;
522 parameters[i].viewPosInClippedLevel = calculateViewPosInClippedLevel(clipmapState, terrainLevel);
523 parameters[i].useClipping = 0;
524 }
525
526 std::vector<const ClipmapLevel *> imageryLevels(imagerySources.size());
527
528 for (size_t j = 0; j < imagerySources.size(); ++j) {
529 imageryLevels[j] = &backgroundImagery[j];
530 }
531
532 calculateLevelParameters(
533 backgroundLevel,
534 imageryLevels,
535 backgroundParameters,
536 backgroundImageryParameters,
537 scaledCenter,
538 worldOptions.enableNoData);
539
540 backgroundParameters.useBlendRegions = 0;
541 backgroundParameters.viewPosInClippedLevel = calculateViewPosInClippedLevel(clipmapState, backgroundLevel);
542 backgroundParameters.useClipping = 1;
543 backgroundParameters.levelIndex = 2;
544
545 clipmapRenderer.initLevels();
546
547 bool rendered = false;
548
549 for (int i = static_cast<int>(maxLevel); i >= 0; --i) {
550 rendered = clipmapRenderer.createRenderLevel(
551 clipmapGeometry,
552 clipmapState,
553 terrainLevels[i],
554 imagery,
555 normalLevels.size() ? &normalLevels[i] : nullptr,
556 globalParameters,
557 parameters[i],
558 imageryParameters[i],
559 !rendered);
560 }
561
562 std::vector<std::vector<ClipmapLevel>> backgroundImageryReferences;
563
564 for (size_t i = 0; i < imagery.size(); ++i) {
565 std::vector<ClipmapLevel> b;
566 b.push_back(backgroundImagery[i]);
567 backgroundImageryReferences.push_back(b);
568 }
569
570 clipmapRenderer.createBackgroundRenderLevel(
571 clipmapGeometry,
572 clipmapState,
573 backgroundLevel,
574 backgroundImageryReferences,
575 this->parameters.precomputeNormals ? &backgroundNormalLevel : nullptr,
576 globalParameters,
577 backgroundParameters,
578 backgroundImageryParameters);
579
580 CommandGroupAnnotation renderGroup(renderContext.context, "Terrain::renderDepth");
581
582 // Check if we are rendering the depth pass of a "raypick camera":
583 auto pickIndex = renderContext.rayPickIndex;
584 if (pickIndex != ClipmapRenderer::NoPickIndex)
585 {
586 // Check if we have already created the state for rendering this camera
587 while(static_cast<int>(clipmapRenderer.raypickDepthData.size()) <= pickIndex) {
588 // First time we render depth for this camera, setup the state for it:
589 auto& depthData = clipmapRenderer.raypickDepthData.emplace_back();
590 depthData.initialize(renderContext.device, renderContext.width, renderContext.height);
591 }
592
593 {
594 CommandGroupAnnotation depthGroup(renderContext.context, "RayPickDepth");
595
596 clipmapRenderer.setupDepthPass(renderContext, pickIndex);
597 clipmapRenderer.updateGlobalParameters(renderContext, renderOptions, clipmapState, globalParameters);
598
599 clipmapRenderer.render(renderContext, renderOptions, worldOptions);
600 }
601 } else if (!previousContext.offsetEnabled && renderContext.renderPass == TerrainRenderPass::Main) {
602 {
603 CommandGroupAnnotation depthGroup(renderContext.context, "DepthOnly");
604
605 clipmapRenderer.setupDepthPass(renderContext, 0);
606 clipmapRenderer.updateGlobalParameters(renderContext, renderOptions, clipmapState, globalParameters);
607
608 clipmapRenderer.render(renderContext, renderOptions, worldOptions);
609 }
610
611 {
612 CommandGroupAnnotation depthQueryGroup(renderContext.context, "DepthQuery");
613 clipmapRenderer.setupDepthQueryPass(renderContext);
614 clipmapRenderer.updateGlobalParameters(renderContext, renderOptions, clipmapState, globalParameters);
615
616 clipmapRenderer.render(renderContext, renderOptions, worldOptions);
617 }
618 }
619
620 // Restore original rendering setup.
621 auto context = renderContext.context;
622 context->setRenderTarget(renderContext.renderTarget, renderContext.depthStencilTarget);
623 context->setViewport(renderContext.viewportX, renderContext.viewportY, renderContext.width, renderContext.height);
624}
625
626void Cogs::ClipmapManager::render(RenderContext & renderContext, size_t maxLevel)
627{
628 if (!updateOptions.enableUpdate) {
629 renderContext.cullVolume.frustum = previousContext.cullVolume.frustum;
630 renderContext.cullOrigin = previousContext.cullOrigin;
631 }
632
633 if (!terrainLevels.size()) {
634 LOG_ERROR(logger, "No terrain levels set. Render not possible.");
635
636 return;
637 }
638
639 if (renderContext.renderPass == TerrainRenderPass::Reflection) maxLevel = maxLevel > 2 ? maxLevel - 2 : maxLevel;
640
641 if (renderOptions.enableMipMapping) {
642 updateMipmaps(maxLevel, renderContext.device);
643 }
644
645 GlobalParameters globalParameters;
646
647 calculateGlobalParameters(terrainLevels[0], renderContext, worldOptions, clipmapState, globalParameters);
648
649 std::vector<LevelParameters> parameters(maxLevel + 1);
650 std::vector<ImageryParameters> imageryParameters(maxLevel + 1);
651 LevelParameters backgroundParameters;
652 ImageryParameters backgroundImageryParameters;
653
654 // Camera center in map coordinates.
655 auto & worldScale = worldOptions.worldScale;
656 Vector3d scaledCenter = Vector3d(renderContext.center[0] * (1.0 / worldScale[0]), renderContext.center[1] * (1.0 / worldScale[1]), renderContext.center[2]);
657
658 for (int i = static_cast<int>(maxLevel); i >= 0; --i) {
659 ClipmapLevel & terrainLevel = terrainLevels[i];
660
661 parameters[i].levelIndex = i;
662
663 std::vector<const ClipmapLevel *> imageryLevels(imagerySources.size());
664
665 for (size_t j = 0; j < imagerySources.size(); ++j) {
666 imageryLevels[j] = &imagery[j][i];
667 }
668
669 calculateLevelParameters(
670 terrainLevel,
671 imageryLevels,
672 parameters[i],
673 imageryParameters[i],
674 scaledCenter,
675 worldOptions.enableNoData);
676
677 parameters[i].useBlendRegions = renderOptions.enableBlending;
678 parameters[i].viewPosInClippedLevel = calculateViewPosInClippedLevel(clipmapState, terrainLevel);
679 parameters[i].useClipping = 0;
680 }
681
682 std::vector<const ClipmapLevel *> imageryLevels(imagerySources.size());
683
684 for (size_t j = 0; j < imagerySources.size(); ++j) {
685 imageryLevels[j] = &backgroundImagery[j];
686 }
687
688 calculateLevelParameters(
689 backgroundLevel,
690 imageryLevels,
691 backgroundParameters,
692 backgroundImageryParameters,
693 scaledCenter,
694 worldOptions.enableNoData);
695
696 backgroundParameters.useBlendRegions = 0;
697 backgroundParameters.viewPosInClippedLevel = calculateViewPosInClippedLevel(clipmapState, backgroundLevel);
698 backgroundParameters.useClipping = 1;
699 backgroundParameters.levelIndex = 2;
700
701 clipmapRenderer.initLevels();
702
703 bool rendered = false;
704
705 for (int i = static_cast<int>(maxLevel); i >= 0; --i) {
706 rendered = clipmapRenderer.createRenderLevel(
707 clipmapGeometry,
708 clipmapState,
709 terrainLevels[i],
710 imagery,
711 normalLevels.size() ? &normalLevels[i] : nullptr,
712 globalParameters,
713 parameters[i],
714 imageryParameters[i],
715 !rendered);
716 }
717
718 std::vector<std::vector<ClipmapLevel>> backgroundImageryReferences;
719
720 for (size_t i = 0; i < imagery.size(); ++i) {
721 std::vector<ClipmapLevel> b;
722 b.push_back(backgroundImagery[i]);
723 backgroundImageryReferences.push_back(b);
724 }
725
726 clipmapRenderer.createBackgroundRenderLevel(
727 clipmapGeometry,
728 clipmapState,
729 backgroundLevel,
730 backgroundImageryReferences,
731 this->parameters.precomputeNormals ? &backgroundNormalLevel : nullptr,
732 globalParameters,
733 backgroundParameters,
734 backgroundImageryParameters);
735
736 {
737 CommandGroupAnnotation renderGroup(renderContext.context, "Terrain::render");
738 clipmapRenderer.setupRegularPass(renderContext);
739 clipmapRenderer.updateGlobalParameters(renderContext, renderOptions, clipmapState, globalParameters);
740
741 clipmapRenderer.render(renderContext, renderOptions, worldOptions);
742 }
743}
744
745void Cogs::ClipmapManager::renderOcean(RenderContext & renderContext, size_t maxLevel)
746{
747 CommandGroupAnnotation renderGroup(renderContext.context, "Terrain::renderOcean");
748
749 if (!terrainLevels.size()) {
750 LOG_ERROR(logger, "No terrain levels set. Render not possible.");
751
752 return;
753 }
754
755 auto p = renderContext.permutation;
756
757 auto & permutationRenderContextData = renderContext.getPermutationDependentRenderContextData(renderContext.permutation);
758 auto & permutationOceanData = oceanRenderer.getPermutationDependentOceanRendererData(renderContext.permutation);
759 if (oceanActive && HandleIsValid(permutationRenderContextData.oceanEffect) &&
760 permutationRenderContextData.oceanEffect != permutationOceanData.oceanEffect) {
761 permutationOceanData.oceanEffect = permutationRenderContextData.oceanEffect;
762
763 oceanRenderer.initializeEffects(device->getBuffers(), device->getEffects(), imagerySources.size(), p);
764 }
765
766 if (renderContext.renderPass == TerrainRenderPass::Reflection) maxLevel = maxLevel > 2 ? maxLevel - 2 : maxLevel;
767
768 GlobalParameters globalParameters;
769
770 calculateGlobalParameters(terrainLevels[0], renderContext, worldOptions, clipmapState, globalParameters);
771
772 std::vector<LevelParameters> parameters(maxLevel + 1);
773 std::vector<ImageryParameters> imageryParameters(maxLevel + 1);
774 LevelParameters backgroundParameters;
775 ImageryParameters backgroundImageryParameters;
776
777 // Camera center in map coordinates.
778 auto & worldScale = worldOptions.worldScale;
779 Vector3d scaledCenter = Vector3d(renderContext.center[0] * (1.0 / worldScale[0]), renderContext.center[1] * (1.0 / worldScale[1]), renderContext.center[2]);
780
781 for (int i = static_cast<int>(maxLevel); i >= 0; --i) {
782 ClipmapLevel & terrainLevel = terrainLevels[i];
783
784 parameters[i].levelIndex = i;
785
786 std::vector<const ClipmapLevel *> imageryLevels(imagerySources.size());
787
788 for (size_t j = 0; j < imagerySources.size(); ++j) {
789 imageryLevels[j] = &imagery[j][i];
790 }
791
792 calculateLevelParameters(
793 terrainLevel,
794 imageryLevels,
795 parameters[i],
796 imageryParameters[i],
797 scaledCenter,
798 worldOptions.enableNoData);
799
800 parameters[i].useBlendRegions = renderOptions.enableBlending;
801 parameters[i].viewPosInClippedLevel = calculateViewPosInClippedLevel(clipmapState, terrainLevel);
802 parameters[i].useClipping = 0;
803 }
804
805 std::vector<const ClipmapLevel *> imageryLevels(imagerySources.size());
806
807 for (size_t j = 0; j < imagerySources.size(); ++j) {
808 imageryLevels[j] = &backgroundImagery[j];
809 }
810
811 calculateLevelParameters(
812 backgroundLevel,
813 imageryLevels,
814 backgroundParameters,
815 backgroundImageryParameters,
816 scaledCenter,
817 worldOptions.enableNoData);
818
819 backgroundParameters.useBlendRegions = 0;
820 backgroundParameters.viewPosInClippedLevel = calculateViewPosInClippedLevel(clipmapState, backgroundLevel);
821 backgroundParameters.useClipping = 1;
822 backgroundParameters.levelIndex = 2;
823
824 clipmapRenderer.initLevels();
825
826 bool rendered = false;
827
828 for (int i = static_cast<int>(maxLevel); i >= 0; --i) {
829 rendered = clipmapRenderer.createRenderLevel(
830 clipmapGeometry,
831 clipmapState,
832 terrainLevels[i],
833 imagery,
834 normalLevels.size() ? &normalLevels[i] : nullptr,
835 globalParameters,
836 parameters[i],
837 imageryParameters[i],
838 !rendered,
839 false);
840 }
841
842 std::vector<std::vector<ClipmapLevel>> backgroundImageryReferences;
843
844 for (size_t i = 0; i < imagery.size(); ++i) {
845 std::vector<ClipmapLevel> b;
846 b.push_back(backgroundImagery[i]);
847 backgroundImageryReferences.push_back(b);
848 }
849
850 clipmapRenderer.createBackgroundRenderLevel(
851 clipmapGeometry,
852 clipmapState,
853 backgroundLevel,
854 backgroundImageryReferences,
855 this->parameters.precomputeNormals ? &backgroundNormalLevel : nullptr,
856 globalParameters,
857 backgroundParameters,
858 backgroundImageryParameters,
859 false);
860
861 if (!oceanActive) return;
862
863 if (renderContext.renderPass != TerrainRenderPass::Reflection) {
864 oceanRenderer.setupOceanPass(renderContext);
865 oceanRenderer.updateGlobalParameters(renderContext, renderOptions, clipmapState, globalParameters);
866
867 oceanRenderer.render(renderContext, renderOptions, worldOptions, true);
868
869 // Maintain continuous rendering when ocean is active.
870 touch();
871 }
872}
873
874void Cogs::ClipmapManager::postRender(RenderContext & renderContext)
875{
876 CommandGroupAnnotation postGroup(renderContext.context, "Terrain::postRender");
877
878 if (!terrainLevels.size()) {
879 return;
880 }
881
882 renderDebugOverlays(renderContext);
883
884 depthQuery.readBack();
885
886 {
887 std::lock_guard<RasterSource> terrainLock(*terrainSource.rasterSource);
888
889 terrainSource->purgeOldData();
890 }
891
892 for (auto & imagerySource : imagerySources) {
893 std::lock_guard<RasterSource> imageryLock(*imagerySource.rasterSource);
894
895 imagerySource->purgeOldData();
896 }
897
898 size_t index = 0;
899 metrics.sourceTextureBuffers[index++] = terrainSource->textureBufferSize;
900
901 for (auto & imagerySource : imagerySources) {
902 metrics.sourceTextureBuffers[index++] = imagerySource->textureBufferSize;
903 }
904
905 metrics.numFrames++;
906}
907
908void Cogs::ClipmapManager::getTerrainMetrics(TerrainMetrics * metrics) const
909{
910 *metrics = this->metrics;
911}
912
913void Cogs::ClipmapManager::invalidateRasterSource(RasterSource * rasterSource, bool clearContents)
914{
915 // Bump the request generation index so in-flight requests get discarded as soon as they are received.
916 // This ensures we don't have to wait for incoming callbacks/requests from external sources.
917 rasterSource->cancelActiveRequests();
918
919 // At this point in time, only requests that are currently being processed can touch shared state, so
920 // we wait until the processing is done before continuing.
921 //
922 //FIX: Perhaps this should be moved inside the cancellation function?
923 while (rasterSource->hasProcessingRequests()) {
924 std::this_thread::sleep_for(std::chrono::milliseconds(0));
925 }
926
927 WriteLock lock(*rasterSource);
928
929 rasterSource->purgeCache();
930
931 clearLevels(rasterSource, clearContents);
932}
933
934void Cogs::ClipmapManager::invalidateTile(RasterSource * rasterSource, const RasterTileIdentifier & id)
935{
936 WriteLock lock(*rasterSource);
937
938 rasterSource->invalidateTile(id);
939}
940
941namespace Cogs
942{
943 TextureOrigin updateOrigin(const Extent & currentExtent, const Extent & nextExtent, const int width, const int height, const TextureOrigin & origin)
944 {
945 int deltaX = nextExtent.west - currentExtent.west;
946 int deltaY = nextExtent.south - currentExtent.south;
947
948 if (deltaX == 0 && deltaY == 0)
949 return origin;
950
951 if (currentExtent.west > currentExtent.east || // initial update
952 std::abs(deltaX) >= width || // complete update
953 std::abs(deltaY) >= height) {
954 return TextureOrigin(0, 0);
955 } else {
956 int newOriginX = (origin.x + deltaX) % width;
957
958 if (newOriginX < 0)
959 newOriginX += width;
960
961 int newOriginY = (origin.y + deltaY) % height;
962
963 if (newOriginY < 0)
964 newOriginY += height;
965
966 return TextureOrigin(newOriginX, newOriginY);
967 }
968 }
969}
970
971void Cogs::ClipmapManager::updateOriginInTextures(ClipmapLevel & level)
972{
973 level.origin = updateOrigin(level.currentExtent, level.nextExtent, level.renderTexture.width, level.renderTexture.height, level.origin);
974}
975
976void Cogs::ClipmapManager::updateClipmapLevels(RenderContext & context, const WorldOptions & worldOptions, const size_t maxLevel)
977{
978 Extent invalidExtent = { 1, 1, 0, 0 };
979
980 for (size_t i = 0; i <= maxLevel; ++i) {
981 updateClipmapLevel(context, worldOptions, terrainLevels[i], *terrainLevels[i].coarserLevel, parameters.precomputeNormals);
982
983 for (size_t j = 0; j < imagerySources.size(); ++j) {
984 if (imageryEnabled[j]) {
985 updateClipmapLevel(context, worldOptions, imagery[j][i], *imagery[j][i].coarserLevel, false);
986 } else {
987 imagery[j][i].currentExtent = invalidExtent;
988 }
989 }
990 }
991
992 // Ensure the levels that are not updated this frame receive full updates when
993 // they are included back in the active set.
994 for (size_t i = maxLevel + 1; i < terrainLevels.size(); ++i) {
995 terrainLevels[i].currentExtent = invalidExtent;
996
997 for (size_t j = 0; j < imagerySources.size(); ++j) {
998 imagery[j][i].currentExtent = invalidExtent;
999 }
1000 }
1001}
1002
1003void Cogs::ClipmapManager::updateClipmapLevel(RenderContext & context, const WorldOptions & worldOptions, ClipmapLevel & level, ClipmapLevel & /*coarserLevel*/, bool normal)
1004{
1005 int deltaX = level.nextExtent.west - level.currentExtent.west;
1006 int deltaY = level.nextExtent.south - level.currentExtent.south;
1007
1008 if (deltaX == 0 && deltaY == 0 && !(normal && updateOptions.invalidateNormals)) {
1009 return;
1010 }
1011
1012 int minLongitude = deltaX > 0 ? level.currentExtent.east + 1 : level.nextExtent.west;
1013 int maxLongitude = deltaX > 0 ? level.nextExtent.east : level.currentExtent.west - 1;
1014 int minLatitude = deltaY > 0 ? level.currentExtent.north + 1 : level.nextExtent.south;
1015 int maxLatitude = deltaY > 0 ? level.nextExtent.north : level.currentExtent.south - 1;
1016
1017 int width = maxLongitude - minLongitude + 1;
1018 int height = maxLatitude - minLatitude + 1;
1019
1020 if (level.currentExtent.west > level.currentExtent.east || // initial update
1021 width >= static_cast<int>(level.renderTexture.width) ||
1022 height >= static_cast<int>(level.renderTexture.height)) // complete update
1023 {
1024 // Initial or complete update.
1025 width = level.renderTexture.width;
1026 height = level.renderTexture.height;
1027 deltaX = level.renderTexture.width;
1028 deltaY = level.renderTexture.height;
1029 minLongitude = level.nextExtent.west;
1030 maxLongitude = level.nextExtent.east;
1031 minLatitude = level.nextExtent.south;
1032 maxLatitude = level.nextExtent.north;
1033 }
1034
1035 if (height > 0)
1036 {
1037 ClipmapUpdate horizontalUpdate(
1038 &level,
1039 level.nextExtent.west,
1040 minLatitude,
1041 level.nextExtent.east,
1042 maxLatitude);
1043
1044 updater.updateRasterLevel(context, &horizontalUpdate, 1);
1045
1046 if (normal) {
1047 normalUpdater.updateNormalLevel(context, worldOptions, horizontalUpdate, normalLevels[level.index]);
1048 }
1049 }
1050
1051 if (width > 0)
1052 {
1053 ClipmapUpdate verticalUpdate(
1054 &level,
1055 minLongitude,
1056 level.nextExtent.south,
1057 maxLongitude,
1058 level.nextExtent.north);
1059
1060 updater.updateRasterLevel(context, &verticalUpdate, 1);
1061
1062 if (normal) {
1063 normalUpdater.updateNormalLevel(context, worldOptions, verticalUpdate, normalLevels[level.index]);
1064 }
1065 }
1066
1067 level.currentExtent = level.nextExtent;
1068
1069 if (normal && updateOptions.invalidateNormals) {
1070 ClipmapUpdate update(
1071 &level,
1072 level.nextExtent.west,
1073 level.nextExtent.south,
1074 level.nextExtent.east,
1075 level.nextExtent.north);
1076
1077 normalUpdater.updateNormalLevel(context, worldOptions, update, normalLevels[level.index]);
1078 }
1079}
1080
1081void Cogs::ClipmapManager::updateBackgroundLevel(RenderContext & renderContext, const WorldOptions & worldOptions)
1082{
1083 backgroundLevel.nextExtent = backgroundLevel.rasterLevel->getIndexExtent();
1084 backgroundLevel.origin = TextureOrigin();
1085
1086 if (parameters.precomputeNormals) {
1087 backgroundNormalLevel.nextExtent = backgroundLevel.nextExtent;
1088 backgroundNormalLevel.origin = TextureOrigin();
1089 }
1090
1091 if (missingTileRegions != 0 || backgroundNeedsUpdate) {
1092 ClipmapUpdate update(
1093 &backgroundLevel,
1094 backgroundLevel.nextExtent.west,
1095 backgroundLevel.nextExtent.south,
1096 backgroundLevel.nextExtent.east,
1097 backgroundLevel.nextExtent.north);
1098
1099 missingTileRegions = static_cast<int>(updater.updateRasterLevel(renderContext, &update, 1));
1100
1101 if (parameters.precomputeNormals) {
1102 normalUpdater.updateNormalLevel(renderContext, worldOptions, update, backgroundNormalLevel);
1103 }
1104 }
1105
1106 backgroundLevel.currentExtent = backgroundLevel.nextExtent;
1107
1108 if (parameters.precomputeNormals) {
1109 backgroundNormalLevel.currentExtent = backgroundNormalLevel.nextExtent;
1110 }
1111
1112 for (size_t i = 0; i < imagerySources.size(); ++i) {
1113 if (missingImageryTileRegions.size() <= i)
1114 missingImageryTileRegions.push_back(1);
1115
1116 if (missingImageryTileRegions[i] || backgroundNeedsUpdate) {
1117 auto & backgroundImageryLevel = backgroundImagery[i];
1118
1119 updater.requestTileResidency(backgroundImageryLevel);
1120
1121 Extent nextImageryExtent = {
1122 static_cast<int>(backgroundImageryLevel.rasterLevel->longitudeToIndex(backgroundLevel.rasterLevel->indexToLongitude(0))),
1123 static_cast<int>(backgroundImageryLevel.rasterLevel->latitudeToIndex(backgroundLevel.rasterLevel->indexToLatitude(0))),
1124 static_cast<int>(backgroundImageryLevel.rasterLevel->longitudeToIndex(backgroundLevel.rasterLevel->indexToLongitude(backgroundLevel.nextExtent.east))),
1125 static_cast<int>(backgroundImageryLevel.rasterLevel->latitudeToIndex(backgroundLevel.rasterLevel->indexToLatitude(backgroundLevel.nextExtent.north)))
1126 };
1127
1128 backgroundImageryLevel.nextExtent = nextImageryExtent;
1129
1130 backgroundImageryLevel.origin = TextureOrigin();
1131
1132 ClipmapUpdate update(
1133 &backgroundImageryLevel,
1134 backgroundImageryLevel.nextExtent.west,
1135 backgroundImageryLevel.nextExtent.south,
1136 backgroundImageryLevel.nextExtent.east,
1137 backgroundImageryLevel.nextExtent.north);
1138
1139 missingImageryTileRegions[i] = updater.updateRasterLevel(renderContext, &update, 1);
1140
1141 backgroundImageryLevel.currentExtent = backgroundImageryLevel.nextExtent;
1142 }
1143 }
1144
1145 backgroundNeedsUpdate = false;
1146}
1147
1148void Cogs::ClipmapManager::updateMipmaps(size_t maxLevel, IGraphicsDevice * device)
1149{
1150 auto textures = device->getTextures();
1151
1152 for (int i = static_cast<int>(maxLevel); i >= 0; --i) {
1153 for (size_t j = 0; j < imagery.size(); ++j) {
1154 textures->generateMipmaps(imagery[j][i].renderTexture.handle);
1155 }
1156 }
1157
1158 for (size_t j = 0; j < imagery.size(); ++j) {
1159 textures->generateMipmaps(backgroundImagery[j].renderTexture.handle);
1160 }
1161}
1162
1163void Cogs::ClipmapManager::requestTileLoads(RenderContext & /*renderContext*/, const size_t maxLevel)
1164{
1165 assert(maxLevel < terrainLevels.size());
1166
1167 for (size_t i = 0; i <= maxLevel; ++i) {
1168 updater.requestTileResidency(terrainLevels[i]);
1169
1170 for (size_t j = 0; j < imagerySources.size(); ++j) {
1171 if (imageryEnabled[j]) {
1172 updater.requestTileResidency(imagery[j][i]);
1173 }
1174 }
1175 }
1176
1177 updater.requestTileResidency(backgroundLevel);
1178
1179 for (size_t j = 0; j < imagerySources.size(); ++j) {
1180 if (imageryEnabled[j]) {
1181 updater.requestTileResidency(backgroundImagery[j]);
1182 }
1183 }
1184}
1185
1186size_t Cogs::ClipmapManager::calculateCenterAndMaxLevel(RenderContext & renderContext)
1187{
1188 if (!terrainLevels.size()) {
1189 LOG_ERROR(logger, "Cannot calculate center and max levels with zero terrain levels.");
1190
1191 return 0;
1192 }
1193
1194 if (previousContext.offsetEnabled) {
1195 renderContext.distance = previousContext.distance;
1196 renderContext.center = previousContext.center;
1197 } else if (updateOptions.enableUpdate) {
1198 if (first) {
1199 renderContext.distance = std::numeric_limits<float>::max();
1200 renderContext.center = Vector3d(0, 0, 0);
1201 first = false;
1202 } else {
1203 if (worldOptions.centerPolicy == CenterCalculationPolicy::TerrainProjected) {
1204 const Matrix viewProjection = previousContext.scene.projectionMatrix * previousContext.scene.viewMatrix;
1205 const Matrix inverseViewProjection = glm::inverse(viewProjection);
1206
1207 float xvalues[] = {0.5f, 0.45f, 0.55f, 0.4f, 0.6f, 0.35f, 0.65f, 0.3f, 0.7f, 0.25f, 0.75f, 0.2f, 0.8f, 0.15f, 0.85f, 0.1f, 0.9f, 0.05f, 0.95f};
1208 float yvaluesa[] = {0.5f, 0.45f, 0.4f, 0.35f, 0.3f, 0.25f, 0.2f, 0.15f, 0.1f, 0.05f};
1209 float yvaluesb[] = {0.55f, 0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f};
1210
1211 std::vector<Vector2> samplePositions;
1212 for(auto x : xvalues){
1213 for(auto y : yvaluesa){
1214 samplePositions.push_back(Vector2(x, y));
1215 }
1216 }
1217 for(auto x : xvalues){
1218 for(auto y : yvaluesb){
1219 samplePositions.push_back(Vector2(x, y));
1220 }
1221 }
1222
1223 const size_t numSamples = samplePositions.size();
1224 std::vector<Vector4> center;
1225 std::vector<char> valid;
1226 center.resize(numSamples);
1227 valid.resize(numSamples);
1228
1229 if (depthQuery.getPositions(samplePositions.data(), inverseViewProjection, numSamples, center.data(), (bool*)valid.data(), ClipmapRenderer::DefaultPickIndex)) {
1230 Vector3 viewCenter(0, 0, 0);
1231 viewCenter = vec3(glm::inverse(previousContext.scene.viewMatrix) * vec4(viewCenter, 1));
1232
1233 renderContext.distance = std::numeric_limits<float>::max();
1234
1235 Vector3 projectedCenter(0, 0, 0);
1236
1237 for (size_t i = 0; i < numSamples; ++i) {
1238 if (valid[i]) {
1239 const Vector3 sampleCenter(center[i]);
1240
1241 const float distance = glm::length(sampleCenter - viewCenter);
1242
1243 if (distance < renderContext.distance) {
1244 renderContext.distance = distance;
1245 renderContext.sampleCenter = sampleCenter;
1246 projectedCenter = sampleCenter;
1247 }
1248 }
1249 }
1250
1251 // Move camera center to global origin.
1252 projectedCenter += Vector3(previousContext.origin);
1253
1254 // Translate back to clipmap coordinates from offset.
1255 projectedCenter -= Vector3(renderContext.offset);
1256
1257 // Set clipmap center to scaled map coordinates.
1258 clipmapState.clipmapCenter = glm::dvec3(projectedCenter[0] * (1.0f / worldOptions.worldScale[0]), projectedCenter[1] * (1.0f / worldOptions.worldScale[1]), 0);
1259
1260 // Clipmap center in world coordinates.
1261 renderContext.center = Vector3d(projectedCenter[0], projectedCenter[1], 0);
1262
1263 renderContext.sampleOrigin = previousContext.origin;
1264
1265 Vector3d cameraCenter = Vector3d(viewCenter) + previousContext.origin;
1266 renderContext.sampleCameraCenter = cameraCenter;
1267 } else {
1268 Vector3 viewCenter = Vector3(glm::inverse(previousContext.scene.viewMatrix) * vec4(0, 0, 0, 1));
1269 Vector3d cameraCenter = Vector3d(viewCenter) + previousContext.origin;
1270
1271 Vector3 cameraDiff = Vector3(cameraCenter-renderContext.sampleCameraCenter);
1272
1273 const float distance = glm::length(renderContext.sampleCenter - viewCenter);
1274 renderContext.distance = distance;
1275
1276 Vector3 projectedCenter = renderContext.sampleCenter;
1277
1278 // Move camera center to global origin.
1279 projectedCenter += Vector3(renderContext.sampleOrigin);
1280
1281 // Translate back to clipmap coordinates from offset.
1282 projectedCenter -= Vector3(renderContext.offset);
1283
1284 projectedCenter += cameraDiff;
1285
1286 // Set clipmap center to scaled map coordinates.
1287 clipmapState.clipmapCenter = glm::dvec3(projectedCenter[0] * (1.0f / worldOptions.worldScale[0]), projectedCenter[1] * (1.0f / worldOptions.worldScale[1]), 0);
1288
1289 // Clipmap center in world coordinates.
1290 renderContext.center = Vector3d(projectedCenter[0], projectedCenter[1], 0);
1291 }
1292 } else if (worldOptions.centerPolicy == CenterCalculationPolicy::Vertical) {
1293 Vector3 viewCenter = Vector3(glm::inverse(previousContext.scene.viewMatrix) * vec4(0, 0, 0, 1));
1294
1295 Vector3 cameraCenter = viewCenter + Vector3(previousContext.origin);
1296
1297 // Translate back to clipmap coordinates from offset.
1298 cameraCenter -= Vector3(renderContext.offset);
1299
1300 // Set clipmap center to scaled map coordinates.
1301 clipmapState.clipmapCenter = glm::dvec3(cameraCenter.x * (1.0f / worldOptions.worldScale.x), cameraCenter.y * (1.0f / worldOptions.worldScale.y), 0);
1302
1303 // Clipmap center in world coordinates.
1304 renderContext.center = Vector3d(cameraCenter.x, cameraCenter.y, 0);
1305 renderContext.distance = std::fabs(cameraCenter.z);
1306 }
1307 }
1308 } else {
1309 renderContext.center = previousContext.center;
1310 renderContext.distance = previousContext.distance;
1311 }
1312
1313 const auto & extent = terrainLevels[0].currentExtent;
1314
1315 if (extent.west != 1 || extent.east != 0) {
1316 const float width = static_cast<float>((extent.east - extent.west) * terrainLevels[0].rasterLevel->getPostDeltaLongitude() * worldOptions.worldScale.x);
1317 float visibleWidth;
1318
1319 if (renderContext.scene.projectionMatrix[3][3] == 1) {
1320 //orthographic camera. Get the distance between the left and right plan in frustum (both distances below are positive values)
1321 visibleWidth = renderContext.cullVolume.frustum.planes[0].distance + renderContext.cullVolume.frustum.planes[1].distance;
1322 } else {
1323 //perspective camera
1324 float fieldOfView = std::atan(1 / renderContext.scene.projectionMatrix[1][1]) * 2;
1325 visibleWidth = renderContext.distance * std::sin(fieldOfView) * 2.0f;
1326 }
1327
1328 const float factor = width / visibleWidth;
1329 //Note : In earlier versions we used a hard coded value for field of view = pi/6. Since most cameras actually used field of view = pi/4, we increase
1330 //the used lodBias here to get same level of detail as before.
1331 float lodBias = renderOptions.lodBias + 0.3465735903f; //magic number is ln(sin(pi/4)/sin(pi/6))
1332
1333 size_t maxLevel = static_cast<size_t>(std::max(lodBias + std::log(factor) / std::log(2.0f), 0.0f));
1334 maxLevel = std::max(static_cast<size_t>(0), std::min(maxLevel, terrainLevels.size() - 1));
1335
1336 if (maxLevel < lastMaxLevel) {
1337 // When zooming out we want to keep the current detail level visible at greater distance than
1338 // the threshold for enabling it. This makes the detail level selection more stable, keeping levels
1339 // just popping in from immediately popping out e.g. due to small differences in terrain height.
1340 // compute corresponding factors for current level and one higher and compare with current factor
1341 const float thresholdAboveFactor = std::pow(2.0f, static_cast<float>(maxLevel - lodBias));
1342 const float thresholdBelowFactor = std::pow(2.0f, static_cast<float>(maxLevel + 1 - lodBias));
1343
1344 const float fraction = (thresholdBelowFactor - factor) / (thresholdBelowFactor - thresholdAboveFactor);
1345
1346 if (fraction < 0.25f) {
1347 // When the camera has not yet moved far into current threshold interval
1348 // keep the more detailed level.
1349 maxLevel = std::min(maxLevel + 1, terrainLevels.size() - 1);
1350 }
1351 }
1352
1353 lastMaxLevel = maxLevel;
1354 }
1355
1356 {
1357 // Clamp clipmap center so that the current most detailed level is never outside the geographic extents
1358 // of the raster data.
1359 const auto & mostDetailedLevel = terrainLevels[lastMaxLevel];
1360 const auto & rasterLevel = mostDetailedLevel.rasterLevel;
1361 const auto & rasterGeoExtent = rasterLevel->getGeoExtent();
1362
1363 const double xOffset = static_cast<double>(parameters.clipmapPosts / 2) * rasterLevel->getPostDeltaLongitude();
1364 const double minX = rasterGeoExtent.getWest() + xOffset;
1365 const double maxX = rasterGeoExtent.getEast() - xOffset;
1366
1367 const double yOffset = static_cast<double>(parameters.clipmapPosts / 2) * rasterLevel->getPostDeltaLatitude();
1368 const double minY = rasterGeoExtent.getSouth() + yOffset;
1369 const double maxY = rasterGeoExtent.getNorth() - yOffset;
1370
1371 clipmapState.clipmapCenter.x = glm::clamp(clipmapState.clipmapCenter.x, minX, maxX);
1372 clipmapState.clipmapCenter.y = glm::clamp(clipmapState.clipmapCenter.y, minY, maxY);
1373 }
1374
1375 return lastMaxLevel;
1376}
1377
1378void Cogs::ClipmapManager::preloadLevelZeroTiles()
1379{
1380 if (!terrainLevels.size()) return;
1381
1382 LOG_DEBUG(logger, "Preloading tiles...");
1383
1384 auto & terrainLevel = terrainLevels[0];
1385
1386 Extent extent = { 0, 0, terrainLevel.rasterLevel->getLongitudePosts() - 1, terrainLevel.rasterLevel->getLatitudePosts() - 1 };
1387 updater.preloadTiles(terrainLevel, extent);
1388
1389 for (size_t i = 0; i < imagerySources.size(); ++i) {
1390 if (imageryEnabled[i]) {
1391 auto & imageryLevel = imagery[i][0];
1392
1393 Extent imageryExtent = {
1394 static_cast<int>(imageryLevel.rasterLevel->longitudeToIndex(terrainLevel.rasterLevel->indexToLongitude(0))),
1395 static_cast<int>(imageryLevel.rasterLevel->latitudeToIndex(terrainLevel.rasterLevel->indexToLatitude(0))),
1396 static_cast<int>(imageryLevel.rasterLevel->longitudeToIndex(terrainLevel.rasterLevel->indexToLongitude(extent.east))),
1397 static_cast<int>(imageryLevel.rasterLevel->latitudeToIndex(terrainLevel.rasterLevel->indexToLatitude(extent.north)))
1398 };
1399
1400 updater.preloadTiles(imageryLevel, imageryExtent);
1401 }
1402 }
1403}
1404
1405void Cogs::ClipmapManager::clearLevels(RasterSource * rasterSource, bool clearContents)
1406{
1407 auto clearLevel = [&](ClipmapLevel & level) {
1408 if (level.rasterLevel != nullptr && level.rasterLevel->getSource() == rasterSource) {
1409 if (clearContents) {
1410 Cogs::clearClipmapTexture(device, level.renderTexture);
1411 }
1412
1413 if (level.normalLevel) {
1414 if (clearContents) {
1415 Cogs::clearClipmapTexture(device, level.normalLevel->renderTexture);
1416 }
1417 }
1418 }
1419
1420 Extent invalidExtent = { 1, 1, 0, 0 };
1421 level.currentExtent = invalidExtent;
1422 };
1423
1424 for (auto & terrainLevel : terrainLevels) {
1425 clearLevel(terrainLevel);
1426 }
1427
1428 for (auto & layer : imagery) {
1429 for (auto & imageryLevel : layer) {
1430 clearLevel(imageryLevel);
1431 }
1432 }
1433
1434 clearLevel(backgroundLevel);
1435
1436 for (auto & level : backgroundImagery) {
1437 clearLevel(level);
1438 }
1439
1440 backgroundNeedsUpdate = true;
1441}
1442
1443Cogs::RenderTexture Cogs::ClipmapManager::createTexture(const size_t width, const size_t height, const TextureFormat textureFormat, const bool mipmapping, glm::vec4 clearColor)
1444{
1445 auto texture = Cogs::createClipmapTexture(device, static_cast<int>(width), static_cast<int>(height), textureFormat, mipmapping && enableMipMapping, clearColor);
1446
1447 metrics.clipmapTextureBuffer += texture.size;
1448
1449 return texture;
1450}
1451
1452void Cogs::ClipmapManager::destroyTexture(RenderTexture & texture)
1453{
1454 metrics.clipmapTextureBuffer -= texture.size;
1455
1456 Cogs::releaseClipmapTexture(device, texture);
1457}
1458
1459void Cogs::ClipmapManager::updateSharedDevice(IGraphicsDevice * device)
1460{
1461 if (terrainSource.isReady()) {
1462 terrainSource->device = device;
1463
1464 for (size_t i = 0; i < imagerySources.size(); ++i) {
1465 imagerySources[i]->device = device;
1466 }
1467 }
1468}
1469
1470void Cogs::ClipmapManager::renderDebugOverlays(RenderContext & renderContext)
1471{
1472 CommandGroupAnnotation debugGroup(renderContext.context, "Terrain::renderDebugOverlays");
1473
1474 if (debugOptions.showDepthBuffer || debugOptions.showHeightMaps || debugOptions.showTextures) {
1475 const int sizeX = static_cast<int>(debugOptions.textureOverlaySize[0]);
1476 const int sizeY = static_cast<int>(debugOptions.textureOverlaySize[1]);
1477
1478 const int screenHeight = renderContext.height;
1479 int xIndex = 0;
1480 int yIndex = 1;
1481
1482 const int numX = renderContext.width / sizeX;
1483
1484 if (debugOptions.showDepthBuffer) {
1485 for (auto & data : clipmapRenderer.raypickDepthData) {
1486 ClipmapDebug::displayClipmapTexture(device, data.depthTextureHandle, xIndex++ * sizeX, screenHeight - yIndex * sizeY, sizeX, sizeY);
1487 if (xIndex > numX) {
1488 xIndex = 0;
1489 yIndex++;
1490 }
1491 }
1492 ClipmapDebug::displayClipmapTexture(device, clipmapRenderer.depthQueryTextureHandle, xIndex++ * sizeX, screenHeight - yIndex * sizeY, sizeX, sizeY);
1493 }
1494
1495 if (debugOptions.showHeightMaps) {
1496 for (size_t i = 0; i < terrainLevels.size(); ++i) {
1497 ClipmapDebug::displayClipmapTexture(device, terrainLevels[i].renderTexture.handle, xIndex++ * sizeX, screenHeight - yIndex * sizeY, sizeX, sizeY, 0.0005f);
1498
1499 if (xIndex > numX) {
1500 xIndex = 0;
1501 yIndex++;
1502 }
1503 }
1504 }
1505
1506 if (debugOptions.showTextures) {
1507 for (size_t i = 0; i < imagery[0].size(); ++i) {
1508 ClipmapDebug::displayClipmapTexture(device, imagery[0][i].renderTexture.handle, xIndex++ * sizeX, screenHeight - yIndex * sizeY, sizeX, sizeY);
1509
1510 if (xIndex > numX) {
1511 xIndex = 0;
1512 yIndex++;
1513 }
1514 }
1515 }
1516 }
1517}
1518
1519float Cogs::ClipmapManager::getNearestSample()
1520{
1521 if (isInitialized()) {
1522 const Matrix inverseProjection = glm::inverse(previousContext.scene.projectionMatrix);
1523
1524 nearestSample = depthQuery.getNearestTerrainSample(inverseProjection);
1525 //nearestSample = depthQuery.getNearestTerrainSample2(inverseProjection, previousContext.scene.viewMatrix);
1526 }
1527
1528 return nearestSample;
1529}
Log implementation class.
Definition: LogManager.h:139
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
@ TerrainProjected
Find the projected center of the screen on the terrain and center the clipmap on these coordinates.
Definition: RenderContext.h:46
@ Vertical
Project the camera coordinates onto the XY-plane and center the clipmap on these coordinates.
Definition: RenderContext.h:54
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:77