Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
TerrainObjectManager.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 "AutoPilot.h"
27#include "CacheSystem.h"
28#include "Collisions.h"
29#include "Console.h"
30#include "ErrorUtils.h"
31#include "Language.h"
32#include "GameContext.h"
33#include "GfxScene.h"
34#include "GUIManager.h"
35#include "GUI_LoadingWindow.h"
36#include "MeshObject.h"
37#include "ODefFileFormat.h"
38#include "PlatformUtils.h"
39#include "ProceduralRoad.h"
40#include "PropertyMaps.h"
41#include "ScriptEngine.h"
42#include "SoundScriptManager.h"
44#include "Terrain.h"
45#include "Terrn2FileFormat.h"
46#include "TObjFileFormat.h"
47#include "Utils.h"
48#include "WriteTextToTexture.h"
49
50// PagedGeometry
51#include "TreeLoader2D.h"
52#include "BatchPage.h"
53#include "GrassLoader.h"
54#include "ImpostorPage.h"
55
56#include <RTShaderSystem/OgreRTShaderSystem.h>
57#include <Overlay/OgreFontManager.h>
58
59#ifdef USE_ANGELSCRIPT
61#endif // USE_ANGELSCRIPT
62
63using namespace Ogre;
64using namespace RoR;
65
66using namespace Forests;
67
68
69//workaround for pagedgeometry
70inline float getTerrainHeight(Real x, Real z, void* unused = 0)
71{
73}
74
76 terrainManager(terrainManager)
77{
78 m_terrn2_grouping_node = App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->createChildSceneNode(fmt::format("Terrain: {}", terrainManager->GetDef()->name));
79
80 m_procedural_manager = new ProceduralManager(m_terrn2_grouping_node->createChildSceneNode("Procedural Roads"));
81}
82
84{
85 for (MeshObject* mo : m_mesh_objects)
86 {
87 if (mo)
88 delete mo;
89 }
90
91 for (auto geom : m_paged_geometry)
92 {
93 delete geom->getPageLoader();
94 delete geom;
95 }
96
97
98 App::GetGfxScene()->GetSceneManager()->destroyAllEntities();
99
101}
102
103void GenerateGridAndPutToScene(Ogre::Vector3 position)
104{
105 Ogre::ColourValue background_color(Ogre::ColourValue::White);
106 Ogre::ColourValue grid_color(0.2f, 0.2f, 0.2f, 1.0f);
107
108 Ogre::ManualObject* mo = new Ogre::ManualObject("ReferenceGrid");
109
110 mo->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST);
111
112 const float step = 1.0f;
113 const size_t count = 50;
114 unsigned int halfCount = count / 2;
115 const float half = (step * count) / 2;
116 const float y = 0;
117 Ogre::ColourValue c;
118 for (size_t i=0; i < count+1; i++)
119 {
120 if (i == halfCount)
121 c = Ogre::ColourValue(1.f, 0.f, 0.f, 1.f);
122 else
123 c = grid_color;
124
125 mo->position(-half, y, -half+(step*i));
126 mo->colour(background_color);
127 mo->position(0, y, -half+(step*i));
128 mo->colour(c);
129 mo->position(0, y, -half+(step*i));
130 mo->colour(c);
131 mo->position(half, y, -half+(step*i));
132 mo->colour(background_color);
133
134 if (i == halfCount)
135 c = Ogre::ColourValue(0,0,1,1.0f);
136 else
137 c = grid_color;
138
139 mo->position(-half+(step*i), y, -half);
140 mo->colour(background_color);
141 mo->position(-half+(step*i), y, 0);
142 mo->colour(c);
143 mo->position(-half+(step*i), y, 0);
144 mo->colour(c);
145 mo->position(-half+(step*i), y, half);
146 mo->colour(background_color);
147 }
148
149 mo->end();
150 mo->setCastShadows(false);
151 Ogre::SceneNode *n = App::GetGameContext()->GetTerrain()->getObjectManager()->getGroupingSceneNode()->createChildSceneNode();
152 n->setPosition(position);
153 n->attachObject(mo);
154 n->setVisible(true);
155}
156
157void TerrainObjectManager::LoadTObjFile(Ogre::String tobj_name)
158{
162
163 TObjDocumentPtr tobj;
164 try
165 {
166 DataStreamPtr stream_ptr = ResourceGroupManager::getSingleton().openResource(
167 tobj_name, this->terrainManager->getCacheEntry()->resource_group);
168 TObjParser parser;
169 parser.Prepare();
170 parser.ProcessOgreStream(stream_ptr.get());
171 tobj = parser.Finalize();
172 m_tobj_cache.push_back(tobj);
173 }
174 catch (...)
175 {
176 HandleGenericException(fmt::format("Loading TObj file '{}'", tobj_name), HANDLEGENERICEXCEPTION_CONSOLE);
177 return;
178 }
179
181 m_tobj_grouping_node = m_terrn2_grouping_node->createChildSceneNode(tobj_name);
182
185
186 // Section 'grid'
187 if (tobj->grid_enabled)
188 {
189 GenerateGridAndPutToScene(tobj->grid_position);
190 }
191
192 // Section 'trees'
193 if (App::gfx_vegetation_mode->getEnum<GfxVegetation>() != GfxVegetation::NONE)
194 {
195 for (TObjTree tree : tobj->trees)
196 {
197 try
198 {
199 // NOTE: OGRE scenenodes must be globally unique, that's why the repetitive TOBJ name.
200 Ogre::SceneNode* trees_grouping_node = this->getGroupingSceneNode()->createChildSceneNode(
201 fmt::format("Paged Grass ({}, line {})", tobj->document_name, tree.origin_tobj_line_number));
202
203 this->ProcessTree(
204 tree.yaw_from, tree.yaw_to,
205 tree.scale_from, tree.scale_to,
206 tree.color_map, tree.density_map, tree.tree_mesh, tree.collision_mesh,
207 tree.grid_spacing, tree.high_density,
208 tree.min_distance, tree.max_distance, mapsizex, mapsizez,
209 trees_grouping_node);
210 }
211 catch (...)
212 {
213 RoR::HandleGenericException(fmt::format("Error processing 'trees' line (mesh: {}) from TOBJ file {}", tree.tree_mesh, tobj_name));
214 }
215 }
216 }
217
218 // Section 'grass' / 'grass2'
219 if (App::gfx_vegetation_mode->getEnum<GfxVegetation>() != GfxVegetation::NONE)
220 {
221 for (TObjGrass grass : tobj->grass)
222 {
223 try
224 {
225 // NOTE: OGRE scenenodes must be globally unique, that's why the repetitive TOBJ name.
226 Ogre::SceneNode* grass_grouping_node = this->getGroupingSceneNode()->createChildSceneNode(
227 fmt::format("Paged Grass ({}, line {})", tobj->document_name, grass.origin_tobj_line_number));
228
229 this->ProcessGrass(
230 grass.sway_speed, grass.sway_length, grass.sway_distrib, grass.density,
231 grass.min_x, grass.min_y, grass.min_h,
232 grass.max_x, grass.max_y, grass.max_h,
233 grass.material_name, grass.color_map_filename, grass.density_map_filename,
234 grass.fadetech, grass.technique, grass.range, mapsizex, mapsizez,
235 grass_grouping_node);
236 }
237 catch (...)
238 {
239 RoR::HandleGenericException(fmt::format("Error processing 'grass' line (material: {}) from TOBJ file {}", grass.material_name, tobj_name));
240 }
241 }
242 }
243
244 // Procedural roads
245 for (ProceduralObjectPtr& po : tobj->proc_objects)
246 {
247 try
248 {
250 }
251 catch (...)
252 {
253 RoR::HandleGenericException(fmt::format("Error processing procedural road {} from TOBJ file {}", po->name, tobj_name));
254 }
255 }
256
257 // Vehicles
258 for (TObjVehicle const& veh : tobj->vehicles)
259 {
260 int tobj_cache_id = (int)m_tobj_cache.size() - 1;
261 this->ProcessPredefinedActor(tobj_cache_id, veh.name, veh.position, veh.tobj_rotation, veh.type);
262 }
263
264 // Entries
265 for (TObjEntry entry : tobj->objects)
266 {
267 try
268 {
269 m_tobj_cache_active_id = (int)m_tobj_cache.size() - 1;
270 size_t num_editor_objects = m_editor_objects.size();
271 this->LoadTerrainObject(entry.odef_name, entry.position, entry.rotation, entry.instance_name, entry.type, entry.rendering_distance);
273 if (m_editor_objects.size() > num_editor_objects)
274 {
275 m_editor_objects.back()->tobj_comments = entry.comments;
276 }
277 }
278 catch (...)
279 {
280 RoR::HandleGenericException(fmt::format("Error processing object line (ODEF: {}) from TOBJ file {}", entry.odef_name, tobj_name));
281 }
282 }
283
284 if (App::diag_terrn_log_roads->getBool())
285 {
287 }
288
289 m_tobj_grouping_node = nullptr;
290}
291
293 float yawfrom, float yawto,
294 float scalefrom, float scaleto,
295 char* ColorMap, char* DensityMap, char* treemesh, char* treeCollmesh,
296 float gridspacing, float highdens,
297 int minDist, int maxDist, int mapsizex, int mapsizez,
298 Ogre::SceneNode* pagedGroupingNode)
299{
300
301 if (strnlen(ColorMap, 3) == 0)
302 {
303 LOG("tree ColorMap map zero!");
304 return;
305 }
306 if (strnlen(DensityMap, 3) == 0)
307 {
308 LOG("tree DensityMap zero!");
309 return;
310 }
312 if (!densityMap)
313 {
314 LOG("could not load densityMap: "+String(DensityMap));
315 return;
316 }
318 //densityMap->setMapBounds(TRect(0, 0, mapsizex, mapsizez));
319
320 PagedGeometry* geom = new PagedGeometry(nullptr, 50, pagedGroupingNode);
321 geom->setTempDir(App::sys_cache_dir->getStr() + PATH_SLASH);
322 geom->setCamera(App::GetCameraManager()->GetCamera());
323 geom->setInfinite();
324 Ogre::TRect<Ogre::Real> bounds = TBounds(0, 0, mapsizex, mapsizez);
325 geom->setBounds(bounds);
326
327 //Set up LODs
328 //trees->addDetailLevel<EntityPage>(50);
329 float min = minDist * terrainManager->getPagedDetailFactor();
330 if (min < 10)
331 min = 10;
332 geom->addDetailLevel<BatchPage>(min, min / 2);
333 float max = maxDist * terrainManager->getPagedDetailFactor();
334 if (max < 10)
335 max = 10;
336
337 // Check if farther details level is greater than closer
338 if (max / 10 > min / 2)
339 {
340 geom->addDetailLevel<ImpostorPage>(max, max / 10);
341 }
342
343 TreeLoader2D *treeLoader = new TreeLoader2D(geom, TBounds(0, 0, mapsizex, mapsizez));
344 treeLoader->setMinimumScale(scalefrom);
345 treeLoader->setMaximumScale(scaleto);
346 geom->setPageLoader(treeLoader);
348 if (String(ColorMap) != "none")
349 {
350 treeLoader->setColorMap(ColorMap);
351 }
352
353 Entity* curTree = App::GetGfxScene()->GetSceneManager()->createEntity(String("paged_") + treemesh + TOSTRING(m_paged_geometry.size()), treemesh);
354
355 if (gridspacing > 0)
356 {
357 // grid style
358 for (float x=0; x < mapsizex; x += gridspacing)
359 {
360 for (float z=0; z < mapsizez; z += gridspacing)
361 {
362 float density = densityMap->_getDensityAt_Unfiltered(x, z, bounds);
363 if (density < 0.8f) continue;
364 float nx = x + gridspacing * 0.5f;
365 float nz = z + gridspacing * 0.5f;
366 float yaw = Math::RangeRandom(yawfrom, yawto);
367 float scale = Math::RangeRandom(scalefrom, scaleto);
368 Vector3 pos = Vector3(nx, 0, nz);
369 treeLoader->addTree(curTree, pos, Degree(yaw), (Ogre::Real)scale);
370 if (strlen(treeCollmesh))
371 {
372 pos.y = terrainManager->getHeightAt(pos.x, pos.z);
373 scale *= 0.1f;
374 terrainManager->GetCollisions()->addCollisionMesh(curTree->getName(), String(treeCollmesh), pos, Quaternion(Degree(yaw), Vector3::UNIT_Y), Vector3(scale, scale, scale));
375 }
376 }
377 }
378 }
379 else
380 {
381 float gridsize = 10;
382 if (gridspacing < 0 && gridspacing != 0)
383 {
384 gridsize = -gridspacing;
385 }
386 float hd = highdens;
387 // normal style, random
388 for (float x=0; x < mapsizex; x += gridsize)
389 {
390 for (float z=0; z < mapsizez; z += gridsize)
391 {
392 if (highdens < 0) hd = Math::RangeRandom(0, -highdens);
393 float density = densityMap->_getDensityAt_Unfiltered(x, z, bounds);
394 int numTreesToPlace = (int)((float)(hd) * density * terrainManager->getPagedDetailFactor());
395 float nx=0, nz=0;
396 while(numTreesToPlace-->0)
397 {
398 nx = Math::RangeRandom(x, x + gridsize);
399 nz = Math::RangeRandom(z, z + gridsize);
400 float yaw = Math::RangeRandom(yawfrom, yawto);
401 float scale = Math::RangeRandom(scalefrom, scaleto);
402 Vector3 pos = Vector3(nx, 0, nz);
403 treeLoader->addTree(curTree, pos, Degree(yaw), (Ogre::Real)scale);
404 if (strlen(treeCollmesh))
405 {
406 pos.y = terrainManager->getHeightAt(pos.x, pos.z);
407 terrainManager->GetCollisions()->addCollisionMesh(treemesh, String(treeCollmesh),pos, Quaternion(Degree(yaw), Vector3::UNIT_Y), Vector3(scale, scale, scale));
408 }
409 }
410 }
411 }
412 }
413 m_paged_geometry.push_back(geom);
414
415}
416
418 float SwaySpeed, float SwayLength, float SwayDistribution, float Density,
419 float minx, float miny, float minH, float maxx, float maxy, float maxH,
420 char* grassmat, char* colorMapFilename, char* densityMapFilename,
422 int range, int mapsizex, int mapsizez,
423 Ogre::SceneNode* pagedGroupingNode)
424{
425
426 //Initialize the PagedGeometry engine
427 try
428 {
429 PagedGeometry *grass = new PagedGeometry(App::GetCameraManager()->GetCamera(), 30, pagedGroupingNode);
430 //Set up LODs
431
432 grass->addDetailLevel<GrassPage>(range * terrainManager->getPagedDetailFactor()); // original value: 80
433
434 //Set up a GrassLoader for easy use
435 GrassLoader *grassLoader = new GrassLoader(grass);
436 grass->setPageLoader(grassLoader);
437 grassLoader->setHeightFunction(&getTerrainHeight);
438
439 // render grass at first
440 grassLoader->setRenderQueueGroup(RENDER_QUEUE_MAIN-1);
441
442 GrassLayer* grassLayer = grassLoader->addLayer(grassmat);
443 grassLayer->setHeightRange(minH, maxH);
444 grassLayer->setLightingEnabled(true);
445
446 grassLayer->setAnimationEnabled((SwaySpeed>0));
447 grassLayer->setSwaySpeed(SwaySpeed);
448 grassLayer->setSwayLength(SwayLength);
449 grassLayer->setSwayDistribution(SwayDistribution);
450
451 grassLayer->setDensity(Density * terrainManager->getPagedDetailFactor());
452 grassLayer->setRenderTechnique(techn);
453
454 grassLayer->setMapBounds(TBounds(0, 0, mapsizex, mapsizez));
455
456 if (strcmp(colorMapFilename,"none") != 0)
457 {
458 grassLayer->setColorMap(colorMapFilename);
460 }
461
462 if (strcmp(densityMapFilename,"none") != 0)
463 {
464 grassLayer->setDensityMap(densityMapFilename);
466 }
467
468 grassLayer->setMinimumSize(minx, miny);
469 grassLayer->setMaximumSize(maxx, maxy);
470
471 grassLayer->setFadeTechnique(fadetech);
472
473 m_paged_geometry.push_back(grass);
474 }
475 catch(...)
476 {
477 LOG("error loading grass!");
478 }
479
480}
481
482void TerrainObjectManager::ProcessPredefinedActor(int tobj_cache_id, const std::string& name, const Ogre::Vector3 position, const Ogre::Vector3 rotation, const TObjSpecialObject type)
483{
484 // Transform TOBJ actor records to EditorObject-s (to be spawned later, if conditions are met).
485 // NOTE: The filename may be in "Bundle-qualified" format, i.e. "mybundle.zip:myactor.truck"
486 // -----------------------------------------------------------------------------------------
487
489 dst->position = position;
490 dst->rotation = rotation;
491 dst->special_object_type = type;
492 dst->name = name;
493 dst->tobj_cache_id = tobj_cache_id;
494 m_editor_objects.push_back(dst);
496}
497
498void TerrainObjectManager::moveObjectVisuals(const String& instancename, const Ogre::Vector3& pos)
499{
500 // Obsolete function kept for backwards-compatibility; does the same as `TerrainEditorObject::setPosition()`
501 // -------------------------------------------------------------------------------------------------------
502
505 {
506 LOG(fmt::format("[RoR] `moveObjectVisuals()`: instance name '{}' not found!", instancename));
507 return;
508 }
509
510 m_editor_objects[id]->setPosition(pos);
511}
512
513void TerrainObjectManager::destroyObject(const String& instancename)
514{
516 if (id == -1)
517 {
518 LOG(fmt::format("[RoR] `destroyObject()`: instance name '{}' not found!", instancename));
519 return;
520 }
521
523
524 if (object->getSpecialObjectType() != TObjSpecialObject::NONE)
525 {
526 // Preloaded actor: despawn it.
527 ROR_ASSERT(!object->static_object_node);
528 ROR_ASSERT(!object->static_collision_tris.size());
529 ROR_ASSERT(!object->static_collision_boxes.size());
530 ActorPtr actor = App::GetGameContext()->GetActorManager()->GetActorById(object->actor_instance_id);
531 if (actor)
532 {
534 }
535 }
536 else
537 {
538 // Static object: Destroy the scene node and everything attached to it.
539 ROR_ASSERT(object->static_object_node);
540 for (Ogre::MovableObject* mova : object->static_object_node->getAttachedObjects())
541 {
542 App::GetGfxScene()->GetSceneManager()->destroyMovableObject(mova);
543 }
544 App::GetGfxScene()->GetSceneManager()->destroySceneNode(object->static_object_node);
545
546 // Undo static collisions
547 for (int tri : object->static_collision_tris)
548 {
550 }
551 for (int box : object->static_collision_boxes)
552 {
554 }
555 }
556
557 // Release the object from editor, if active.
559 {
561 }
562
563 // Forget the object ever existed.
564 m_editor_objects.erase(m_editor_objects.begin() + id);
565}
566
567ODefDocument* TerrainObjectManager::FetchODef(std::string const & odef_name)
568{
569 // Consult cache first
570 auto search_res = m_odef_cache.find(odef_name);
571 if (search_res != m_odef_cache.end())
572 {
573 return search_res->second.get();
574 }
575
576 // Search for the file
577 const std::string filename = odef_name + ".odef";
578 std::string group_name;
579 try
580 {
581 group_name = Ogre::ResourceGroupManager::getSingleton().findGroupContainingResource(filename);
582 }
583 catch (...) // This means "not found"
584 {
585 LOG(fmt::format("[ODEF] Could not find {} in any resource group", filename));
586 return nullptr;
587 }
588
589 try
590 {
591 // Load and parse the file
592 Ogre::DataStreamPtr ds = ResourceGroupManager::getSingleton().openResource(filename, group_name);
593 ODefParser parser;
594 parser.Prepare();
595 parser.ProcessOgreStream(ds.get());
596 std::shared_ptr<ODefDocument> odef = parser.Finalize();
597
598 // Add to cache and return
599 m_odef_cache.insert(std::make_pair(odef_name, odef));
600 return odef.get();
601 }
602 catch (...)
603 {
604 LOG(fmt::format("[ODEF] An exception occurred when loading or parsing {}", filename));
605 return nullptr;
606 }
607}
608
609bool TerrainObjectManager::LoadTerrainObject(const Ogre::String& name, const Ogre::Vector3& pos, const Ogre::Vector3& rot, const Ogre::String& instancename, const Ogre::String& type, float rendering_distance /* = 0 */, bool enable_collisions /* = true */, int scripthandler /* = -1 */, bool uniquifyMaterial /* = false */)
610{
611 if (type == "grid")
612 {
613 // some fast grid object hacks :)
614 for (int x = 0; x < 500; x += 50)
615 {
616 for (int z = 0; z < 500; z += 50)
617 {
618 const String notype = "";
619 LoadTerrainObject(name, pos + Vector3(x, 0.0f, z), rot, name, notype, /*rendering_distance:*/0, enable_collisions, scripthandler, uniquifyMaterial);
620 }
621 }
622 return true;
623 }
624
625 const std::string odefname = name + ".odef"; // for logging
626 ODefDocument* odef = this->FetchODef(name);
627 if (odef == nullptr)
628 {
629 // Only log to console if requested from Console UI or script (debug message to RoR.log is written anyway).
630 if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
631 {
633 fmt::format(_L("Could not load file '{}'"), odefname));
634 }
635 return false;
636 }
637
638 SceneNode* tenode = this->getGroupingSceneNode()->createChildSceneNode();
639
640 MeshObject* mo = nullptr;
641 if (odef->header.mesh_name != "none")
642 {
643 Str<100> ebuf; ebuf << m_entity_counter++ << "-" << odef->header.mesh_name;
645 if (mo->getEntity())
646 {
647 mo->getEntity()->setCastShadows(odef->header.cast_shadows);
648 mo->getEntity()->setRenderingDistance(rendering_distance);
649 m_mesh_objects.push_back(mo);
650 }
651 else
652 {
653 delete mo;
654 // Only log to console if requested from Console UI or script (debug message to RoR.log is written anyway).
655 if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
656 {
658 fmt::format(_L("Could not load mesh '{}' (used by object '{}')"), odef->header.mesh_name, odefname));
659 }
660 }
661 }
662
663 tenode->setScale(odef->header.scale);
664 tenode->setPosition(pos);
665 Quaternion rotation = Quaternion(Degree(rot.x), Vector3::UNIT_X) * Quaternion(Degree(rot.y), Vector3::UNIT_Y) * Quaternion(Degree(rot.z), Vector3::UNIT_Z);
666 tenode->rotate(rotation);
667 tenode->pitch(Degree(-90));
668 tenode->setVisible(true);
669
671 object->name = name;
672 object->instance_name = instancename;
673 object->type = type;
674 object->position = pos;
675 object->rotation = rot;
676 object->initial_position = pos;
677 object->initial_rotation = rot;
678 object->static_object_node = tenode;
679 object->enable_collisions = enable_collisions;
680 object->script_handler = scripthandler;
681 object->tobj_cache_id = m_tobj_cache_active_id;
682 m_editor_objects.push_back(object);
683
684 if (mo && uniquifyMaterial && !instancename.empty())
685 {
686 for (unsigned int i = 0; i < mo->getEntity()->getNumSubEntities(); i++)
687 {
688 SubEntity* se = mo->getEntity()->getSubEntity(i);
689 String matname = se->getMaterialName();
690 String newmatname = matname + "/" + instancename;
691 se->getMaterial()->clone(newmatname);
692 se->setMaterialName(newmatname);
693 }
694 }
695
696 for (LocalizerType type : odef->localizers)
697 {
698 Localizer loc;
699 loc.position = Vector3(pos.x, pos.y, pos.z);
700 loc.rotation = rotation;
701 loc.type = type;
702 m_localizers.push_back(loc);
703 }
704
705 if (odef->mode_standard)
706 {
707 tenode->pitch(Degree(90));
708 }
709
710#ifdef USE_OPENAL
711 if (!App::GetSoundScriptManager()->isDisabled())
712 {
713 for (std::string& snd_name : odef->sounds)
714 {
716 sound->setPosition(tenode->getPosition());
717 sound->start();
718 }
719 }
720#endif //USE_OPENAL
721
722 for (std::string& gmodel_file: odef->groundmodel_files)
723 {
725 }
726
727 bool race_event = !object->instance_name.compare(0, 10, "checkpoint") ||
728 !object->instance_name.compare(0, 4, "race");
729
730 if (race_event)
731 {
732 String type = "checkpoint";
733 auto res = StringUtil::split(object->instance_name, "|");
734 if ((res.size() == 4 && res[2] == "0") || !object->instance_name.compare(0, 4, "race"))
735 {
736 type = "racestart";
737 }
738 int race_id = res.size() > 1 ? StringConverter::parseInt(res[1], -1) : -1;
739 m_map_entities.push_back(SurveyMapEntity(type, /*caption:*/type, fmt::format("icon_{}.dds", type), /*resource_group:*/"", object->position, Ogre::Radian(0), race_id));
740 }
741 else if (object->type != "" && object->type != "-")
742 {
743 String caption = "";
744 if (object->type == "station" || object->type == "hotel" || object->type == "village" ||
745 object->type == "observatory" || object->type == "farm" || object->type == "ship" || object->type == "sign")
746 {
747 caption = object->instance_name + " " + object->type;
748 }
749 m_map_entities.push_back(SurveyMapEntity(object->type, caption, fmt::format("icon_{}.dds", object->type), /*resource_group:*/"", object->position, Ogre::Radian(0), -1));
750 }
751
752 this->ProcessODefCollisionBoxes(object, odef, object, race_event);
753
754 for (ODefCollisionMesh& cmesh : odef->collision_meshes)
755 {
756 if (cmesh.mesh_name == "")
757 {
758 LOG("[ODEF] Skipping collision mesh with empty name. Object: " + odefname);
759 continue;
760 }
761
762 auto gm = terrainManager->GetCollisions()->getGroundModelByString(cmesh.groundmodel_name);
764 odefname,
765 cmesh.mesh_name, pos, tenode->getOrientation(),
766 cmesh.scale, gm, &(object->static_collision_tris));
767 }
768
769 for (ODefParticleSys& psys : odef->particle_systems)
770 {
771
772 // hacky: prevent duplicates
773 String paname = String(psys.instance_name);
774 while (App::GetGfxScene()->GetSceneManager()->hasParticleSystem(paname))
775 paname += "_";
776
777 // create particle system
778 ParticleSystem* pParticleSys = App::GetGfxScene()->GetSceneManager()->createParticleSystem(paname, String(psys.template_name));
779 pParticleSys->setCastShadows(false);
780 pParticleSys->setVisibilityFlags(DEPTHMAP_DISABLED); // disable particles in depthmap
781
782 // Some affectors may need its instance name (e.g. for script feedback purposes)
783#ifdef USE_ANGELSCRIPT
784 unsigned short affCount = pParticleSys->getNumAffectors();
785 ParticleAffector* pAff;
786 for (unsigned short i = 0; i < affCount; ++i)
787 {
788 pAff = pParticleSys->getAffector(i);
789 if (pAff->getType() == "ExtinguishableFire")
790 {
791 ((ExtinguishableFireAffector*)pAff)->setInstanceName(object->instance_name);
792 }
793 }
794#endif // USE_ANGELSCRIPT
795
796 SceneNode* sn = tenode->createChildSceneNode();
797 sn->attachObject(pParticleSys);
798 sn->pitch(Degree(90));
799
801 peo.node = sn;
802 peo.psys = pParticleSys;
803 m_particle_effect_objects.push_back(peo);
804 }
805
806 if (!odef->mat_name.empty())
807 {
808 if (mo->getEntity())
809 {
810 mo->getEntity()->setMaterialName(odef->mat_name);
811 }
812 }
813
814 if (odef->mat_name_generate != "")
815 {
816 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(odef->mat_name_generate,"generatedMaterialShaders");
817 Ogre::RTShader::ShaderGenerator::getSingleton().createShaderBasedTechnique(*mat, Ogre::MaterialManager::DEFAULT_SCHEME_NAME, Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
818 Ogre::RTShader::ShaderGenerator::getSingleton().invalidateMaterial(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, String(odef->mat_name_generate));
819 }
820
821 for (ODefAnimation& anim : odef->animations)
822 {
823 if (tenode && mo->getEntity())
824 {
825 AnimationStateSet *s = mo->getEntity()->getAllAnimationStates();
826 String anim_name_str(anim.name);
827 if (!s->hasAnimationState(anim_name_str))
828 {
829 LOG("[ODEF] animation '" + anim_name_str + "' for mesh: '" + odef->header.mesh_name + "' in odef file '" + name + ".odef' not found!");
830 //continue;
831 }
833 ao.node = tenode;
834 ao.ent = mo->getEntity();
835 ao.speedfactor = anim.speed_min;
836 if (anim.speed_min != anim.speed_max)
837 ao.speedfactor = Math::RangeRandom(anim.speed_min, anim.speed_max);
838 ao.anim = 0;
839 try
840 {
841 ao.anim = mo->getEntity()->getAnimationState(anim_name_str);
842 } catch (...)
843 {
844 ao.anim = 0;
845 }
846 if (!ao.anim)
847 {
848 LOG("[ODEF] animation '" + anim_name_str + "' for mesh: '" + odef->header.mesh_name + "' in odef file '" + name + ".odef' not found!");
849 continue;
850 }
851 ao.anim->setEnabled(true);
852 m_animated_objects.push_back(ao);
853 }
854 }
855
856 for (ODefTexPrint& tex_print : odef->texture_prints)
857 {
858 if (!mo->getEntity())
859 continue;
860 String matName = mo->getEntity()->getSubEntity(0)->getMaterialName();
861 MaterialPtr m = MaterialManager::getSingleton().getByName(matName);
862 if (m.get() == 0)
863 {
864 LOG("[ODEF] problem with drawTextOnMeshTexture command: mesh material not found: "+odefname+" : "+matName);
865 continue;
866 }
867 String texName = m->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureName();
868 Texture* background = (Texture *)TextureManager::getSingleton().getByName(texName).get();
869 if (!background)
870 {
871 LOG("[ODEF] problem with drawTextOnMeshTexture command: mesh texture not found: "+odefname+" : "+texName);
872 continue;
873 }
874
875 static int textureNumber = 0;
876 textureNumber++;
877 char tmpTextName[256] = "", tmpMatName[256] = "";
878 sprintf(tmpTextName, "TextOnTexture_%d_Texture", textureNumber);
879 sprintf(tmpMatName, "TextOnTexture_%d_Material", textureNumber); // Make sure the texture is not WRITE_ONLY, we need to read the buffer to do the blending with the font (get the alpha for example)
880 TexturePtr texture = TextureManager::getSingleton().createManual(tmpTextName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, (Ogre::uint)background->getWidth(), (Ogre::uint)background->getHeight(), MIP_UNLIMITED, PF_X8R8G8B8, Ogre::TU_STATIC | Ogre::TU_AUTOMIPMAP);
881 if (texture.get() == 0)
882 {
883 LOG("[ODEF] problem with drawTextOnMeshTexture command: could not create texture: "+odefname+" : "+tmpTextName);
884 continue;
885 }
886
887 Str<200> text_buf; text_buf << tex_print.text;
888
889 // check if we got a template argument
890 if (!strncmp(text_buf.GetBuffer(), "{{argument1}}", 13))
891 {
892 text_buf.Clear();
893 text_buf << instancename;
894 }
895
896 // replace '_' with ' '
897 char *text_pointer = text_buf.GetBuffer();
898 while (*text_pointer!=0) {if (*text_pointer=='_') *text_pointer=' ';text_pointer++;};
899
900 String font_name_str(tex_print.font_name);
901 Ogre::Font* font = (Ogre::Font *)FontManager::getSingleton().getByName(font_name_str).get();
902 if (!font)
903 {
904 LOG("[ODEF] problem with drawTextOnMeshTexture command: font not found: "+odefname+" : "+font_name_str);
905 continue;
906 }
907
908 //Draw the background to the new texture
909 texture->getBuffer()->blit(background->getBuffer());
910
911 float x = background->getWidth() * tex_print.x;
912 float y = background->getHeight() * tex_print.y;
913 float w = background->getWidth() * tex_print.w;
914 float h = background->getHeight() * tex_print.h;
915
916 ColourValue color(tex_print.r, tex_print.g, tex_print.b, tex_print.a);
917 Ogre::Box box = Ogre::Box((size_t)x, (size_t)y, (size_t)(x+w), (size_t)(y+h));
918 WriteToTexture(text_buf.ToCStr(), texture, box, font, color, tex_print.font_size, tex_print.font_dpi, tex_print.option);
919
920 // we can save it to disc for debug purposes:
921 //SaveImage(texture, "test.png");
922
923 m->clone(tmpMatName);
924 MaterialPtr mNew = MaterialManager::getSingleton().getByName(tmpMatName);
925 mNew->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(tmpTextName);
926
927 mo->getEntity()->setMaterialName(String(tmpMatName));
928 }
929
930 for (ODefSpotlight& spotl: odef->spotlights)
931 {
932 Light* spotLight = App::GetGfxScene()->GetSceneManager()->createLight();
933
934 spotLight->setType(Light::LT_SPOTLIGHT);
935 spotLight->setPosition(spotl.pos);
936 spotLight->setDirection(spotl.dir);
937 spotLight->setAttenuation(spotl.range, 1.0, 0.3, 0.0);
938 spotLight->setDiffuseColour(spotl.color);
939 spotLight->setSpecularColour(spotl.color);
940 spotLight->setSpotlightRange(Degree(spotl.angle_inner), Degree(spotl.angle_outer));
941
942 BillboardSet* lflare = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1);
943 lflare->createBillboard(spotl.pos, spotl.color);
944 lflare->setMaterialName("tracks/flare");
945 lflare->setVisibilityFlags(DEPTHMAP_DISABLED);
946
947 float fsize = Math::Clamp(spotl.range / 10, 0.2f, 2.0f);
948 lflare->setDefaultDimensions(fsize, fsize);
949
950 SceneNode *sn = tenode->createChildSceneNode();
951 sn->attachObject(spotLight);
952 sn->attachObject(lflare);
953 }
954
955 for (ODefPointLight& plight : odef->point_lights)
956 {
957 Light* pointlight = App::GetGfxScene()->GetSceneManager()->createLight();
958
959 pointlight->setType(Light::LT_POINT);
960 pointlight->setPosition(plight.pos);
961 pointlight->setDirection(plight.dir);
962 pointlight->setAttenuation(plight.range, 1.0, 0.3, 0.0);
963 pointlight->setDiffuseColour(plight.color);
964 pointlight->setSpecularColour(plight.color);
965
966 BillboardSet* lflare = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1);
967 lflare->createBillboard(plight.pos, plight.color);
968 lflare->setMaterialName("tracks/flare");
969 lflare->setVisibilityFlags(DEPTHMAP_DISABLED);
970
971 float fsize = Math::Clamp(plight.range / 10, 0.2f, 2.0f);
972 lflare->setDefaultDimensions(fsize, fsize);
973
974 SceneNode *sn = tenode->createChildSceneNode();
975 sn->attachObject(pointlight);
976 sn->attachObject(lflare);
977 }
978
979 return true;
980}
981
982bool TerrainObjectManager::LoadTerrainScript(const Ogre::String& filename)
983{
985
986 m_angelscript_grouping_node = m_terrn2_grouping_node->createChildSceneNode(filename);
987 ScriptUnitID_t result = App::GetScriptEngine()->loadScript(filename);
989
990 return result != SCRIPTUNITID_INVALID;
991}
992
994{
995 if (m_animated_objects.size() == 0)
996 return;
997
998 std::vector<AnimatedObject>::iterator it;
999
1000 for (it = m_animated_objects.begin(); it != m_animated_objects.end(); it++)
1001 {
1002 if (it->anim && it->speedfactor != 0)
1003 {
1004 Real time = dt * it->speedfactor;
1005 it->anim->addTime(time);
1006 }
1007 }
1008}
1009
1011{
1013 {
1014 if (peo.psys)
1015 {
1017 }
1018 }
1019}
1020
1022{
1023 for (Terrn2Telepoint& telepoint: terrainManager->GetDef()->telepoints)
1024 {
1025 m_map_entities.push_back(SurveyMapEntity("telepoint", telepoint.name, "icon_telepoint.dds", /*resource_group:*/"", telepoint.position, Ogre::Radian(0), -1));
1026 }
1027}
1028
1030{
1031 // We need the 'rot_yxz' flag - look up the TOBJ document in cache
1032 if (object->tobj_cache_id == -1 || object->tobj_cache_id >= (int)m_tobj_cache.size())
1033 {
1035 fmt::format("Assuming no 'rot_yxz' when spawning preselected actor '{}' - TOBJ document not found", object->getName()));
1036 return false;
1037 }
1038 else
1039 {
1040 return m_tobj_cache[object->tobj_cache_id]->rot_yxz;
1041 }
1042}
1043
1044
1046{
1047 // For terrain editor to work, all preloaded actors must be spawned.
1048 // Most will spawn with terrain, however, some may be excluded for reasons.
1049 // -----------------------------------------------------------------------
1050
1051 const bool rot_yxz = GetEditorObjectFlagRotYXZ(object);
1052
1053 // Check if already spawned.
1054 if (object->actor_instance_id == ACTORINSTANCEID_INVALID)
1055 {
1056 // Not spawned yet - assign custom ID so that Terrain Editor can reset and move the actor.
1057 object->actor_instance_id = App::GetGameContext()->GetActorManager()->GetActorNextInstanceId();
1058 }
1059 else
1060 {
1061 // Spawned before; check if still existing and respawn if not.
1063 object->actor_instance_id);
1064 if (actor != ActorManager::ACTORPTR_NULL)
1065 {
1066 return; // We're done.
1067 }
1068 }
1069
1071 rq->asr_instance_id = object->actor_instance_id;
1072 rq->asr_position = object->position;
1073 rq->asr_filename = object->name;
1074 rq->asr_rotation = TObjParser::CalcRotation(object->rotation, rot_yxz);
1076 rq->asr_free_position = (object->special_object_type == TObjSpecialObject::TRUCK2);
1077 rq->asr_terrn_machine = (object->special_object_type == TObjSpecialObject::MACHINE);
1079}
1080
1082{
1083 // in netmode, don't load other actors!
1084 if (RoR::App::mp_state->getEnum<MpState>() == RoR::MpState::CONNECTED)
1085 {
1086 return;
1087 }
1088
1090 {
1091 if (object->special_object_type == TObjSpecialObject::NONE)
1092 {
1093 continue; // Skip static objects
1094 }
1095
1096 if ((object->special_object_type == TObjSpecialObject::BOAT) && (terrainManager->getWater() == nullptr))
1097 {
1098 continue; // Don't spawn boats if there's no water.
1099 }
1100
1101 this->SpawnSinglePredefinedActor(object);
1102 }
1103}
1104
1106{
1107
1108 for (auto geom : m_paged_geometry)
1109 {
1110 geom->update();
1111 }
1112
1113 this->UpdateAnimatedObjects(dt);
1115
1116 return true;
1117}
1118
1120{
1121 for (ODefCollisionBox& cbox : odef->collision_boxes)
1122 {
1123 if (params->enable_collisions && (App::sim_races_enabled->getBool() || !race_event))
1124 {
1125 // Validate AABB (minimum corners must be less or equal to maximum corners)
1126 if (cbox.aabb_min.x > cbox.aabb_max.x || cbox.aabb_min.y > cbox.aabb_max.y || cbox.aabb_min.z > cbox.aabb_max.z)
1127 {
1128 // Only log to console if invoked from Console UI or script.
1129 std::string msg = "Skipping invalid collision box, min: " + TOSTRING(cbox.aabb_min) + ", max: " + TOSTRING(cbox.aabb_max);
1130 if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
1131 {
1133 }
1134 else
1135 {
1136 LOG(fmt::format("[ODEF] {}", msg));
1137 }
1138 continue;
1139 }
1140
1142 cbox.is_rotating, cbox.is_virtual, params->position, params->rotation,
1143 cbox.aabb_min, cbox.aabb_max, cbox.box_rot, cbox.event_name,
1144 params->instance_name, cbox.reverb_preset_name, cbox.force_cam_pos,
1145 cbox.cam_pos, cbox.scale, cbox.direction, cbox.event_filter,
1146 params->script_handler);
1147
1148 obj->static_collision_boxes.push_back(boxnum);
1149 }
1150 }
1151}
1152
1154{
1155 // This has no effect on rendering, it just helps users to diagnose the scene graph.
1156 // --------------------------------------------------------------------------------
1157
1160 else if (m_tobj_grouping_node)
1161 return m_tobj_grouping_node;
1162 else if (m_terrn2_grouping_node)
1164 else
1165 return App::GetGfxScene()->GetSceneManager()->getRootSceneNode();
1166}
1167
1169{
1170 // Is this the right 'ModernC++' approach? :/
1171 auto itor = std::find_if(m_editor_objects.begin(), m_editor_objects.end(),
1172 [needle_instance_name](TerrainEditorObjectPtr& obj) { return obj->instance_name == needle_instance_name; });
1173 if (itor != m_editor_objects.end())
1174 {
1175 return static_cast<int>(std::distance(m_editor_objects.begin(), itor));
1176 }
1177 else
1178 {
1180 }
1181}
1182
Central state/object manager and communications hub.
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
#define TOSTRING(x)
Definition Application.h:58
void LOG(const char *msg)
Legacy alias - formerly a macro.
BatchPage is an extension to PagedGeometry which displays entities as static geometry.
A database of user-installed content alias 'mods' (vehicles, terrains...)
#define _L
Game state manager and message-queue provider.
#define strnlen(str, len)
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
float getTerrainHeight(Real x, Real z, void *unused=0)
void GenerateGridAndPutToScene(Ogre::Vector3 position)
void WriteToTexture(const String &str, TexturePtr destTexture, Ogre::Box destRectangle, Ogre::Font *Reffont, const ColourValue &color, int fontSize, int fontDPI, char justify, bool wordwrap)
The BatchPage class renders entities as StaticGeometry.
Definition BatchPage.h:48
A 2D greyscale image that is assigned to a certain region of your world to represent color levels.
A 2D greyscale image that is assigned to a certain region of your world to represent density levels.
static DensityMap * load(const Ogre::String &fileName, MapChannel channel=CHANNEL_COLOR)
void setFilter(MapFilter filter)
Sets the filtering mode used for this density map.
Ogre::Real _getDensityAt_Unfiltered(Ogre::Real x, Ogre::Real z, const Ogre::TRect< Ogre::Real > &mapBounds)
A data structure giving you full control over grass properties.
void setColorMapFilter(MapFilter filter)
Sets the filtering mode used for color maps.
void setMaximumSize(float width, float height)
Sets the maximum size that grass quads/billboards will be.
void setDensityMapFilter(MapFilter filter)
Sets the filtering mode used for density maps.
void setColorMap(const Ogre::String &mapFile, MapChannel channel=CHANNEL_COLOR)
Sets the color map used for this grass layer.
void setSwayDistribution(Ogre::Real freq)
Sets the smooth distribution (positional phase shift) of the grass swaying animation.
void setLightingEnabled(bool enabled)
Enables/disables lighting on this layer.
void setRenderTechnique(GrassTechnique style, bool blendBase=false)
Sets the technique used to render this grass layer.
void setSwaySpeed(Ogre::Real speed)
Sets the sway speed of the grass (measured in "sways-per-second")
void setMapBounds(const TBounds &bounds)
Sets the boundaries of the density/color maps.
void setMinimumSize(float width, float height)
Sets the minimum size that grass quads/billboards will be.
void setDensity(float density)
Sets the maximum density (measured in grass quads/billboards per square unit) of grass.
void setFadeTechnique(FadeTechnique style)
Sets the technique used when fading out distant grass.
void setHeightRange(float minHeight, float maxHeight=0)
Sets a minimum / maximum height where grass may appear.
void setAnimationEnabled(bool enabled)
Enables/disables animation on this layer.
void setDensityMap(const Ogre::String &mapFile, MapChannel channel=CHANNEL_COLOR)
Sets the density map used for this grass layer.
void setSwayLength(Ogre::Real mag)
Sets how far grass should sway back and forth.
A PageLoader-derived object you can use with PagedGeometry to produce realistic grass.
Definition GrassLoader.h:50
A custom page type designed specifically for use with GrassLoader.
The ImpostorPage class renders entities as impostors (billboard images that look just like the real e...
A class providing highly optimized methods for rendering massive amounts of geometry.
void setCamera(Ogre::Camera *cam)
Sets the camera to use when calculating levels of detail.
void setTempDir(Ogre::String dir)
Sets the output directory for the imposter pages.
void setPageLoader(PageLoader *loader)
Assigns a PageLoader object for the PagedGeometry.
GeometryPageManager * addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength=0, const Ogre::Any &data=Ogre::Any())
Adds a detail level to the PagedGeometry object.
void setInfinite()
Switches to infinite mode.
void setBounds(const TBounds bounds)
Switches to bounded mode and uses the given boundaries.
A PageLoader-derived object you can use with PagedGeometry to easily place trees on your terrain.
void setMaximumScale(Ogre::Real maxScale)
Sets the maximum tree scale value.
void setMinimumScale(Ogre::Real minScale)
Sets the minimum tree scale value.
void setColorMap(const Ogre::String &mapFile, MapChannel channel=CHANNEL_COLOR)
Sets the color map used to color trees.
void setHeightFunction(Ogre::Real(*heightFunction)(Ogre::Real x, Ogre::Real z, void *userData), void *userData=NULL)
Sets the height function used to calculate tree height coordinates.
void addTree(Ogre::Entity *entity, const Ogre::Vector3 &position, Ogre::Degree yaw=Ogre::Degree(0), Ogre::Real scale=1.0f, void *userData=NULL)
Adds an entity to the scene with the specified location, rotation, and scale.
Ogre::Entity * getEntity()
Definition MeshObject.h:43
This class defines a ParticleAffector which deflects particles.
const ActorPtr & GetActorById(ActorInstanceID_t actor_id)
ActorInstanceID_t GetActorNextInstanceId()
Script proxy: game.getActorNextInstanceId()
static const ActorPtr ACTORPTR_NULL
bool getBool() const
Definition CVar.h:98
Ogre::String resource_group
Resource group of the loaded bundle. Empty if not loaded yet.
Definition CacheSystem.h:89
int addCollisionBox(bool rotating, bool virt, Ogre::Vector3 pos, Ogre::Vector3 rot, Ogre::Vector3 l, Ogre::Vector3 h, Ogre::Vector3 sr, const Ogre::String &eventname, const Ogre::String &instancename, const Ogre::String &reverb_preset_name, bool forcecam, Ogre::Vector3 campos, Ogre::Vector3 sc=Ogre::Vector3::UNIT_SCALE, Ogre::Vector3 dr=Ogre::Vector3::ZERO, CollisionEventFilter event_filter=EVENT_ALL, int scripthandler=-1)
int loadGroundModelsConfigFile(Ogre::String filename)
void removeCollisionBox(int number)
ground_model_t * getGroundModelByString(const Ogre::String name)
void removeCollisionTri(int number)
void addCollisionMesh(Ogre::String const &srcname, Ogre::String const &meshname, Ogre::Vector3 const &pos, Ogre::Quaternion const &q, Ogre::Vector3 const &scale, ground_model_t *gm=0, std::vector< int > *collTris=0)
generate collision tris from existing mesh resource
@ CONSOLE_MSGTYPE_TERRN
Parsing/spawn/simulation messages for terrain.
Definition Console.h:64
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition Console.cpp:103
@ CONSOLE_SYSTEM_ERROR
Definition Console.h:52
@ CONSOLE_SYSTEM_WARNING
Definition Console.h:53
const TerrainPtr & GetTerrain()
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
ActorManager * GetActorManager()
void AdjustParticleSystemTimeFactor(Ogre::ParticleSystem *psys, GfxActor *gfx_actor=nullptr)
Definition GfxScene.cpp:435
Ogre::SceneManager * GetSceneManager()
Definition GfxScene.h:83
std::shared_ptr< ODefDocument > Finalize()
Passes ownership.
void ProcessOgreStream(Ogre::DataStream *stream)
void addObject(ProceduralObjectPtr po)
Generates road mesh and adds to internal list.
ScriptUnitID_t loadScript(Ogre::String filename, ScriptCategory category=ScriptCategory::TERRAIN, ActorPtr associatedActor=nullptr, int32_t associatedNetUid=-1, std::string buffer="")
Loads a script.
void setPosition(Ogre::Vector3 pos)
static const int ACTOR_ID_TERRAIN_OBJECT
SoundScriptInstancePtr createInstance(Ogre::String templatename, int actor_id, int soundLinkType=SL_DEFAULT, int soundLinkItemId=-1)
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition Str.h:36
const char * ToCStr() const
Definition Str.h:46
Str & Clear()
Definition Str.h:54
char * GetBuffer()
Definition Str.h:48
TObjDocumentPtr Finalize()
Passes ownership.
static Ogre::Quaternion CalcRotation(Ogre::Vector3 const &rot, bool rot_yxz)
void ProcessOgreStream(Ogre::DataStream *stream)
TerrainEditorObjectID_t GetSelectedObjectID() const
Represents an instance of static terrain object (.ODEF file format)
std::string getTerrainFileResourceGroup()
Definition Terrain.cpp:563
TerrainObjectManager * getObjectManager()
Definition Terrain.h:80
TerrainGeometryManager * getGeometryManager()
Definition Terrain.h:79
float getHeightAt(float x, float z)
Definition Terrain.cpp:511
float getPagedDetailFactor() const
Definition Terrain.h:95
CacheEntryPtr getCacheEntry()
Definition Terrain.cpp:591
Collisions * GetCollisions()
Definition Terrain.h:86
TerrainEditor * GetTerrainEditor()
Definition Terrain.h:85
Wavefield * getWater()
Definition Terrain.h:87
Terrn2DocumentPtr GetDef()
Definition Terrain.cpp:583
Ogre::SceneNode * getGroupingSceneNode()
RoR::ODefDocument * FetchODef(std::string const &odef_name)
TerrainObjectManager(Terrain *terrainManager)
bool LoadTerrainObject(const Ogre::String &name, const Ogre::Vector3 &pos, const Ogre::Vector3 &rot, const Ogre::String &instancename, const Ogre::String &type, float rendering_distance=0, bool enable_collisions=true, int scripthandler=-1, bool uniquifyMaterial=false)
TerrainEditorObjectID_t FindEditorObjectByInstanceName(std::string const &instance_name)
Returns offset to m_editor_objects or -1 if not found.
void ProcessODefCollisionBoxes(TerrainEditorObjectPtr obj, ODefDocument *odef, const TerrainEditorObjectPtr &params, bool race_event)
std::vector< TObjDocumentPtr > m_tobj_cache
void ProcessTree(float yawfrom, float yawto, float scalefrom, float scaleto, char *ColorMap, char *DensityMap, char *treemesh, char *treeCollmesh, float gridspacing, float highdens, int minDist, int maxDist, int mapsizex, int mapsizez, Ogre::SceneNode *pagedGroupingNode)
std::vector< MeshObject * > m_mesh_objects
std::vector< Forests::PagedGeometry * > m_paged_geometry
Ogre::SceneNode * m_terrn2_grouping_node
For a readable scene graph (via inspector script)
void LoadTObjFile(Ogre::String filename)
std::unordered_map< std::string, std::shared_ptr< RoR::ODefDocument > > m_odef_cache
ProceduralManagerPtr m_procedural_manager
bool GetEditorObjectFlagRotYXZ(TerrainEditorObjectPtr const &object)
TerrainEditorObjectPtrVec m_editor_objects
std::vector< ParticleEffectObject > m_particle_effect_objects
void moveObjectVisuals(const Ogre::String &instancename, const Ogre::Vector3 &pos)
Ogre::SceneNode * m_tobj_grouping_node
For even more readable scene graph (via inspector script)
bool LoadTerrainScript(const Ogre::String &filename)
std::vector< AnimatedObject > m_animated_objects
void SpawnSinglePredefinedActor(TerrainEditorObjectPtr const &object)
void destroyObject(const Ogre::String &instancename)
Ogre::SceneNode * m_angelscript_grouping_node
For even more readable scene graph (via inspector script)
void ProcessGrass(float SwaySpeed, float SwayLength, float SwayDistribution, float Density, float minx, float miny, float minH, float maxx, float maxy, float maxH, char *grassmat, char *colorMapFilename, char *densityMapFilename, Forests::FadeTechnique fadetech, Forests::GrassTechnique techn, int range, int mapsizex, int mapsizez, Ogre::SceneNode *pagedGroupingNode)
void ProcessPredefinedActor(int tobj_cache_id, const std::string &name, const Ogre::Vector3 position, const Ogre::Vector3 rotation, const TObjSpecialObject type)
LocalizerType
Definition SimData.h:225
char PATH_SLASH
@ MSG_SIM_SPAWN_ACTOR_REQUESTED
Payload = RoR::ActorSpawnRequest* (owner)
@ MSG_SIM_DELETE_ACTOR_REQUESTED
Payload = RoR::ActorPtr* (owner)
@ MAPFILTER_BILINEAR
Use bilinear filtering - slower, but will appear less blocky.
GrassTechnique
A technique used to render grass.
Ogre::TRect< Ogre::Real > TBounds
Define TBounds as a TRect using Real numeric units.
FadeTechnique
A technique used to fade grass into the distance.
@ CHANNEL_COLOR
Use the image's full RGB color information.
CVar * sim_races_enabled
CameraManager * GetCameraManager()
SoundScriptManager * GetSoundScriptManager()
GameContext * GetGameContext()
CVar * diag_terrn_log_roads
GfxScene * GetGfxScene()
CVar * app_state
CVar * mp_state
CVar * sys_cache_dir
CVar * gfx_vegetation_mode
Console * GetConsole()
ScriptEngine * GetScriptEngine()
std::shared_ptr< TObjDocument > TObjDocumentPtr
void HandleGenericException(const std::string &from, BitMask_t flags)
int TerrainEditorObjectID_t
Offset into RoR::TerrainObjectManager::m_editor_objects, use RoR::TERRAINEDITOROBJECTID_INVALID as em...
int ScriptUnitID_t
Unique sequentially generated ID of a loaded and running scriptin session. Use ScriptEngine::getScrip...
static const ActorInstanceID_t ACTORINSTANCEID_INVALID
RefCountingObjectPtr< Actor > ActorPtr
static const ScriptUnitID_t SCRIPTUNITID_INVALID
TObjSpecialObject
@ TRUCK2
Free position (not auto-adjusted to fit terrain or water surface)
@ DEPTHMAP_DISABLED
@ HANDLEGENERICEXCEPTION_CONSOLE
static const TerrainEditorObjectID_t TERRAINEDITOROBJECTID_INVALID
bool asr_terrn_machine
This is a fixed machinery.
Definition SimData.h:852
Ogre::Vector3 asr_position
Definition SimData.h:837
ActorInstanceID_t asr_instance_id
Optional; see ActorManager::GetActorNextInstanceID();.
Definition SimData.h:833
std::string asr_filename
Can be in "Bundle-qualified" format, i.e. "mybundle.zip:myactor.truck".
Definition SimData.h:835
Ogre::Quaternion asr_rotation
Definition SimData.h:838
@ TERRN_DEF
Preloaded with terrain.
bool asr_free_position
Disables the automatic spawn position adjustment.
Definition SimData.h:850
Ogre::Vector3 position
Ogre::Quaternion rotation
Unified game event system - all requests and state changes are reported using a message.
Definition GameContext.h:52
std::list< ODefAnimation > animations
std::list< ODefParticleSys > particle_systems
std::list< ODefCollisionMesh > collision_meshes
struct RoR::ODefDocument::ODefDocumentHeader header
std::list< ODefTexPrint > texture_prints
Section 'drawTextOnMeshTexture'.
std::string mat_name
Section 'setMeshMaterial'.
std::list< std::string > sounds
std::list< ODefPointLight > point_lights
std::list< std::string > groundmodel_files
std::vector< LocalizerType > localizers
std::list< ODefSpotlight > spotlights
std::list< ODefCollisionBox > collision_boxes
std::string mat_name_generate
Section 'generateMaterialShaders'.
Unified 'grass' and 'grass2'.
< Teleport drop location