Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
Terrain.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-2016 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
22#include "Terrain.h"
23
24#include "Actor.h"
25#include "ActorManager.h"
26#include "CacheSystem.h"
27#include "Collisions.h"
28#include "ContentManager.h"
29#include "GfxScene.h"
30#include "GUIManager.h"
31#include "GUI_LoadingWindow.h"
32#include "GUI_SurveyMap.h"
33#include "HydraxWater.h"
34#include "Language.h"
35#include "ScriptEngine.h"
36#include "ShadowManager.h"
37#include "SkyManager.h"
38#include "SkyXManager.h"
41#include "Terrn2FileFormat.h"
42#include "Utils.h"
43#include "GfxWater.h"
44
45#include <Terrain/OgreTerrainPaging.h>
46#include <Terrain/OgreTerrainGroup.h>
47
48#include <algorithm>
49
50using namespace RoR;
51using namespace Ogre;
52
54 : m_collisions(0)
55 , m_geometry_manager(0)
56 , m_object_manager(0)
57 , m_shadow_manager(0)
58 , m_sky_manager(0)
59 , SkyX_manager(0)
60 , m_sight_range(1000)
61 , m_main_light(0)
62 , m_paged_detail_factor(0.0f)
63 , m_cur_gravity(DEFAULT_GRAVITY)
64 , m_hydrax_water(nullptr)
65 , m_cache_entry(entry)
66 , m_def(def)
67{
68}
69
71{
72 if (!m_disposed)
73 {
74 this->dispose();
75 }
76}
77
79{
80 if (App::app_state->getEnum<AppState>() == AppState::SHUTDOWN)
81 {
82 // Rush to exit
83 return;
84 }
85
86 //I think that the order is important
87
88#ifdef USE_CAELUM
89 if (m_sky_manager != nullptr)
90 {
91 delete(m_sky_manager);
92 m_sky_manager = nullptr;
93 }
94#endif // USE_CAELUM
95
96 if (SkyX_manager != nullptr)
97 {
98 delete(SkyX_manager);
99 SkyX_manager = nullptr;
100 }
101
102 if (m_main_light != nullptr)
103 {
104 App::GetGfxScene()->GetSceneManager()->destroyAllLights();
105 m_main_light = nullptr;
106 }
107
108 if (m_hydrax_water != nullptr)
109 {
110 m_gfx_water.reset(); // TODO: Currently needed - research and get rid of this ~ only_a_ptr, 08/2018
111 }
112
113 if (m_object_manager != nullptr)
114 {
115 delete(m_object_manager);
116 m_object_manager = nullptr;
117 }
118
119 if (m_geometry_manager != nullptr)
120 {
121 delete(m_geometry_manager);
122 m_geometry_manager = nullptr;
123 }
124
125 if (m_shadow_manager != nullptr)
126 {
127 delete(m_shadow_manager);
128 m_shadow_manager = nullptr;
129 }
130
131 if (m_collisions != nullptr)
132 {
133 delete(m_collisions);
134 m_collisions = nullptr;
135 }
136
137 if (m_wavefield)
138 {
139 m_wavefield.reset();
140 }
141
143 {
144 App::GetScriptEngine()->unloadScript(App::GetScriptEngine()->getTerrainScriptUnit());
145 }
146
147 m_disposed = true;
148}
149
151{
152 auto* loading_window = &App::GetGuiManager()->LoadingWindow;
153
154 this->setGravity(this->m_def->gravity);
155
156 loading_window->SetProgress(10, _L("Initializing Object Subsystem"));
157 this->initObjects(); // *.odef files
158
159 loading_window->SetProgress(14, _L("Initializing Shadow Subsystem"));
160 this->initShadows();
161
162 loading_window->SetProgress(17, _L("Initializing Geometry Subsystem"));
163 this->m_geometry_manager = new TerrainGeometryManager(this);
164
165 loading_window->SetProgress(23, _L("Initializing Camera Subsystem"));
166 this->initCamera();
167
168 // sky, must come after camera due to m_sight_range
169 loading_window->SetProgress(25, _L("Initializing Sky Subsystem"));
170 this->initSkySubSystem();
171
172 loading_window->SetProgress(27, _L("Initializing Light Subsystem"));
173 this->initLight();
174
175 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() != GfxSkyMode::CAELUM) //Caelum has its own fog management
176 {
177 loading_window->SetProgress(29, _L("Initializing Fog Subsystem"));
178 this->initFog();
179 }
180
181 loading_window->SetProgress(31, _L("Initializing Vegetation Subsystem"));
182 this->initVegetation();
183
184 this->fixCompositorClearColor();
185
186 loading_window->SetProgress(40, _L("Loading Terrain Geometry"));
187 if (!this->m_geometry_manager->InitTerrain(this->m_def->ogre_ter_conf_filename))
188 {
189 return false; // Error already reported
190 }
191
192 loading_window->SetProgress(60, _L("Initializing Collision Subsystem"));
193 this->m_collisions = new Collisions(this->getMaxTerrainSize());
194
195 loading_window->SetProgress(75, _L("Initializing Script Subsystem"));
196 this->initScripting();
197 this->initAiPresets();
198
199 loading_window->SetProgress(77, _L("Initializing Water Subsystem"));
200 this->initWater();
201
202 loading_window->SetProgress(80, _L("Loading Terrain Objects"));
203 this->loadTerrainObjects(); // *.tobj files
204
205 // init things after loading the terrain
206 this->initTerrainCollisions();
207
208 loading_window->SetProgress(90, _L("Initializing terrain light properties"));
209 this->m_geometry_manager->UpdateMainLightPosition(); // Initial update takes a while
210 this->m_collisions->finishLoadingTerrain();
211
212 this->LoadTelepoints(); // *.terrn2 file feature
213
214 App::GetGfxScene()->CreateDustPools(); // Particle effects
215
216 loading_window->SetProgress(92, _L("Initializing Overview Map Subsystem"));
217 App::GetGuiManager()->SurveyMap.CreateTerrainTextures(); // Should be done before actors are loaded, otherwise they'd show up in the static texture
218
219 LOG(" ===== LOADING TERRAIN ACTORS " + m_cache_entry->fname);
220 loading_window->SetProgress(95, _L("Loading Terrain Actors"));
221 this->LoadPredefinedActors();
222
223 LOG(" ===== TERRAIN LOADING DONE " + m_cache_entry->fname);
224
225 App::sim_terrain_name->setStr(m_cache_entry->fname);
226 App::sim_terrain_gui_name->setStr(this->m_def->name);
227
228 return this;
229}
230
232{
233 App::GetCameraManager()->GetCamera()->getViewport()->setBackgroundColour(m_def->ambient_color);
234 App::GetCameraManager()->GetCameraNode()->setPosition(m_def->start_position);
235
236 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::SKYX)
237 {
238 m_sight_range = 5000; //Force unlimited for SkyX, lower settings are glitchy
239 }
240 else
241 {
242 m_sight_range = App::gfx_sight_range->getInt();
243 }
244
245 if (m_sight_range < UNLIMITED_SIGHTRANGE && App::gfx_sky_mode->getEnum<GfxSkyMode>() != GfxSkyMode::SKYX)
246 {
247 App::GetCameraManager()->GetCamera()->setFarClipDistance(m_sight_range);
248 }
249 else
250 {
251 // disabled in global config
252 if (App::gfx_water_mode->getEnum<GfxWaterMode>() != GfxWaterMode::HYDRAX)
253 App::GetCameraManager()->GetCamera()->setFarClipDistance(0); //Unlimited
254 else
255 App::GetCameraManager()->GetCamera()->setFarClipDistance(9999 * 6); //Unlimited for hydrax and stuff
256 }
257}
258
260{
261#ifdef USE_CAELUM
262 // Caelum skies
263 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::CAELUM)
264 {
265 m_sky_manager = new SkyManager();
266
267 // try to load caelum config
268 if (!m_def->caelum_config.empty() && ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(m_def->caelum_config))
269 {
270 // config provided and existing, use it :)
271 m_sky_manager->LoadCaelumScript(m_def->caelum_config, m_def->caelum_fog_start, m_def->caelum_fog_end);
272 }
273 else
274 {
275 // no config provided, fall back to the default one
276 m_sky_manager->LoadCaelumScript("ror_default_sky");
277 }
278 }
279 else
280#endif //USE_CAELUM
281 // SkyX skies
282 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::SKYX)
283 {
284 // try to load SkyX config
285 if (!m_def->skyx_config.empty() && ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(m_def->skyx_config))
286 SkyX_manager = new SkyXManager(m_def->skyx_config);
287 else
288 SkyX_manager = new SkyXManager("SkyXDefault.skx");
289 }
290 else
291 {
292 if (!m_def->cubemap_config.empty())
293 {
294 // use custom
295 App::GetGfxScene()->GetSceneManager()->setSkyBox(true, m_def->cubemap_config, 100, true);
296 }
297 else
298 {
299 // use default
300 App::GetGfxScene()->GetSceneManager()->setSkyBox(true, "tracks/skyboxcol", 100, true);
301 }
302 }
303}
304
306{
307 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::CAELUM)
308 {
309#ifdef USE_CAELUM
310 m_main_light = m_sky_manager->GetSkyMainLight();
311#endif
312 }
313 else if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::SKYX)
314 {
315 m_main_light = SkyX_manager->getMainLight();
316 }
317 else
318 {
319 // screw caelum, we will roll our own light
320
321 // Create a light
322 m_main_light = App::GetGfxScene()->GetSceneManager()->createLight("MainLight");
323 //directional light for shadow
324 m_main_light->setType(Light::LT_DIRECTIONAL);
325 m_main_light->setDirection(Ogre::Vector3(0.785, -0.423, 0.453).normalisedCopy());
326
327 m_main_light->setDiffuseColour(m_def->ambient_color);
328 m_main_light->setSpecularColour(m_def->ambient_color);
329 m_main_light->setCastShadows(true);
330 m_main_light->setShadowFarDistance(1000.0f);
331 m_main_light->setShadowNearClipDistance(-1);
332 }
333}
334
336{
337 if (m_sight_range >= UNLIMITED_SIGHTRANGE)
338 App::GetGfxScene()->GetSceneManager()->setFog(FOG_NONE);
339 else
340 App::GetGfxScene()->GetSceneManager()->setFog(FOG_LINEAR, m_def->ambient_color, 0.000f, m_sight_range * 0.65f, m_sight_range*0.9);
341}
342
344{
345 switch (App::gfx_vegetation_mode->getEnum<GfxVegetation>())
346 {
348 m_paged_detail_factor = 0.2f;
349 break;
351 m_paged_detail_factor = 0.5f;
352 break;
354 m_paged_detail_factor = 1.0f;
355 break;
356 default:
357 m_paged_detail_factor = 0.0f;
358 break;
359 }
360}
361
363{
364 // hack
365 // now with extensive error checking
366 if (CompositorManager::getSingleton().hasCompositorChain(App::GetCameraManager()->GetCamera()->getViewport()))
367 {
368 CompositorInstance* co = CompositorManager::getSingleton().getCompositorChain(App::GetCameraManager()->GetCamera()->getViewport())->_getOriginalSceneCompositor();
369 if (co)
370 {
371 CompositionTechnique* ct = co->getTechnique();
372 if (ct)
373 {
374 CompositionTargetPass* ctp = ct->getOutputTargetPass();
375 if (ctp)
376 {
377 ROR_ASSERT(ctp->getPasses().size() > 0);
378 CompositionPass* p = ctp->getPasses()[0];
379 if (p)
380 {
381 p->setClearColour(Ogre::ColourValue::Black);
382 }
383 }
384 }
385 }
386 }
387}
388
390{
391 // disabled in global config
392 if (App::gfx_water_mode->getEnum<GfxWaterMode>() == GfxWaterMode::NONE)
393 return;
394
395 // disabled in map config
396 if (!m_def->has_water)
397 {
398 return;
399 }
400
401 m_wavefield = std::unique_ptr<Wavefield>(new Wavefield(this->getMaxTerrainSize()));
402 m_wavefield->SetStaticWaterHeight(m_def->water_height);
403
404 if (App::gfx_water_mode->getEnum<GfxWaterMode>() == GfxWaterMode::HYDRAX)
405 {
406 // try to load hydrax config
407 if (!m_def->hydrax_conf_file.empty() && ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(m_def->hydrax_conf_file))
408 {
409 m_hydrax_water = new HydraxWater(m_def->water_height, m_def->hydrax_conf_file);
410 }
411 else
412 {
413 // no config provided, fall back to the default one
414 m_hydrax_water = new HydraxWater(m_def->water_height);
415 }
416
417 m_gfx_water = std::unique_ptr<IGfxWater>(m_hydrax_water);
418
419 //Apply depth technique to the terrain
420 TerrainGroup::TerrainIterator ti = m_geometry_manager->getTerrainGroup()->getTerrainIterator();
421 while (ti.hasMoreElements())
422 {
423 Ogre::Terrain* t = ti.getNext()->instance;
424 MaterialPtr ptr = t->getMaterial();
425 m_hydrax_water->GetHydrax()->getMaterialManager()->addDepthTechnique(ptr->createTechnique());
426 }
427 }
428 else
429 {
430 m_gfx_water = std::unique_ptr<IGfxWater>(new GfxWater(this->getMaxTerrainSize(), m_def->water_height));
431 m_gfx_water->SetWaterBottomHeight(m_def->water_bottom_height);
432 }
433}
434
436{
437 m_shadow_manager = new ShadowManager();
438 m_shadow_manager->loadConfiguration();
439}
440
442{
443 for (std::string tobj_filename : m_def->tobj_files)
444 {
445 m_object_manager->LoadTObjFile(tobj_filename);
446 }
447}
448
450{
451 if (!m_def->traction_map_file.empty())
452 {
453 m_collisions->setupLandUse(m_def->traction_map_file.c_str());
454 }
455}
456
458{
459#ifdef USE_ANGELSCRIPT
460 // suspend AS logging, so we dont spam the users screen with initialization messages
462
463 bool loaded = false;
464
465 for (std::string as_filename : m_def->as_files)
466 {
467 loaded |= this->getObjectManager()->LoadTerrainScript(as_filename);
468 }
469
470 if (!loaded)
471 {
472 // load a default script that does the most basic things
473 this->getObjectManager()->LoadTerrainScript(DEFAULT_TERRAIN_SCRIPT);
474 }
475
476 // finally resume AS logging
478#endif //USE_ANGELSCRIPT
479}
480
482{
483 // Load 'bundled' AI presets - see section `[AI Presets]` in terrn2 file format
484 // ----------------------------------------------------------------------------
485
487}
488
490{
491 m_cur_gravity = value;
492}
493
495{
496 m_object_manager = new TerrainObjectManager(this);
497}
498
500{
501 return m_collisions->getCollisionAAB();
502}
503
505{
506 if (!m_geometry_manager)
507 return Vector3::ZERO;
508 return m_geometry_manager->getMaxTerrainSize();
509}
510
511float RoR::Terrain::getHeightAt(float x, float z)
512{
513 return m_geometry_manager->getHeightAt(x, z);
514}
515
516Ogre::Vector3 RoR::Terrain::GetNormalAt(float x, float y, float z)
517{
518 return m_geometry_manager->getNormalAt(x, y, z);
519}
520
522{
523 return m_sky_manager;
524}
525
527{
528 if (m_disposed)
529 return false;
530 else
531 return m_geometry_manager->isFlat();
532}
533
535{
536 if (m_object_manager)
537 m_object_manager->LoadTelepoints();
538}
539
541{
542 if (m_object_manager)
543 m_object_manager->LoadPredefinedActors();
544}
545
547{
548 if (m_object_manager)
549 return m_object_manager->HasPredefinedActors();
550 return false;
551}
552
554{
555 return m_object_manager->getProceduralManager();
556}
557
559{
560 return m_cache_entry->fname;
561}
562
564{
565 return m_cache_entry->resource_group;
566}
567
568void RoR::Terrain::addSurveyMapEntity(const std::string& type, const std::string& filename, const std::string& resource_group, const std::string& caption, const Ogre::Vector3& pos, float angle, int id)
569{
570 m_object_manager->m_map_entities.push_back(SurveyMapEntity(type, caption, filename, resource_group, pos, Ogre::Radian(angle), id));
571}
572
574{
575 EraseIf(m_object_manager->m_map_entities, [id](const SurveyMapEntity& e) { return e.id == id; });
576}
577
579{
580 return m_object_manager->m_map_entities;
581}
582
584
585std::string RoR::Terrain::getTerrainName() const { return m_def->name; }
586
587std::string RoR::Terrain::getGUID() const { return m_def->guid; }
588
589int RoR::Terrain::getVersion() const { return m_def->version; }
590
592
593Ogre::Vector3 RoR::Terrain::getSpawnPos() { return m_def->start_position; }
594
595Ogre::Degree RoR::Terrain::getSpawnRot() { return m_def->start_rotation; }
596
597float RoR::Terrain::getWaterHeight() const { return m_def->water_height; }
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
void LOG(const char *msg)
Legacy alias - formerly a macro.
A database of user-installed content alias 'mods' (vehicles, terrains...)
#define _L
#define DEFAULT_TERRAIN_SCRIPT
void setStr(std::string const &str)
Definition CVar.h:83
int getInt() const
Definition CVar.h:97
Ogre::SceneNode * GetCameraNode()
Ogre::Camera * GetCamera()
void LoadBundledAiPresets(TerrainPtr terrain)
Loads JSON files from [AI Presets] section in .terrn2 file format.
GUI::TopMenubar TopMenubar
Definition GUIManager.h:134
GUI::LoadingWindow LoadingWindow
Definition GUIManager.h:133
GUI::SurveyMap SurveyMap
Definition GUIManager.h:136
void CreateDustPools()
Definition GfxScene.cpp:50
Ogre::SceneManager * GetSceneManager()
Definition GfxScene.h:83
ScriptUnitID_t getTerrainScriptUnit() const
void setForwardScriptLogToConsole(bool doForward)
void unloadScript(ScriptUnitID_t unique_id)
Unloads a script.
this class handles all interactions with the Ogre Terrain system
void setGravity(float value)
Definition Terrain.cpp:489
std::string getTerrainFileResourceGroup()
Definition Terrain.cpp:563
void delSurveyMapEntities(int id)
Definition Terrain.cpp:573
void fixCompositorClearColor()
Definition Terrain.cpp:362
Ogre::Vector3 getMaxTerrainSize()
Definition Terrain.cpp:504
void initVegetation()
Definition Terrain.cpp:343
void initWater()
Definition Terrain.cpp:389
int getVersion() const
Definition Terrain.cpp:589
void LoadTelepoints()
Definition Terrain.cpp:534
void initAiPresets()
Definition Terrain.cpp:481
void initShadows()
Definition Terrain.cpp:435
float getWaterHeight() const
Definition Terrain.cpp:597
bool initialize()
Definition Terrain.cpp:150
void loadTerrainObjects()
Definition Terrain.cpp:441
bool HasPredefinedActors()
Definition Terrain.cpp:546
void initCamera()
Definition Terrain.cpp:231
SkyManager * getSkyManager()
Definition Terrain.cpp:521
SurveyMapEntityVec & getSurveyMapEntities()
Definition Terrain.cpp:578
std::string getTerrainName() const
Definition Terrain.cpp:585
void initFog()
Definition Terrain.cpp:335
void initTerrainCollisions()
Definition Terrain.cpp:449
float getHeightAt(float x, float z)
Definition Terrain.cpp:511
void initScripting()
Definition Terrain.cpp:457
void initSkySubSystem()
Definition Terrain.cpp:259
std::string getTerrainFileName()
Definition Terrain.cpp:558
void initObjects()
Definition Terrain.cpp:494
void addSurveyMapEntity(const std::string &type, const std::string &filename, const std::string &resource_group, const std::string &caption, const Ogre::Vector3 &pos, float angle, int id)
Definition Terrain.cpp:568
CacheEntryPtr getCacheEntry()
Definition Terrain.cpp:591
Ogre::Degree getSpawnRot()
Definition Terrain.cpp:595
Ogre::Vector3 GetNormalAt(float x, float y, float z)
Definition Terrain.cpp:516
bool isFlat()
Definition Terrain.cpp:526
std::string getGUID() const
Definition Terrain.cpp:587
Terrain(CacheEntryPtr entry, Terrn2DocumentPtr def)
Definition Terrain.cpp:53
Ogre::AxisAlignedBox getTerrainCollisionAAB()
Definition Terrain.cpp:499
void dispose()
Definition Terrain.cpp:78
void LoadPredefinedActors()
Definition Terrain.cpp:540
Ogre::Vector3 getSpawnPos()
Definition Terrain.cpp:593
void initLight()
Definition Terrain.cpp:305
virtual ~Terrain() override
Definition Terrain.cpp:70
ProceduralManagerPtr getProceduralManager()
Definition Terrain.cpp:553
Terrn2DocumentPtr GetDef()
Definition Terrain.cpp:583
< Water physics, see 'wavefield.cfg' in your config directory.
Definition Wavefield.h:33
static const float DEFAULT_GRAVITY
earth gravity
std::vector< SurveyMapEntity > SurveyMapEntityVec
CVar * gfx_sky_mode
CVar * gfx_water_mode
CVar * sim_terrain_name
CVar * sim_terrain_gui_name
CameraManager * GetCameraManager()
CVar * gfx_sight_range
GUIManager * GetGuiManager()
GfxScene * GetGfxScene()
CVar * app_state
CVar * gfx_vegetation_mode
ScriptEngine * GetScriptEngine()
std::shared_ptr< Terrn2Document > Terrn2DocumentPtr
static const ScriptUnitID_t SCRIPTUNITID_INVALID
@ CAELUM
Caelum (best looking, slower)
@ SKYX
SkyX (best looking, slower)
void EraseIf(std::vector< T, A > &c, Predicate pred)
Definition Utils.h:75