Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
TerrainGeometryManager.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5 Copyright 2013-2020 Petr Ohlidal
6
7 For more information, see http://www.rigsofrods.org/
8
9 Rigs of Rods is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 3, as
11 published by the Free Software Foundation.
12
13 Rigs of Rods is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20*/
21
23
24#include "Actor.h"
25#include "Application.h"
26#include "ContentManager.h"
27#include "Language.h"
28#include "GfxScene.h"
29#include "GUIManager.h"
30#include "GUI_LoadingWindow.h"
31#include "Terrain.h"
32#include "Terrn2FileFormat.h"
33#include "ShadowManager.h"
35#include "OTCFileFormat.h"
36
37#include <OgreLight.h>
38#include <Terrain/OgreTerrainGroup.h>
39
40using namespace Ogre;
41using namespace RoR;
42
43#define CUSTOM_MAT_PROFILE_NAME "Terrn2CustomMat"
44
46class Terrn2CustomMaterial : public Ogre::TerrainMaterialGenerator
47{
48public:
49
50 Terrn2CustomMaterial(Ogre::String materialName, bool addNormalmap, bool cloneMaterial)
51 : m_material_name(materialName), m_add_normal_map(addNormalmap), m_clone_material(cloneMaterial)
52 {
53 mProfiles.push_back(OGRE_NEW Profile(this, CUSTOM_MAT_PROFILE_NAME, "Renders RoR terrn2 with custom material"));
54 this->setActiveProfile(CUSTOM_MAT_PROFILE_NAME);
55 }
56
57 void setMaterialByName(const Ogre::String materialName)
58 {
59 m_material_name = materialName;
60 this->_markChanged();
61 };
62
63 class Profile : public Ogre::TerrainMaterialGenerator::Profile
64 {
65 public:
66 Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc)
67 : Ogre::TerrainMaterialGenerator::Profile(parent, name, desc)
68 {
69 };
70 ~Profile() override {};
71
72 bool isVertexCompressionSupported () const { return false; }
73 void setLightmapEnabled (bool set) /*override*/ {} // OGRE 1.8 doesn't have this method
74 Ogre::MaterialPtr generate (const Ogre::Terrain* terrain) override;
75 Ogre::uint8 getMaxLayers (const Ogre::Terrain* terrain) const override { return 0; };
76 void updateParams (const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) override {};
77 void updateParamsForCompositeMap (const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) override {};
78
79 Ogre::MaterialPtr generateForCompositeMap(const Ogre::Terrain* terrain) override
80 {
81 return terrain->_getCompositeMapMaterial();
82 };
83
84 void requestOptions(Ogre::Terrain* terrain) override
85 {
86 terrain->_setMorphRequired(false);
87 terrain->_setNormalMapRequired(true); // enable global normal map
88 terrain->_setLightMapRequired(false);
89 terrain->_setCompositeMapRequired(false);
90 };
91 };
92
93protected:
94 Ogre::String m_material_name;
97};
98
99Ogre::MaterialPtr Terrn2CustomMaterial::Profile::generate(const Ogre::Terrain* terrain)
100{
101 const Ogre::String& matName = terrain->getMaterialName();
102
103 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(matName);
104 if (mat)
105 Ogre::MaterialManager::getSingleton().remove(matName);
106
107 Terrn2CustomMaterial* parent = static_cast<Terrn2CustomMaterial*>(this->getParent());
108
109 // Set Ogre material
110 mat = Ogre::MaterialManager::getSingleton().getByName(parent->m_material_name);
111
112 // Clone material
113 if(parent->m_clone_material)
114 {
115 mat = mat->clone(matName);
116 parent->m_material_name = matName;
117 }
118
119 // Add normalmap
120 if(parent->m_add_normal_map)
121 {
122 // Get default pass
123 Ogre::Pass *p = mat->getTechnique(0)->getPass(0);
124
125 // Add terrain's global normalmap to renderpass so the fragment program can find it.
126 Ogre::TextureUnitState *tu = p->createTextureUnitState(matName+"/nm");
127
128 Ogre::TexturePtr nmtx = terrain->getTerrainNormalMap();
129 tu->_setTexturePtr(nmtx);
130 }
131
132 return mat;
133};
134
135// ----------------------------------------------------------------------------
136
137#define XZSTR(X,Z) String("[") + TOSTRING(X) + String(",") + TOSTRING(Z) + String("]")
138
140 : mHeightData(nullptr)
141 , mIsFlat(false)
142 , mMinHeight(0.0f)
143 , mMaxHeight(std::numeric_limits<float>::min())
144 , m_was_new_geometry_generated(false)
145 , terrainManager(terrainManager)
146 , m_ogre_terrain_group(nullptr)
147{
148}
149
151{
152 if (m_ogre_terrain_group != nullptr)
153 {
154 m_ogre_terrain_group->removeAllTerrains();
155 }
156}
157
160{
161 // get left / bottom points (rounded down)
162 Real factor = (Real)mSize - 1.0f;
163 Real invFactor = 1.0f / factor;
164
165 long startX = static_cast<long>(x * factor);
166 long startY = static_cast<long>(y * factor);
167 long endX = startX + 1;
168 long endY = startY + 1;
169
170 // now get points in terrain space (effectively rounding them to boundaries)
171 // note that we do not clamp! We need a valid plane
172 Real startXTS = startX * invFactor;
173 Real startYTS = startY * invFactor;
174 Real endXTS = endX * invFactor;
175 Real endYTS = endY * invFactor;
176
177 // get parametric from start coord to next point
178 Real xParam = (x * factor - startX);
179 Real yParam = (y * factor - startY);
180
181 /* For even / odd tri strip rows, triangles are this shape:
182 even odd
183 3---2 3---2
184 | / | | \ |
185 0---1 0---1
186 */
187
188 // Build all 4 positions in terrain space, using point-sampled height
189 Vector3 v0(startXTS, startYTS, mHeightData[startY * mSize + startX]);
190 Vector3 v1(endXTS , startYTS, mHeightData[startY * mSize + endX]);
191 Vector3 v2(endXTS , endYTS , mHeightData[endY * mSize + endX]);
192 Vector3 v3(startXTS, endYTS , mHeightData[endY * mSize + startX]);
193
194 // define this plane in terrain space
195 Vector3 normal;
196 Real d;
197 if (startY % 2)
198 {
199 // odd row
200 bool secondTri = ((1.0 - yParam) > xParam);
201 if (secondTri)
202 {
203 normal = (v1 - v0).crossProduct(v3 - v0);
204 d = -normal.dotProduct(v0);
205 }
206 else
207 {
208 normal = (v2 - v1).crossProduct(v3 - v1);
209 d = -normal.dotProduct(v1);
210 }
211 }
212 else
213 {
214 // even row
215 bool secondTri = (yParam > xParam);
216 if (secondTri)
217 {
218 normal = (v2 - v0).crossProduct(v3 - v0);
219 d = -normal.dotProduct(v0);
220 }
221 else
222 {
223 normal = (v1 - v0).crossProduct(v2 - v0);
224 d = -normal.dotProduct(v0);
225 }
226 }
227
228 // Solve plane equation for z
229 return (-normal.x * x - normal.y * y - d) / normal.z;
230}
231
233{
234 if (m_spec->is_flat)
235 return 0.0f;
236
237 float tx = (x - mBase - mPos.x) / ((mSize - 1) * mScale);
238 float ty = (z + mBase - mPos.z) / ((mSize - 1) * -mScale);
239
240 if (tx <= 0.0f || ty <= 0.0f || tx >= 1.0f || ty >= 1.0f)
241 return terrainManager->GetDef()->water_bottom_height;
242 else if (mIsFlat)
243 return mMinHeight;
244
245 return getHeightAtTerrainPosition(tx, ty);
246}
247
248Ogre::Vector3 TerrainGeometryManager::getNormalAt(float x, float y, float z)
249{
250 const float precision = 0.1f;
251 Vector3 normal(getHeightAt(x - precision, z) - y, precision, y - getHeightAt(x, z + precision));
252 normal.normalise();
253 return normal;
254}
255
256bool TerrainGeometryManager::InitTerrain(std::string otc_filename)
257{
258 OTCParser otc_parser;
259
260 // Load main *.otc file
261 try
262 {
263 DataStreamPtr ds_config = ResourceGroupManager::getSingleton().openResource(otc_filename);
264 if (!ds_config || !ds_config->isReadable())
265 {
266 RoR::LogFormat("[RoR|Terrain] Cannot read main *.otc file [%s].", otc_filename.c_str());
267 return false;
268 }
269 if (!otc_parser.LoadMasterConfig(ds_config, otc_filename.c_str()))
270 {
271 return false; // Error already reported
272 }
273 }
274 catch (...)
275 {
276 RoR::HandleGenericException(fmt::format("TerrainGeometryManager::InitTerrain({})", otc_filename));
277 // If we stop parsing we might break some legacy maps
278 //return false;
279 }
280
281 // Load *.otc files for pages
282 for (OTCPage& page : otc_parser.GetDefinition()->pages)
283 {
284 if (page.pageconf_filename.empty())
285 {
286 continue; // For backwards compatibility.
287 }
288
289 try
290 {
291 DataStreamPtr ds_page = ResourceGroupManager::getSingleton().openResource(page.pageconf_filename);
292 if (!ds_page || !ds_page->isReadable())
293 {
294 RoR::LogFormat("[RoR|Terrain] Cannot read file [%s].", page.pageconf_filename.c_str());
295 return false;
296 }
297
298 // NOTE: Empty file is accepted (leaving all values to defaults) for backwards compatibility.
299 if (!otc_parser.LoadPageConfig(ds_page, page, page.pageconf_filename.c_str()))
300 {
301 return false; // Error already logged
302 }
303 }
304 catch (...)
305 {
306 RoR::HandleGenericException(fmt::format("TerrainGeometryManager::InitTerrain({})", page.pageconf_filename));
307 // If we stop parsing we might break some legacy maps
308 // return false;
309 }
310 }
311
312 m_spec = otc_parser.GetDefinition();
313
314 const std::string cache_filename_format = m_spec->cache_filename_base + "_OGRE_" + TOSTRING(OGRE_VERSION) + "_";
315
316 m_ogre_terrain_group = OGRE_NEW TerrainGroup(App::GetGfxScene()->GetSceneManager(), Ogre::Terrain::ALIGN_X_Z, m_spec->page_size, m_spec->world_size);
317 m_ogre_terrain_group->setFilenameConvention(cache_filename_format, "mapbin");
318 m_ogre_terrain_group->setOrigin(m_spec->origin_pos);
319 m_ogre_terrain_group->setResourceGroup(RGN_CACHE);
320
322
323 for (OTCPage& page : m_spec->pages)
324 {
325 this->SetupGeometry(page, m_spec->is_flat);
326 }
327
328 // sync load since we want everything in place when we start
329 App::GetGuiManager()->LoadingWindow.SetProgress(44, _L("Loading terrain pages ..."));
330 m_ogre_terrain_group->loadAllTerrains(true);
331
332 Ogre::Terrain* terrain = m_ogre_terrain_group->getTerrain(0, 0);
333
334 if (terrain == nullptr)
335 return true;
336
337 mHeightData = terrain->getHeightData();
338 mSize = terrain->getSize();
339 const float world_size = terrain->getWorldSize();
340 mBase = -world_size * 0.5f;
341 mScale = world_size / (Real)(mSize - 1);
342 mPos = terrain->getPosition();
343
344 // terrain->getMinHeight() / terrain->getMaxHeight() seem to be unreliable ~ ulteq 12/18
345 for (int x = 0; x < mSize; x++)
346 {
347 for (int y = 0; y < mSize; y++)
348 {
349 float h = mHeightData[y * mSize + x];
350 mMinHeight = std::min(h, mMinHeight);
351 mMaxHeight = std::max(mMaxHeight, h);
352 }
353 }
354 mIsFlat = std::abs(mMaxHeight - mMinHeight) < std::numeric_limits<float>::epsilon();
355
357 {
358 // update the blend maps
359 if (terrainManager->GetDef()->custom_material_name.empty())
360 {
361 for (OTCPage& page : m_spec->pages)
362 {
363 Ogre::Terrain* terrain = m_ogre_terrain_group->getTerrain(page.pos_x, page.pos_z);
364
365 if (terrain != nullptr)
366 {
367 this->SetupLayers(page, terrain);
368 this->SetupBlendMaps(page, terrain);
369 }
370 }
371 }
372
373 // always save the results when it was imported
374 if (!m_spec->disable_cache)
375 {
376 App::GetGuiManager()->LoadingWindow.SetProgress(50, _L("Saving all terrain pages ..."));
377 m_ogre_terrain_group->saveAllTerrains(false);
378 }
379 }
380 else
381 {
382 LOG(" *** Terrain loaded from cache ***");
383 }
384
385 m_ogre_terrain_group->freeTemporaryResources();
386 return true;
387}
388
390{
391 TerrainGroup::TerrainIterator ti = m_ogre_terrain_group->getTerrainIterator();
392
393 while (ti.hasMoreElements())
394 {
395 Ogre::Terrain* terrain = ti.getNext()->instance;
396 if (!terrain)
397 continue;
398
399 if (!terrain->isDerivedDataUpdateInProgress())
400 {
401 terrain->dirtyLightmap();
402 terrain->updateDerivedData();
403 }
404 }
405}
406
408{
409 Light* light = terrainManager->getMainLight();
410 TerrainGlobalOptions* terrainOptions = TerrainGlobalOptions::getSingletonPtr();
411 if (light)
412 {
413 terrainOptions->setLightMapDirection(light->getDerivedDirection());
414 terrainOptions->setCompositeMapDiffuse(light->getDiffuseColour());
415 }
416 terrainOptions->setCompositeMapAmbient(App::GetGfxScene()->GetSceneManager()->getAmbientLight());
417
418 m_ogre_terrain_group->update();
419}
420
422{
423 if (!TerrainGlobalOptions::getSingletonPtr())
424 {
425 OGRE_NEW TerrainGlobalOptions();
426 }
427
428 TerrainGlobalOptions* terrainOptions = TerrainGlobalOptions::getSingletonPtr();
429 std::string const & custom_mat = terrainManager->GetDef()->custom_material_name;
430 if (!custom_mat.empty())
431 {
432 terrainOptions->setDefaultMaterialGenerator(
433 Ogre::TerrainMaterialGeneratorPtr(new Terrn2CustomMaterial(custom_mat, false, true)));
434 }
435 else
436 {
437 terrainOptions->setDefaultMaterialGenerator(
438 Ogre::TerrainMaterialGeneratorPtr(new Ogre::TerrainPSSMMaterialGenerator()));
439 }
440 // Configure global
441 terrainOptions->setMaxPixelError(m_spec->max_pixel_error);
442
443 // Important to set these so that the terrain knows what to use for derived (non-realtime) data
444 Light* light = terrainManager->getMainLight();
445 if (light)
446 {
447 terrainOptions->setLightMapDirection(light->getDerivedDirection());
448 if (custom_mat.empty())
449 {
450 terrainOptions->setCompositeMapDiffuse(light->getDiffuseColour());
451 }
452 }
453 terrainOptions->setCompositeMapAmbient(App::GetGfxScene()->GetSceneManager()->getAmbientLight());
454
455 // Configure default import settings for if we use imported image
456 Ogre::Terrain::ImportData& defaultimp = m_ogre_terrain_group->getDefaultImportSettings();
457 defaultimp.terrainSize = m_spec->page_size; // the heightmap size
458 defaultimp.worldSize = m_spec->world_size; // this is the scaled up size, like 12km
459 defaultimp.inputScale = m_spec->world_size_y;
460 defaultimp.minBatchSize = m_spec->batch_size_min;
461 defaultimp.maxBatchSize = m_spec->batch_size_max;
462
463 // optimizations
464 TerrainPSSMMaterialGenerator::SM2Profile* matProfile = nullptr;
465 if (custom_mat.empty())
466 {
467 matProfile = static_cast<TerrainPSSMMaterialGenerator::SM2Profile*>(terrainOptions->getDefaultMaterialGenerator()->getActiveProfile());
468 if (matProfile)
469 {
470 matProfile->setLightmapEnabled(m_spec->lightmap_enabled);
471 // Fix for OpenGL, otherwise terrains are black
472 if (Root::getSingleton().getRenderSystem()->getName() == "OpenGL Rendering Subsystem")
473 {
474 matProfile->setLayerNormalMappingEnabled(true);
475 matProfile->setLayerSpecularMappingEnabled(true);
476 }
477 else
478 {
479 matProfile->setLayerNormalMappingEnabled(m_spec->norm_map_enabled);
480 matProfile->setLayerSpecularMappingEnabled(m_spec->spec_map_enabled);
481 }
482 matProfile->setLayerParallaxMappingEnabled(m_spec->parallax_enabled);
483 matProfile->setGlobalColourMapEnabled(m_spec->global_colormap_enabled);
484 matProfile->setReceiveDynamicShadowsDepth(m_spec->recv_dyn_shadows_depth);
485
487 }
488 }
489
490 terrainOptions->setLayerBlendMapSize (m_spec->layer_blendmap_size);
491 terrainOptions->setCompositeMapSize (m_spec->composite_map_size);
492 terrainOptions->setCompositeMapDistance(m_spec->composite_map_distance);
493 terrainOptions->setSkirtSize (m_spec->skirt_size);
494 terrainOptions->setLightMapSize (m_spec->lightmap_size);
495
496 if (custom_mat.empty())
497 {
498 if (matProfile->getReceiveDynamicShadowsPSSM())
499 {
500 terrainOptions->setCastsDynamicShadows(true);
501 }
502 }
503
504 terrainOptions->setUseRayBoxDistanceCalculation(false);
505
506 //TODO: Make this only when hydrax is enabled.
507 terrainOptions->setUseVertexCompressionWhenAvailable(false);
508
509 // HACK: Load the single page config now
510 // This is how it "worked before" ~ only_a_ptr, 04/2017
511 if (!m_spec->pages.empty())
512 {
513 this->SetupLayers(*m_spec->pages.begin(), nullptr);
514 }
515}
516
517// if terrain is set, we operate on the already loaded terrain
518void TerrainGeometryManager::SetupLayers(RoR::OTCPage& page, Ogre::Terrain *terrain)
519{
520 if (page.num_layers == 0)
521 return;
522
523 Ogre::Terrain::ImportData& defaultimp = m_ogre_terrain_group->getDefaultImportSettings();
524
525 if (!terrain)
526 defaultimp.layerList.resize(page.num_layers);
527
528 int layer_idx = 0;
529
530 for (OTCLayer& layer : page.layers)
531 {
532 if (!terrain)
533 {
534 defaultimp.layerList[layer_idx].worldSize = layer.world_size;
535 defaultimp.layerList[layer_idx].textureNames.push_back(layer.diffusespecular_filename);
536 defaultimp.layerList[layer_idx].textureNames.push_back(layer.normalheight_filename);
537 }
538 else
539 {
540 terrain->setLayerWorldSize(layer_idx, layer.world_size);
541 terrain->setLayerTextureName(layer_idx, 0, layer.diffusespecular_filename);
542 terrain->setLayerTextureName(layer_idx, 1, layer.normalheight_filename);
543 }
544
545 layer_idx++;
546 }
547 LOG("done loading page: loaded " + TOSTRING(layer_idx) + " layers");
548}
549
550void TerrainGeometryManager::SetupBlendMaps(OTCPage& page, Ogre::Terrain* terrain )
551{
552 const int layerCount = terrain->getLayerCount();
553 auto layer_def_itor = page.layers.begin();
554
555 if (page.layers.size() < 2)
556 {
557 LOG(fmt::format("[RoR|Terrain] Page {}-{} has no blend layers defined, blendmap will not be set up.", page.pos_x, page.pos_z));
558 return;
559 }
560
561 ++layer_def_itor;
562 for (int i = 1; i < layerCount; i++)
563 {
564 if (layer_def_itor->blendmap_filename.empty())
565 continue;
566
567 Ogre::Image img;
568 try
569 {
570 img.load(layer_def_itor->blendmap_filename, ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
571 }
572 catch (Exception& e)
573 {
574 LOG("Error loading blendmap: " + layer_def_itor->blendmap_filename + " : " + e.getFullDescription());
575 continue;
576 }
577
578 TerrainLayerBlendMap* blendmap = terrain->getLayerBlendMap(i);
579
580 // resize that blending map so it will fit
581 const Ogre::uint32 blendmapSize = terrain->getLayerBlendMapSize();
582 if (img.getWidth() != blendmapSize)
583 img.resize(blendmapSize, blendmapSize);
584
585 // now to the ugly part
586 float* ptr = blendmap->getBlendPointer();
587 for (Ogre::uint32 z = 0; z != blendmapSize; z++)
588 {
589 for (Ogre::uint32 x = 0; x != blendmapSize; x++)
590 {
591 Ogre::ColourValue c = img.getColourAt(x, z, 0);
592 const float alpha = layer_def_itor->alpha;
593 if (layer_def_itor->blend_mode == 'R')
594 *ptr++ = c.r * alpha;
595 else if (layer_def_itor->blend_mode == 'G')
596 *ptr++ = c.g * alpha;
597 else if (layer_def_itor->blend_mode == 'B')
598 *ptr++ = c.b * alpha;
599 else if (layer_def_itor->blend_mode == 'A')
600 *ptr++ = c.a * alpha;
601 }
602 }
603 blendmap->dirty();
604 blendmap->update();
605 ++layer_def_itor;
606 }
607
608 if (m_spec->blendmap_dbg_enabled)
609 {
610 for (int i = 1; i < layerCount; i++)
611 {
612 Ogre::TerrainLayerBlendMap* blendMap = terrain->getLayerBlendMap(i);
613 Ogre::uint32 blendmapSize = terrain->getLayerBlendMapSize();
614 Ogre::Image img;
615 unsigned short* idata = OGRE_ALLOC_T(unsigned short, blendmapSize * blendmapSize, Ogre::MEMCATEGORY_RESOURCE);
616 float scale = 65535.0f;
617 for (unsigned int x = 0; x < blendmapSize; x++)
618 for (unsigned int z = 0; z < blendmapSize; z++)
619 idata[x + z * blendmapSize] = (unsigned short)(blendMap->getBlendValue(x, blendmapSize - z) * scale);
620 img.loadDynamicImage((Ogre::uchar*)(idata), blendmapSize, blendmapSize, Ogre::PF_L16);
621 std::string fileName = "blendmap_layer_" + Ogre::StringConverter::toString(i) + ".png";
622 img.save(fileName);
623 OGRE_FREE(idata, Ogre::MEMCATEGORY_RESOURCE);
624 }
625 }
626}
627
628// Internal helper
629bool LoadHeightmap(OTCPage& page, Image& img)
630{
631 if (page.heightmap_filename.empty())
632 {
633 LOG("[RoR|Terrain] Empty Heightmap provided in OTC, please use 'Flat=1' instead");
634 return false;
635 }
636
637 if (page.heightmap_filename.find(".raw") != String::npos)
638 {
639 // load raw data
640 DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource(page.heightmap_filename);
641 LOG("[RoR|Terrain] loading RAW image: " + TOSTRING(stream->size()) + " / " + TOSTRING(page.raw_size*page.raw_size*page.raw_bpp));
642 PixelFormat pix_format = (page.raw_bpp == 2) ? PF_L16 : PF_L8;
643 img.loadRawData(stream, page.raw_size, page.raw_size, 1, pix_format);
644 }
645 else
646 {
647 img.load(page.heightmap_filename, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
648 }
649
650 if (page.raw_flip_x)
651 img.flipAroundX();
652 if (page.raw_flip_y)
653 img.flipAroundY();
654
655 return true;
656}
657
659{
660 if (flat)
661 {
662 // very simple, no height data to load at all
663 m_ogre_terrain_group->defineTerrain(page.pos_x, page.pos_z, 0.0f);
664 return;
665 }
666
667 const std::string page_cache_filename = m_ogre_terrain_group->generateFilename(page.pos_x, page.pos_z);
668 const std::string res_group = m_ogre_terrain_group->getResourceGroup();
669 if (!m_spec->disable_cache && ResourceGroupManager::getSingleton().resourceExists(res_group, page_cache_filename))
670 {
671 // load from cache
672 m_ogre_terrain_group->defineTerrain(page.pos_x, page.pos_z);
673 }
674 else
675 {
676 Image img;
677 if (LoadHeightmap(page, img))
678 {
679 m_ogre_terrain_group->defineTerrain(page.pos_x, page.pos_z, &img);
681 }
682 else
683 {
684 // fall back to no heightmap
685 m_ogre_terrain_group->defineTerrain(page.pos_x, page.pos_z, 0.0f);
686 }
687 }
688}
689
691{
692 return Vector3(m_spec->world_size_x, mMaxHeight, m_spec->world_size_z);
693}
694
Central state/object manager and communications hub.
#define TOSTRING(x)
Definition Application.h:57
void LOG(const char *msg)
Legacy alias - formerly a macro.
#define RGN_CACHE
Definition Application.h:46
#define _L
#define CUSTOM_MAT_PROFILE_NAME
bool LoadHeightmap(OTCPage &page, Image &img)
void setReceiveDynamicShadowsDepth(bool enabled)
Whether to use depth shadows (default false).
void setLayerNormalMappingEnabled(bool enabled)
Whether to support normal mapping per layer in the shader (default true).
PSSMShadowCameraSetup * getReceiveDynamicShadowsPSSM() const
Whether to use PSSM support dynamic texture shadows, and if so the settings to use (default 0).
void setLightmapEnabled(bool enabled)
Whether to support a light map over the terrain in the shader, if it's present (default true).
void setLayerSpecularMappingEnabled(bool enabled)
Whether to support specular mapping per layer in the shader (default true).
void setGlobalColourMapEnabled(bool enabled)
Whether to support a global colour map over the terrain in the shader, if it's present (default true)...
void setLayerParallaxMappingEnabled(bool enabled)
Whether to support parallax mapping per layer in the shader (default true).
A TerrainMaterialGenerator which can cope with normal mapped, specular mapped terrain.
void SetProgress(int _percent, const std::string &_text="", bool render_frame=true)
GUI::LoadingWindow LoadingWindow
Definition GUIManager.h:132
OTCDocumentPtr GetDefinition()
bool LoadMasterConfig(Ogre::DataStreamPtr &ds, const char *filename)
bool LoadPageConfig(Ogre::DataStreamPtr &ds, OTCPage &page, const char *filename)
void updateTerrainMaterial(Ogre::TerrainPSSMMaterialGenerator::SM2Profile *matProfile)
bool InitTerrain(std::string otc_filename)
void SetupLayers(RoR::OTCPage &page, Ogre::Terrain *terrain)
Ogre::TerrainGroup * m_ogre_terrain_group
float getHeightAtTerrainPosition(float x, float z)
TerrainGeometryManager(Terrain *terrainManager)
Ogre::Vector3 getNormalAt(float x, float y, float z)
void SetupBlendMaps(RoR::OTCPage &page, Ogre::Terrain *t)
void SetupGeometry(RoR::OTCPage &page, bool flat=false)
std::shared_ptr< RoR::OTCDocument > m_spec
Ogre::Light * getMainLight()
Definition Terrain.h:93
ShadowManager * getShadowManager()
Definition Terrain.h:84
Terrn2DocumentPtr GetDef()
Definition Terrain.cpp:584
void requestOptions(Ogre::Terrain *terrain) override
void updateParamsForCompositeMap(const Ogre::MaterialPtr &mat, const Ogre::Terrain *terrain) override
Profile(Ogre::TerrainMaterialGenerator *parent, const Ogre::String &name, const Ogre::String &desc)
Ogre::MaterialPtr generate(const Ogre::Terrain *terrain) override
Ogre::uint8 getMaxLayers(const Ogre::Terrain *terrain) const override
void updateParams(const Ogre::MaterialPtr &mat, const Ogre::Terrain *terrain) override
Ogre::MaterialPtr generateForCompositeMap(const Ogre::Terrain *terrain) override
void setMaterialByName(const Ogre::String materialName)
Terrn2CustomMaterial(Ogre::String materialName, bool addNormalmap, bool cloneMaterial)
GUIManager * GetGuiManager()
GfxScene * GetGfxScene()
void HandleGenericException(const std::string &from, BitMask_t flags)
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
std::list< OTCLayer > layers
std::string heightmap_filename