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
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 "ScriptEngine.h"
41#include "SoundScriptManager.h"
43#include "Terrain.h"
44#include "Terrn2FileFormat.h"
45#include "TObjFileFormat.h"
46#include "Utils.h"
47#include "WriteTextToTexture.h"
48
49#include <RTShaderSystem/OgreRTShaderSystem.h>
50#include <Overlay/OgreFontManager.h>
51
52#ifdef USE_ANGELSCRIPT
54#endif // USE_ANGELSCRIPT
55
56using namespace Ogre;
57using namespace RoR;
58#ifdef USE_PAGED
59using namespace Forests;
60#endif //USE_PAGED
61
62//workaround for pagedgeometry
63inline float getTerrainHeight(Real x, Real z, void* unused = 0)
64{
66}
67
69 terrainManager(terrainManager)
70{
71 m_terrn2_grouping_node = App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->createChildSceneNode(fmt::format("Terrain: {}", terrainManager->GetDef()->name));
72
73 m_procedural_manager = new ProceduralManager(m_terrn2_grouping_node->createChildSceneNode("Procedural Roads"));
74}
75
77{
78 for (MeshObject* mo : m_mesh_objects)
79 {
80 if (mo)
81 delete mo;
82 }
83#ifdef USE_PAGED
84 for (auto geom : m_paged_geometry)
85 {
86 delete geom->getPageLoader();
87 delete geom;
88 }
89#endif //USE_PAGED
90
91 App::GetGfxScene()->GetSceneManager()->destroyAllEntities();
92
94}
95
96void GenerateGridAndPutToScene(Ogre::Vector3 position)
97{
98 Ogre::ColourValue background_color(Ogre::ColourValue::White);
99 Ogre::ColourValue grid_color(0.2f, 0.2f, 0.2f, 1.0f);
100
101 Ogre::ManualObject* mo = new Ogre::ManualObject("ReferenceGrid");
102
103 mo->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST);
104
105 const float step = 1.0f;
106 const size_t count = 50;
107 unsigned int halfCount = count / 2;
108 const float half = (step * count) / 2;
109 const float y = 0;
110 Ogre::ColourValue c;
111 for (size_t i=0; i < count+1; i++)
112 {
113 if (i == halfCount)
114 c = Ogre::ColourValue(1.f, 0.f, 0.f, 1.f);
115 else
116 c = grid_color;
117
118 mo->position(-half, y, -half+(step*i));
119 mo->colour(background_color);
120 mo->position(0, y, -half+(step*i));
121 mo->colour(c);
122 mo->position(0, y, -half+(step*i));
123 mo->colour(c);
124 mo->position(half, y, -half+(step*i));
125 mo->colour(background_color);
126
127 if (i == halfCount)
128 c = Ogre::ColourValue(0,0,1,1.0f);
129 else
130 c = grid_color;
131
132 mo->position(-half+(step*i), y, -half);
133 mo->colour(background_color);
134 mo->position(-half+(step*i), y, 0);
135 mo->colour(c);
136 mo->position(-half+(step*i), y, 0);
137 mo->colour(c);
138 mo->position(-half+(step*i), y, half);
139 mo->colour(background_color);
140 }
141
142 mo->end();
143 mo->setCastShadows(false);
144 Ogre::SceneNode *n = App::GetGameContext()->GetTerrain()->getObjectManager()->getGroupingSceneNode()->createChildSceneNode();
145 n->setPosition(position);
146 n->attachObject(mo);
147 n->setVisible(true);
148}
149
150void TerrainObjectManager::LoadTObjFile(Ogre::String tobj_name)
151{
155
156 TObjDocumentPtr tobj;
157 try
158 {
159 DataStreamPtr stream_ptr = ResourceGroupManager::getSingleton().openResource(
160 tobj_name, this->terrainManager->getCacheEntry()->resource_group);
161 TObjParser parser;
162 parser.Prepare();
163 parser.ProcessOgreStream(stream_ptr.get());
164 tobj = parser.Finalize();
165 m_tobj_cache.push_back(tobj);
166 }
167 catch (...)
168 {
169 HandleGenericException(fmt::format("Loading TObj file '{}'", tobj_name), HANDLEGENERICEXCEPTION_CONSOLE);
170 return;
171 }
172
174 m_tobj_grouping_node = m_terrn2_grouping_node->createChildSceneNode(tobj_name);
175
178
179 // Section 'grid'
180 if (tobj->grid_enabled)
181 {
182 GenerateGridAndPutToScene(tobj->grid_position);
183 }
184
185 // Section 'trees'
186 if (App::gfx_vegetation_mode->getEnum<GfxVegetation>() != GfxVegetation::NONE)
187 {
188 for (TObjTree tree : tobj->trees)
189 {
190 try
191 {
192 this->ProcessTree(
193 tree.yaw_from, tree.yaw_to,
194 tree.scale_from, tree.scale_to,
195 tree.color_map, tree.density_map, tree.tree_mesh, tree.collision_mesh,
196 tree.grid_spacing, tree.high_density,
197 tree.min_distance, tree.max_distance, mapsizex, mapsizez);
198 }
199 catch (...)
200 {
201 RoR::HandleGenericException(fmt::format("Error processing 'trees' line (mesh: {}) from TOBJ file {}", tree.tree_mesh, tobj_name));
202 }
203 }
204 }
205
206 // Section 'grass' / 'grass2'
207 if (App::gfx_vegetation_mode->getEnum<GfxVegetation>() != GfxVegetation::NONE)
208 {
209 for (TObjGrass grass : tobj->grass)
210 {
211 try
212 {
213 this->ProcessGrass(
214 grass.sway_speed, grass.sway_length, grass.sway_distrib, grass.density,
215 grass.min_x, grass.min_y, grass.min_h,
216 grass.max_x, grass.max_y, grass.max_h,
217 grass.material_name, grass.color_map_filename, grass.density_map_filename,
218 grass.grow_techniq, grass.technique, grass.range, mapsizex, mapsizez);
219 }
220 catch (...)
221 {
222 RoR::HandleGenericException(fmt::format("Error processing 'grass' line (material: {}) from TOBJ file {}", grass.material_name, tobj_name));
223 }
224 }
225 }
226
227 // Procedural roads
228 for (ProceduralObjectPtr& po : tobj->proc_objects)
229 {
230 try
231 {
233 }
234 catch (...)
235 {
236 RoR::HandleGenericException(fmt::format("Error processing procedural road {} from TOBJ file {}", po->name, tobj_name));
237 }
238 }
239
240 // Vehicles
241 for (TObjVehicle const& veh : tobj->vehicles)
242 {
243 int tobj_cache_id = (int)m_tobj_cache.size() - 1;
244 this->ProcessPredefinedActor(tobj_cache_id, veh.name, veh.position, veh.tobj_rotation, veh.type);
245 }
246
247 // Entries
248 for (TObjEntry entry : tobj->objects)
249 {
250 try
251 {
252 m_tobj_cache_active_id = (int)m_tobj_cache.size() - 1;
253 size_t num_editor_objects = m_editor_objects.size();
254 this->LoadTerrainObject(entry.odef_name, entry.position, entry.rotation, entry.instance_name, entry.type, entry.rendering_distance);
256 if (m_editor_objects.size() > num_editor_objects)
257 {
258 m_editor_objects.back()->tobj_comments = entry.comments;
259 }
260 }
261 catch (...)
262 {
263 RoR::HandleGenericException(fmt::format("Error processing object line (ODEF: {}) from TOBJ file {}", entry.odef_name, tobj_name));
264 }
265 }
266
267 if (App::diag_terrn_log_roads->getBool())
268 {
270 }
271
272 m_tobj_grouping_node = nullptr;
273}
274
276 float yawfrom, float yawto,
277 float scalefrom, float scaleto,
278 char* ColorMap, char* DensityMap, char* treemesh, char* treeCollmesh,
279 float gridspacing, float highdens,
280 int minDist, int maxDist, int mapsizex, int mapsizez)
281{
282#ifdef USE_PAGED
283 if (strnlen(ColorMap, 3) == 0)
284 {
285 LOG("tree ColorMap map zero!");
286 return;
287 }
288 if (strnlen(DensityMap, 3) == 0)
289 {
290 LOG("tree DensityMap zero!");
291 return;
292 }
293 Forests::DensityMap *densityMap = Forests::DensityMap::load(DensityMap, Forests::CHANNEL_COLOR);
294 if (!densityMap)
295 {
296 LOG("could not load densityMap: "+String(DensityMap));
297 return;
298 }
299 densityMap->setFilter(Forests::MAPFILTER_BILINEAR);
300 //densityMap->setMapBounds(TRect(0, 0, mapsizex, mapsizez));
301
302 PagedGeometry* geom = new PagedGeometry();
303 geom->setTempDir(App::sys_cache_dir->getStr() + PATH_SLASH);
304 geom->setCamera(App::GetCameraManager()->GetCamera());
305 geom->setPageSize(50);
306 geom->setInfinite();
307 Ogre::TRect<Ogre::Real> bounds = TBounds(0, 0, mapsizex, mapsizez);
308 geom->setBounds(bounds);
309
310 //Set up LODs
311 //trees->addDetailLevel<EntityPage>(50);
312 float min = minDist * terrainManager->getPagedDetailFactor();
313 if (min < 10)
314 min = 10;
315 geom->addDetailLevel<BatchPage>(min, min / 2);
316 float max = maxDist * terrainManager->getPagedDetailFactor();
317 if (max < 10)
318 max = 10;
319
320 // Check if farther details level is greater than closer
321 if (max / 10 > min / 2)
322 {
323 geom->addDetailLevel<ImpostorPage>(max, max / 10);
324 }
325
326 TreeLoader2D *treeLoader = new TreeLoader2D(geom, TBounds(0, 0, mapsizex, mapsizez));
327 treeLoader->setMinimumScale(scalefrom);
328 treeLoader->setMaximumScale(scaleto);
329 geom->setPageLoader(treeLoader);
330 treeLoader->setHeightFunction(&getTerrainHeight);
331 if (String(ColorMap) != "none")
332 {
333 treeLoader->setColorMap(ColorMap);
334 }
335
336 Entity* curTree = App::GetGfxScene()->GetSceneManager()->createEntity(String("paged_") + treemesh + TOSTRING(m_paged_geometry.size()), treemesh);
337
338 if (gridspacing > 0)
339 {
340 // grid style
341 for (float x=0; x < mapsizex; x += gridspacing)
342 {
343 for (float z=0; z < mapsizez; z += gridspacing)
344 {
345 float density = densityMap->_getDensityAt_Unfiltered(x, z, bounds);
346 if (density < 0.8f) continue;
347 float nx = x + gridspacing * 0.5f;
348 float nz = z + gridspacing * 0.5f;
349 float yaw = Math::RangeRandom(yawfrom, yawto);
350 float scale = Math::RangeRandom(scalefrom, scaleto);
351 Vector3 pos = Vector3(nx, 0, nz);
352 treeLoader->addTree(curTree, pos, Degree(yaw), (Ogre::Real)scale);
353 if (strlen(treeCollmesh))
354 {
355 pos.y = terrainManager->getHeightAt(pos.x, pos.z);
356 scale *= 0.1f;
357 terrainManager->GetCollisions()->addCollisionMesh(curTree->getName(), String(treeCollmesh), pos, Quaternion(Degree(yaw), Vector3::UNIT_Y), Vector3(scale, scale, scale));
358 }
359 }
360 }
361 }
362 else
363 {
364 float gridsize = 10;
365 if (gridspacing < 0 && gridspacing != 0)
366 {
367 gridsize = -gridspacing;
368 }
369 float hd = highdens;
370 // normal style, random
371 for (float x=0; x < mapsizex; x += gridsize)
372 {
373 for (float z=0; z < mapsizez; z += gridsize)
374 {
375 if (highdens < 0) hd = Math::RangeRandom(0, -highdens);
376 float density = densityMap->_getDensityAt_Unfiltered(x, z, bounds);
377 int numTreesToPlace = (int)((float)(hd) * density * terrainManager->getPagedDetailFactor());
378 float nx=0, nz=0;
379 while(numTreesToPlace-->0)
380 {
381 nx = Math::RangeRandom(x, x + gridsize);
382 nz = Math::RangeRandom(z, z + gridsize);
383 float yaw = Math::RangeRandom(yawfrom, yawto);
384 float scale = Math::RangeRandom(scalefrom, scaleto);
385 Vector3 pos = Vector3(nx, 0, nz);
386 treeLoader->addTree(curTree, pos, Degree(yaw), (Ogre::Real)scale);
387 if (strlen(treeCollmesh))
388 {
389 pos.y = terrainManager->getHeightAt(pos.x, pos.z);
390 terrainManager->GetCollisions()->addCollisionMesh(treemesh, String(treeCollmesh),pos, Quaternion(Degree(yaw), Vector3::UNIT_Y), Vector3(scale, scale, scale));
391 }
392 }
393 }
394 }
395 }
396 m_paged_geometry.push_back(geom);
397#endif //USE_PAGED
398}
399
401 float SwaySpeed, float SwayLength, float SwayDistribution, float Density,
402 float minx, float miny, float minH, float maxx, float maxy, float maxH,
403 char* grassmat, char* colorMapFilename, char* densityMapFilename,
404 int growtechnique, int techn, int range,
405 int mapsizex, int mapsizez)
406{
407#ifdef USE_PAGED
408 //Initialize the PagedGeometry engine
409 try
410 {
411 PagedGeometry *grass = new PagedGeometry(App::GetCameraManager()->GetCamera(), 30);
412 //Set up LODs
413
414 grass->addDetailLevel<GrassPage>(range * terrainManager->getPagedDetailFactor()); // original value: 80
415
416 //Set up a GrassLoader for easy use
417 GrassLoader *grassLoader = new GrassLoader(grass);
418 grass->setPageLoader(grassLoader);
419 grassLoader->setHeightFunction(&getTerrainHeight);
420
421 // render grass at first
422 grassLoader->setRenderQueueGroup(RENDER_QUEUE_MAIN-1);
423
424 GrassLayer* grassLayer = grassLoader->addLayer(grassmat);
425 grassLayer->setHeightRange(minH, maxH);
426 grassLayer->setLightingEnabled(true);
427
428 grassLayer->setAnimationEnabled((SwaySpeed>0));
429 grassLayer->setSwaySpeed(SwaySpeed);
430 grassLayer->setSwayLength(SwayLength);
431 grassLayer->setSwayDistribution(SwayDistribution);
432
433 grassLayer->setDensity(Density * terrainManager->getPagedDetailFactor());
434 if (techn>10)
435 grassLayer->setRenderTechnique(static_cast<GrassTechnique>(techn-10), true);
436 else
437 grassLayer->setRenderTechnique(static_cast<GrassTechnique>(techn), false);
438
439 grassLayer->setMapBounds(TBounds(0, 0, mapsizex, mapsizez));
440
441 if (strcmp(colorMapFilename,"none") != 0)
442 {
443 grassLayer->setColorMap(colorMapFilename);
444 grassLayer->setColorMapFilter(MAPFILTER_BILINEAR);
445 }
446
447 if (strcmp(densityMapFilename,"none") != 0)
448 {
449 grassLayer->setDensityMap(densityMapFilename);
450 grassLayer->setDensityMapFilter(MAPFILTER_BILINEAR);
451 }
452
453 grassLayer->setMinimumSize(minx, miny);
454 grassLayer->setMaximumSize(maxx, maxy);
455
456 // growtechnique
457 if (growtechnique == 0)
458 grassLayer->setFadeTechnique(FADETECH_GROW);
459 else if (growtechnique == 1)
460 grassLayer->setFadeTechnique(FADETECH_ALPHAGROW);
461 else if (growtechnique == 2)
462 grassLayer->setFadeTechnique(FADETECH_ALPHA);
463
464 m_paged_geometry.push_back(grass);
465 }
466 catch(...)
467 {
468 LOG("error loading grass!");
469 }
470#endif //USE_PAGED
471}
472
473void TerrainObjectManager::ProcessPredefinedActor(int tobj_cache_id, const std::string& name, const Ogre::Vector3 position, const Ogre::Vector3 rotation, const TObjSpecialObject type)
474{
475 // Transform TOBJ actor records to EditorObject-s (to be spawned later, if conditions are met).
476 // NOTE: The filename may be in "Bundle-qualified" format, i.e. "mybundle.zip:myactor.truck"
477 // -----------------------------------------------------------------------------------------
478
480 dst->position = position;
481 dst->rotation = rotation;
482 dst->special_object_type = type;
483 dst->name = name;
484 dst->tobj_cache_id = tobj_cache_id;
485 m_editor_objects.push_back(dst);
487}
488
489void TerrainObjectManager::moveObjectVisuals(const String& instancename, const Ogre::Vector3& pos)
490{
491 // Obsolete function kept for backwards-compatibility; does the same as `TerrainEditorObject::setPosition()`
492 // -------------------------------------------------------------------------------------------------------
493
496 {
497 LOG(fmt::format("[RoR] `moveObjectVisuals()`: instance name '{}' not found!", instancename));
498 return;
499 }
500
501 m_editor_objects[id]->setPosition(pos);
502}
503
504void TerrainObjectManager::destroyObject(const String& instancename)
505{
507 if (id == -1)
508 {
509 LOG(fmt::format("[RoR] `destroyObject()`: instance name '{}' not found!", instancename));
510 return;
511 }
512
514
515 if (object->getSpecialObjectType() != TObjSpecialObject::NONE)
516 {
517 // Preloaded actor: despawn it.
518 ROR_ASSERT(!object->static_object_node);
519 ROR_ASSERT(!object->static_collision_tris.size());
520 ROR_ASSERT(!object->static_collision_boxes.size());
521 ActorPtr actor = App::GetGameContext()->GetActorManager()->GetActorById(object->actor_instance_id);
522 if (actor)
523 {
525 }
526 }
527 else
528 {
529 // Static object: Destroy the scene node and everything attached to it.
530 ROR_ASSERT(object->static_object_node);
531 for (Ogre::MovableObject* mova : object->static_object_node->getAttachedObjects())
532 {
533 App::GetGfxScene()->GetSceneManager()->destroyMovableObject(mova);
534 }
535 App::GetGfxScene()->GetSceneManager()->destroySceneNode(object->static_object_node);
536
537 // Undo static collisions
538 for (int tri : object->static_collision_tris)
539 {
541 }
542 for (int box : object->static_collision_boxes)
543 {
545 }
546 }
547
548 // Release the object from editor, if active.
550 {
552 }
553
554 // Forget the object ever existed.
555 m_editor_objects.erase(m_editor_objects.begin() + id);
556}
557
558ODefDocument* TerrainObjectManager::FetchODef(std::string const & odef_name)
559{
560 // Consult cache first
561 auto search_res = m_odef_cache.find(odef_name);
562 if (search_res != m_odef_cache.end())
563 {
564 return search_res->second.get();
565 }
566
567 // Search for the file
568 const std::string filename = odef_name + ".odef";
569 std::string group_name;
570 try
571 {
572 group_name = Ogre::ResourceGroupManager::getSingleton().findGroupContainingResource(filename);
573 }
574 catch (...) // This means "not found"
575 {
576 LOG(fmt::format("[ODEF] Could not find {} in any resource group", filename));
577 return nullptr;
578 }
579
580 try
581 {
582 // Load and parse the file
583 Ogre::DataStreamPtr ds = ResourceGroupManager::getSingleton().openResource(filename, group_name);
584 ODefParser parser;
585 parser.Prepare();
586 parser.ProcessOgreStream(ds.get());
587 std::shared_ptr<ODefDocument> odef = parser.Finalize();
588
589 // Add to cache and return
590 m_odef_cache.insert(std::make_pair(odef_name, odef));
591 return odef.get();
592 }
593 catch (...)
594 {
595 LOG(fmt::format("[ODEF] An exception occurred when loading or parsing {}", filename));
596 return nullptr;
597 }
598}
599
600bool 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 */)
601{
602 if (type == "grid")
603 {
604 // some fast grid object hacks :)
605 for (int x = 0; x < 500; x += 50)
606 {
607 for (int z = 0; z < 500; z += 50)
608 {
609 const String notype = "";
610 LoadTerrainObject(name, pos + Vector3(x, 0.0f, z), rot, name, notype, /*rendering_distance:*/0, enable_collisions, scripthandler, uniquifyMaterial);
611 }
612 }
613 return true;
614 }
615
616 const std::string odefname = name + ".odef"; // for logging
617 ODefDocument* odef = this->FetchODef(name);
618 if (odef == nullptr)
619 {
620 // Only log to console if requested from Console UI or script (debug message to RoR.log is written anyway).
621 if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
622 {
624 fmt::format(_L("Could not load file '{}'"), odefname));
625 }
626 return false;
627 }
628
629 SceneNode* tenode = this->getGroupingSceneNode()->createChildSceneNode();
630
631 MeshObject* mo = nullptr;
632 if (odef->header.mesh_name != "none")
633 {
634 Str<100> ebuf; ebuf << m_entity_counter++ << "-" << odef->header.mesh_name;
636 if (mo->getEntity())
637 {
638 mo->getEntity()->setCastShadows(odef->header.cast_shadows);
639 mo->getEntity()->setRenderingDistance(rendering_distance);
640 m_mesh_objects.push_back(mo);
641 }
642 else
643 {
644 delete mo;
645 // Only log to console if requested from Console UI or script (debug message to RoR.log is written anyway).
646 if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
647 {
649 fmt::format(_L("Could not load mesh '{}' (used by object '{}')"), odef->header.mesh_name, odefname));
650 }
651 }
652 }
653
654 tenode->setScale(odef->header.scale);
655 tenode->setPosition(pos);
656 Quaternion rotation = Quaternion(Degree(rot.x), Vector3::UNIT_X) * Quaternion(Degree(rot.y), Vector3::UNIT_Y) * Quaternion(Degree(rot.z), Vector3::UNIT_Z);
657 tenode->rotate(rotation);
658 tenode->pitch(Degree(-90));
659 tenode->setVisible(true);
660
662 object->name = name;
663 object->instance_name = instancename;
664 object->type = type;
665 object->position = pos;
666 object->rotation = rot;
667 object->initial_position = pos;
668 object->initial_rotation = rot;
669 object->static_object_node = tenode;
670 object->enable_collisions = enable_collisions;
671 object->script_handler = scripthandler;
672 object->tobj_cache_id = m_tobj_cache_active_id;
673 m_editor_objects.push_back(object);
674
675 if (mo && uniquifyMaterial && !instancename.empty())
676 {
677 for (unsigned int i = 0; i < mo->getEntity()->getNumSubEntities(); i++)
678 {
679 SubEntity* se = mo->getEntity()->getSubEntity(i);
680 String matname = se->getMaterialName();
681 String newmatname = matname + "/" + instancename;
682 se->getMaterial()->clone(newmatname);
683 se->setMaterialName(newmatname);
684 }
685 }
686
687 for (LocalizerType type : odef->localizers)
688 {
689 Localizer loc;
690 loc.position = Vector3(pos.x, pos.y, pos.z);
691 loc.rotation = rotation;
692 loc.type = type;
693 m_localizers.push_back(loc);
694 }
695
696 if (odef->mode_standard)
697 {
698 tenode->pitch(Degree(90));
699 }
700
701#ifdef USE_OPENAL
702 if (!App::GetSoundScriptManager()->isDisabled())
703 {
704 for (std::string& snd_name : odef->sounds)
705 {
707 sound->setPosition(tenode->getPosition());
708 sound->start();
709 }
710 }
711#endif //USE_OPENAL
712
713 for (std::string& gmodel_file: odef->groundmodel_files)
714 {
716 }
717
718 bool race_event = !object->instance_name.compare(0, 10, "checkpoint") ||
719 !object->instance_name.compare(0, 4, "race");
720
721 if (race_event)
722 {
723 String type = "checkpoint";
724 auto res = StringUtil::split(object->instance_name, "|");
725 if ((res.size() == 4 && res[2] == "0") || !object->instance_name.compare(0, 4, "race"))
726 {
727 type = "racestart";
728 }
729 int race_id = res.size() > 1 ? StringConverter::parseInt(res[1], -1) : -1;
730 m_map_entities.push_back(SurveyMapEntity(type, /*caption:*/type, fmt::format("icon_{}.dds", type), /*resource_group:*/"", object->position, Ogre::Radian(0), race_id));
731 }
732 else if (object->type != "" && object->type != "-")
733 {
734 String caption = "";
735 if (object->type == "station" || object->type == "hotel" || object->type == "village" ||
736 object->type == "observatory" || object->type == "farm" || object->type == "ship" || object->type == "sign")
737 {
738 caption = object->instance_name + " " + object->type;
739 }
740 m_map_entities.push_back(SurveyMapEntity(object->type, caption, fmt::format("icon_{}.dds", object->type), /*resource_group:*/"", object->position, Ogre::Radian(0), -1));
741 }
742
743 this->ProcessODefCollisionBoxes(object, odef, object, race_event);
744
745 for (ODefCollisionMesh& cmesh : odef->collision_meshes)
746 {
747 if (cmesh.mesh_name == "")
748 {
749 LOG("[ODEF] Skipping collision mesh with empty name. Object: " + odefname);
750 continue;
751 }
752
753 auto gm = terrainManager->GetCollisions()->getGroundModelByString(cmesh.groundmodel_name);
755 odefname,
756 cmesh.mesh_name, pos, tenode->getOrientation(),
757 cmesh.scale, gm, &(object->static_collision_tris));
758 }
759
760 for (ODefParticleSys& psys : odef->particle_systems)
761 {
762
763 // hacky: prevent duplicates
764 String paname = String(psys.instance_name);
765 while (App::GetGfxScene()->GetSceneManager()->hasParticleSystem(paname))
766 paname += "_";
767
768 // create particle system
769 ParticleSystem* pParticleSys = App::GetGfxScene()->GetSceneManager()->createParticleSystem(paname, String(psys.template_name));
770 pParticleSys->setCastShadows(false);
771 pParticleSys->setVisibilityFlags(DEPTHMAP_DISABLED); // disable particles in depthmap
772
773 // Some affectors may need its instance name (e.g. for script feedback purposes)
774#ifdef USE_ANGELSCRIPT
775 unsigned short affCount = pParticleSys->getNumAffectors();
776 ParticleAffector* pAff;
777 for (unsigned short i = 0; i < affCount; ++i)
778 {
779 pAff = pParticleSys->getAffector(i);
780 if (pAff->getType() == "ExtinguishableFire")
781 {
782 ((ExtinguishableFireAffector*)pAff)->setInstanceName(object->instance_name);
783 }
784 }
785#endif // USE_ANGELSCRIPT
786
787 SceneNode* sn = tenode->createChildSceneNode();
788 sn->attachObject(pParticleSys);
789 sn->pitch(Degree(90));
790
792 peo.node = sn;
793 peo.psys = pParticleSys;
794 m_particle_effect_objects.push_back(peo);
795 }
796
797 if (!odef->mat_name.empty())
798 {
799 if (mo->getEntity())
800 {
801 mo->getEntity()->setMaterialName(odef->mat_name);
802 }
803 }
804
805 if (odef->mat_name_generate != "")
806 {
807 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(odef->mat_name_generate,"generatedMaterialShaders");
808 Ogre::RTShader::ShaderGenerator::getSingleton().createShaderBasedTechnique(*mat, Ogre::MaterialManager::DEFAULT_SCHEME_NAME, Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
809 Ogre::RTShader::ShaderGenerator::getSingleton().invalidateMaterial(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, String(odef->mat_name_generate));
810 }
811
812 for (ODefAnimation& anim : odef->animations)
813 {
814 if (tenode && mo->getEntity())
815 {
816 AnimationStateSet *s = mo->getEntity()->getAllAnimationStates();
817 String anim_name_str(anim.name);
818 if (!s->hasAnimationState(anim_name_str))
819 {
820 LOG("[ODEF] animation '" + anim_name_str + "' for mesh: '" + odef->header.mesh_name + "' in odef file '" + name + ".odef' not found!");
821 //continue;
822 }
824 ao.node = tenode;
825 ao.ent = mo->getEntity();
826 ao.speedfactor = anim.speed_min;
827 if (anim.speed_min != anim.speed_max)
828 ao.speedfactor = Math::RangeRandom(anim.speed_min, anim.speed_max);
829 ao.anim = 0;
830 try
831 {
832 ao.anim = mo->getEntity()->getAnimationState(anim_name_str);
833 } catch (...)
834 {
835 ao.anim = 0;
836 }
837 if (!ao.anim)
838 {
839 LOG("[ODEF] animation '" + anim_name_str + "' for mesh: '" + odef->header.mesh_name + "' in odef file '" + name + ".odef' not found!");
840 continue;
841 }
842 ao.anim->setEnabled(true);
843 m_animated_objects.push_back(ao);
844 }
845 }
846
847 for (ODefTexPrint& tex_print : odef->texture_prints)
848 {
849 if (!mo->getEntity())
850 continue;
851 String matName = mo->getEntity()->getSubEntity(0)->getMaterialName();
852 MaterialPtr m = MaterialManager::getSingleton().getByName(matName);
853 if (m.get() == 0)
854 {
855 LOG("[ODEF] problem with drawTextOnMeshTexture command: mesh material not found: "+odefname+" : "+matName);
856 continue;
857 }
858 String texName = m->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureName();
859 Texture* background = (Texture *)TextureManager::getSingleton().getByName(texName).get();
860 if (!background)
861 {
862 LOG("[ODEF] problem with drawTextOnMeshTexture command: mesh texture not found: "+odefname+" : "+texName);
863 continue;
864 }
865
866 static int textureNumber = 0;
867 textureNumber++;
868 char tmpTextName[256] = "", tmpMatName[256] = "";
869 sprintf(tmpTextName, "TextOnTexture_%d_Texture", textureNumber);
870 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)
871 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);
872 if (texture.get() == 0)
873 {
874 LOG("[ODEF] problem with drawTextOnMeshTexture command: could not create texture: "+odefname+" : "+tmpTextName);
875 continue;
876 }
877
878 Str<200> text_buf; text_buf << tex_print.text;
879
880 // check if we got a template argument
881 if (!strncmp(text_buf.GetBuffer(), "{{argument1}}", 13))
882 {
883 text_buf.Clear();
884 text_buf << instancename;
885 }
886
887 // replace '_' with ' '
888 char *text_pointer = text_buf.GetBuffer();
889 while (*text_pointer!=0) {if (*text_pointer=='_') *text_pointer=' ';text_pointer++;};
890
891 String font_name_str(tex_print.font_name);
892 Ogre::Font* font = (Ogre::Font *)FontManager::getSingleton().getByName(font_name_str).get();
893 if (!font)
894 {
895 LOG("[ODEF] problem with drawTextOnMeshTexture command: font not found: "+odefname+" : "+font_name_str);
896 continue;
897 }
898
899 //Draw the background to the new texture
900 texture->getBuffer()->blit(background->getBuffer());
901
902 float x = background->getWidth() * tex_print.x;
903 float y = background->getHeight() * tex_print.y;
904 float w = background->getWidth() * tex_print.w;
905 float h = background->getHeight() * tex_print.h;
906
907 ColourValue color(tex_print.r, tex_print.g, tex_print.b, tex_print.a);
908 Ogre::Box box = Ogre::Box((size_t)x, (size_t)y, (size_t)(x+w), (size_t)(y+h));
909 WriteToTexture(text_buf.ToCStr(), texture, box, font, color, tex_print.font_size, tex_print.font_dpi, tex_print.option);
910
911 // we can save it to disc for debug purposes:
912 //SaveImage(texture, "test.png");
913
914 m->clone(tmpMatName);
915 MaterialPtr mNew = MaterialManager::getSingleton().getByName(tmpMatName);
916 mNew->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(tmpTextName);
917
918 mo->getEntity()->setMaterialName(String(tmpMatName));
919 }
920
921 for (ODefSpotlight& spotl: odef->spotlights)
922 {
923 Light* spotLight = App::GetGfxScene()->GetSceneManager()->createLight();
924
925 spotLight->setType(Light::LT_SPOTLIGHT);
926 spotLight->setPosition(spotl.pos);
927 spotLight->setDirection(spotl.dir);
928 spotLight->setAttenuation(spotl.range, 1.0, 0.3, 0.0);
929 spotLight->setDiffuseColour(spotl.color);
930 spotLight->setSpecularColour(spotl.color);
931 spotLight->setSpotlightRange(Degree(spotl.angle_inner), Degree(spotl.angle_outer));
932
933 BillboardSet* lflare = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1);
934 lflare->createBillboard(spotl.pos, spotl.color);
935 lflare->setMaterialName("tracks/flare");
936 lflare->setVisibilityFlags(DEPTHMAP_DISABLED);
937
938 float fsize = Math::Clamp(spotl.range / 10, 0.2f, 2.0f);
939 lflare->setDefaultDimensions(fsize, fsize);
940
941 SceneNode *sn = tenode->createChildSceneNode();
942 sn->attachObject(spotLight);
943 sn->attachObject(lflare);
944 }
945
946 for (ODefPointLight& plight : odef->point_lights)
947 {
948 Light* pointlight = App::GetGfxScene()->GetSceneManager()->createLight();
949
950 pointlight->setType(Light::LT_POINT);
951 pointlight->setPosition(plight.pos);
952 pointlight->setDirection(plight.dir);
953 pointlight->setAttenuation(plight.range, 1.0, 0.3, 0.0);
954 pointlight->setDiffuseColour(plight.color);
955 pointlight->setSpecularColour(plight.color);
956
957 BillboardSet* lflare = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1);
958 lflare->createBillboard(plight.pos, plight.color);
959 lflare->setMaterialName("tracks/flare");
960 lflare->setVisibilityFlags(DEPTHMAP_DISABLED);
961
962 float fsize = Math::Clamp(plight.range / 10, 0.2f, 2.0f);
963 lflare->setDefaultDimensions(fsize, fsize);
964
965 SceneNode *sn = tenode->createChildSceneNode();
966 sn->attachObject(pointlight);
967 sn->attachObject(lflare);
968 }
969
970 return true;
971}
972
973bool TerrainObjectManager::LoadTerrainScript(const Ogre::String& filename)
974{
976
977 m_angelscript_grouping_node = m_terrn2_grouping_node->createChildSceneNode(filename);
978 ScriptUnitID_t result = App::GetScriptEngine()->loadScript(filename);
980
981 return result != SCRIPTUNITID_INVALID;
982}
983
985{
986 if (m_animated_objects.size() == 0)
987 return;
988
989 std::vector<AnimatedObject>::iterator it;
990
991 for (it = m_animated_objects.begin(); it != m_animated_objects.end(); it++)
992 {
993 if (it->anim && it->speedfactor != 0)
994 {
995 Real time = dt * it->speedfactor;
996 it->anim->addTime(time);
997 }
998 }
999}
1000
1002{
1004 {
1005 if (peo.psys)
1006 {
1008 }
1009 }
1010}
1011
1013{
1014 for (Terrn2Telepoint& telepoint: terrainManager->GetDef()->telepoints)
1015 {
1016 m_map_entities.push_back(SurveyMapEntity("telepoint", telepoint.name, "icon_telepoint.dds", /*resource_group:*/"", telepoint.position, Ogre::Radian(0), -1));
1017 }
1018}
1019
1021{
1022 // We need the 'rot_yxz' flag - look up the TOBJ document in cache
1023 if (object->tobj_cache_id == -1 || object->tobj_cache_id >= (int)m_tobj_cache.size())
1024 {
1026 fmt::format("Assuming no 'rot_yxz' when spawning preselected actor '{}' - TOBJ document not found", object->getName()));
1027 return false;
1028 }
1029 else
1030 {
1031 return m_tobj_cache[object->tobj_cache_id]->rot_yxz;
1032 }
1033}
1034
1035
1037{
1038 // For terrain editor to work, all preloaded actors must be spawned.
1039 // Most will spawn with terrain, however, some may be excluded for reasons.
1040 // -----------------------------------------------------------------------
1041
1042 const bool rot_yxz = GetEditorObjectFlagRotYXZ(object);
1043
1044 // Check if already spawned.
1045 if (object->actor_instance_id == ACTORINSTANCEID_INVALID)
1046 {
1047 // Not spawned yet - assign custom ID so that Terrain Editor can reset and move the actor.
1048 object->actor_instance_id = App::GetGameContext()->GetActorManager()->GetActorNextInstanceId();
1049 }
1050 else
1051 {
1052 // Spawned before; check if still existing and respawn if not.
1054 object->actor_instance_id);
1055 if (actor != ActorManager::ACTORPTR_NULL)
1056 {
1057 return; // We're done.
1058 }
1059 }
1060
1062 rq->asr_instance_id = object->actor_instance_id;
1063 rq->asr_position = object->position;
1064 rq->asr_filename = object->name;
1065 rq->asr_rotation = TObjParser::CalcRotation(object->rotation, rot_yxz);
1067 rq->asr_free_position = (object->special_object_type == TObjSpecialObject::TRUCK2);
1068 rq->asr_terrn_machine = (object->special_object_type == TObjSpecialObject::MACHINE);
1070}
1071
1073{
1074 // in netmode, don't load other actors!
1075 if (RoR::App::mp_state->getEnum<MpState>() == RoR::MpState::CONNECTED)
1076 {
1077 return;
1078 }
1079
1081 {
1082 if (object->special_object_type == TObjSpecialObject::NONE)
1083 {
1084 continue; // Skip static objects
1085 }
1086
1087 if ((object->special_object_type == TObjSpecialObject::BOAT) && (terrainManager->getWater() == nullptr))
1088 {
1089 continue; // Don't spawn boats if there's no water.
1090 }
1091
1092 this->SpawnSinglePredefinedActor(object);
1093 }
1094}
1095
1097{
1098#ifdef USE_PAGED
1099 for (auto geom : m_paged_geometry)
1100 {
1101 geom->update();
1102 }
1103#endif //USE_PAGED
1104 this->UpdateAnimatedObjects(dt);
1106
1107 return true;
1108}
1109
1111{
1112 for (ODefCollisionBox& cbox : odef->collision_boxes)
1113 {
1114 if (params->enable_collisions && (App::sim_races_enabled->getBool() || !race_event))
1115 {
1116 // Validate AABB (minimum corners must be less or equal to maximum corners)
1117 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)
1118 {
1119 // Only log to console if invoked from Console UI or script.
1120 std::string msg = "Skipping invalid collision box, min: " + TOSTRING(cbox.aabb_min) + ", max: " + TOSTRING(cbox.aabb_max);
1121 if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
1122 {
1124 }
1125 else
1126 {
1127 LOG(fmt::format("[ODEF] {}", msg));
1128 }
1129 continue;
1130 }
1131
1133 cbox.is_rotating, cbox.is_virtual, params->position, params->rotation,
1134 cbox.aabb_min, cbox.aabb_max, cbox.box_rot, cbox.event_name,
1135 params->instance_name, cbox.reverb_preset_name, cbox.force_cam_pos,
1136 cbox.cam_pos, cbox.scale, cbox.direction, cbox.event_filter,
1137 params->script_handler);
1138
1139 obj->static_collision_boxes.push_back(boxnum);
1140 }
1141 }
1142}
1143
1145{
1146 // This has no effect on rendering, it just helps users to diagnose the scene graph.
1147 // --------------------------------------------------------------------------------
1148
1151 else if (m_tobj_grouping_node)
1152 return m_tobj_grouping_node;
1153 else if (m_terrn2_grouping_node)
1155 else
1156 return App::GetGfxScene()->GetSceneManager()->getRootSceneNode();
1157}
1158
1160{
1161 // Is this the right 'ModernC++' approach? :/
1162 auto itor = std::find_if(m_editor_objects.begin(), m_editor_objects.end(),
1163 [needle_instance_name](TerrainEditorObjectPtr& obj) { return obj->instance_name == needle_instance_name; });
1164 if (itor != m_editor_objects.end())
1165 {
1166 return static_cast<int>(std::distance(m_editor_objects.begin(), itor));
1167 }
1168 else
1169 {
1171 }
1172}
1173
Central state/object manager and communications hub.
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
#define TOSTRING(x)
Definition Application.h:57
void LOG(const char *msg)
Legacy alias - formerly a macro.
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)
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)
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, 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:564
TerrainObjectManager * getObjectManager()
Definition Terrain.h:80
TerrainGeometryManager * getGeometryManager()
Definition Terrain.h:79
float getHeightAt(float x, float z)
Definition Terrain.cpp:512
float getPagedDetailFactor() const
Definition Terrain.h:95
CacheEntryPtr getCacheEntry()
Definition Terrain.cpp:592
Collisions * GetCollisions()
Definition Terrain.h:86
TerrainEditor * GetTerrainEditor()
Definition Terrain.h:85
Wavefield * getWater()
Definition Terrain.h:87
Terrn2DocumentPtr GetDef()
Definition Terrain.cpp:584
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 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, int growtechnique, int techn, int range, int mapsizex, int mapsizez)
std::vector< MeshObject * > m_mesh_objects
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 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)
void destroyObject(const Ogre::String &instancename)
Ogre::SceneNode * m_angelscript_grouping_node
For even more readable scene graph (via inspector script)
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)
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