Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
ActorSpawner.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
22
27
28
29#include "ActorSpawner.h"
30
31#include "AddonPartFileFormat.h"
32#include "AppContext.h"
33#include "Application.h"
34#include "AirBrake.h"
35#include "Airfoil.h"
36#include "ApproxMath.h"
37#include "AutoPilot.h"
38#include "Actor.h"
39#include "ActorManager.h"
40#include "BitFlags.h"
41#include "Buoyance.h"
42#include "CacheSystem.h"
43#include "CameraManager.h"
44#include "CmdKeyInertia.h"
45#include "Collisions.h"
46#include "DashBoardManager.h"
47#include "Differentials.h"
48#include "Engine.h"
49#include "FlexAirfoil.h"
50#include "FlexBody.h"
51#include "FlexMesh.h"
52#include "FlexMeshWheel.h"
53#include "FlexObj.h"
54#include "GameContext.h"
55#include "GUIManager.h"
56#include "GfxActor.h"
57#include "GfxScene.h"
58#include "Console.h"
59#include "InputEngine.h"
60#include "Language.h"
61#include "MeshObject.h"
62#include "PointColDetector.h"
63#include "ScrewProp.h"
64#include "ScriptEngine.h"
65#include "Skidmark.h"
66#include "SkinFileFormat.h"
67#include "SlideNode.h"
68#include "SoundScriptManager.h"
69#include "Terrain.h"
70#include "TorqueCurve.h"
71#include "TuneupFileFormat.h"
72#include "TurboJet.h"
73#include "TurboProp.h"
74#include "Utils.h"
75#include "VehicleAI.h"
76
77#include <OgreMaterialManager.h>
78#include <OgreSceneManager.h>
79#include <OgreMovableObject.h>
80#include <OgreParticleSystem.h>
81#include <OgreEntity.h>
82#include <climits>
83#include <fmt/format.h>
84
85using namespace RoR;
86
87/* -------------------------------------------------------------------------- */
88// Prepare for loading
89/* -------------------------------------------------------------------------- */
90
91void ActorSpawner::ConfigureSections(Ogre::String const & sectionconfig, RigDef::DocumentPtr def)
92{
93 m_selected_modules.push_back(def->root_module);
94 if (sectionconfig != "")
95 {
96 auto result = def->user_modules.find(sectionconfig);
97
98 if (result != def->user_modules.end())
99 {
100 m_selected_modules.push_back(result->second);
101 LOG(" == ActorSpawner: Module added to configuration: " + sectionconfig);
102 }
103 else
104 {
105 this->AddMessage(Message::TYPE_WARNING, "Selected module not found: " + sectionconfig);
106 }
107 }
108}
109
111{
112 if (actor->m_working_tuneup_def)
113 {
115
116 for (const std::string& addonpart: actor->m_working_tuneup_def->use_addonparts)
117 {
118 CacheEntryPtr addonpart_entry = App::GetCacheSystem()->FindEntryByFilename(LT_AddonPart, /*partial:*/false, addonpart);
119 if (addonpart_entry)
120 {
121 App::GetCacheSystem()->LoadResource(addonpart_entry);
122
123 AddonPartUtility util;
124 util.ResolveUnwantedAndTweakedElements(actor->m_working_tuneup_def, addonpart_entry);
125 auto module = util.TransformToRigDefModule(addonpart_entry);
126 if (module)
127 {
128 m_selected_modules.push_back(module);
129 actor->m_used_addonpart_entries.push_back(addonpart_entry);
130 LOG(" == ActorSpawner: Addon part added to configuration: " + addonpart);
131 }
132 else
133 {
134 this->AddMessage(Message::TYPE_WARNING, fmt::format(_L("Could not load addon part '{}' (file '{}')"), addonpart_entry->dname, addonpart_entry->fname));
135 }
136 }
137 else
138 {
139 this->AddMessage(Message::TYPE_WARNING, fmt::format(_L("Requested addon part '{}' is not installed"), addonpart));
140 }
141 }
142 }
143}
144
146{
147 for (auto& module: m_selected_modules)
148 {
149 for (RigDef::Assetpack const& assetpack: module->assetpacks)
150 {
151 App::GetCacheSystem()->LoadAssetPack(actor->getUsedActorEntry(), assetpack.filename);
152
153 // Record used assetpack entry
154 CacheEntryPtr assetpack_entry = App::GetCacheSystem()->FindEntryByFilename(LT_AssetPack, /*partial:*/false, assetpack.filename);
155 if (assetpack_entry)
156 {
157 actor->m_used_assetpack_entries.push_back(assetpack_entry);
158 }
159 }
160 }
161}
162
164{
165 // 'nodes'
166 req.num_nodes += module_def->nodes.size();
167 for (auto& def: module_def->nodes)
168 {
170 {
171 req.num_beams += 1;
172 }
173 }
174
175 // 'beams'
176 req.num_beams += module_def->beams.size();
177
178 // 'ties'
179 req.num_beams += module_def->ties.size();
180
181 // 'ropes'
182 req.num_beams += module_def->ropes.size();
183
184 // 'hydros'
185 req.num_beams += module_def->hydros.size();
186
187 // 'triggers'
188 req.num_beams += module_def->triggers.size();
189 req.num_shocks += module_def->triggers.size();
190
191 // 'animators'
192 req.num_beams += module_def->animators.size();
193
194 // 'cinecam'
195 req.num_nodes += module_def->cinecam.size();
196 req.num_beams += module_def->cinecam.size() * 8;
197
198 // 'shocks', 'shocks2', 'shocks3'
199 req.num_beams += module_def->shocks.size();
200 req.num_shocks += module_def->shocks.size();
201 req.num_beams += module_def->shocks2.size();
202 req.num_shocks += module_def->shocks2.size();
203 req.num_beams += module_def->shocks3.size();
204 req.num_shocks += module_def->shocks3.size();
205
206 // 'commands' and 'commands2' (unified)
207 req.num_beams += module_def->commands2.size();
208
209 // 'rotators'
210 req.num_rotators += module_def->rotators.size();
211 req.num_rotators += module_def->rotators2.size();
212
213 // 'wings'
214 req.num_wings += module_def->wings.size();
215
216 // 'wheels'
217 for (RigDef::Wheel& wheel: module_def->wheels)
218 {
219 req.num_nodes += wheel.num_rays * 2; // BuildWheelObjectAndNodes()
220 req.num_beams += wheel.num_rays * ((wheel.rigidity_node.IsValidAnyState()) ? 9 : 8); // BuildWheelBeams()
221 }
222
223 // 'wheels2'
224 for (RigDef::Wheel2& wheel2: module_def->wheels2)
225 {
226 req.num_nodes += wheel2.num_rays * 4;
227 // Rim beams: num_rays*10 (*11 with valid rigidity_node)
228 // Tyre beams: num_rays*14
229 req.num_beams += wheel2.num_rays * ((wheel2.rigidity_node.IsValidAnyState()) ? 25 : 24);
230 }
231
232 // 'meshwheels' & 'meshwheels2'
233 for (RigDef::MeshWheel& meshwheel: module_def->meshwheels)
234 {
235 req.num_nodes += meshwheel.num_rays * 2; // BuildWheelObjectAndNodes()
236 req.num_beams += meshwheel.num_rays * ((meshwheel.rigidity_node.IsValidAnyState()) ? 9 : 8); // BuildWheelBeams()
237 }
238 for (RigDef::MeshWheel2& meshwheel2: module_def->meshwheels2)
239 {
240 req.num_nodes += meshwheel2.num_rays * 2; // BuildWheelObjectAndNodes()
241 req.num_beams += meshwheel2.num_rays * ((meshwheel2.rigidity_node.IsValidAnyState()) ? 9 : 8); // BuildWheelBeams()
242 }
243
244 // 'flexbodywheels'
245 for (RigDef::FlexBodyWheel& flexwheel: module_def->flexbodywheels)
246 {
247 req.num_nodes += flexwheel.num_rays * 4;
248 // Rim beams: num_rays*8
249 // Tyre beams: num_rays*10 (num_rays*11 with valid rigidity_node)
250 // Support beams: num_rays*2
251 req.num_beams += flexwheel.num_rays * ((flexwheel.rigidity_node.IsValidAnyState()) ? 21 : 20);
252 }
253
254 // 'airbrakes'
255 req.num_airbrakes += module_def->airbrakes.size();
256
257 // 'fixes'
258 req.num_fixes += module_def->fixes.size();
259}
260
262{
264 for (auto module: m_selected_modules) // _Root_ module is included
265 {
266 this->CalcMemoryRequirements(req, module.get());
267 }
268
269 // Allocate memory as needed
270 m_actor->ar_beams = new beam_t[req.num_beams];
271 m_actor->ar_beams_invisible.resize(req.num_beams, false);
272 m_actor->ar_beams_user_defined.resize(req.num_beams, false);
273
274 m_actor->ar_nodes = new node_t[req.num_nodes];
275 m_actor->ar_nodes_id = new int[req.num_nodes];
276 for (size_t i = 0; i < req.num_nodes; ++i)
277 {
278 m_actor->ar_nodes_id[i] = -1;
279 }
280 m_actor->ar_nodes_name = new std::string[req.num_nodes];
281 m_actor->ar_nodes_spawn_offsets = new Ogre::Vector3[req.num_nodes];
284 m_actor->ar_nodes_options.resize(req.num_nodes, 0);
285
286 if (req.num_shocks > 0)
288
289 if (req.num_rotators > 0)
291
292 if (req.num_wings > 0)
293 m_actor->ar_wings = new wing_t[req.num_wings];
294
295 m_actor->ar_minimass.resize(req.num_nodes);
296
299 memset(m_actor->ar_collcabs, 0, sizeof(int) * MAX_CABS);
303 memset(m_actor->ar_buoycabs, 0, sizeof(int) * MAX_CABS);
305 memset(m_actor->ar_buoycab_types, 0, sizeof(int) * MAX_CABS);
306 memset(m_actor->m_skid_trails, 0, sizeof(Skidmark *) * (MAX_WHEELS*2));
307
308 m_actor->authors.clear();
309
312
318 m_actor->ar_origin=Ogre::Vector3::ZERO;
319 m_actor->m_slidenodes.clear();
322
324 m_actor->m_fusealge_airfoil = nullptr;
325 m_actor->m_fusealge_front = nullptr;
326 m_actor->m_fusealge_back = nullptr;
328 m_actor->ar_brake_force=30000.0;
330
332
335 for (int i = 0; i < MAX_CAMERAS; ++i)
336 {
340 }
341
342#ifdef USE_ANGELSCRIPT
344#endif // USE_ANGELSCRIPT
345
347 m_actor->alb_minspeed = 0.0f;
348 m_actor->alb_mode = false;
349 m_actor->alb_nodash = true;
350 m_actor->alb_notoggle = false;
351 m_actor->alb_pulse_time = 2000.0f;
352 m_actor->alb_pulse_state = false;
353 m_actor->alb_ratio = 1.0f;
354 m_actor->alb_timer = 0.0f;
356
357 m_actor->cc_mode = false;
358 m_actor->cc_can_brake = false;
359 m_actor->cc_target_rpm = 0.0f;
360 m_actor->cc_target_speed = 0.0f;
362
364
366
368
369 m_actor->sl_enabled = false;
370 m_actor->sl_speed_limit = 0.f;
371
372 m_actor->tc_mode = false;
373 m_actor->tc_nodash = true;
374 m_actor->tc_notoggle = false;
375 m_actor->tc_pulse_time = 2000.0f;
376 m_actor->tc_pulse_state = false;
377 m_actor->tc_ratio = 1.f;
378 m_actor->tc_timer = 0.f;
379
381
382 /* Collisions */
383
384 if (!App::sim_no_collisions->getBool())
385 {
387 }
388
389 if (!App::sim_no_self_collisions->getBool())
390 {
392 }
393
395
396 // Lights mode
398
400
402
403 m_managedmat_placeholder_template = Ogre::MaterialManager::getSingleton().getByName("rigsofrods/managedmaterial-placeholder"); // Built-in
404
407 {
408 m_simple_material_base = Ogre::MaterialManager::getSingleton().getByName("tracks/simple"); // Built-in material
410 {
412 "Failed to load built-in material 'tracks/simple'; disabling 'SimpleMaterials'");
414 }
415 }
416
419}
420
422{
423 // we should post-process the torque curve if existing
424 if (m_actor->ar_engine)
425 {
427 if (result)
428 {
430 if (result == 1)
431 {
432 AddMessage(Message::TYPE_ERROR, "TorqueCurve: Points (rpm) must be in an ascending order. Using default curve");
433 }
434 }
435
436 //Gearbox
437 m_actor->ar_engine->setAutoMode(App::sim_gearbox_mode->getEnum<SimGearboxMode>());
438 }
439
440 // Sanitize trigger_cmdshort and trigger_cmdlong
441 for (int i=0; i<m_actor->ar_num_beams; i++)
442 {
443 shock_t* shock = m_actor->ar_beams[i].shock;
444 if (shock && ((shock->flags & SHOCK_FLAG_TRG_BLOCKER) || (shock->flags & SHOCK_FLAG_TRG_BLOCKER_A)))
445 {
446 shock->trigger_cmdshort = std::min(shock->trigger_cmdshort, m_actor->ar_num_beams - i - 1);
447 shock->trigger_cmdlong = std::min(shock->trigger_cmdlong , m_actor->ar_num_beams - i - 1);
448 }
449 }
450
451 //calculate gwps height offset
452 //get a starting value
454 //start at 0 to avoid a crash whith a 1-node truck
455 for (int i=0; i<m_actor->ar_num_nodes; i++)
456 {
457 // scan and store the y-coord for the lowest node of the truck
459 {
461 }
462 }
463
467
469
470 // Calculate mass of each wheel (without rim)
471 for (int i = 0; i < m_actor->ar_num_wheels; i++)
472 {
473 m_actor->ar_wheels[i].wh_mass = 0.0f;
474 for (int j = 0; j < m_actor->ar_wheels[i].wh_num_nodes; j++)
475 {
477 }
478 }
479
481 {
482 float proped_wheels_radius_sum = 0.0f;
483 for (int i = 0; i < m_actor->ar_num_wheels; i++)
484 {
486 {
487 proped_wheels_radius_sum += m_actor->ar_wheels[i].wh_radius;
488 }
489 }
490 m_actor->m_avg_proped_wheel_radius = proped_wheels_radius_sum / m_actor->m_num_proped_wheels;
491 }
492
493 // Automatically build interwheel differentials from proped wheel pairs
494 if (m_actor->m_num_wheel_diffs == 0)
495 {
496 for (int i = 1; i < m_actor->m_num_proped_wheels; i++)
497 {
498 if (i % 2)
499 {
500 Differential *diff = new Differential();
501
502 diff->di_idx_1 = m_actor->m_proped_wheel_pairs[i - 1];
503 diff->di_idx_2 = m_actor->m_proped_wheel_pairs[i - 0];
504
506
509 }
510 }
511 }
512
513 // Automatically build interaxle differentials from interwheel differentials pairs
514 if (m_actor->m_num_axle_diffs == 0)
515 {
516 for (int i = 1; i < m_actor->m_num_wheel_diffs; i++)
517 {
519 {
522 if ((a1 == i - 1) && (a2 == i - 0))
523 continue;
524 }
525
526 Differential *diff = new Differential();
527
528 diff->di_idx_1 = i - 1;
529 diff->di_idx_2 = i - 0;
530
533 else
535
538 }
539 }
540
541 // Automatically build an additional interaxle differential for the transfer case
543 {
544 Differential *diff = new Differential();
549 }
550
552 {
554 // Step 1: Find a suitable camera node dir
555 float max_dist = 0.0f;
556 NodeNum_t furthest_node = 0;
557 for (int i = 0; i < m_actor->ar_num_nodes; i++)
558 {
559 float dist = m_actor->ar_nodes[i].RelPosition.squaredDistance(ref);
560 if (dist > max_dist)
561 {
562 max_dist = dist;
563 furthest_node = (NodeNum_t)i;
564 }
565 }
566 m_actor->ar_main_camera_node_dir = furthest_node;
567 // Step 2: Correct the misalignment
568 Ogre::Vector3 dir = m_actor->ar_nodes[furthest_node].RelPosition - ref;
569 float offset = atan2(dir.dotProduct(Ogre::Vector3::UNIT_Z), dir.dotProduct(Ogre::Vector3::UNIT_X));
570 m_actor->ar_main_camera_dir_corr = Ogre::Quaternion(Ogre::Radian(offset), Ogre::Vector3::UNIT_Y);
571 }
572
574 {
575 // store the y-difference between the trucks lowest node and the campos-node for the gwps system
577 }
578 else
579 {
580 //this can not be an airplane, just set it to 0.
582 }
583
584 //cameras workaround
585 for (int i=0; i<m_actor->ar_num_cameras; i++)
586 {
589 Ogre::Vector3 cross = dir_node_offset.crossProduct(roll_node_offset);
590
591 m_actor->ar_camera_node_roll_inv[i]=cross.y > 0;
593 {
594 AddMessage(Message::TYPE_WARNING, "camera definition is probably invalid and has been corrected. It should be center, back, left");
595 }
596 }
597
598 //wing closure
599 if (m_first_wing_index!=-1)
600 {
601 if (m_actor->ar_autopilot != nullptr)
602 {
608 );
609 }
610 //inform wing segments
612
615 //wash calculator
617 }
618
620
621 // Set the engine air intake node to the camera ref node - user can override via `ACTORSIMATTR_ENGOPTION_AIR_INTAKE_NODE` if desired.
622 // For electric engine, leave it as NODENUM_INVALID (engine needs no air intake).
624 {
626 }
627
629
631}
632
633/* -------------------------------------------------------------------------- */
634// Processing functions and utilities.
635/* -------------------------------------------------------------------------- */
636
638{
639 //we will compute wash
640 int w,p;
641 for (p=0; p<m_actor->ar_num_aeroengines; p++)
642 {
643 Ogre::Vector3 prop=m_actor->ar_nodes[m_actor->ar_aeroengines[p]->getNoderef()].RelPosition;
644 float radius=m_actor->ar_aeroengines[p]->getRadius();
645 for (w=0; w<m_actor->ar_num_wings; w++)
646 {
647 //left wash
649 //check if wing is near enough along X (less than 15m back)
650 if (wcent.x>prop.x && wcent.x<prop.x+15.0)
651 {
652 //check if it's okay vertically
653 if (wcent.y>prop.y-radius && wcent.y<prop.y+radius)
654 {
655 //okay, compute wash coverage ratio along Z
656 float wleft=(m_actor->ar_nodes[m_actor->ar_wings[w].fa->nfld].RelPosition).z;
657 float wright=(m_actor->ar_nodes[m_actor->ar_wings[w].fa->nfrd].RelPosition).z;
658 float pleft=prop.z+radius;
659 float pright=prop.z-radius;
660 float aleft=wleft;
661 if (pleft<aleft) aleft=pleft;
662 float aright=wright;
663 if (pright>aright) aright=pright;
664 if (aright<aleft)
665 {
666 //we have a wash
667 float wratio=(aleft-aright)/(wleft-wright);
668 m_actor->ar_wings[w].fa->addwash(p, wratio);
669 Ogre::String msg = "Wing "+TOSTRING(w)+" is washed by prop "+TOSTRING(p)+" at "+TOSTRING((float)(wratio*100.0))+"%";
671 }
672 }
673 }
674 }
675 }
676}
677
679{
680 NodeNum_t front,back,ref;
681 front = GetNodeIndexOrThrow(def.front_node);
682 back = GetNodeIndexOrThrow(def.back_node);
684
685 Turbojet* tj = new Turbojet(m_actor, front, back, ref, def);
686
687 // Visuals
688 std::string nozzle_name = this->ComposeName("nozzle @ turbojet", m_actor->ar_num_aeroengines);
689 Ogre::Entity* nozzle_ent = App::GetGfxScene()->GetSceneManager()->createEntity(nozzle_name, "nozzle.mesh", m_custom_resource_group);
690 this->SetupNewEntity(nozzle_ent, Ogre::ColourValue(1, 0.5, 0.5));
691 Ogre::Entity* afterburn_ent = nullptr;
692 if (def.wet_thrust > 0.f)
693 {
694 std::string flame_name = this->ComposeName("ab flame @ turbojet", m_actor->ar_num_aeroengines);
695 afterburn_ent = App::GetGfxScene()->GetSceneManager()->createEntity(flame_name, "abflame.mesh", m_custom_resource_group);
696 this->SetupNewEntity(afterburn_ent, Ogre::ColourValue(1, 1, 0));
697 }
698 std::string propname = this->ComposeName("turbojet", m_actor->ar_num_aeroengines);
699 tj->tjet_visual.SetNodes(front, back, ref);
701 propname, nozzle_ent, afterburn_ent);
703 {
704 tj->tjet_visual.SetVisible(true);
705 }
706
710 {
712 }
713
715}
716
717std::string ActorSpawner::ComposeName(const std::string& object, int number /* = -1 */)
718{
719 // Under OGRE, each scene node must have a GLOBALLY unique name, even if under different parents.
720 // For that reason the names are intentionally repetitive - the critical part is the Instance ID.
721 // ----------------------------------------------------------------------------------------------
722
723 if (number != -1)
724 {
725 return fmt::format("{}#{} ({} [Instance ID {}])",
726 object, number, m_actor->getTruckFileName(), m_actor->getInstanceId());
727 }
728 else
729 {
730 return fmt::format("{} ({} [Instance ID {}])",
732 }
733}
734
752
754{
755 // Load actor script
756 // We have to push messages to load actor scripts because
757 // this code can be called from a script execution context
758 // (e.g. using game.spawnTruck()).
759 // If we are in a script context, calling loadScript() directly
760 // will crash the game immediately, since AngelScript doesn't
761 // allow scripts to load other scripts.
764 req->lsr_filename = def.filename;
767 // ActorManager::DeleteActorInternal() will unload scripts later.
768}
769
771{
772 //parse fusedrag
773 NodeNum_t front_node_idx = GetNodeIndexOrThrow(def.front_node);
774 float width = 1.f;
775 float factor = 1.f;
776 char fusefoil[256];
777 strncpy(fusefoil, def.airfoil_name.c_str(), 255);
778
779 if (def.autocalc)
780 {
781 // fusedrag autocalculation
782
783 // calculate fusedrag by truck size
784 factor = def.area_coefficient;
785 width = (m_fuse_z_max - m_fuse_z_min) * (m_fuse_y_max - m_fuse_y_min) * factor;
786
787 m_actor->m_fusealge_airfoil = new Airfoil(fusefoil);
788
789 m_actor->m_fusealge_front = & m_actor->ar_nodes[front_node_idx];
790 m_actor->m_fusealge_back = & m_actor->ar_nodes[front_node_idx]; // This equals v0.38 / v0.4.0.7, but it's probably a bug
791 m_actor->m_fusealge_width = width;
792 AddMessage(Message::TYPE_INFO, "Fusedrag autocalculation size: "+TOSTRING(width)+" m^2");
793 }
794 else
795 {
796 // original fusedrag calculation
797
798 width = def.approximate_width;
799
800 m_actor->m_fusealge_airfoil = new Airfoil(fusefoil);
801
802 m_actor->m_fusealge_front = & m_actor->ar_nodes[front_node_idx];
803 m_actor->m_fusealge_back = & m_actor->ar_nodes[front_node_idx]; // This equals v0.38 / v0.4.0.7, but it's probably a bug
804 m_actor->m_fusealge_width = width;
805 }
806}
807
809 NodeNum_t ref_node_index,
810 NodeNum_t back_node_index,
811 NodeNum_t blade_1_node_index,
812 NodeNum_t blade_2_node_index,
813 NodeNum_t blade_3_node_index,
814 NodeNum_t blade_4_node_index,
815 NodeNum_t couplenode_index,
816 bool is_turboprops,
817 Ogre::String const & airfoil,
818 float power,
819 float pitch
820 )
821{
822 int aeroengine_index = m_actor->ar_num_aeroengines;
823
824 Turboprop* turbo_prop = new Turboprop(
825 m_actor,
826 this->ComposeName("turboprop", aeroengine_index).c_str(),
827 ref_node_index,
828 back_node_index,
829 blade_1_node_index,
830 blade_2_node_index,
831 blade_3_node_index,
832 blade_4_node_index,
833 couplenode_index,
834 power,
835 airfoil,
837 ! is_turboprops,
838 pitch
839 );
840
844
845 /* Autopilot */
847 {
849 }
850
851 /* Visuals */
852 float scale = m_actor->ar_nodes[ref_node_index].RelPosition.distance(m_actor->ar_nodes[blade_1_node_index].RelPosition) / 2.25f;
853 for (RoR::Prop& prop: m_actor->m_gfx_actor->m_props)
854 {
855 if ((prop.pp_node_ref == ref_node_index) && (prop.pp_aero_propeller_blade || prop.pp_aero_propeller_spin))
856 {
857 prop.pp_scene_node->scale(scale, scale, scale);
858 prop.pp_aero_engine_idx = aeroengine_index;
859 }
860 }
861}
862
864{
865 const NodeNum_t p3_node_index = (def.blade_tip_nodes[2].IsValidAnyState()) ? GetNodeIndexOrThrow(def.blade_tip_nodes[2]) : -1;
866 const NodeNum_t p4_node_index = (def.blade_tip_nodes[3].IsValidAnyState()) ? GetNodeIndexOrThrow(def.blade_tip_nodes[3]) : -1;
867 const NodeNum_t couple_node_index = (def.couple_node.IsValidAnyState()) ? GetNodeIndexOrThrow(def.couple_node) : -1;
868
874 p3_node_index,
875 p4_node_index,
876 couple_node_index,
877 true,
878 def.airfoil,
880 -10
881 );
882}
883
884void ActorSpawner::ProcessDescription(Ogre::String const& line)
885{
886 m_actor->m_description.push_back(line);
887}
888
890{
891 const NodeNum_t p3_node_index = (def.blade_tip_nodes[2].IsValidAnyState()) ? GetNodeIndexOrThrow(def.blade_tip_nodes[2]) : -1;
892 const NodeNum_t p4_node_index = (def.blade_tip_nodes[3].IsValidAnyState()) ? GetNodeIndexOrThrow(def.blade_tip_nodes[3]) : -1;
893 const NodeNum_t couple_node_index = (def.couple_node.IsValidAnyState()) ? GetNodeIndexOrThrow(def.couple_node) : -1;
894
900 p3_node_index,
901 p4_node_index,
902 couple_node_index,
903 false,
904 def.airfoil,
906 def.pitch
907 );
908}
909
911{
912 const int airbrake_idx = static_cast<int>(m_actor->ar_airbrakes.size());
913 Airbrake* ab = new Airbrake(
914 m_actor,
915 this->ComposeName("airbrake", airbrake_idx).c_str(),
916 airbrake_idx,
921 def.offset,
922 def.width,
923 def.height,
926 def.texcoord_x1,
927 def.texcoord_y1,
928 def.texcoord_x2,
929 def.texcoord_y2,
931 );
932 m_actor->ar_airbrakes.push_back(ab);
933
934 AirbrakeGfx abx;
935 // entity
936 abx.abx_entity = ab->ec;
937 ab->ec = nullptr;
938 // mesh
939 abx.abx_mesh = ab->msh;
940 ab->msh.reset();
941 // scenenode
942 abx.abx_scenenode = ab->snode;
943 ab->snode = nullptr;
944 // offset
945 abx.abx_offset = ab->offset;
946 ab->offset = Ogre::Vector3::ZERO;
947 // Nodes - just copy
948 abx.abx_ref_node = ab->noderef->pos;
949 abx.abx_x_node = ab->nodex->pos;
950 abx.abx_y_node = ab->nodey->pos;
951
952 m_actor->m_gfx_actor->m_gfx_airbrakes.push_back(abx);
953}
954
956{
957 if ((m_first_wing_index != -1) && (m_actor->ar_wings[m_actor->ar_num_wings - 1].fa == nullptr))
958 {
959 this->AddMessage(Message::TYPE_ERROR, "Unable to process wing, previous wing has no Airfoil");
960 return;
961 }
962
963 if (def.airfoil == "") // May happen for malformed `wings` entry in truckfile
964 {
965 this->AddMessage(Message::TYPE_ERROR, "Unable to process wing, no Airfoil defined");
966 return;
967 }
968
969 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
970
971 NodeNum_t node1 = this->GetNodeIndexOrThrow(def.nodes[1]);
972
973 const std::string wing_name = this->ComposeName("wing", m_actor->ar_num_wings);
974 auto flex_airfoil = new FlexAirfoil(
975 wing_name,
976 m_actor,
977 this->GetNodeIndexOrThrow(def.nodes[0]),
978 node1,
979 this->GetNodeIndexOrThrow(def.nodes[2]),
980 this->GetNodeIndexOrThrow(def.nodes[3]),
981 this->GetNodeIndexOrThrow(def.nodes[4]),
982 this->GetNodeIndexOrThrow(def.nodes[5]),
983 this->GetNodeIndexOrThrow(def.nodes[6]),
984 this->GetNodeIndexOrThrow(def.nodes[7]),
986 Ogre::Vector2(def.tex_coords[0], def.tex_coords[1]),
987 Ogre::Vector2(def.tex_coords[2], def.tex_coords[3]),
988 Ogre::Vector2(def.tex_coords[4], def.tex_coords[5]),
989 Ogre::Vector2(def.tex_coords[6], def.tex_coords[7]),
990 (char)def.control_surface,
991 def.chord_point,
992 def.min_deflection,
993 def.max_deflection,
994 def.airfoil,
995 def.efficacy_coef,
997 );
998
999 Ogre::Entity* entity = nullptr;
1000 try
1001 {
1002 const std::string wing_instance_name = this->ComposeName("entity @ wing", m_actor->ar_num_wings);
1003 entity = App::GetGfxScene()->GetSceneManager()->createEntity(wing_instance_name, wing_name);
1004 m_actor->m_deletion_entities.emplace_back(entity);
1005 this->SetupNewEntity(entity, Ogre::ColourValue(0.5, 1, 0));
1006 }
1007 catch (...)
1008 {
1009 this->AddMessage(Message::TYPE_ERROR, std::string("Failed to load mesh (flexbody wing): ") + wing_name);
1010 delete flex_airfoil;
1011 return;
1012 }
1013
1014 // induced drag
1015 if (m_first_wing_index == -1)
1016 {
1019 m_actor->ar_nodes[flex_airfoil->nfld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nfrd].AbsPosition,
1020 m_actor->ar_nodes[flex_airfoil->nbld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nbrd].AbsPosition
1021 );
1022 }
1023 else
1024 {
1025 wing_t & previous_wing = m_actor->ar_wings[m_actor->ar_num_wings - 1];
1026
1027 if (node1 != previous_wing.fa->nfld)
1028 {
1029 wing_t & start_wing = m_actor->ar_wings[m_first_wing_index];
1030
1031 //discontinuity
1032 //inform wing segments
1033 float span = m_actor->ar_nodes[start_wing.fa->nfrd].RelPosition.distance(m_actor->ar_nodes[previous_wing.fa->nfld].RelPosition );
1034
1035 start_wing.fa->enableInducedDrag(span, m_wing_area, false);
1036 previous_wing.fa->enableInducedDrag(span, m_wing_area, true);
1037
1038 //we want also to add positional lights for first wing
1040 {
1041 //Left green
1042 m_airplane_left_light=previous_wing.fa->nfld;
1043 RoR::Prop left_green_prop;
1044
1045 left_green_prop.pp_node_ref=previous_wing.fa->nfld;
1046 left_green_prop.pp_node_x=previous_wing.fa->nflu;
1047 left_green_prop.pp_node_y=previous_wing.fa->nfld; //ignored
1048 left_green_prop.pp_offset.x=0.5;
1049 left_green_prop.pp_offset.y=0.0;
1050 left_green_prop.pp_offset.z=0.0;
1051 left_green_prop.pp_beacon_rot_angle[0]=0.0;
1052 left_green_prop.pp_beacon_rot_rate[0]=1.0;
1053 left_green_prop.pp_beacon_type='L';
1054 left_green_prop.pp_beacon_light[0]=nullptr; //no light
1055 //the flare billboard
1056 left_green_prop.pp_beacon_scene_node[0] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("left green flare @ wing", m_actor->ar_num_wings));
1057 left_green_prop.pp_beacon_bbs[0]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(this->ComposeName("left green flare bbs @ wing", m_actor->ar_num_wings),1);
1058 left_green_prop.pp_beacon_bbs[0]->createBillboard(0,0,0);
1059 if (left_green_prop.pp_beacon_bbs[0])
1060 {
1061 left_green_prop.pp_beacon_bbs[0]->setVisibilityFlags(DEPTHMAP_DISABLED);
1062 left_green_prop.pp_beacon_bbs[0]->setMaterialName("tracks/greenflare");
1063 left_green_prop.pp_beacon_scene_node[0]->attachObject(left_green_prop.pp_beacon_bbs[0]);
1064 }
1065 left_green_prop.pp_beacon_scene_node[0]->setVisible(false);
1066 left_green_prop.pp_beacon_bbs[0]->setDefaultDimensions(0.5, 0.5);
1067 m_actor->m_gfx_actor->m_props.push_back(left_green_prop);
1068
1069 //Left flash
1070 RoR::Prop left_flash_prop;
1071
1072 left_flash_prop.pp_node_ref=previous_wing.fa->nbld;
1073 left_flash_prop.pp_node_x=previous_wing.fa->nblu;
1074 left_flash_prop.pp_node_y=previous_wing.fa->nbld; //ignored
1075 left_flash_prop.pp_offset.x=0.5;
1076 left_flash_prop.pp_offset.y=0.0;
1077 left_flash_prop.pp_offset.z=0.0;
1078 left_flash_prop.pp_beacon_rot_angle[0]=0.5; //alt
1079 left_flash_prop.pp_beacon_rot_rate[0]=1.0;
1080 left_flash_prop.pp_beacon_type='w';
1081 //light
1082 left_flash_prop.pp_beacon_light[0]=App::GetGfxScene()->GetSceneManager()->createLight(this->ComposeName("left flash light @ wing", m_actor->ar_num_wings));
1083 left_flash_prop.pp_beacon_light[0]->setType(Ogre::Light::LT_POINT);
1084 left_flash_prop.pp_beacon_light[0]->setDiffuseColour( Ogre::ColourValue(1.0, 1.0, 1.0));
1085 left_flash_prop.pp_beacon_light[0]->setSpecularColour( Ogre::ColourValue(1.0, 1.0, 1.0));
1086 left_flash_prop.pp_beacon_light[0]->setAttenuation(50.0, 1.0, 0.3, 0.0);
1087 left_flash_prop.pp_beacon_light[0]->setCastShadows(false);
1088 left_flash_prop.pp_beacon_light[0]->setVisible(false);
1089 //the flare billboard
1090 left_flash_prop.pp_beacon_scene_node[0] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("left flash flare @ wing", m_actor->ar_num_wings));
1091 left_flash_prop.pp_beacon_bbs[0]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(this->ComposeName("left flash flare bbs @ wing", m_actor->ar_num_wings),1);
1092 left_flash_prop.pp_beacon_bbs[0]->createBillboard(0,0,0);
1093 if (left_flash_prop.pp_beacon_bbs[0])
1094 {
1095 left_flash_prop.pp_beacon_bbs[0]->setVisibilityFlags(DEPTHMAP_DISABLED);
1096 left_flash_prop.pp_beacon_bbs[0]->setMaterialName("tracks/flare");
1097 left_flash_prop.pp_beacon_scene_node[0]->attachObject(left_flash_prop.pp_beacon_bbs[0]);
1098 }
1099 left_flash_prop.pp_beacon_scene_node[0]->setVisible(false);
1100 left_flash_prop.pp_beacon_bbs[0]->setDefaultDimensions(1.0, 1.0);
1101 m_actor->m_gfx_actor->m_props.push_back(left_flash_prop);
1102
1103 //Right red
1104 m_airplane_right_light=previous_wing.fa->nfrd;
1105 RoR::Prop right_red_prop;
1106
1107
1108 right_red_prop.pp_node_ref=start_wing.fa->nfrd;
1109 right_red_prop.pp_node_x=start_wing.fa->nfru;
1110 right_red_prop.pp_node_y=start_wing.fa->nfrd; //ignored
1111 right_red_prop.pp_offset.x=0.5;
1112 right_red_prop.pp_offset.y=0.0;
1113 right_red_prop.pp_offset.z=0.0;
1114 right_red_prop.pp_beacon_rot_angle[0]=0.0;
1115 right_red_prop.pp_beacon_rot_rate[0]=1.0;
1116 right_red_prop.pp_beacon_type='R';
1117 right_red_prop.pp_beacon_light[0]=nullptr; /* No light */
1118 //the flare billboard
1119 right_red_prop.pp_beacon_scene_node[0] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("right red flare @ wing", m_actor->ar_num_wings));
1120 right_red_prop.pp_beacon_bbs[0]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(this->ComposeName("right red flare bbs @ wing", m_actor->ar_num_wings),1);
1121 right_red_prop.pp_beacon_bbs[0]->createBillboard(0,0,0);
1122 if (right_red_prop.pp_beacon_bbs[0])
1123 {
1124 right_red_prop.pp_beacon_bbs[0]->setVisibilityFlags(DEPTHMAP_DISABLED);
1125 right_red_prop.pp_beacon_bbs[0]->setMaterialName("tracks/redflare");
1126 right_red_prop.pp_beacon_scene_node[0]->attachObject(right_red_prop.pp_beacon_bbs[0]);
1127 }
1128 right_red_prop.pp_beacon_scene_node[0]->setVisible(false);
1129 right_red_prop.pp_beacon_bbs[0]->setDefaultDimensions(0.5, 0.5);
1130 m_actor->m_gfx_actor->m_props.push_back(right_red_prop);
1131
1132 //Right flash
1133 RoR::Prop right_flash_prop;
1134
1135 right_flash_prop.pp_node_ref=start_wing.fa->nbrd;
1136 right_flash_prop.pp_node_x=start_wing.fa->nbru;
1137 right_flash_prop.pp_node_y=start_wing.fa->nbrd; //ignored
1138 right_flash_prop.pp_offset.x=0.5;
1139 right_flash_prop.pp_offset.y=0.0;
1140 right_flash_prop.pp_offset.z=0.0;
1141 right_flash_prop.pp_beacon_rot_angle[0]=0.5; //alt
1142 right_flash_prop.pp_beacon_rot_rate[0]=1.0;
1143 right_flash_prop.pp_beacon_type='w';
1144 //light
1145 right_flash_prop.pp_beacon_light[0]=App::GetGfxScene()->GetSceneManager()->createLight(this->ComposeName("right flash flare light @ wing", m_actor->ar_num_wings));
1146 right_flash_prop.pp_beacon_light[0]->setType(Ogre::Light::LT_POINT);
1147 right_flash_prop.pp_beacon_light[0]->setDiffuseColour( Ogre::ColourValue(1.0, 1.0, 1.0));
1148 right_flash_prop.pp_beacon_light[0]->setSpecularColour( Ogre::ColourValue(1.0, 1.0, 1.0));
1149 right_flash_prop.pp_beacon_light[0]->setAttenuation(50.0, 1.0, 0.3, 0.0);
1150 right_flash_prop.pp_beacon_light[0]->setCastShadows(false);
1151 right_flash_prop.pp_beacon_light[0]->setVisible(false);
1152 //the flare billboard
1153 right_flash_prop.pp_beacon_scene_node[0] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("right flash flare @ wing", m_actor->ar_num_wings));
1154 right_flash_prop.pp_beacon_bbs[0]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(this->ComposeName("right flash flare bbs @ wing", m_actor->ar_num_wings),1);
1155 right_flash_prop.pp_beacon_bbs[0]->createBillboard(0,0,0);
1156 if (right_flash_prop.pp_beacon_bbs[0] != nullptr)
1157 {
1158 right_flash_prop.pp_beacon_bbs[0]->setVisibilityFlags(DEPTHMAP_DISABLED);
1159 right_flash_prop.pp_beacon_bbs[0]->setMaterialName("tracks/flare");
1160 right_flash_prop.pp_beacon_scene_node[0]->attachObject(right_flash_prop.pp_beacon_bbs[0]);
1161 }
1162 right_flash_prop.pp_beacon_scene_node[0]->setVisible(false);
1163 right_flash_prop.pp_beacon_bbs[0]->setDefaultDimensions(1.0, 1.0);
1164 m_actor->m_gfx_actor->m_props.push_back(right_flash_prop);
1165
1166 m_generate_wing_position_lights = false; // Already done
1167 }
1168
1171 m_actor->ar_nodes[flex_airfoil->nfld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nfrd].AbsPosition,
1172 m_actor->ar_nodes[flex_airfoil->nbld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nbrd].AbsPosition
1173 );
1174 }
1175 else
1176 {
1178 m_actor->ar_nodes[flex_airfoil->nfld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nfrd].AbsPosition,
1179 m_actor->ar_nodes[flex_airfoil->nbld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nbrd].AbsPosition
1180 );
1181 }
1182 }
1183
1184 // Add new wing to rig
1185 m_actor->ar_wings[m_actor->ar_num_wings].fa = flex_airfoil;
1187 m_actor->ar_wings[m_actor->ar_num_wings].cnode->attachObject(entity);
1188
1190}
1191
1192float ActorSpawner::ComputeWingArea(Ogre::Vector3 const & ref, Ogre::Vector3 const & x, Ogre::Vector3 const & y, Ogre::Vector3 const & aref)
1193{
1194 return (((x-ref).crossProduct(y-ref)).length()+((x-aref).crossProduct(y-aref)).length())*0.5f;
1195}
1196
1198{
1199#ifdef USE_OPENAL
1200 NodeNum_t node = ResolveNodeRef(def.node);
1201 if (node == NODENUM_INVALID)
1202 {
1203 return;
1204 }
1206 m_actor,
1208 node,
1209 def.mode
1210 );
1211#endif // USE_OPENAL
1212}
1213
1214void ActorSpawner::AddSoundSourceInstance(ActorPtr const& vehicle, Ogre::String const & sound_script_name, int node_index, int type)
1215{
1216#ifdef USE_OPENAL
1217 AddSoundSource(vehicle, App::GetSoundScriptManager()->createInstance(sound_script_name, vehicle->ar_instance_id), (NodeNum_t)node_index);
1218#endif // USE_OPENAL
1219}
1220
1221void ActorSpawner::AddSoundSource(ActorPtr const& vehicle, SoundScriptInstancePtr sound_script, NodeNum_t node_index, int type)
1222{
1223 if (! CheckSoundScriptLimit(vehicle, 1))
1224 {
1225 return;
1226 }
1227
1228 if (sound_script == nullptr)
1229 {
1230 return;
1231 }
1232
1233 vehicle->ar_soundsources[vehicle->ar_num_soundsources].ssi=sound_script;
1234 vehicle->ar_soundsources[vehicle->ar_num_soundsources].nodenum=node_index;
1235 vehicle->ar_soundsources[vehicle->ar_num_soundsources].type=type;
1236 vehicle->ar_num_soundsources++;
1237}
1238
1240{
1241#ifdef USE_OPENAL
1243 m_actor,
1246 -2
1247 );
1248#endif // USE_OPENAL
1249}
1250
1252{
1253 auto itor = def.nodes.begin();
1254 auto end = def.nodes.end();
1255 for(; itor != end; ++itor)
1256 {
1257 if (! CheckCameraRailLimit(1))
1258 {
1259 return;
1260 }
1263 }
1264}
1265
1274
1279
1281{
1282 if (def.key == "helpMaterial")
1283 {
1284 // Don't override value from 'help' section
1286 }
1287 else if (def.key == "speedoMax")
1288 {
1289 float maxKph = PARSEREAL(def.value);
1290 if (maxKph > 10 && maxKph < 32000)
1291 {
1293 }
1294 else
1295 {
1297 fmt::format("Invalid 'speedoMax' ({}), allowed range is <10 -32000>, using default ({})", maxKph, DEFAULT_SPEEDO_MAX_KPH));
1299 }
1300 }
1301 else if (def.key == "useMaxRPM")
1302 {
1304 }
1305 else if (def.key == "shifterAnimTime")
1306 {
1308 }
1309 else if (def.key == "tachoMaterial")
1310 {
1312 }
1313 else if (def.key == "speedoMaterial")
1314 {
1316 }
1317
1318 // NOTE: Dashboard layouts are processed later
1319}
1320
1322{
1323 NodeNum_t node = GetNodeIndexOrThrow(node_ref);
1324 m_actor->ar_nodes[node].nd_immovable = true;
1325}
1326
1328{
1330 {
1331 return;
1332 }
1333
1334 const ExhaustID_t exhaust_id = (ExhaustID_t)m_actor->m_gfx_actor->m_exhausts.size();
1335 Exhaust exhaust;
1336 exhaust.emitterNode = this->GetNodeIndexOrThrow(def.reference_node);
1338
1339 std::string template_name = def.particle_name;
1340 if (template_name == "" || template_name == "default")
1341 {
1342 template_name = "tracks/Smoke"; // defined in `particles/smoke.particle`
1343 }
1344 exhaust.particleSystemName = template_name;
1345
1347 {
1348 std::string name = this->ComposeName(template_name.c_str(), (int)m_actor->m_gfx_actor->m_exhausts.size());
1349 exhaust.smoker = this->CreateParticleSystem(name, template_name);
1350 if (exhaust.smoker == nullptr)
1351 {
1352 std::stringstream msg;
1353 msg << "Failed to create particle system '" << name << "' (template: '" << template_name <<"')";
1354 AddMessage(Message::TYPE_ERROR, msg.str());
1355 return;
1356 }
1357
1358 exhaust.smokeNode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("exhaust", (int)m_actor->m_gfx_actor->m_exhausts.size()));
1359 exhaust.smokeNode->attachObject(exhaust.smoker);
1360 exhaust.smokeNode->setPosition(m_actor->ar_nodes[exhaust.emitterNode].AbsPosition);
1361
1362 m_actor->m_gfx_actor->SetNodeHot(exhaust.emitterNode, true);
1363 m_actor->m_gfx_actor->SetNodeHot(exhaust.directionNode, true);
1364 }
1365
1366 m_actor->m_gfx_actor->m_exhausts.push_back(exhaust);
1367}
1368
1370{
1371 auto module_itor = m_selected_modules.begin();
1372 auto module_end = m_selected_modules.end();
1373 for (; module_itor != module_end; ++module_itor)
1374 {
1375 if (! module_itor->get()->submesh_groundmodel.empty())
1376 {
1377 return module_itor->get()->submesh_groundmodel[0];
1378 }
1379 }
1380 return std::string();
1381};
1382
1384{
1385 if (! CheckSubmeshLimit(1))
1386 {
1387 return;
1388 }
1389
1390 /* TEXCOORDS */
1391
1392 std::vector<RigDef::Texcoord>::iterator texcoord_itor = def.texcoords.begin();
1393 for ( ; texcoord_itor != def.texcoords.end(); texcoord_itor++)
1394 {
1395 if (! CheckTexcoordLimit(1))
1396 {
1397 break;
1398 }
1399
1400 CabTexcoord texcoord;
1401 texcoord.node_id = GetNodeIndexOrThrow(texcoord_itor->node);
1402 texcoord.texcoord_u = texcoord_itor->u;
1403 texcoord.texcoord_v = texcoord_itor->v;
1404 m_oldstyle_cab_texcoords.push_back(texcoord);
1405 }
1406
1407 /* CAB */
1408
1409 auto cab_itor = def.cab_triangles.begin();
1410 auto cab_itor_end = def.cab_triangles.end();
1411 for ( ; cab_itor != cab_itor_end; ++cab_itor)
1412 {
1413 if (! CheckCabLimit(1))
1414 {
1415 return;
1416 }
1417 else if (m_actor->ar_num_collcabs >= MAX_CABS)
1418 {
1419 std::stringstream msg;
1420 msg << "Collcab limit (" << MAX_CABS << ") exceeded";
1421 AddMessage(Message::TYPE_ERROR, msg.str());
1422 return;
1423 }
1424
1425 bool mk_buoyance = false;
1426
1427 m_actor->ar_cabs[m_actor->ar_num_cabs*3]=GetNodeIndexOrThrow(cab_itor->nodes[0]);
1428 m_actor->ar_cabs[m_actor->ar_num_cabs*3+1]=GetNodeIndexOrThrow(cab_itor->nodes[1]);
1429 m_actor->ar_cabs[m_actor->ar_num_cabs*3+2]=GetNodeIndexOrThrow(cab_itor->nodes[2]);
1430
1431 // TODO: Clean this up properly ~ ulteq 10/2018
1432 if (BITMASK_IS_1(cab_itor->options, RigDef::Cab::OPTION_c_CONTACT) ||
1433 BITMASK_IS_1(cab_itor->options, RigDef::Cab::OPTION_p_10xTOUGHER) ||
1435 {
1438 }
1439 if (BITMASK_IS_1(cab_itor->options, RigDef::Cab::OPTION_b_BUOYANT))
1440 {
1444 mk_buoyance = true;
1445 }
1447 {
1451 mk_buoyance = true;
1452 }
1454 {
1458 mk_buoyance = true;
1459 }
1460
1461 if (BITMASK_IS_1(cab_itor->options, RigDef::Cab::OPTION_D_CONTACT_BUOYANT) ||
1464 {
1465
1467 {
1468 std::stringstream msg;
1469 msg << "Collcab limit (" << MAX_CABS << ") exceeded";
1470 AddMessage(Message::TYPE_ERROR, msg.str());
1471 return;
1472 }
1473 else if (m_actor->ar_num_buoycabs >= MAX_CABS)
1474 {
1475 std::stringstream msg;
1476 msg << "Buoycab limit (" << MAX_CABS << ") exceeded";
1477 AddMessage(Message::TYPE_ERROR, msg.str());
1478 return;
1479 }
1480
1486 mk_buoyance = true;
1487 }
1488
1489 if (mk_buoyance && (m_actor->m_buoyance == nullptr))
1490 {
1491 Buoyance* buoy = new Buoyance(App::GetGfxScene()->GetDustPool("splash"), App::GetGfxScene()->GetDustPool("ripple"));
1492 m_actor->m_buoyance.reset(buoy);
1493 }
1495 }
1496
1497 //close the current mesh
1498 CabSubmesh submesh;
1499 submesh.texcoords_pos = m_oldstyle_cab_texcoords.size();
1500 submesh.cabs_pos = static_cast<unsigned int>(m_actor->ar_num_cabs);
1502 m_oldstyle_cab_submeshes.push_back(submesh);
1503
1504 /* BACKMESH */
1505
1506 if (def.backmesh)
1507 {
1508
1509 // Check limit
1510 if (! CheckCabLimit(1))
1511 {
1512 return;
1513 }
1514
1515 // === add an extra front mesh ===
1516 //texcoords
1517 int uv_start = (m_oldstyle_cab_submeshes.size()==1) ? 0 : static_cast<int>((m_oldstyle_cab_submeshes.rbegin()+1)->texcoords_pos);
1518 for (size_t i=uv_start; i<m_oldstyle_cab_submeshes.back().texcoords_pos; i++)
1519 {
1521 }
1522 //cab
1523 int cab_start = (m_oldstyle_cab_submeshes.size()==1) ? 0 : static_cast<int>((m_oldstyle_cab_submeshes.rbegin()+1)->cabs_pos);
1524 for (size_t i=cab_start; i<m_oldstyle_cab_submeshes.back().cabs_pos; i++)
1525 {
1530 }
1531 // Finalize
1532 CabSubmesh submesh;
1534 submesh.texcoords_pos = m_oldstyle_cab_texcoords.size();
1535 submesh.cabs_pos = static_cast<unsigned int>(m_actor->ar_num_cabs);
1536 m_oldstyle_cab_submeshes.push_back(submesh);
1537
1538 // === add an extra back mesh ===
1539 //texcoords
1540 uv_start = (m_oldstyle_cab_submeshes.size()==1) ? 0 : static_cast<int>((m_oldstyle_cab_submeshes.rbegin()+1)->texcoords_pos);
1541 for (size_t i=uv_start; i<m_oldstyle_cab_submeshes.back().texcoords_pos; i++)
1542 {
1544 }
1545
1546 //cab
1547 cab_start = (m_oldstyle_cab_submeshes.size()==1) ? 0 : static_cast<int>((m_oldstyle_cab_submeshes.rbegin()+1)->cabs_pos);
1548 for (size_t i=cab_start; i<m_oldstyle_cab_submeshes.back().cabs_pos; i++)
1549 {
1554 }
1555
1556 //close the current mesh
1557 CabSubmesh submesh2;
1558 submesh2.texcoords_pos = m_oldstyle_cab_texcoords.size();
1559 submesh2.cabs_pos = static_cast<unsigned int>(m_actor->ar_num_cabs);
1561 m_oldstyle_cab_submeshes.push_back(submesh2);
1562 }
1563}
1564
1566{
1567 const FlexbodyID_t flexbody_id = (FlexbodyID_t)m_actor->m_gfx_actor->m_flexbodies.size();
1568
1569 // Check if disabled by .tuneup mod.
1571 {
1572 // Create placeholder
1573 m_actor->m_gfx_actor->m_flexbodies.emplace_back(new FlexBody(FlexBody::PlaceholderType::TUNING_REMOVED_PLACEHOLDER, flexbody_id, def.mesh_name));
1574 return;
1575 }
1576
1577 // Collect 'forset' nodes
1578 std::vector<unsigned int> node_indices;
1579 bool nodes_found = true;
1580 for (auto& node_def: def.node_list)
1581 {
1582 NodeNum_t node = this->ResolveNodeRef(node_def);
1583 if (node == NODENUM_INVALID)
1584 {
1585 nodes_found = false;
1586 break;
1587 }
1588 node_indices.push_back(node);
1589 }
1590
1591 // Resolve 'forvert' nodes
1592 std::vector<ForvertTempData> forverts_tmp;
1593 for (size_t i = 0; i < def.forvert.size(); i++)
1594 {
1595 const auto& forvert_item = def.forvert[i];
1596 ForvertTempData fvtd;
1597 fvtd.vert_index = forvert_item.vert_index;
1598 fvtd.nref = this->ResolveNodeRef(forvert_item.node_ref);
1599 fvtd.nx = this->ResolveNodeRef(forvert_item.node_x);
1600 fvtd.ny = this->ResolveNodeRef(forvert_item.node_y);
1601 if (fvtd.nref == NODENUM_INVALID || fvtd.nx == NODENUM_INVALID || fvtd.ny == NODENUM_INVALID)
1602 {
1603 // Note: although the `RigDef::Node::Ref` was designed to handle numbered/named nodes transparently,
1604 // here we assume nobody will use named nodes with 'forvert' as they're unpopular due to many bugs.
1606 fmt::format("Flexbody {}({}) 'forvert'{}/{}: Some nodes not resolved (nref {}->{}, nx {}->{}, ny {}->{}).",
1607 i, def.forvert.size(), (int)flexbody_id, def.mesh_name,
1608 forvert_item.node_ref.Num(), fvtd.nref != NODENUM_INVALID,
1609 forvert_item.node_x.Num(), fvtd.nx != NODENUM_INVALID,
1610 forvert_item.node_y.Num(), fvtd.ny != NODENUM_INVALID
1611 ));
1612 }
1613 else
1614 {
1615 forverts_tmp.push_back(fvtd);
1616 }
1617 }
1618
1619 if (! nodes_found)
1620 {
1621 this->AddMessage(Message::TYPE_ERROR, "Failed to collect nodes from 'forset' at flexbody: " + def.mesh_name);
1622 // Create placeholder too keep the order of flexbodies (helps Tuning and diagnostics)
1623 m_actor->m_gfx_actor->m_flexbodies.emplace_back(new FlexBody(FlexBody::PlaceholderType::FAULTY_FORSET_PLACEHOLDER, flexbody_id, def.mesh_name));
1624 return;
1625 }
1626
1627 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
1628
1629 try
1630 {
1632 (FlexbodyID_t)m_actor->m_gfx_actor->m_flexbodies.size(),
1633 this->GetNodeIndexOrThrow(def.reference_node),
1634 this->GetNodeIndexOrThrow(def.x_axis_node),
1635 this->GetNodeIndexOrThrow(def.y_axis_node),
1638 node_indices,
1639 forverts_tmp,
1641 TuneupUtil::getTweakedFlexbodyMediaRG(m_actor->getWorkingTuneupDef(), flexbody_id, 0, this->GetCurrentElementMediaRG())
1642 );
1643
1644 // Dynamic visibility - same as with props
1647 m_actor->m_gfx_actor->m_flexbodies.emplace_back(flexbody);
1648 }
1649 catch (Ogre::Exception&)
1650 {
1651 // Ogre::Exception description already logged by OGRE
1652 this->AddMessage(Message::TYPE_ERROR, "Failed to create flexbody '" + def.mesh_name + "'");
1653 // Create placeholder too keep the order of flexbodies (helps Tuning and diagnostics)
1654 m_actor->m_gfx_actor->m_flexbodies.emplace_back(new FlexBody(FlexBody::PlaceholderType::FAULTY_MESH_PLACEHOLDER, flexbody_id, def.mesh_name));
1655 }
1656}
1657
1663
1665{
1666 PropID_t prop_id = static_cast<int>(m_actor->m_gfx_actor->m_props.size());
1667
1668 // Check if removed via .tuneup
1670 {
1671 RoR::Prop pprop; // placeholder
1672 pprop.pp_id = prop_id;
1676 m_actor->m_gfx_actor->m_props.push_back(pprop);
1677 return;
1678 }
1679
1680 RoR::Prop prop;
1681 prop.pp_id = prop_id;
1683 prop.pp_node_x = this->GetNodeIndexOrThrow(def.x_axis_node);
1684 prop.pp_node_y = this->GetNodeIndexOrThrow(def.y_axis_node);
1686 prop.pp_offset_orig = prop.pp_offset;
1688 prop.pp_rot = Ogre::Quaternion(Ogre::Degree(prop.pp_rota.z), Ogre::Vector3::UNIT_Z)
1689 * Ogre::Quaternion(Ogre::Degree(prop.pp_rota.y), Ogre::Vector3::UNIT_Y)
1690 * Ogre::Quaternion(Ogre::Degree(prop.pp_rota.x), Ogre::Vector3::UNIT_X);
1691 prop.pp_camera_mode_active = def.camera_settings.mode; /* Handles default value */
1692 prop.pp_camera_mode_orig = def.camera_settings.mode; /* Handles default value */
1693 prop.pp_wheel_rot_degree = 160.f; // ??
1694
1695 /* SPECIAL PROPS */
1696
1697 /* Rear view mirror (left) */
1699 {
1701 }
1702
1703 /* Rear view mirror (right) */
1705 {
1707 }
1708
1709 /* Custom steering wheel */
1710 Ogre::Vector3 steering_wheel_offset = Ogre::Vector3::ZERO;
1712 {
1713 steering_wheel_offset = Ogre::Vector3(-0.67, -0.61,0.24);
1714 }
1716 {
1717 steering_wheel_offset = Ogre::Vector3(0.67, -0.61,0.24);
1718 }
1719 if (steering_wheel_offset != Ogre::Vector3::ZERO)
1720 {
1722 {
1723 steering_wheel_offset = def.special_prop_dashboard.offset;
1724 }
1726 prop.pp_wheel_scene_node = m_props_parent_scenenode->createChildSceneNode(this->ComposeName("steering wheel @ prop", prop_id));
1727 prop.pp_wheel_pos = steering_wheel_offset;
1729 prop.pp_wheel_mesh_obj = new MeshObject(
1730 prop.pp_media[1],
1731 TuneupUtil::getTweakedPropMediaRG(m_actor->getWorkingTuneupDef(), prop_id, 1, this->GetCurrentElementMediaRG()),
1732 this->ComposeName("steering wheel entity @ prop", prop_id),
1734 );
1735 this->SetupNewEntity(prop.pp_wheel_mesh_obj->getEntity(), Ogre::ColourValue(0, 0.5, 0.5));
1736 }
1737
1738 /* CREATE THE PROP */
1739 prop.pp_scene_node = m_props_parent_scenenode->createChildSceneNode(this->ComposeName("prop", prop_id));
1741 prop.pp_mesh_obj = new MeshObject(
1742 prop.pp_media[0],
1743 TuneupUtil::getTweakedPropMediaRG(m_actor->getWorkingTuneupDef(), prop_id, 0, this->GetCurrentElementMediaRG()),
1744 this->ComposeName("prop entity", prop_id),
1745 prop.pp_scene_node);
1746
1747 prop.pp_mesh_obj->setCastShadows(true); // Orig code {{ prop.pp_mesh_obj->setCastShadows(shadowmode != 0); }}, shadowmode has default value 1 and changes with undocumented directive 'set_shadows'
1748
1750 {
1751 prop.pp_aero_propeller_spin = true;
1752 prop.pp_mesh_obj->setCastShadows(false);
1753 prop.pp_scene_node->setVisible(false);
1754 }
1756 {
1757 prop.pp_aero_propeller_blade = true;
1758 }
1760 {
1761 //driver seat, used to position the driver and make the seat translucent at times
1762 if (m_actor->m_gfx_actor->m_driverseat_prop_index == -1)
1763 {
1764 m_actor->m_gfx_actor->m_driverseat_prop_index = prop_id;
1765 prop.pp_mesh_obj->setMaterialName("driversseat");
1766 }
1767 else
1768 {
1769 this->AddMessage(Message::TYPE_INFO, "Found more than one 'seat[2]' special props. Only the first one will be the driver's seat.");
1770 }
1771 }
1773 {
1774 // Same as DRIVER_SEAT, except it doesn't force the "driversseat" material
1775 if (m_actor->m_gfx_actor->m_driverseat_prop_index == -1)
1776 {
1777 m_actor->m_gfx_actor->m_driverseat_prop_index = prop_id;
1778 }
1779 else
1780 {
1781 this->AddMessage(Message::TYPE_INFO, "Found more than one 'seat[2]' special props. Only the first one will be the driver's seat.");
1782 }
1783 }
1785 {
1787 {
1788 prop.pp_beacon_type = 'b';
1789 prop.pp_beacon_rot_angle[0] = 2.0 * 3.14 * frand();
1790 prop.pp_beacon_rot_rate[0] = 4.0 * 3.14 + frand() - 0.5;
1791 /* the light */
1792 auto pp_beacon_light = App::GetGfxScene()->GetSceneManager()->createLight();
1793 pp_beacon_light->setType(Ogre::Light::LT_SPOTLIGHT);
1794 pp_beacon_light->setDiffuseColour(def.special_prop_beacon.color);
1795 pp_beacon_light->setSpecularColour(def.special_prop_beacon.color);
1796 pp_beacon_light->setAttenuation(50.0, 1.0, 0.3, 0.0);
1797 pp_beacon_light->setSpotlightRange( Ogre::Degree(35), Ogre::Degree(45) );
1798 pp_beacon_light->setCastShadows(false);
1799 pp_beacon_light->setVisible(false);
1800 /* the flare billboard */
1802 auto flare_scene_node = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("beacon @ prop", prop_id));
1803 auto flare_billboard_sys = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1); //(propname,1);
1804 if (flare_billboard_sys)
1805 {
1806 flare_billboard_sys->createBillboard(0,0,0);
1807 flare_billboard_sys->setMaterialName(prop.pp_media[1]);
1808 flare_billboard_sys->setVisibilityFlags(DEPTHMAP_DISABLED);
1809 flare_scene_node->attachObject(flare_billboard_sys);
1810 }
1811 flare_scene_node->setVisible(false);
1812
1813 // Complete
1814 prop.pp_beacon_scene_node[0] = flare_scene_node;
1815 prop.pp_beacon_bbs[0] = flare_billboard_sys;
1816 prop.pp_beacon_light[0] = pp_beacon_light;
1817 }
1819 {
1820 prop.pp_beacon_rot_angle[0] = 0.f;
1821 prop.pp_beacon_rot_rate[0] = 1.0;
1822 prop.pp_beacon_type = 'r';
1823 //the light
1824 auto pp_beacon_light=App::GetGfxScene()->GetSceneManager()->createLight();//propname);
1825 pp_beacon_light->setType(Ogre::Light::LT_POINT);
1826 pp_beacon_light->setDiffuseColour( Ogre::ColourValue(1.0, 0.0, 0.0));
1827 pp_beacon_light->setSpecularColour( Ogre::ColourValue(1.0, 0.0, 0.0));
1828 pp_beacon_light->setAttenuation(50.0, 1.0, 0.3, 0.0);
1829 pp_beacon_light->setCastShadows(false);
1830 pp_beacon_light->setVisible(false);
1831 //the flare billboard
1832 auto flare_scene_node = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("redbeacon @ prop", prop_id));
1833 auto flare_billboard_sys = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1); //propname,1);
1834 if (flare_billboard_sys)
1835 {
1836 flare_billboard_sys->createBillboard(0,0,0);
1837 flare_billboard_sys->setMaterialName("tracks/redbeaconflare");
1838 flare_billboard_sys->setVisibilityFlags(DEPTHMAP_DISABLED);
1839 flare_billboard_sys->setDefaultDimensions(1.0, 1.0);
1840 flare_scene_node->attachObject(flare_billboard_sys);
1841 }
1842 flare_scene_node->setVisible(false);
1843
1844 // Finalize
1845 prop.pp_beacon_light[0] = pp_beacon_light;
1846 prop.pp_beacon_scene_node[0] = flare_scene_node;
1847 prop.pp_beacon_bbs[0] = flare_billboard_sys;
1848
1849 }
1851 {
1852 m_actor->ar_is_police = true;
1853 prop.pp_beacon_type='p';
1854 for (int k=0; k<4; k++)
1855 {
1856 prop.pp_beacon_rot_angle[k] = 2.0 * 3.14 * frand();
1857 prop.pp_beacon_rot_rate[k] = 4.0 * 3.14 + frand() - 0.5;
1858 prop.pp_beacon_bbs[k] = nullptr;
1859 //the light
1860 prop.pp_beacon_light[k]=App::GetGfxScene()->GetSceneManager()->createLight();
1861 prop.pp_beacon_light[k]->setType(Ogre::Light::LT_SPOTLIGHT);
1862 if (k>1)
1863 {
1864 prop.pp_beacon_light[k]->setDiffuseColour( Ogre::ColourValue(1.0, 0.0, 0.0));
1865 prop.pp_beacon_light[k]->setSpecularColour( Ogre::ColourValue(1.0, 0.0, 0.0));
1866 }
1867 else
1868 {
1869 prop.pp_beacon_light[k]->setDiffuseColour( Ogre::ColourValue(0.0, 0.5, 1.0));
1870 prop.pp_beacon_light[k]->setSpecularColour( Ogre::ColourValue(0.0, 0.5, 1.0));
1871 }
1872 prop.pp_beacon_light[k]->setAttenuation(50.0, 1.0, 0.3, 0.0);
1873 prop.pp_beacon_light[k]->setSpotlightRange( Ogre::Degree(35), Ogre::Degree(45) );
1874 prop.pp_beacon_light[k]->setCastShadows(false);
1875 prop.pp_beacon_light[k]->setVisible(false);
1876 //the flare billboard
1877 prop.pp_beacon_scene_node[k] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName(fmt::format("lightbar {}/4 @ prop", k), prop_id));
1878 prop.pp_beacon_bbs[k]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(1);
1879 prop.pp_beacon_bbs[k]->createBillboard(0,0,0);
1880 if (prop.pp_beacon_bbs[k])
1881 {
1882 if (k>1)
1883 {
1884 prop.pp_beacon_bbs[k]->setMaterialName("tracks/brightredflare");
1885 }
1886 else
1887 {
1888 prop.pp_beacon_bbs[k]->setMaterialName("tracks/brightblueflare");
1889 }
1890
1891 prop.pp_beacon_bbs[k]->setVisibilityFlags(DEPTHMAP_DISABLED);
1892 prop.pp_beacon_scene_node[k]->attachObject(prop.pp_beacon_bbs[k]);
1893 }
1894 prop.pp_beacon_scene_node[k]->setVisible(false);
1895 }
1896 }
1897
1899 {
1901 }
1902 }
1903
1904 this->SetupNewEntity(prop.pp_mesh_obj->getEntity(), Ogre::ColourValue(1.f, 1.f, 0.f));
1905
1908
1909 /* PROCESS ANIMATIONS */
1910
1911 for (RigDef::Animation& anim_def: def.animations)
1912 {
1913 PropAnim anim;
1914
1915 /* Arg #1: ratio */
1916 anim.animratio = anim_def.ratio;
1917 if (anim_def.ratio == 0)
1918 {
1919 std::stringstream msg;
1920 msg << "Prop (mesh: " << def.mesh_name << ") has invalid animation ratio (0), using it anyway (compatibility)...";
1922 }
1923
1924 /* Arg #2: option1 (lower limit) */
1925 anim.lower_limit = anim_def.lower_limit; /* Handles default */
1926
1927 /* Arg #3: option2 (upper limit) */
1928 anim.upper_limit = anim_def.upper_limit; /* Handles default */
1929
1930 /* Arg #4: source */
1931 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_AIRSPEED)) { /* (NOTE: code formatting relaxed) */
1933 }
1936 }
1939 anim.animOpt3 = 1.f;
1940 }
1943 anim.animOpt3 = 2.f;
1944 }
1947 anim.animOpt3 = 3.f;
1948 }
1951 }
1952 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_FLAP)) {
1954 }
1955 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_AIR_BRAKE)) {
1957 }
1958 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_ROLL)) {
1960 }
1961 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_PITCH)) {
1963 }
1964 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_BRAKES)) {
1966 }
1967 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_ACCEL)) {
1969 }
1970 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_CLUTCH)) {
1972 }
1973 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_SPEEDO)) {
1975 }
1976 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_TACHO)) {
1978 }
1979 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_TURBO)) {
1981 }
1982 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_PARKING)) {
1984 }
1987 anim.animOpt3 = SHIFTERMAN1;
1988 }
1991 anim.animOpt3 = SHIFTERMAN2;
1992 }
1995 anim.animOpt3 = SHIFTERSEQ;
1996 }
1997 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_SHIFTERLIN)) {
1999 anim.animOpt3 = SHIFTERLIN;
2000 }
2003 anim.animOpt3 = AUTOSHIFTERLIN;
2004 }
2005 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_TORQUE)) {
2007 }
2008 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_HEADING)) {
2010 }
2011 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_DIFFLOCK)) {
2013 }
2016 }
2017 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_AILERON)) {
2019 }
2020 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_ELEVATOR)) {
2022 }
2023 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_AIR_RUDDER)) {
2025 }
2028 }
2031 }
2032 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_PERMANENT)) {
2034 }
2037 }
2040 anim.animOpt3 = -1;
2041 }
2044 anim.animOpt3 = 0;
2045 }
2046 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_IGNITION)) {
2048 }
2049
2050 /* Motor/Gear-indexed sources */
2051 std::list<RigDef::Animation::MotorSource>::iterator source_itor = anim_def.motor_sources.begin();
2052 for ( ; source_itor != anim_def.motor_sources.end(); source_itor++)
2053 {
2054 // aeroengines
2057 anim.animOpt3 = static_cast<float>(source_itor->motor);
2058 }
2061 anim.animOpt3 = static_cast<float>(source_itor->motor);
2062 }
2065 anim.animOpt3 = static_cast<float>(source_itor->motor);
2066 }
2069 anim.animOpt3 = static_cast<float>(source_itor->motor);
2070 }
2073 anim.animOpt3 = static_cast<float>(source_itor->motor);
2074 }
2075
2076 // gears (hack)
2079 anim.animOpt3 = static_cast<float>(source_itor->motor);
2080 }
2081 }
2082
2083 // Source 'event' - make sure there is parameter 'event:'
2084 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_EVENT) &&
2085 anim_def.event_name != "")
2086 {
2087 int event_id = RoR::App::GetInputEngine()->resolveEventName(anim_def.event_name);
2088 if (event_id == -1)
2089 {
2090 AddMessage(Message::TYPE_ERROR, "Unknown animation event: " + anim_def.event_name);
2091 }
2092 else
2093 {
2094 PropAnimKeyState state;
2096 state.event_id = static_cast<events>(event_id);
2097 m_actor->m_prop_anim_key_states.push_back(state);
2099 }
2100 }
2101
2102 // Source 'dashboard' - make sure there is parameter 'link:'
2103 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_DASHBOARD) &&
2104 anim_def.dash_link_name != "")
2105 {
2106 int link_id = m_actor->ar_dashboard->getLinkIDForName(anim_def.dash_link_name);
2107 if (link_id == -1)
2108 {
2109 AddMessage(Message::TYPE_ERROR, "Unknown animation dashboard link: " + anim_def.dash_link_name);
2110 }
2111 else
2112 {
2114 anim.animOpt3 = static_cast<float>(link_id);
2115 }
2116 }
2117
2118 if (anim.animFlags == 0)
2119 {
2120 AddMessage(Message::TYPE_ERROR, fmt::format("Prop (mesh: '{}') will not be animated, no valid `source` was set.", def.mesh_name));
2121 }
2122
2123 /* Anim modes */
2126 }
2129 }
2132 }
2133 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_X)) {
2135 }
2136 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_Y)) {
2138 }
2139 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_Z)) {
2141 }
2142 if (anim.animMode == 0)
2143 {
2144 AddMessage(Message::TYPE_ERROR, fmt::format("Prop (mesh: '{}') will not be animated, no valid `mode` was set.", def.mesh_name));
2145 }
2146
2148 {
2150
2151 // Flag whether default lower and/or upper animation limit constraints are effective
2152 const bool use_default_lower_limit = (anim_def.lower_limit == 0.f);
2153 const bool use_default_upper_limit = (anim_def.upper_limit == 0.f);
2154
2156 anim.lower_limit = (use_default_lower_limit) ? (-180.f) : (anim_def.lower_limit + prop.pp_rota.x);
2157 anim.upper_limit = (use_default_upper_limit) ? ( 180.f) : (anim_def.upper_limit + prop.pp_rota.x);
2158 }
2160 anim.lower_limit = (use_default_lower_limit) ? (-180.f) : (anim_def.lower_limit + prop.pp_rota.y);
2161 anim.upper_limit = (use_default_upper_limit) ? ( 180.f) : (anim_def.upper_limit + prop.pp_rota.y);
2162 }
2164 anim.lower_limit = (use_default_lower_limit) ? (-180.f) : (anim_def.lower_limit + prop.pp_rota.z);
2165 anim.upper_limit = (use_default_upper_limit) ? ( 180.f) : (anim_def.upper_limit + prop.pp_rota.z);
2166 }
2167 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_X)) {
2168 anim.lower_limit = (use_default_lower_limit) ? (-10.f) : (anim_def.lower_limit + prop.pp_offset_orig.x);
2169 anim.upper_limit = (use_default_upper_limit) ? ( 10.f) : (anim_def.upper_limit + prop.pp_offset_orig.x);
2170 }
2171 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_Y)) {
2172 anim.lower_limit = (use_default_lower_limit) ? (-10.f) : (anim_def.lower_limit + prop.pp_offset_orig.y);
2173 anim.upper_limit = (use_default_upper_limit) ? ( 10.f) : (anim_def.upper_limit + prop.pp_offset_orig.y);
2174 }
2175 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_Z)) {
2176 anim.lower_limit = (use_default_lower_limit) ? (-10.f) : (anim_def.lower_limit + prop.pp_offset_orig.z);
2177 anim.upper_limit = (use_default_upper_limit) ? ( 10.f) : (anim_def.upper_limit + prop.pp_offset_orig.z);
2178 }
2179 }
2180 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_NO_FLIP))
2181 {
2183 }
2184 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_BOUNCE))
2185 {
2187 anim.animOpt5 = 1.f;
2188 }
2189
2190 prop.pp_animations.push_back(anim);
2191 }
2192
2193 m_actor->m_gfx_actor->m_props.push_back(prop);
2194}
2195
2197{
2198 // Handles both 'flares' and 'flares2' (both use struct `Flare2`)
2199 // --------------------------------------------------------------
2200
2201 if (m_actor->m_flares_mode == GfxFlaresMode::NONE) { return; }
2202
2203 // Do the common processing
2204 this->AddBaseFlare(def);
2205}
2206
2208{
2209 if (m_actor->m_flares_mode == GfxFlaresMode::NONE) { return; }
2210
2211 // Do the common processing
2212 this->AddBaseFlare(def);
2213
2214 // Now setup the extra inertia feature
2215 flare_t& f = m_actor->ar_flares.back();
2216 f.uses_inertia = true;
2218
2219 // Also create unique copy of the material, so we can adjust opacity via Ogre::Material to simulate incandescence.
2220 if (f.bbs != nullptr)
2221 {
2222 ROR_ASSERT(f.bbs->getMaterial());
2223 f.bbs->setMaterial(f.bbs->getMaterial()->clone(f.snode->getName() + "_mat"));
2224 }
2225}
2226
2228{
2229 if (m_actor->m_flares_mode == GfxFlaresMode::NONE) { return; }
2230
2231 int blink_delay = def.blink_delay_milis;
2232 float size = def.size;
2233 const FlareID_t flare_id = static_cast<FlareID_t>(m_actor->ar_flares.size());
2234 bool is_placeholder = TuneupUtil::isFlareAnyhowRemoved(m_actor->getWorkingTuneupDef(), flare_id);
2235
2236 /* Backwards compatibility */
2237 if (blink_delay == -2)
2238 {
2240 {
2241 blink_delay = -1; /* Default blink */
2242 }
2243 else
2244 {
2245 blink_delay = 0; /* Default no blink */
2246 }
2247 }
2248
2249 if (size == -2.f && def.type == FlareType::HEADLIGHT)
2250 {
2251 size = 1.f;
2252 }
2253 else if ((size == -2.f && def.type != FlareType::HEADLIGHT) || size == -1.f)
2254 {
2255 size = 0.5f;
2256 }
2257
2258 flare_t flare;
2259 flare.fl_type = def.type;
2260 flare.controlnumber = -1;
2261 flare.blinkdelay = (blink_delay == -1) ? 0.5f : blink_delay / 1000.f;
2262 flare.blinkdelay_curr = 0.f;
2263 flare.blinkdelay_state = false;
2267 flare.offsetx = def.offset.x;
2268 flare.offsety = def.offset.y;
2269 flare.offsetz = def.offset.z;
2270 flare.size = size;
2271
2272 if (def.type == FlareType::USER)
2273 {
2274 // control number: convert from 1-10 to 0-9
2275 if (def.control_number == 12) // Special case - legacy parking brake indicator
2276 {
2279 }
2280 else if (def.control_number < 1)
2281 {
2283 fmt::format("Bad flare control num {}, must be 1-{}, using 1.",
2285 flare.controlnumber = 0;
2286 }
2287 else if (def.control_number > MAX_CLIGHTS)
2288 {
2290 fmt::format("Bad flare control num {}, must be 1-{}, using {}.",
2292 flare.controlnumber = MAX_CLIGHTS-1;
2293 }
2294 else
2295 {
2296 flare.controlnumber = def.control_number - 1;
2297 }
2298 }
2299
2300 if (def.type == FlareType::DASHBOARD)
2301 {
2303 if (flare.dashboard_link == -1)
2304 {
2306 fmt::format("Disabling 'd' flare - invalid input link '{}'", def.dashboard_link));
2307 is_placeholder = true;
2308 }
2309 }
2310
2311 /* Visuals */
2312 std::string flare_name = this->ComposeName("Flare", flare_id);
2313 if (!is_placeholder)
2314 {
2315 flare.snode = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("flareX", flare_id));
2316 flare.bbs = App::GetGfxScene()->GetSceneManager()->createBillboardSet(flare_name, 1);
2317 }
2318
2319 // Backwards compatibility:
2320 // before 't' (tail light) was introduced in 2022, tail lights were indicated as 'f' (headlight) + custom material.
2321 bool using_default_material = (def.material_name.length() == 0 || def.material_name == "default");
2322 if (flare.fl_type == FlareType::HEADLIGHT && !using_default_material)
2323 {
2325 }
2326
2327 if (flare.bbs != nullptr)
2328 {
2329 flare.bbs->createBillboard(0,0,0);
2330 flare.bbs->setVisibilityFlags(DEPTHMAP_DISABLED);
2331 std::string material_name = def.material_name;
2332 if (using_default_material)
2333 {
2334 if (flare.fl_type == FlareType::BRAKE_LIGHT)
2335 {
2336 material_name = "tracks/brakeflare";
2337 }
2338 else if (flare.fl_type == FlareType::BLINKER_LEFT || (flare.fl_type == FlareType::BLINKER_RIGHT))
2339 {
2340 material_name = "tracks/blinkflare";
2341 }
2342 else if (flare.fl_type == FlareType::DASHBOARD)
2343 {
2344 material_name = "tracks/greenflare";
2345 }
2346 else if (flare.fl_type == FlareType::TAIL_LIGHT)
2347 {
2348 material_name = "tracks/redflare";
2349 }
2350 else
2351 {
2352 material_name = "tracks/flare";
2353 }
2354 }
2355
2356 Ogre::MaterialPtr material = this->FindOrCreateCustomizedMaterial(material_name, this->GetCurrentElementMediaRG());
2357 if (material)
2358 {
2359 flare.bbs->setMaterial(material);
2360 flare.snode->attachObject(flare.bbs);
2361 }
2362 }
2363 flare.intensity = 1.f;
2364 flare.light = nullptr;
2365
2366 if ((App::gfx_flares_mode->getEnum<GfxFlaresMode>() >= GfxFlaresMode::CURR_VEHICLE_HEAD_ONLY) && size > 0.001 && !is_placeholder)
2367 {
2368 if (flare.fl_type == FlareType::HEADLIGHT)
2369 {
2370 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2371 flare.light->setType(Ogre::Light::LT_SPOTLIGHT);
2372 flare.light->setDiffuseColour( Ogre::ColourValue(1, 1, 1));
2373 flare.light->setSpecularColour( Ogre::ColourValue(1, 1, 1));
2374 flare.light->setAttenuation(200, 0.9, 0, 0);
2375 flare.light->setSpotlightRange( Ogre::Degree(35), Ogre::Degree(45) );
2376 flare.light->setCastShadows(false);
2377 }
2378 else if (flare.fl_type == FlareType::HIGH_BEAM)
2379 {
2380 flare.light = App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2381 flare.light->setType(Ogre::Light::LT_SPOTLIGHT);
2382 flare.light->setDiffuseColour(Ogre::ColourValue(1, 1, 1));
2383 flare.light->setSpecularColour(Ogre::ColourValue(1, 1, 1));
2384 flare.light->setAttenuation(400, 0.9, 0, 0);
2385 flare.light->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(45));
2386 flare.light->setCastShadows(false);
2387 }
2388 else if (flare.fl_type == FlareType::FOG_LIGHT)
2389 {
2390 flare.light = App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2391 flare.light->setType(Ogre::Light::LT_SPOTLIGHT);
2392 flare.light->setDiffuseColour(Ogre::ColourValue(1, 1, 1));
2393 flare.light->setSpecularColour(Ogre::ColourValue(1, 1, 1));
2394 flare.light->setAttenuation(400, 0.9, 0, 0);
2395 flare.light->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(45));
2396 flare.light->setCastShadows(false);
2397 }
2398 }
2399 if ((App::gfx_flares_mode->getEnum<GfxFlaresMode>() >= GfxFlaresMode::ALL_VEHICLES_ALL_LIGHTS) && size > 0.001 && !is_placeholder)
2400 {
2401 if (flare.fl_type == FlareType::TAIL_LIGHT)
2402 {
2403 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2404 flare.light->setDiffuseColour( Ogre::ColourValue(1.0, 0, 0));
2405 flare.light->setSpecularColour( Ogre::ColourValue(1.0, 0, 0));
2406 flare.light->setAttenuation(10.0, 1.0, 0, 0);
2407 }
2408 else if (flare.fl_type == FlareType::REVERSE_LIGHT)
2409 {
2410 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2411 flare.light->setDiffuseColour(Ogre::ColourValue(1, 1, 1));
2412 flare.light->setSpecularColour(Ogre::ColourValue(1, 1, 1));
2413 flare.light->setAttenuation(20.0, 1, 0, 0);
2414 }
2415 else if (flare.fl_type == FlareType::BRAKE_LIGHT)
2416 {
2417 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2418 flare.light->setDiffuseColour( Ogre::ColourValue(1.0, 0, 0));
2419 flare.light->setSpecularColour( Ogre::ColourValue(1.0, 0, 0));
2420 flare.light->setAttenuation(10.0, 1.0, 0, 0);
2421 }
2422 else if (flare.fl_type == FlareType::BLINKER_LEFT || (flare.fl_type == FlareType::BLINKER_RIGHT))
2423 {
2424 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2425 flare.light->setDiffuseColour( Ogre::ColourValue(1, 1, 0));
2426 flare.light->setSpecularColour( Ogre::ColourValue(1, 1, 0));
2427 flare.light->setAttenuation(10.0, 1, 1, 0);
2428 }
2429 else if (flare.fl_type == FlareType::USER)
2430 {
2431 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2432 flare.light->setDiffuseColour( Ogre::ColourValue(1, 1, 1));
2433 flare.light->setSpecularColour( Ogre::ColourValue(1, 1, 1));
2434 flare.light->setAttenuation(1.0, 1.0, 1, 0.2);
2435 }
2436 else if (flare.fl_type == FlareType::SIDELIGHT)
2437 {
2438 flare.light = App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2439 flare.light->setDiffuseColour(Ogre::ColourValue(1, 1, 1));
2440 flare.light->setSpecularColour(Ogre::ColourValue(1, 1, 1));
2441 flare.light->setAttenuation(5.0, 1.0, 1, 0.2);
2442 }
2443 }
2444
2445 /* Finalize light */
2446 if (flare.light != nullptr)
2447 {
2448 flare.light->setType(Ogre::Light::LT_SPOTLIGHT);
2449 flare.light->setSpotlightRange( Ogre::Degree(35), Ogre::Degree(45) );
2450 flare.light->setCastShadows(false);
2451 }
2452 m_actor->ar_flares.push_back(flare);
2453}
2454
2456{
2457 LOG(fmt::format("[RoR|ActorSpawner] processing FlaregroupNoImport ({} {})", (char)def.type, def.control_number));
2458 switch (def.type)
2459 {
2469 //case FlareType::DASHBOARD: ~ Not subject to syncing between linked actors.
2470 case FlareType::USER:
2471 switch (def.control_number - 1)
2472 {
2483 default: break;
2484 }
2485 break;
2486 default: break;
2487 }
2488}
2489
2490Ogre::MaterialPtr ActorSpawner::InstantiateManagedMaterial(const Ogre::String& rg_name, Ogre::String const & source_name, Ogre::String const & clone_name)
2491{
2492 Ogre::MaterialPtr src_mat = Ogre::MaterialManager::getSingleton().getByName(source_name, rg_name);
2493 if (!src_mat)
2494 {
2495 std::stringstream msg;
2496 msg << "Built-in material '" << source_name << "' missing! Skipping...";
2497 AddMessage(Message::TYPE_ERROR, msg.str());
2498 return Ogre::MaterialPtr();
2499 }
2500
2501 return src_mat->clone(clone_name);
2502}
2503
2505{
2506 // This is how textures map between `RigDef::Document` (*.truck etc...) and `RoR::TuneupDef` (*.tuneup):
2507 // def.diffuse_map ~~ tuneup.media[0]
2508 // def.specular_map ~~ tuneup.media[1]
2509 // def.damaged_diffuse_map ~~ tuneup.media[2]
2510 // ==========================================================================
2511
2512 if (m_managed_materials.find(def.name) != m_managed_materials.end())
2513 {
2514 this->AddMessage(Message::TYPE_ERROR, "Duplicate managed material name: '" + def.name + "'. Ignoring definition...");
2515 return;
2516 }
2517
2518 // Check all textures exist
2519 std::string resource_group = this->GetCurrentElementMediaRG();
2520 if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(resource_group, def.diffuse_map))
2521 {
2522 this->AddMessage(Message::TYPE_WARNING, "Skipping managed material, missing texture file: " + def.diffuse_map);
2523 return;
2524 }
2525
2526 if (def.damaged_diffuse_map != "" &&
2527 !Ogre::ResourceGroupManager::getSingleton().resourceExists(resource_group, def.damaged_diffuse_map))
2528 {
2529 this->AddMessage(Message::TYPE_WARNING, "Damage texture not found: " + def.damaged_diffuse_map);
2530 def.damaged_diffuse_map = "";
2531 }
2532
2533 if (def.specular_map != "" &&
2534 !Ogre::ResourceGroupManager::getSingleton().resourceExists(resource_group, def.specular_map))
2535 {
2536 this->AddMessage(Message::TYPE_WARNING, "Specular texture not found: " + def.specular_map);
2537 def.specular_map = "";
2538 }
2539
2540 // Create fallback placeholders
2541 // This is necessary to load meshes with original material names (= unchanged managed mat names)
2542 // - if not found, OGRE substitutes them with 'BaseWhite' which breaks subsequent processing.
2543 // Note this must be done for all addonparts, too, as any of them can use managed materials defined in the truck file.
2544 for (auto& module: m_selected_modules)
2545 {
2546 std::string module_rg = (module->origin_addonpart) ? module->origin_addonpart->resource_group : m_actor->getTruckFileResourceGroup();
2547 if (!Ogre::MaterialManager::getSingleton().getByName(def.name, module_rg))
2548 {
2549 LOG(fmt::format("[RoR] DBG ActorSpawner::ProcessManagedMaterial(): Creating placeholder for material '{}' in group '{}'", def.name, module_rg));
2550 m_managedmat_placeholder_template->clone(def.name, /*changeGroup=*/true, module_rg);
2551 }
2552 else
2553 {
2554 LOG(fmt::format("[RoR] DBG ActorSpawner::ProcessManagedMaterial(): Placeholder already exists: '{}' in group '{}'", def.name, module_rg));
2555 }
2556 }
2557
2558 std::string custom_name = this->ComposeName(def.name);
2559 Ogre::MaterialPtr material;
2561 {
2562 // Create a placeholder material
2563 material = Ogre::MaterialManager::getSingleton().getByName("tracks/transred")->clone(custom_name, /*changeGroup:*/true, resource_group);
2564 }
2566 {
2567 std::string mat_name_base
2569 ? "managed/flexmesh_standard"
2570 : "managed/flexmesh_transparent";
2571
2572 if (def.damaged_diffuse_map != "")
2573 {
2574 if (def.specular_map != "")
2575 {
2576 /* FLEXMESH, damage, specular */
2577 if (App::gfx_alt_actor_materials->getBool())
2578 {
2579 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/speculardamage", custom_name);
2580 }
2581 else
2582 {
2583 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/speculardamage_nicemetal", custom_name);
2584 }
2585
2586 if (!material)
2587 {
2588 return;
2589 }
2590
2591 if (App::gfx_alt_actor_materials->getBool())
2592 {
2593 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2594 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Dmg_Diffuse_Map"), def.name, 2, def.damaged_diffuse_map);
2595 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("SpecularMapping1")->getTextureUnitState("SpecularMapping1_Tex"), def.name, 1, def.specular_map);
2596 }
2597 else
2598 {
2599 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2600 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Dmg_Diffuse_Map"), def.name, 2, def.damaged_diffuse_map);
2601 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Specular_Map"), def.name, 1, def.specular_map);
2602 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("Specular")->getTextureUnitState("Specular_Map"), def.name, 1, def.specular_map);
2603 }
2604 }
2605 else
2606 {
2607 /* FLEXMESH, damage, no_specular */
2608 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/damageonly", custom_name);
2609 if (!material)
2610 {
2611 return;
2612 }
2613 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2614 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Dmg_Diffuse_Map"), def.name, 2, def.damaged_diffuse_map);
2615 }
2616 }
2617 else
2618 {
2619 if (def.specular_map != "")
2620 {
2621 /* FLEXMESH, no_damage, specular */
2622 if (App::gfx_alt_actor_materials->getBool())
2623 {
2624 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/specularonly", custom_name);
2625 }
2626 else
2627 {
2628 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/specularonly_nicemetal", custom_name);
2629 }
2630
2631 if (!material)
2632 {
2633 return;
2634 }
2635
2636 if (App::gfx_alt_actor_materials->getBool())
2637 {
2638 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2639 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("SpecularMapping1")->getTextureUnitState("SpecularMapping1_Tex"), def.name, 1, def.specular_map);
2640 }
2641 else
2642 {
2643 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map") , def.name, 0, def.diffuse_map);
2644 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Specular_Map"), def.name, 1, def.specular_map);
2645 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("Specular")->getTextureUnitState("Specular_Map") , def.name, 1, def.specular_map);
2646 }
2647 }
2648 else
2649 {
2650 /* FLEXMESH, no_damage, no_specular */
2651 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/simple", custom_name);
2652 if (!material)
2653 {
2654 return;
2655 }
2656 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2657 }
2658 }
2659 }
2661 {
2662 Ogre::String mat_name_base
2664 ? "managed/mesh_standard"
2665 : "managed/mesh_transparent";
2666
2667 if (def.specular_map != "")
2668 {
2669 /* MESH, specular */
2670 if (App::gfx_alt_actor_materials->getBool())
2671 {
2672 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/specular", custom_name);
2673 }
2674 else
2675 {
2676 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/specular_nicemetal", custom_name);
2677 }
2678
2679 if (!material)
2680 {
2681 return;
2682 }
2683
2684 if (App::gfx_alt_actor_materials->getBool())
2685 {
2686 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2687 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("SpecularMapping1")->getTextureUnitState("SpecularMapping1_Tex"), def.name, 1, def.specular_map);
2688 }
2689 else
2690 {
2691 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map") ,def.name, 0, def.diffuse_map);
2692 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Specular_Map"),def.name, 1, def.specular_map);
2693 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("Specular")->getTextureUnitState("Specular_Map") ,def.name, 1, def.specular_map);
2694 }
2695 }
2696 else
2697 {
2698 /* MESH, no_specular */
2699 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/simple", custom_name);
2700 if (!material)
2701 {
2702 return;
2703 }
2704 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2705
2706 }
2707 }
2708
2711 {
2712 if (def.options.double_sided)
2713 {
2714 material->getTechnique("BaseTechnique")->getPass("BaseRender")->setCullingMode(Ogre::CULL_NONE);
2715 if (def.specular_map != "")
2716 {
2717 if (App::gfx_alt_actor_materials->getBool())
2718 {
2719 material->getTechnique("BaseTechnique")->getPass("SpecularMapping1")->setCullingMode(Ogre::CULL_NONE);
2720 }
2721 else
2722 {
2723 material->getTechnique("BaseTechnique")->getPass("Specular")->setCullingMode(Ogre::CULL_NONE);
2724 }
2725 }
2726 }
2727 }
2728
2729 material->compile();
2730 m_managed_materials.insert(std::make_pair(def.name, material));
2731}
2732
2734{
2735 int8_t bbox_id = static_cast<int8_t>(m_actor->ar_collision_bounding_boxes.size());
2736 for (RigDef::Node::Ref& node_ref: def.nodes)
2737 {
2738 NodeNum_t node = this->ResolveNodeRef(node_ref);
2739 if (node == NODENUM_INVALID)
2740 {
2741 RoR::LogFormat("[RoR|Spawner] Collision box: skipping invalid node '%s'", node_ref.ToString().c_str());
2742 continue;
2743 }
2745 {
2746 RoR::LogFormat("[RoR|Spawner] Collision box: re-assigning node '%s' from box ID '%d' to '%d'",
2747 node_ref.ToString().c_str(),
2749 bbox_id);
2750 }
2751 m_actor->ar_nodes[node].nd_coll_bbox_id = bbox_id;
2752 }
2753
2754 m_actor->ar_collision_bounding_boxes.push_back(Ogre::AxisAlignedBox()); // Updated later
2755 m_actor->ar_predicted_coll_bounding_boxes.push_back(Ogre::AxisAlignedBox());
2756}
2757
2765
2766bool ActorSpawner::AssignWheelToAxle(int & _out_axle_wheel, node_t *axis_node_1, node_t *axis_node_2)
2767{
2768 for (int i = 0; i < m_actor->ar_num_wheels; i++)
2769 {
2770 wheel_t & wheel = m_actor->ar_wheels[i];
2771 if ( (wheel.wh_axis_node_0 == axis_node_1 && wheel.wh_axis_node_1 == axis_node_2)
2772 || (wheel.wh_axis_node_0 == axis_node_2 && wheel.wh_axis_node_1 == axis_node_1)
2773 )
2774 {
2775 _out_axle_wheel = i;
2776 return true;
2777 }
2778 }
2779 return false;
2780}
2781
2783{
2784 if (! CheckAxleLimit(1))
2785 {
2786 return;
2787 }
2788
2789 node_t *wheel_1_node_1 = GetNodePointerOrThrow(def.wheels[0][0]);
2790 node_t *wheel_1_node_2 = GetNodePointerOrThrow(def.wheels[0][1]);
2791 node_t *wheel_2_node_1 = GetNodePointerOrThrow(def.wheels[1][0]);
2792 node_t *wheel_2_node_2 = GetNodePointerOrThrow(def.wheels[1][1]);
2793
2794 Differential *diff = new Differential();
2795
2796 if (! AssignWheelToAxle(diff->di_idx_1, wheel_1_node_1, wheel_1_node_2))
2797 {
2798 std::stringstream msg;
2799 msg << "Couldn't find wheel with axis nodes '" << def.wheels[0][0].ToString()
2800 << "' and '" << def.wheels[0][1].ToString() << "'";
2802 }
2803
2804 if (! AssignWheelToAxle(diff->di_idx_2, wheel_2_node_1, wheel_2_node_2))
2805 {
2806 std::stringstream msg;
2807 msg << "Couldn't find wheel with axis nodes '" << def.wheels[1][0].ToString()
2808 << "' and '" << def.wheels[1][1].ToString() << "'";
2810 }
2811
2812 if (def.options.size() == 0)
2813 {
2814 AddMessage(Message::TYPE_INFO, "No differential defined, defaulting to Open & Locked");
2817 }
2818 else
2819 {
2820 auto end = def.options.end();
2821 for (auto itor = def.options.begin(); itor != end; ++itor)
2822 {
2823 switch (*itor)
2824 {
2827 break;
2830 break;
2833 break;
2836 break;
2837 default:
2838 AddMessage(Message::TYPE_WARNING, fmt::format("Unknown differential type: '{}'", (char)*itor));
2839 break;
2840 }
2841 }
2842 }
2843
2846}
2847
2849{
2850 if (def.a1 == def.a2 || std::min(def.a1, def.a2) < 0 || std::max(def.a1 , def.a2) >= m_actor->m_num_wheel_diffs)
2851 {
2852 AddMessage(Message::TYPE_ERROR, "Invalid 'interaxle' axle ids, skipping...");
2853 return;
2854 }
2855
2857 {
2858 if ((m_actor->m_transfer_case->tr_ax_1 == def.a1 && m_actor->m_transfer_case->tr_ax_2 == def.a2) ||
2860 {
2861 AddMessage(Message::TYPE_ERROR, "You cannot have both an inter-axle differential and a transfercase between the same two axles, skipping...");
2862 return;
2863 }
2864 }
2865
2866 Differential *diff = new Differential();
2867
2868 diff->di_idx_1 = def.a1;
2869 diff->di_idx_2 = def.a2;
2870
2871 if (def.options.size() == 0)
2872 {
2873 AddMessage(Message::TYPE_INFO, "No differential defined, defaulting to Locked");
2875 }
2876 else
2877 {
2878 for (RigDef::DifferentialType val: def.options)
2879 {
2880 switch (val)
2881 {
2884 break;
2887 break;
2890 break;
2893 break;
2894 default:
2895 AddMessage(Message::TYPE_WARNING, fmt::format("Unknown differential type: '{}'", (char)val));
2896 break;
2897 }
2898 }
2899 }
2900
2903}
2904
2906{
2907 if (def.a1 == def.a2 || def.a1 < 0 || std::max(def.a1 , def.a2) >= m_actor->m_num_wheel_diffs)
2908 {
2909 AddMessage(Message::TYPE_ERROR, "Invalid 'transfercase' axle ids, skipping...");
2910 return;
2911 }
2912 if (def.a2 < 0) // No 4WD mode
2913 {
2914 if (!def.has_2wd) // No 2WD mode
2915 {
2916 AddMessage(Message::TYPE_ERROR, "Invalid 'transfercase': Define alternate axle or allow 2WD, skipping...");
2917 return;
2918 }
2919 else // Only 2WD
2920 {
2921 AddMessage(Message::TYPE_INFO, "No alternate axle defined, defaulting to 2WD only");
2922 }
2923 }
2924
2925 m_actor->m_transfer_case = new TransferCase(def.a1, def.a2, def.has_2wd, def.has_2wd_lo, def.gear_ratios);
2926
2927 for (int i = 0; i < m_actor->ar_num_wheels; i++)
2928 {
2930 }
2934 if (!def.has_2wd)
2935 {
2940 }
2941}
2942
2944{
2947 {
2948 std::stringstream msg;
2949 msg << "Invalid parameter 'lower_limit' (" << m_actor->cc_target_speed_lower_limit
2950 << ") must be positive nonzero number. Using it anyway (compatibility)";
2951 }
2952 m_actor->cc_can_brake = def.autobrake != 0;
2953}
2954
2960
2962{
2963 if (m_actor->ar_engine == nullptr)
2964 {
2965 AddMessage(Message::TYPE_WARNING, "Section 'torquecurve' found but no 'engine' defined, skipping...");
2966 return;
2967 }
2968
2969 TorqueCurve *target_torque_curve = m_actor->ar_engine->getTorqueCurve();
2970
2971 if (def.predefined_func_name.length() != 0)
2972 {
2973 target_torque_curve->setTorqueModel(def.predefined_func_name);
2974 }
2975 else
2976 {
2977 target_torque_curve->CreateNewCurve(); /* Use default name for custom curve */
2978 std::vector<RigDef::TorqueCurve::Sample>::iterator itor = def.samples.begin();
2979 for ( ; itor != def.samples.end(); itor++)
2980 {
2981 target_torque_curve->AddCurveSample(itor->power, itor->torque_percent);
2982 }
2983 }
2984}
2985
2987{
2988 if (App::gfx_particles_mode->getInt() != 1)
2989 {
2990 return;
2991 }
2992
2993 CParticleID_t particle_id = static_cast<CParticleID_t>(m_actor->m_gfx_actor->m_cparticles.size());
2994 CParticle particle;
2995
2998
2999 std::string name = this->ComposeName(def.particle_system_name.c_str(), particle_id);
3000 particle.psys = this->CreateParticleSystem(name, def.particle_system_name);
3001 if (particle.psys == nullptr)
3002 {
3003 std::stringstream msg;
3004 msg << "Failed to create particle system '" << name << "' (template: '" << def.particle_system_name <<"')";
3005 AddMessage(Message::TYPE_ERROR, msg.str());
3006 return;
3007 }
3008
3009 particle.snode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("cparticles", particle_id));
3010 particle.snode->attachObject(particle.psys);
3011 particle.snode->setPosition(m_actor->ar_nodes[particle.emitterNode].AbsPosition);
3012
3013 m_actor->m_gfx_actor->m_cparticles.push_back(particle);
3014}
3015
3017{
3018 ropable_t ropable;
3019 ropable.node = GetNodePointerOrThrow(def.node);
3020 ropable.pos = static_cast<int>(m_actor->ar_ropables.size());
3021 ropable.group = def.group;
3022 ropable.attached_ties = 0;
3023 ropable.attached_ropes = 0;
3024 ropable.multilock = def.has_multilock;
3025 m_actor->ar_ropables.push_back(ropable);
3026}
3027
3029{
3031 node_t & node_2 = m_actor->ar_nodes[( (node_1.pos == 0) ? 1 : 0 )];
3032
3033 int beam_index = m_actor->ar_num_beams;
3034 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
3035 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold());
3036 beam.k = def.beam_defaults->GetScaledSpringiness();
3037 beam.d = def.beam_defaults->GetScaledDamping();
3038 beam.bm_type = BEAM_HYDRO;
3039 beam.L = def.max_reach_length;
3040 beam.refL = def.max_reach_length;
3041 beam.bounded = ROPE;
3042 beam.bm_disabled = true;
3043
3045 {
3046 this->CreateBeamVisuals(beam, beam_index, false, def.beam_defaults);
3047 }
3048 else
3049 {
3050 m_actor->ar_beams_invisible[beam_index] = true;
3051 }
3052
3053 /* Register tie */
3054 tie_t tie;
3055 tie.ti_group = def.group;
3056 tie.ti_tying = false;
3057 tie.ti_tied = false;
3058 tie.ti_beam = & beam;
3059 tie.ti_locked_actor = nullptr;
3060 tie.ti_locked_ropable = nullptr;
3062 tie.ti_max_stress = def.max_stress;
3063 tie.ti_min_length = def.min_length;
3065 m_actor->ar_ties.push_back(tie);
3066
3068}
3069
3071{
3074
3075 /* Add beam */
3076 int beam_index = m_actor->ar_num_beams;
3077 beam_t & beam = AddBeam(root_node, end_node, def.beam_defaults, def.detacher_group);
3078 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold());
3079 beam.k = def.beam_defaults->GetScaledSpringiness();
3080 beam.d = def.beam_defaults->GetScaledDamping();
3081 beam.bounded = ROPE;
3082 beam.bm_type = BEAM_HYDRO;
3083 beam.L = root_node.AbsPosition.distance(end_node.AbsPosition);
3084 beam.refL = beam.L;
3085
3086 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults, "tracks/beam");
3087
3088 /* Register rope */
3089 rope_t rope;
3090 rope.rp_beam = & beam;
3091 rope.rp_locked = UNLOCKED;
3092 rope.rp_locked_ropable = nullptr;
3093 rope.rp_group = 0; // Orig: hardcoded in BTS_ROPES. TODO: To be used.
3094 m_actor->ar_ropes.push_back(rope);
3095}
3096
3098{
3099 RailGroup* rail_group = this->CreateRail(def.node_list);
3100 rail_group->rg_id = def.id;
3101 m_actor->m_railgroups.push_back(rail_group);
3102}
3103
3105{
3107 SlideNode slide_node(& node, nullptr);
3108
3109 // Optional args
3110 if (def._spring_rate_set) { slide_node.SetSpringRate(def.spring_rate); }
3111 if (def._break_force_set) { slide_node.SetBreakForce(def.break_force); }
3112 if (def._tolerance_set) { slide_node.SetCorThreshold(def.tolerance); }
3113 if (def._attachment_rate_set) { slide_node.SetAttachmentRate(def.attachment_rate); }
3114 if (def._max_attach_dist_set) { slide_node.SetAttachmentDistance(def.max_attach_dist); }
3115
3116 // Constraints
3118 {
3119 slide_node.sn_attach_self = true;
3120 slide_node.sn_attach_foreign = true;
3121 }
3123 {
3124 slide_node.sn_attach_self = true;
3125 slide_node.sn_attach_foreign = false;
3126 }
3128 {
3129 slide_node.sn_attach_self = false;
3130 slide_node.sn_attach_foreign = true;
3131 }
3133 {
3134 slide_node.sn_attach_self = false;
3135 slide_node.sn_attach_foreign = false;
3136 }
3137
3138 // RailGroup
3139 RailGroup *rail_group = nullptr;
3140 if (def._railgroup_id_set)
3141 {
3142 std::vector<RailGroup*>::iterator itor = m_actor->m_railgroups.begin();
3143 for ( ; itor != m_actor->m_railgroups.end(); itor++)
3144 {
3145 if ((*itor)->rg_id == def.railgroup_id)
3146 {
3147 rail_group = *itor;
3148 break;
3149 }
3150 }
3151
3152 if (rail_group == nullptr)
3153 {
3154 std::stringstream msg;
3155 msg << "Specified rail group id '" << def.railgroup_id << "' not found. Ignoring slidenode...";
3156 AddMessage(Message::TYPE_ERROR, msg.str());
3157 return;
3158 }
3159 }
3160 else if (def.rail_node_ranges.size() > 0)
3161 {
3162 rail_group = this->CreateRail(def.rail_node_ranges);
3163 if (rail_group != nullptr)
3164 {
3165 m_actor->m_railgroups.push_back(rail_group);
3166 }
3167 }
3168 else
3169 {
3170 AddMessage(Message::TYPE_ERROR, "No RailGroup available for SlideNode, skipping...");
3171 }
3172
3173 slide_node.SetDefaultRail(rail_group);
3174 m_actor->m_slidenodes.push_back(slide_node);
3175}
3176
3178 std::vector<RigDef::Node::Range> & node_ranges,
3179 std::vector<NodeNum_t> & out_node_indices
3180 )
3181{
3182 std::vector<RigDef::Node::Range>::iterator itor = node_ranges.begin();
3183 for ( ; itor != node_ranges.end(); itor++)
3184 {
3185 if (itor->IsRange())
3186 {
3187 NodeNum_t start = this->ResolveNodeRef(itor->start);
3188 if (start == NODENUM_INVALID)
3189 {
3190 AddMessage(Message::TYPE_WARNING, fmt::format("Invalid start node in range: {}", itor->start.ToString()));
3191 return false;
3192 }
3193
3194 NodeNum_t end = this->ResolveNodeRef(itor->end, /* optional: */ true);
3195 if (end == NODENUM_INVALID)
3196 {
3197 std::stringstream msg;
3198 msg << "Encountered non-existent node '" << itor->end.ToString() << "' in range [" << itor->start.ToString() << " - " << itor->end.ToString() << "], "
3199 << "highest node index is '" << m_actor->ar_num_nodes - 1 << "'.";
3200
3201 if (itor->end.Str().empty()) /* If the node is numeric... */
3202 {
3203 msg << " However, this node must be accepted anyway for backwards compatibility."
3204 << " Please fix this as soon as possible.";
3205 end = itor->end.Num();
3206 AddMessage(Message::TYPE_ERROR, msg.str());
3207 }
3208 else
3209 {
3210 AddMessage(Message::TYPE_ERROR, msg.str());
3211 return false;
3212 }
3213 }
3214
3215 if (end < start)
3216 {
3217 NodeNum_t swap = start;
3218 start = end;
3219 end = swap;
3220 }
3221
3222 for (NodeNum_t i = start; i <= end; i++)
3223 {
3224 out_node_indices.push_back(i);
3225 }
3226 }
3227 else
3228 {
3229 out_node_indices.push_back(GetNodeIndexOrThrow(itor->start));
3230 }
3231 }
3232 return true;
3233}
3234
3235RailGroup *ActorSpawner::CreateRail(std::vector<RigDef::Node::Range> & node_ranges)
3236{
3237 // Collect nodes
3238 std::vector<NodeNum_t> node_indices;
3239 this->CollectNodesFromRanges(node_ranges, node_indices);
3240
3241 // Build the rail
3242 RailGroup* rg = new RailGroup();
3243 for (unsigned int i = 0; i < node_indices.size() - 1; i++)
3244 {
3245 beam_t *beam = FindBeamInRig(node_indices[i], node_indices[i + 1]);
3246 if (beam == nullptr)
3247 {
3248 std::stringstream msg;
3249 msg << "No beam between nodes indexed '" << node_indices[i] << "' and '" << node_indices[i + 1] << "'";
3250 AddMessage(Message::TYPE_ERROR, msg.str());
3251 delete rg;
3252 return nullptr;
3253 }
3254 rg->rg_segments.emplace_back(beam);
3255 }
3256
3257 // Link middle segments
3258 const size_t num_seg = rg->rg_segments.size();
3259 for (size_t i = 1; i < (num_seg - 1); ++i)
3260 {
3261 rg->rg_segments[i].rs_prev = &rg->rg_segments[i - 1];
3262 rg->rg_segments[i].rs_next = &rg->rg_segments[i + 1];
3263 }
3264
3265 // Link end segments
3266 const bool is_loop = (node_indices.front() == node_indices.back());
3267 if (rg->rg_segments.size() > 1)
3268 {
3269 rg->rg_segments[0].rs_next = &rg->rg_segments[1];
3270 rg->rg_segments[num_seg - 1].rs_prev = &rg->rg_segments[num_seg - 2];
3271 if (is_loop)
3272 {
3273 rg->rg_segments[0].rs_prev = &rg->rg_segments[num_seg - 1];
3274 rg->rg_segments[num_seg - 1].rs_next = &rg->rg_segments[0];
3275 }
3276 }
3277
3278 return rg; // Transfers memory ownership
3279}
3280
3282{
3283 node_t *node_a = & m_actor->ar_nodes[node_a_index];
3284 node_t *node_b = & m_actor->ar_nodes[node_b_index];
3285
3286 for (unsigned int i = 0; i < static_cast<unsigned int>(m_actor->ar_num_beams); i++)
3287 {
3288 if (
3289 (GetBeam(i).p1 == node_a && GetBeam(i).p2 == node_b)
3290 || (GetBeam(i).p2 == node_a && GetBeam(i).p1 == node_b)
3291 )
3292 {
3293 return & GetBeam(i);
3294 }
3295 }
3296 return nullptr;
3297}
3298
3300{
3301 /* Find the node */
3302 node_t *node = GetNodePointer(def.node);
3303 if (node == nullptr)
3304 {
3305 return;
3306 }
3307
3308 /* Find the hook */
3309 hook_t *hook = nullptr;
3310 std::vector <hook_t>::iterator itor = m_actor->ar_hooks.begin();
3311 for (; itor != m_actor->ar_hooks.end(); itor++)
3312 {
3313 if (itor->hk_hook_node == node)
3314 {
3315 hook = &*itor;
3316 break;
3317 }
3318 }
3319
3320 if (hook == nullptr)
3321 {
3322 std::stringstream msg;
3323 msg << "Node '" << def.node.ToString() << "' is not a hook-node (not marked with flag 'h'), ignoring...";
3324 AddMessage(Message::TYPE_ERROR, msg.str());
3325 return;
3326 }
3327
3328 /* Process options */
3329 hook->hk_lockrange = def.option_hook_range;
3331 hook->hk_maxforce = def.option_max_force;
3332 hook->hk_group = def.option_hookgroup;
3333 hook->hk_lockgroup = def.option_lockgroup;
3334 hook->hk_timer = 0.f; // Hardcoded in BTS_HOOKS
3335 hook->hk_timer_preset = def.option_timer;
3337 hook->hk_selflock = def.flag_self_lock;
3338 hook->hk_nodisable = def.flag_no_disable;
3339 if (def.flag_auto_lock)
3340 {
3341 hook->hk_autolock = true;
3342 if (hook->hk_group == -1)
3343 {
3344 hook->hk_group = -2; /* only overwrite hgroup when its still default (-1) */
3345 }
3346 }
3347 if (def.flag_no_rope)
3348 {
3349 hook->hk_beam->bounded = NOSHOCK;
3350 }
3351 if (!def.flag_visible) // NOTE: This flag can only hide a visible beam - it won't show a beam defined with 'invisible' flag.
3352 {
3353 // Find beam index
3354 int beam_index = -1;
3355 for (int i = 0; i < m_actor->ar_num_beams; ++i)
3356 {
3357 if (hook->hk_beam == &m_actor->ar_beams[i])
3358 {
3359 beam_index = i;
3360 break;
3361 }
3362 }
3363
3364 // Erase beam visuals (only exist if defined without 'invisible' flag - we don't know at this point)
3365 m_actor->m_gfx_actor->RemoveBeam(beam_index);
3366 }
3367}
3368
3370{
3371 auto itor = lockgroup.nodes.begin();
3372 auto end = lockgroup.nodes.end();
3373 for (; itor != end; ++itor)
3374 {
3375 NodeNum_t node = this->GetNodeIndexOrThrow(*itor);
3376 m_actor->ar_nodes[node].nd_lockgroup = lockgroup.number;
3377 }
3378}
3379
3381{
3382 float sbound = def.contraction_trigger_limit; // shortbound
3383 float lbound = def.expansion_trigger_limit; // longbound
3384
3385 // options
3386 bool invisible = false;
3388 bool shock_trigger_enabled = true;
3389 bool triggerblocker = false;
3390 bool triggerblocker_inverted = false;
3391 bool cmdkeyblock = false;
3392 bool hooktoggle = false;
3393 bool enginetrigger = false;
3394
3395 bool trigger_cmdkeyblock_state_short = false;
3396 bool trigger_cmdkeyblock_state_long = true;
3397 if (def.longbound_trigger_action != -1) trigger_cmdkeyblock_state_long = false;
3398
3399 // now 'parse' the options
3401 {
3402 invisible = true;
3403 }
3404 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_x_START_DISABLED)) // this trigger is disabled on startup, default is enabled
3405 {
3406 shock_trigger_enabled = false;
3407 }
3408 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_B_TRIGGER_BLOCKER)) // Blocker that enable/disable other triggers
3409 {
3410 shockflag |= SHOCK_FLAG_TRG_BLOCKER;
3411 triggerblocker = true;
3412 }
3413 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_b_KEY_BLOCKER)) // Set the CommandKeys that are set in a commandkeyblocker or trigger to blocked on startup, default is released
3414 {
3415 cmdkeyblock = true;
3416 }
3417 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_s_CMD_NUM_SWITCH)) // switch that exchanges cmdshort/cmdshort for all triggers with the same commandnumbers, default false
3418 {
3419 shockflag |= SHOCK_FLAG_TRG_CMD_SWITCH;
3420 }
3421 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_c_COMMAND_STYLE)) // trigger is set with commandstyle boundaries instead of shocksytle
3422 {
3423 sbound = abs(sbound-1);
3424 lbound = lbound-1;
3425 }
3426 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_A_INV_TRIGGER_BLOCKER)) // Blocker that enable/disable other triggers, reversed activation method (inverted Blocker style, auto-ON)
3427 {
3428 shockflag |= SHOCK_FLAG_TRG_BLOCKER_A;
3429 triggerblocker_inverted = true;
3430 }
3432 {
3433 shockflag |= SHOCK_FLAG_TRG_HOOK_UNLOCK;
3434 hooktoggle = true;
3435 }
3437 {
3438 shockflag |= SHOCK_FLAG_TRG_HOOK_LOCK;
3439 hooktoggle = true;
3440 }
3441 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_t_CONTINUOUS)) // this trigger sends values between 0 and 1
3442 {
3443 shockflag |= SHOCK_FLAG_TRG_CONTINUOUS;
3444 }
3445 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_E_ENGINE_TRIGGER)) // this trigger is used to control an engine
3446 {
3447 shockflag |= SHOCK_FLAG_TRG_ENGINE;
3448 enginetrigger = true;
3449 }
3450
3451 if (!triggerblocker && !triggerblocker_inverted && !hooktoggle && !enginetrigger)
3452 {
3453 // make the full check
3455 {
3456 AddMessage(Message::TYPE_ERROR, fmt::format("Wrong 'shortbound_trigger_action': '{}' - must be between 1 and {}. Trigger deactivated.", def.shortbound_trigger_action, MAX_COMMANDS));
3457 return;
3458 }
3459 }
3460 else if (!hooktoggle && !enginetrigger)
3461 {
3462 // this is a Trigger-Blocker, make special check
3464 {
3465 AddMessage(Message::TYPE_ERROR, "Wrong command-eventnumber (Triggers). Trigger-Blocker deactivated.");
3466 return;
3467 }
3468 }
3469 else if (enginetrigger)
3470 {
3471 if (triggerblocker || triggerblocker_inverted || hooktoggle || (shockflag & SHOCK_FLAG_TRG_CMD_SWITCH))
3472 {
3473 AddMessage(Message::TYPE_ERROR, "Error: Wrong command-eventnumber (Triggers). Engine trigger deactivated.");
3474 return;
3475 }
3476 }
3477
3478 // `add_beam()`
3479 const NodeNum_t node_1_index = this->ResolveNodeRef(def.nodes[0]);
3480 const NodeNum_t node_2_index = this->ResolveNodeRef(def.nodes[1]);
3481 if (node_1_index == NODENUM_INVALID || node_2_index == NODENUM_INVALID)
3482 {
3483 this->AddMessage(Message::TYPE_WARNING, "Skipping trigger, some nodes not found");
3484 return;
3485 }
3486 int beam_index = m_actor->ar_num_beams;
3487 beam_t & beam = AddBeam(m_actor->ar_nodes[node_1_index], m_actor->ar_nodes[node_2_index], def.beam_defaults, def.detacher_group);
3488 beam.bm_type = BEAM_HYDRO;
3489 SetBeamStrength(beam, def.beam_defaults->breaking_threshold);
3490 SetBeamSpring(beam, 0.f);
3491 SetBeamDamping(beam, 0.f);
3492 CalculateBeamLength(beam);
3493 beam.shortbound = sbound;
3494 beam.longbound = lbound;
3495 beam.bounded = TRIGGER;
3496
3497 if (!invisible)
3498 {
3499 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
3500 }
3501 else
3502 {
3503 m_actor->ar_beams_invisible[beam_index] = true;
3504 }
3505 // end `add_beam()`
3506
3508 {
3509 LOG("Trigger added. BeamID " + TOSTRING(beam_index));
3510 }
3511
3512 shock_t& shock = this->GetFreeShock();
3513 beam.shock = &shock;
3514 shock.beamid = beam_index;
3515 shock.trigger_switch_state = 0.0f; // used as bool and countdowntimer, dont touch!
3516 if (!triggerblocker && !triggerblocker_inverted) // this is no triggerblocker (A/B)
3517 {
3519 if (def.longbound_trigger_action != -1 || (def.longbound_trigger_action == -1 && hooktoggle)) // this is a trigger or a hooktoggle
3521 else // this is a commandkeyblocker
3522 shockflag |= SHOCK_FLAG_TRG_CMD_BLOCKER;
3523 }
3524 else // this is a triggerblocker
3525 {
3526 if (!triggerblocker_inverted)
3527 {
3528 //normal BLOCKER
3529 shockflag |= SHOCK_FLAG_TRG_BLOCKER;
3532 }
3533 else
3534 {
3535 //inverted BLOCKER
3536 shockflag |= SHOCK_FLAG_TRG_BLOCKER_A;
3539 }
3540 }
3541
3542 if (cmdkeyblock && !triggerblocker)
3543 {
3544 trigger_cmdkeyblock_state_short = true;
3545 if (def.longbound_trigger_action != -1) trigger_cmdkeyblock_state_long = true;
3546 }
3547 if (def.boundary_timer > 0)
3549 else
3550 shock.trigger_boundary_t = 1.0f;
3551
3552 shock.flags = shockflag;
3553 shock.trigger_enabled = shock_trigger_enabled;
3554 shock.sbd_spring = def.beam_defaults->springiness;
3555 shock.sbd_damp = def.beam_defaults->damping_constant;
3556 shock.last_debug_state = 0;
3557
3558 // Note this is a special 'array' object - any negative indices are valid!
3559 m_actor->ar_command_key[def.shortbound_trigger_action].trigger_cmdkeyblock_state = trigger_cmdkeyblock_state_short;
3560 m_actor->ar_command_key[def.longbound_trigger_action].trigger_cmdkeyblock_state = trigger_cmdkeyblock_state_long;
3561}
3562
3564{
3565 unsigned int node_index = GetNodeIndexOrThrow(node_ref);
3566 m_actor->ar_nodes[node_index].nd_contacter = true;
3567};
3568
3570{
3572
3573 rotator.angle = 0;
3574 rotator.rate = def.rate;
3575 rotator.axis1 = GetNodeIndexOrThrow(def.axis_nodes[0]);
3576 rotator.axis2 = GetNodeIndexOrThrow(def.axis_nodes[1]);
3577 rotator.force = ROTATOR_FORCE_DEFAULT;
3579 rotator.engine_coupling = def.engine_coupling;
3580 rotator.needs_engine = def.needs_engine;
3581 for (unsigned int i = 0; i < 4; i++)
3582 {
3583 rotator.nodes1[i] = GetNodeIndexOrThrow(def.base_plate_nodes[i]);
3584 rotator.nodes2[i] = GetNodeIndexOrThrow(def.rotating_plate_nodes[i]);
3585 }
3586
3587 // Validate the reference structure
3588 this->ValidateRotator(m_actor->ar_num_rotators + 1, rotator.axis1, rotator.axis2, rotator.nodes1, rotator.nodes2);
3589
3590 // Rotate left key
3591 m_actor->ar_command_key[def.spin_left_key].rotators.push_back(- (m_actor->ar_num_rotators + 1));
3592 m_actor->ar_command_key[def.spin_left_key].description = "Rotate_Left/Right";
3593
3594 // Rotate right key
3595 m_actor->ar_command_key[def.spin_right_key].rotators.push_back(m_actor->ar_num_rotators + 1);
3596
3598 m_actor->ar_command_key[def.spin_left_key].rotator_inertia,
3599 m_actor->ar_command_key[def.spin_right_key].rotator_inertia);
3600
3603}
3604
3606{
3608
3609 rotator.angle = 0;
3610 rotator.rate = def.rate;
3611 rotator.axis1 = GetNodeIndexOrThrow(def.axis_nodes[0]);
3612 rotator.axis2 = GetNodeIndexOrThrow(def.axis_nodes[1]);
3613 rotator.force = def.rotating_force; // Default value is set in constructor
3614 rotator.tolerance = def.tolerance; // Default value is set in constructor
3615 rotator.engine_coupling = def.engine_coupling;
3616 rotator.needs_engine = def.needs_engine;
3617 for (unsigned int i = 0; i < 4; i++)
3618 {
3619 rotator.nodes1[i] = GetNodeIndexOrThrow(def.base_plate_nodes[i]);
3620 rotator.nodes2[i] = GetNodeIndexOrThrow(def.rotating_plate_nodes[i]);
3621 }
3622
3623 // Validate the reference structure
3624 this->ValidateRotator(m_actor->ar_num_rotators + 1, rotator.axis1, rotator.axis2, rotator.nodes1, rotator.nodes2);
3625
3626 // Rotate left key
3627 m_actor->ar_command_key[def.spin_left_key].rotators.push_back(- (m_actor->ar_num_rotators + 1));
3628 if (! def.description.empty())
3629 {
3630 m_actor->ar_command_key[def.spin_left_key].description = def.description;
3631 }
3632 else
3633 {
3634 m_actor->ar_command_key[def.spin_left_key].description = "Rotate_Left/Right";
3635 }
3636
3637 // Rotate right key
3638 m_actor->ar_command_key[def.spin_right_key].rotators.push_back(m_actor->ar_num_rotators + 1);
3639
3641 m_actor->ar_command_key[def.spin_left_key].rotator_inertia,
3642 m_actor->ar_command_key[def.spin_right_key].rotator_inertia);
3643
3646}
3647
3649{
3650 // TODO: refactor _ProcessKeyInertia() to use this.
3651
3652 // Handle placeholders
3653 std::string start_function;
3654 std::string stop_function;
3655 if (inertia.start_function != "" && inertia.start_function != "/" && inertia.start_function != "-")
3656 {
3657 start_function = inertia.start_function;
3658 }
3659 if (inertia.stop_function != "" && inertia.stop_function != "/" && inertia.stop_function != "-")
3660 {
3661 stop_function = inertia.stop_function;
3662 }
3663
3664 obj.SetSimpleDelay(
3665 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3666 inertia.start_delay_factor,
3667 inertia.stop_delay_factor,
3668 start_function,
3669 stop_function
3670 );
3671}
3672
3674 RigDef::Inertia & inertia,
3675 RigDef::Inertia & inertia_defaults,
3676 RoR::CmdKeyInertia& contract_cmd,
3677 RoR::CmdKeyInertia& extend_cmd
3678)
3679{
3680 /* Handle placeholders */
3681 Ogre::String start_function;
3682 Ogre::String stop_function;
3683 if (! inertia.start_function.empty() && inertia.start_function != "/" && inertia.start_function != "-")
3684 {
3685 start_function = inertia.start_function;
3686 }
3687 if (! inertia.stop_function.empty() && inertia.stop_function != "/" && inertia.stop_function != "-")
3688 {
3689 stop_function = inertia.stop_function;
3690 }
3691 if (inertia.start_delay_factor != 0.f && inertia.stop_delay_factor != 0.f)
3692 {
3693 contract_cmd.SetCmdKeyDelay(
3694 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3695 inertia.start_delay_factor,
3696 inertia.stop_delay_factor,
3697 start_function,
3698 stop_function
3699 );
3700
3701 extend_cmd.SetCmdKeyDelay(
3702 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3703 inertia.start_delay_factor,
3704 inertia.stop_delay_factor,
3705 start_function,
3706 stop_function
3707 );
3708 }
3709 else if (inertia_defaults.start_delay_factor > 0 || inertia_defaults.stop_delay_factor > 0)
3710 {
3711 contract_cmd.SetCmdKeyDelay(
3712 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3713 inertia_defaults.start_delay_factor,
3714 inertia_defaults.stop_delay_factor,
3715 inertia_defaults.start_function,
3716 inertia_defaults.stop_function
3717 );
3718
3719 extend_cmd.SetCmdKeyDelay(
3720 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3721 inertia_defaults.start_delay_factor,
3722 inertia_defaults.stop_delay_factor,
3723 inertia_defaults.start_function,
3724 inertia_defaults.stop_function
3725 );
3726 }
3727}
3728
3730{
3731 const NodeNum_t beam_index = m_actor->ar_num_beams;
3732 const NodeNum_t node_1_index = this->ResolveNodeRef(def.nodes[0]);
3733 const NodeNum_t node_2_index = this->ResolveNodeRef(def.nodes[1]);
3734 if (node_1_index == NODENUM_INVALID || node_2_index == NODENUM_INVALID)
3735 {
3736 AddMessage(Message::TYPE_ERROR, "Skipping command, some nodes not found.");
3737 return;
3738 }
3739 beam_t & beam = AddBeam(m_actor->ar_nodes[node_1_index], m_actor->ar_nodes[node_2_index], def.beam_defaults, def.detacher_group);
3740 CalculateBeamLength(beam);
3741 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold()); /* Override settings from AddBeam() */
3742 SetBeamSpring(beam, def.beam_defaults->GetScaledSpringiness());
3743 SetBeamDamping(beam, def.beam_defaults->GetScaledDamping());
3744 beam.bm_type = BEAM_HYDRO;
3745
3746 /* Options */
3747 if (def.option_r_rope) { beam.bounded = ROPE; }
3748
3749 /* set the middle of the command, so its not required to recalculate this everytime ... */
3750 float center_length = 0.f;
3751 if (def.max_extension > def.max_contraction)
3752 {
3753 center_length = (def.max_extension - def.max_contraction) / 2 + def.max_contraction;
3754 }
3755 else
3756 {
3757 center_length = (def.max_contraction - def.max_extension) / 2 + def.max_extension;
3758 }
3759
3760 /* Add keys */
3761 command_t* contract_command = &m_actor->ar_command_key[def.contract_key];
3762 commandbeam_t cmd_beam;
3763 cmd_beam.cmb_beam_index = static_cast<uint16_t>(beam_index);
3764 cmd_beam.cmb_is_contraction = true;
3765 cmd_beam.cmb_speed = def.shorten_rate;
3766 cmd_beam.cmb_boundary_length = def.max_contraction;
3769 cmd_beam.cmb_needs_engine = def.needs_engine;
3770 cmd_beam.cmb_is_1press = def.option_p_1press;
3772 cmd_beam.cmb_plays_sound = def.plays_sound;
3773 cmd_beam.cmb_engine_coupling = def.affect_engine;
3774 cmd_beam.cmb_center_length = center_length;
3775 cmd_beam.cmb_state = std::shared_ptr<commandbeam_state_t>(new commandbeam_state_t);
3776 contract_command->beams.push_back(cmd_beam);
3777 if (contract_command->description.empty())
3778 {
3779 contract_command->description = def.description;
3780 }
3781
3782 command_t* extend_command = &m_actor->ar_command_key[def.extend_key];
3783 cmd_beam.cmb_is_contraction = false;
3784 cmd_beam.cmb_speed = def.lengthen_rate;
3785 cmd_beam.cmb_boundary_length = def.max_extension;
3786 extend_command->beams.push_back(cmd_beam);
3787 if (extend_command->description.empty())
3788 {
3789 extend_command->description = def.description;
3790 }
3791
3793 contract_command->command_inertia,
3794 extend_command->command_inertia);
3795
3796 if (! def.option_i_invisible)
3797 {
3798 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
3799 }
3800 else
3801 {
3802 m_actor->ar_beams_invisible[beam_index] = true;
3803 }
3804
3807
3808 // Update the unique pair list
3809 if (def.description != "hide")
3810 {
3811 bool must_insert_qpair = true;
3813 {
3814 // seek match on both keys (in any order)
3815 if ((def.contract_key == qpair.uckp_key1 && def.extend_key == qpair.uckp_key2)
3816 || (def.contract_key == qpair.uckp_key2 && def.extend_key == qpair.uckp_key1))
3817 {
3818 must_insert_qpair = false;
3819 if (def.description != "")
3820 {
3821 qpair.uckp_description = def.description; // Last description always wins
3822 }
3823 break;
3824 }
3825 }
3826 if (must_insert_qpair)
3827 {
3829 qpair.uckp_key1 = def.contract_key;
3830 qpair.uckp_key2 = def.extend_key;
3831 qpair.uckp_description = def.description;
3832 m_actor->ar_unique_commandkey_pairs.push_back(qpair);
3833 }
3834 }
3835}
3836
3838{
3839 int anim_flags = 0;
3840 float anim_option = 0;
3841
3842 /* Options. '{' intentionally misplaced. */
3843
3845 BITMASK_SET_1(anim_flags, ANIM_FLAG_AIRSPEED);
3846 }
3848 BITMASK_SET_1(anim_flags, ANIM_FLAG_VVI);
3849 }
3851 BITMASK_SET_1(anim_flags, ANIM_FLAG_AOA);
3852 }
3854 BITMASK_SET_1(anim_flags, ANIM_FLAG_FLAP);
3855 }
3857 BITMASK_SET_1(anim_flags, ANIM_FLAG_AIRBRAKE);
3858 }
3860 BITMASK_SET_1(anim_flags, ANIM_FLAG_ROLL);
3861 }
3863 BITMASK_SET_1(anim_flags, ANIM_FLAG_PITCH);
3864 }
3866 BITMASK_SET_1(anim_flags, ANIM_FLAG_BRAKE);
3867 }
3869 BITMASK_SET_1(anim_flags, ANIM_FLAG_ACCEL);
3870 }
3872 BITMASK_SET_1(anim_flags, ANIM_FLAG_CLUTCH);
3873 }
3875 BITMASK_SET_1(anim_flags, ANIM_FLAG_SPEEDO);
3876 }
3878 BITMASK_SET_1(anim_flags, ANIM_FLAG_TACHO);
3879 }
3881 BITMASK_SET_1(anim_flags, ANIM_FLAG_TURBO);
3882 }
3884 BITMASK_SET_1(anim_flags, ANIM_FLAG_PBRAKE);
3885 }
3887 BITMASK_SET_1(anim_flags, ANIM_FLAG_TORQUE);
3888 }
3891 }
3893 BITMASK_SET_1(anim_flags, ANIM_FLAG_BRUDDER);
3894 }
3896 BITMASK_SET_1(anim_flags, ANIM_FLAG_SHIFTER);
3897 anim_option = 1.f;
3898 }
3900 BITMASK_SET_1(anim_flags, ANIM_FLAG_SHIFTER);
3901 anim_option = 2.f;
3902 }
3904 BITMASK_SET_1(anim_flags, ANIM_FLAG_SHIFTER);
3905 anim_option = 3.f;
3906 }
3908 BITMASK_SET_1(anim_flags, ANIM_FLAG_SHIFTER);
3909 anim_option = 4.f;
3910 }
3913 anim_option = 1.f;
3914 }
3917 anim_option = 2.f;
3918 }
3921 anim_option = 3.f;
3922 }
3923
3924 /* Aerial */
3926 BITMASK_SET_1(anim_flags, ANIM_FLAG_THROTTLE);
3927 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3928 }
3930 BITMASK_SET_1(anim_flags, ANIM_FLAG_RPM);
3931 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3932 }
3934 BITMASK_SET_1(anim_flags, ANIM_FLAG_AETORQUE);
3935 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3936 }
3938 BITMASK_SET_1(anim_flags, ANIM_FLAG_AEPITCH);
3939 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3940 }
3942 BITMASK_SET_1(anim_flags, ANIM_FLAG_AESTATUS);
3943 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3944 }
3945
3946 unsigned int beam_index = m_actor->ar_num_beams;
3947 NodeNum_t n1 = this->GetNodeIndexOrThrow(def.nodes[0]);
3948 NodeNum_t n2 = this->GetNodeIndexOrThrow(def.nodes[1]);
3950 /* set the limits to something with sense by default */
3951 beam.shortbound = 0.99999f;
3952 beam.longbound = 1000000.0f;
3953 beam.bm_type = BEAM_HYDRO;
3954 CalculateBeamLength(beam);
3955 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold());
3956 SetBeamSpring(beam, def.beam_defaults->GetScaledSpringiness());
3957 SetBeamDamping(beam, def.beam_defaults->GetScaledDamping());
3958
3960 {
3961 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
3962 }
3963 else
3964 {
3965 m_actor->ar_beams_invisible[beam_index] = true;
3966 }
3967
3969 {
3970 beam.shortbound = def.short_limit;
3971 }
3973 {
3974 beam.longbound = def.long_limit;
3975 }
3976
3977 hydrobeam_t hb;
3978 hb.hb_beam_index = static_cast<uint16_t>(beam_index);
3980 hb.hb_ref_length = beam.L;
3981 hb.hb_flags = 0;
3982 hb.hb_anim_flags = anim_flags;
3983 hb.hb_anim_param = anim_option;
3984
3985 if (def.inertia_defaults->start_delay_factor > 0 && def.inertia_defaults->stop_delay_factor > 0)
3986 {
3989 def.inertia_defaults->start_delay_factor,
3990 def.inertia_defaults->stop_delay_factor,
3991 def.inertia_defaults->start_function,
3992 def.inertia_defaults->stop_function
3993 );
3994 }
3995
3996 m_actor->ar_hydros.push_back(hb);
3997}
3998
4000 node_t & node_1,
4001 node_t & node_2,
4002 std::shared_ptr<RigDef::BeamDefaults> & beam_defaults,
4003 int detacher_group
4004)
4005{
4006 /* Init */
4007 beam_t & beam = GetAndInitFreeBeam(node_1, node_2);
4008 beam.detacher_group = detacher_group;
4009 beam.bm_disabled = false;
4010
4011 /* Breaking threshold (strength) */
4012 float strength = beam_defaults->breaking_threshold;
4013 beam.strength = strength;
4014
4015 /* Deformation */
4016 SetBeamDeformationThreshold(beam, beam_defaults);
4017
4018 float plastic_coef = beam_defaults->plastic_deform_coef;
4019 beam.plastic_coef = plastic_coef;
4020
4021 return beam;
4022}
4023
4024void ActorSpawner::SetBeamStrength(beam_t & beam, float strength)
4025{
4026 beam.strength = strength;
4027}
4028
4030{
4031 bool invisible = false;
4032 unsigned int hydro_flags = 0;
4033
4035 {
4036 invisible = true;
4037 }
4039 {
4040 hydro_flags |= HYDRO_FLAG_SPEED;
4041 }
4043 {
4044 hydro_flags |= HYDRO_FLAG_AILERON;
4045 }
4047 {
4048 hydro_flags |= HYDRO_FLAG_RUDDER;
4049 }
4051 {
4052 hydro_flags |= HYDRO_FLAG_ELEVATOR;
4053 }
4055 {
4056 hydro_flags |= (HYDRO_FLAG_AILERON | HYDRO_FLAG_ELEVATOR);
4057 }
4059 {
4061 }
4063 {
4064 hydro_flags |= (HYDRO_FLAG_AILERON | HYDRO_FLAG_RUDDER);
4065 }
4067 {
4068 hydro_flags |= (HYDRO_FLAG_REV_AILERON | HYDRO_FLAG_RUDDER);
4069 }
4071 {
4072 hydro_flags |= (HYDRO_FLAG_ELEVATOR | HYDRO_FLAG_RUDDER);
4073 }
4075 {
4076 hydro_flags |= (HYDRO_FLAG_REV_ELEVATOR | HYDRO_FLAG_RUDDER);
4077 }
4079 {
4080 hydro_flags |= HYDRO_FLAG_DIR;
4081 }
4082
4083 node_t & node_1 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4084 node_t & node_2 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4085
4086 int beam_index = m_actor->ar_num_beams;
4087 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
4088 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold());
4089 CalculateBeamLength(beam);
4090 beam.bm_type = BEAM_HYDRO;
4091 beam.k = def.beam_defaults->GetScaledSpringiness();
4092 beam.d = def.beam_defaults->GetScaledDamping();
4093
4094 if (!invisible)
4095 {
4096 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
4097 }
4098 else
4099 {
4100 m_actor->ar_beams_invisible[beam_index] = true;
4101 }
4102
4103 hydrobeam_t hb;
4104 hb.hb_flags = hydro_flags;
4106 hb.hb_beam_index = static_cast<uint16_t>(beam_index);
4107 hb.hb_ref_length = beam.L;
4108 hb.hb_anim_flags = 0;
4109 hb.hb_anim_param = 0.f;
4111
4112 m_actor->ar_hydros.push_back(hb);
4113}
4114
4116{
4117 node_t & node_1 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4118 node_t & node_2 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4119 float short_bound = def.short_bound;
4120 float long_bound = def.long_bound;
4121 unsigned int shock_flags = SHOCK_FLAG_NORMAL | SHOCK_FLAG_ISSHOCK3;
4122
4124 {
4125 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4126 short_bound /= beam_length;
4127 long_bound /= beam_length;
4128 }
4130 {
4131 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4132 short_bound = (beam_length - short_bound) / beam_length;
4133 long_bound = (long_bound - beam_length) / beam_length;
4134
4135 if (long_bound < 0.f)
4136 {
4137 AddMessage(
4139 "Metric shock length calculation failed, 'short_bound' less than beams spawn length. Resetting to beam's spawn length (short_bound = 0)"
4140 );
4141 long_bound = 0.f;
4142 }
4143
4144 if (short_bound > 1.f)
4145 {
4146 AddMessage(
4148 "Metric shock length calculation failed, 'short_bound' less than 0 meters. Resetting to 0 meters (short_bound = 1)"
4149 );
4150 short_bound = 1.f;
4151 }
4152 }
4153
4154 int beam_index = m_actor->ar_num_beams;
4155 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
4156 SetBeamStrength(beam, def.beam_defaults->breaking_threshold * 4.f);
4157 beam.bm_type = BEAM_HYDRO;
4158 beam.bounded = SHOCK3;
4159 beam.k = def.spring_in;
4160 beam.d = def.damp_in;
4161 beam.shortbound = short_bound;
4162 beam.longbound = long_bound;
4163
4164 /* Length + pre-compression */
4165 CalculateBeamLength(beam);
4166 beam.L *= def.precompression;
4167 beam.refL *= def.precompression;
4168
4170 {
4171 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
4172 }
4173 else
4174 {
4175 m_actor->ar_beams_invisible[beam_index] = true;
4176 }
4177
4178 shock_t & shock = GetFreeShock();
4179 shock.flags = shock_flags;
4180 shock.sbd_spring = def.beam_defaults->springiness;
4181 shock.sbd_damp = def.beam_defaults->damping_constant;
4182 shock.sbd_break = def.beam_defaults->breaking_threshold;
4183 shock.springin = def.spring_in;
4184 shock.dampin = def.damp_in;
4185 shock.springout = def.spring_out;
4186 shock.dampout = def.damp_out;
4187 shock.splitin = def.split_vel_in;
4188 shock.dslowin = def.damp_in_slow;
4189 shock.dfastin = def.damp_in_fast;
4190 shock.splitout = def.split_vel_out;
4191 shock.dslowout = def.damp_out_slow;
4192 shock.dfastout = def.damp_out_fast;
4194
4195 beam.shock = & shock;
4196 shock.beamid = beam_index;
4197}
4198
4200{
4201 node_t & node_1 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4202 node_t & node_2 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4203 float short_bound = def.short_bound;
4204 float long_bound = def.long_bound;
4205 unsigned int shock_flags = SHOCK_FLAG_NORMAL | SHOCK_FLAG_ISSHOCK2;
4206
4208 {
4209 BITMASK_SET_0(shock_flags, SHOCK_FLAG_NORMAL); /* Not normal anymore */
4210 BITMASK_SET_1(shock_flags, SHOCK_FLAG_SOFTBUMP);
4211 }
4213 {
4214 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4215 short_bound /= beam_length;
4216 long_bound /= beam_length;
4217 }
4219 {
4220 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4221 short_bound = (beam_length - short_bound) / beam_length;
4222 long_bound = (long_bound - beam_length) / beam_length;
4223
4224 if (long_bound < 0.f)
4225 {
4226 AddMessage(
4228 "Metric shock length calculation failed, 'short_bound' less than beams spawn length. Resetting to beam's spawn length (short_bound = 0)"
4229 );
4230 long_bound = 0.f;
4231 }
4232
4233 if (short_bound > 1.f)
4234 {
4235 AddMessage(
4237 "Metric shock length calculation failed, 'short_bound' less than 0 meters. Resetting to 0 meters (short_bound = 1)"
4238 );
4239 short_bound = 1.f;
4240 }
4241 }
4242
4243 int beam_index = m_actor->ar_num_beams;
4244 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
4245 SetBeamStrength(beam, def.beam_defaults->breaking_threshold * 4.f);
4246 beam.bm_type = BEAM_HYDRO;
4247 beam.bounded = SHOCK2;
4248 beam.k = def.spring_in;
4249 beam.d = def.damp_in;
4250 beam.shortbound = short_bound;
4251 beam.longbound = long_bound;
4252
4253 /* Length + pre-compression */
4254 CalculateBeamLength(beam);
4255 beam.L *= def.precompression;
4256 beam.refL *= def.precompression;
4257
4259 {
4260 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
4261 }
4262 else
4263 {
4264 m_actor->ar_beams_invisible[beam_index] = true;
4265 }
4266
4267 shock_t & shock = GetFreeShock();
4268 shock.flags = shock_flags;
4269 shock.sbd_spring = def.beam_defaults->springiness;
4270 shock.sbd_damp = def.beam_defaults->damping_constant;
4271 shock.sbd_break = def.beam_defaults->breaking_threshold;
4272 shock.springin = def.spring_in;
4273 shock.dampin = def.damp_in;
4274 shock.springout = def.spring_out;
4275 shock.dampout = def.damp_out;
4277 shock.dprogin = def.progress_factor_damp_in;
4281
4282 beam.shock = & shock;
4283 shock.beamid = beam_index;
4284}
4285
4287{
4288 node_t & node_1 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4289 node_t & node_2 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4290 float short_bound = def.short_bound;
4291 float long_bound = def.long_bound;
4292 unsigned int shock_flags = SHOCK_FLAG_NORMAL;
4293
4295 {
4296 BITMASK_SET_0(shock_flags, SHOCK_FLAG_NORMAL); /* Not normal anymore */
4297 BITMASK_SET_1(shock_flags, SHOCK_FLAG_LACTIVE);
4299 }
4301 {
4302 BITMASK_SET_0(shock_flags, SHOCK_FLAG_NORMAL); /* Not normal anymore */
4303 BITMASK_SET_1(shock_flags, SHOCK_FLAG_RACTIVE);
4305 }
4307 {
4308 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4309 short_bound /= beam_length;
4310 long_bound /= beam_length;
4311 }
4312
4313 int beam_index = m_actor->ar_num_beams;
4314 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
4315 beam.shortbound = short_bound;
4316 beam.longbound = long_bound;
4317 beam.bounded = SHOCK1;
4318 beam.bm_type = BEAM_HYDRO;
4319 beam.k = def.spring_rate;
4320 beam.d = def.damping;
4321 SetBeamStrength(beam, def.beam_defaults->breaking_threshold * 4.f);
4322
4323 /* Length + pre-compression */
4324 CalculateBeamLength(beam);
4325 beam.L *= def.precompression;
4326 beam.refL *= def.precompression;
4327
4328 shock_t & shock = GetFreeShock();
4329 shock.flags = shock_flags;
4330 shock.sbd_spring = def.beam_defaults->springiness;
4331 shock.sbd_damp = def.beam_defaults->damping_constant;
4332 shock.sbd_break = def.beam_defaults->breaking_threshold;
4334
4336 {
4337 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
4338 }
4339 else
4340 {
4341 m_actor->ar_beams_invisible[beam_index] = true;
4342 }
4343
4344 beam.shock = & shock;
4345 shock.beamid = beam_index;
4346}
4347
4349{
4350 NodeNum_t base_node_index = m_actor->ar_num_nodes;
4351 WheelID_t wheel_id = m_actor->ar_num_wheels;
4352 wheel_t & wheel = m_actor->ar_wheels[wheel_id];
4353
4354 node_t *axis_node_1 = &m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4355 node_t *axis_node_2 = &m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4356 // Enforce the "second node must have a larger Z coordinate than the first" constraint
4357 if (axis_node_1->AbsPosition.z > axis_node_2->AbsPosition.z)
4358 {
4359 node_t* swap = axis_node_1;
4360 axis_node_1 = axis_node_2;
4361 axis_node_2 = swap;
4362 }
4363
4364 // Rigidity node
4365 node_t *rigidity_node = nullptr;
4366 node_t *axis_node_closest_to_rigidity_node = nullptr;
4368 {
4369 rigidity_node = GetNodePointer(def.rigidity_node);
4370 Ogre::Real distance_1 = (rigidity_node->RelPosition - axis_node_1->RelPosition).length();
4371 Ogre::Real distance_2 = (rigidity_node->RelPosition - axis_node_2->RelPosition).length();
4372 axis_node_closest_to_rigidity_node = ((distance_1 < distance_2)) ? axis_node_1 : axis_node_2;
4373 }
4374
4375 // Tweaks
4376 float override_rim_radius = TuneupUtil::getTweakedWheelRimRadius(m_actor->getWorkingTuneupDef(), wheel_id, def.rim_radius);
4377 float override_tire_radius = TuneupUtil::getTweakedWheelTireRadius(m_actor->getWorkingTuneupDef(), wheel_id, def.tyre_radius);
4378
4379 // Node&beam generation
4380 Ogre::Vector3 axis_vector = axis_node_2->RelPosition - axis_node_1->RelPosition;
4381 wheel.wh_width = axis_vector.length(); // wheel_def.width is ignored.
4382 axis_vector.normalise();
4383 Ogre::Vector3 rim_ray_vector = axis_vector.perpendicular() * override_rim_radius;
4384 Ogre::Quaternion rim_ray_rotator = Ogre::Quaternion(Ogre::Degree(-360.f / (def.num_rays * 2)), axis_vector);
4385
4386 // Rim nodes
4387 for (unsigned int i = 0; i < def.num_rays; i++)
4388 {
4389 float node_mass = def.mass / (4.f * def.num_rays);
4390
4391 // Outer ring
4392 Ogre::Vector3 ray_point = axis_node_1->RelPosition + rim_ray_vector;
4393 rim_ray_vector = rim_ray_rotator * rim_ray_vector;
4394
4395 node_t & outer_node = GetFreeNode();
4396 InitNode(outer_node, ray_point, def.node_defaults);
4397
4398 outer_node.mass = node_mass;
4399 outer_node.friction_coef = def.node_defaults->friction;
4400 outer_node.nd_rim_node = true;
4401 AdjustNodeBuoyancy(outer_node, def.node_defaults);
4403
4404 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
4405
4406 // Inner ring
4407 ray_point = axis_node_2->RelPosition + rim_ray_vector;
4408 rim_ray_vector = rim_ray_rotator * rim_ray_vector;
4409
4410 node_t & inner_node = GetFreeNode();
4411 InitNode(inner_node, ray_point, def.node_defaults);
4412
4413 inner_node.mass = node_mass;
4414 inner_node.friction_coef = def.node_defaults->friction;
4415 inner_node.nd_rim_node = true;
4416 AdjustNodeBuoyancy(inner_node, def.node_defaults);
4418
4419 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
4420
4421 // Wheel object
4422 wheel.wh_rim_nodes[i * 2] = & outer_node;
4423 wheel.wh_rim_nodes[(i * 2) + 1] = & inner_node;
4424 }
4425
4426 Ogre::Vector3 tyre_ray_vector = axis_vector.perpendicular() * override_tire_radius;
4427 Ogre::Quaternion& tyre_ray_rotator = rim_ray_rotator;
4428 tyre_ray_vector = tyre_ray_rotator * tyre_ray_vector;
4429
4430 // Tyre nodes
4431 for (unsigned int i = 0; i < def.num_rays; i++)
4432 {
4433 /* Outer ring */
4434 float node_mass = def.mass / (4.f * def.num_rays);
4435 Ogre::Vector3 ray_point = axis_node_1->RelPosition + tyre_ray_vector;
4436 tyre_ray_vector = tyre_ray_rotator * tyre_ray_vector;
4437
4438 node_t & outer_node = GetFreeNode();
4439 InitNode(outer_node, ray_point);
4440 outer_node.mass = node_mass;
4441 outer_node.friction_coef = def.node_defaults->friction;
4442 outer_node.volume_coef = def.node_defaults->volume;
4443 outer_node.surface_coef = def.node_defaults->surface;
4444 outer_node.nd_contacter = true;
4445 outer_node.nd_tyre_node = true;
4446 AdjustNodeBuoyancy(outer_node, def.node_defaults);
4447
4448 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
4449
4450 // Inner ring
4451 ray_point = axis_node_2->RelPosition + tyre_ray_vector;
4452 tyre_ray_vector = tyre_ray_rotator * tyre_ray_vector;
4453
4454 node_t & inner_node = GetFreeNode();
4455 InitNode(inner_node, ray_point);
4456 inner_node.mass = node_mass;
4457 inner_node.friction_coef = def.node_defaults->friction;
4458 inner_node.volume_coef = def.node_defaults->volume;
4459 inner_node.surface_coef = def.node_defaults->surface;
4460 inner_node.nd_contacter = true;
4461 inner_node.nd_tyre_node = true;
4462 AdjustNodeBuoyancy(inner_node, def.node_defaults);
4463
4464 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
4465
4466 // Wheel object
4467 wheel.wh_nodes[i * 2] = & outer_node;
4468 wheel.wh_nodes[(i * 2) + 1] = & inner_node;
4469 }
4470
4472 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = def.num_rays;
4476
4477 // Beams
4478 float rim_spring = def.rim_springiness;
4479 float rim_damp = def.rim_damping;
4480 float tyre_spring = def.tyre_springiness;
4481 float tyre_damp = def.tyre_damping;
4482 float tread_spring = def.beam_defaults->springiness;
4483 float tread_damp = def.beam_defaults->damping_constant;
4484
4485 for (unsigned int i = 0; i < def.num_rays; i++)
4486 {
4487 // --- Rim ---
4488
4489 // Rim axis to rim ring
4490 unsigned int rim_outer_node_index = base_node_index + (i * 2);
4491 node_t *rim_outer_node = & m_actor->ar_nodes[rim_outer_node_index];
4492 node_t *rim_inner_node = & m_actor->ar_nodes[rim_outer_node_index + 1];
4493
4494 AddWheelBeam(axis_node_1, rim_outer_node, rim_spring, rim_damp, def.beam_defaults);
4495 AddWheelBeam(axis_node_2, rim_inner_node, rim_spring, rim_damp, def.beam_defaults);
4496 AddWheelBeam(axis_node_2, rim_outer_node, rim_spring, rim_damp, def.beam_defaults);
4497 AddWheelBeam(axis_node_1, rim_inner_node, rim_spring, rim_damp, def.beam_defaults);
4498
4499 // Reinforcement rim ring
4500 unsigned int rim_next_outer_node_index = base_node_index + (((i + 1) % def.num_rays) * 2);
4501 node_t *rim_next_outer_node = & m_actor->ar_nodes[rim_next_outer_node_index];
4502 node_t *rim_next_inner_node = & m_actor->ar_nodes[rim_next_outer_node_index + 1];
4503
4504 AddWheelBeam(rim_outer_node, rim_inner_node, rim_spring, rim_damp, def.beam_defaults);
4505 AddWheelBeam(rim_outer_node, rim_next_outer_node, rim_spring, rim_damp, def.beam_defaults);
4506 AddWheelBeam(rim_inner_node, rim_next_inner_node, rim_spring, rim_damp, def.beam_defaults);
4507 AddWheelBeam(rim_inner_node, rim_next_outer_node, rim_spring, rim_damp, def.beam_defaults);
4508 }
4509
4510 // Tyre beams
4511 // Quick&dirty port from original SerializedRig::addWheel3()
4512 for (unsigned int i = 0; i < def.num_rays; i++)
4513 {
4514 int rim_node_index = base_node_index + i*2;
4515 int tyre_node_index = base_node_index + i*2 + def.num_rays*2;
4516 node_t * rim_node = & m_actor->ar_nodes[rim_node_index];
4517
4518 AddWheelBeam(rim_node, & m_actor->ar_nodes[tyre_node_index], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4519
4520 int tyre_base_index = (i == 0) ? tyre_node_index + (def.num_rays * 2) : tyre_node_index;
4521 AddWheelBeam(rim_node, & m_actor->ar_nodes[tyre_base_index - 1], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4522 AddWheelBeam(rim_node, & m_actor->ar_nodes[tyre_base_index - 2], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4523
4524 node_t * next_rim_node = & m_actor->ar_nodes[rim_node_index + 1];
4525 AddWheelBeam(next_rim_node, & m_actor->ar_nodes[tyre_node_index], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4526 AddWheelBeam(next_rim_node, & m_actor->ar_nodes[tyre_node_index + 1], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4527
4528 {
4529 int index = (i == 0) ? tyre_node_index + (def.num_rays * 2) - 1 : tyre_node_index - 1;
4530 node_t * tyre_node = & m_actor->ar_nodes[index];
4531 AddWheelBeam(next_rim_node, tyre_node, tyre_spring/2.f, tyre_damp, def.beam_defaults);
4532 }
4533
4534 //reinforcement (tire tread)
4535 {
4536 // Very messy port :(
4537 // Aliases
4538 int rimnode = rim_node_index;
4539 int rays = def.num_rays;
4540
4541 AddWheelBeam(&m_actor->ar_nodes[rimnode+rays*2], &m_actor->ar_nodes[base_node_index+i*2+1+rays*2], tread_spring, tread_damp, def.beam_defaults);
4542 AddWheelBeam(&m_actor->ar_nodes[rimnode+rays*2], &m_actor->ar_nodes[base_node_index+((i+1)%rays)*2+rays*2], tread_spring, tread_damp, def.beam_defaults);
4543 AddWheelBeam(&m_actor->ar_nodes[base_node_index+i*2+1+rays*2], &m_actor->ar_nodes[base_node_index+((i+1)%rays)*2+1+rays*2], tread_spring, tread_damp, def.beam_defaults);
4544 AddWheelBeam(&m_actor->ar_nodes[rimnode+1+rays*2], &m_actor->ar_nodes[base_node_index+((i+1)%rays)*2+rays*2], tread_spring, tread_damp, def.beam_defaults);
4545
4546 if (rigidity_node != nullptr)
4547 {
4548 if (axis_node_closest_to_rigidity_node == axis_node_1)
4549 {
4550 axis_node_closest_to_rigidity_node = & m_actor->ar_nodes[base_node_index+i*2+rays*2];
4551 } else
4552 {
4553 axis_node_closest_to_rigidity_node = & m_actor->ar_nodes[base_node_index+i*2+1+rays*2];
4554 };
4555 unsigned int beam_index = AddWheelBeam(rigidity_node, axis_node_closest_to_rigidity_node, tyre_spring, tyre_damp, def.beam_defaults);
4556 GetBeam(beam_index).bm_type = BEAM_VIRTUAL;
4557 }
4558 }
4559 }
4560
4561 // Calculate the point where the support beams get stiff and prevent the tire tread nodes
4562 // bounce into the rim rimradius / tire radius and add 5%, this is a shortbound calc in % !
4563
4564 float support_beams_short_bound = 1.0f - ((override_rim_radius / override_tire_radius) * 0.95f);
4565
4566 for (unsigned int i=0; i<def.num_rays; i++)
4567 {
4568 // tiretread anti collapse reinforcements, using precalced support beams
4569 unsigned int tirenode = base_node_index + i*2 + def.num_rays*2;
4570 unsigned int beam_index;
4571
4572 beam_index = AddWheelBeam(axis_node_1, &m_actor->ar_nodes[tirenode], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4573 GetBeam(beam_index).shortbound = support_beams_short_bound;
4574 GetBeam(beam_index).longbound = 0.f;
4575 GetBeam(beam_index).bounded = SHOCK1;
4576
4577 beam_index = AddWheelBeam(axis_node_2, &m_actor->ar_nodes[tirenode + 1], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4578 GetBeam(beam_index).shortbound = support_beams_short_bound;
4579 GetBeam(beam_index).longbound = 0.f;
4580 GetBeam(beam_index).bounded = SHOCK1;
4581 }
4582
4583 // Wheel object
4584 wheel.wh_braking = def.braking;
4585 wheel.wh_propulsed = def.propulsion;
4586 wheel.wh_num_nodes = 2 * def.num_rays;
4587 wheel.wh_num_rim_nodes = wheel.wh_num_nodes;
4588 wheel.wh_axis_node_0 = axis_node_1;
4589 wheel.wh_axis_node_1 = axis_node_2;
4590 wheel.wh_radius = override_tire_radius;
4591 wheel.wh_rim_radius = override_rim_radius;
4593
4595 {
4596 // for inter-differential locking
4599 }
4600
4601 // Find near attach
4602 Ogre::Real length_1 = (axis_node_1->RelPosition - wheel.wh_arm_node->RelPosition).length();
4603 Ogre::Real length_2 = (axis_node_2->RelPosition - wheel.wh_arm_node->RelPosition).length();
4604 wheel.wh_near_attach_node = (length_1 < length_2) ? axis_node_1 : axis_node_2;
4605
4606 this->CreateFlexBodyWheelVisuals(wheel_id,
4607 base_node_index,
4608 axis_node_1->pos,
4609 axis_node_2->pos,
4610 def.num_rays,
4611 override_rim_radius,
4617 );
4618
4619 // Commit the wheel
4621}
4622
4624{
4625 node_t *def_node_1 = GetNodePointerOrThrow(def.nodes[0]);
4626 node_t *def_node_2 = GetNodePointerOrThrow(def.nodes[1]);
4627
4628 if (def_node_1 == def_node_2)
4629 {
4630 throw Exception("Wheel axis nodes must not be set to single node!");
4631 }
4632
4633 /* Enforce the "second node must have a larger Z coordinate than the first" constraint */
4634 if (def_node_1->AbsPosition.z > def_node_2->AbsPosition.z)
4635 {
4636 out_node_1 = def_node_2;
4637 out_node_2 = def_node_1;
4638 }
4639 else
4640 {
4641 out_node_1 = def_node_1;
4642 out_node_2 = def_node_2;
4643 }
4644}
4645
4647{
4648 WheelID_t wheel_id = m_actor->ar_num_wheels;
4649
4650 node_t* axis_node_1 = nullptr;
4651 node_t* axis_node_2 = nullptr;
4652 this->GetWheelAxisNodes(meshwheel_def, axis_node_1, axis_node_2);
4653
4654 NodeNum_t base_node_index = (NodeNum_t)m_actor->ar_num_nodes;
4656 wheel_id,
4657 meshwheel_def.num_rays,
4658 axis_node_1,
4659 axis_node_2,
4660 GetNodePointer(meshwheel_def.reference_arm_node), /*optional*/
4661 meshwheel_def.num_rays * 2,
4662 meshwheel_def.num_rays * 8,
4664 meshwheel_def.propulsion,
4665 meshwheel_def.braking,
4666 meshwheel_def.node_defaults,
4667 meshwheel_def.mass
4668 );
4669
4671 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = meshwheel_def.num_rays;
4672 m_actor->ar_wheels[wheel_id].wh_arg_rigidity_node = this->ResolveNodeRef(meshwheel_def.rigidity_node, /* optional: */ true);
4673 m_actor->ar_wheels[wheel_id].wh_arg_simple_spring = meshwheel_def.spring;
4674 m_actor->ar_wheels[wheel_id].wh_arg_simple_damping = meshwheel_def.damping;
4675 m_actor->ar_wheels[wheel_id].wh_arg_side = meshwheel_def.side;
4676 m_actor->ar_wheels[wheel_id].wh_arg_media1 = meshwheel_def.mesh_name;
4677 m_actor->ar_wheels[wheel_id].wh_arg_media2 = meshwheel_def.material_name;
4679
4680 this->BuildWheelBeams(
4681 meshwheel_def.num_rays,
4682 base_node_index,
4683 axis_node_1,
4684 axis_node_2,
4685 meshwheel_def.spring, /* Tyre */
4686 meshwheel_def.damping, /* Tyre */
4687 meshwheel_def.spring, /* Rim */
4688 meshwheel_def.damping, /* Rim */
4689 meshwheel_def.beam_defaults,
4690 meshwheel_def.rigidity_node
4691 );
4692
4694 wheel_id,
4695 base_node_index,
4696 axis_node_1->pos,
4697 axis_node_2->pos,
4698 meshwheel_def.num_rays,
4704 meshwheel_def.rim_radius
4705 );
4706
4707 this->CreateWheelSkidmarks(wheel_id);
4708
4710}
4711
4713{
4714 WheelID_t wheel_id = m_actor->ar_num_wheels;
4715
4716 node_t* axis_node_1 = nullptr;
4717 node_t* axis_node_2 = nullptr;
4718 this->GetWheelAxisNodes(def, axis_node_1, axis_node_2);
4719
4720 // --- Nodes ---
4721 NodeNum_t base_node_index = (NodeNum_t)m_actor->ar_num_nodes;
4722
4724 wheel_id,
4725 def.num_rays,
4726 axis_node_1,
4727 axis_node_2,
4729 def.num_rays * 2,
4730 def.num_rays * 8,
4732 def.propulsion,
4733 def.braking,
4734 def.node_defaults,
4735 def.mass
4736 );
4737
4738 // --- Args ---
4740 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = def.num_rays;
4741 m_actor->ar_wheels[wheel_id].wh_arg_rigidity_node = this->ResolveNodeRef(def.rigidity_node, /* optional: */ true);
4744 m_actor->ar_wheels[wheel_id].wh_arg_side = def.side;
4745 m_actor->ar_wheels[wheel_id].wh_arg_rim_spring = def.beam_defaults->springiness;
4746 m_actor->ar_wheels[wheel_id].wh_arg_rim_damping = def.beam_defaults->damping_constant;
4747 m_actor->ar_wheels[wheel_id].wh_arg_media1 = def.mesh_name;
4750
4751 /* --- Beams --- */
4752 /* Use data from directive 'set_beam_defaults' for the tiretread beams */
4753 float tyre_spring = def.spring;
4754 float tyre_damp = def.damping;
4755 float rim_spring = def.beam_defaults->springiness;
4756 float rim_damp = def.beam_defaults->damping_constant;
4757
4759 def.num_rays,
4760 base_node_index,
4761 axis_node_1,
4762 axis_node_2,
4763 tyre_spring,
4764 tyre_damp,
4765 rim_spring,
4766 rim_damp,
4767 def.beam_defaults,
4768 def.rigidity_node,
4769 0.15 // max_extension
4770 );
4771
4773 wheel_id,
4774 base_node_index,
4775 axis_node_1->pos,
4776 axis_node_2->pos,
4777 def.num_rays,
4784 );
4785
4786 CreateWheelSkidmarks(wheel_id);
4787
4789}
4790
4792 WheelID_t wheel_index,
4793 NodeNum_t base_node_index,
4794 NodeNum_t axis_node_1_index,
4795 NodeNum_t axis_node_2_index,
4796 unsigned int num_rays,
4797 WheelSide side,
4798 Ogre::String mesh_name,
4799 Ogre::String mesh_rg,
4800 Ogre::String material_name,
4801 Ogre::String material_rg,
4802 float rim_radius
4803)
4804{
4805 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
4806
4807 try
4808 {
4810 wheel_index,
4811 axis_node_1_index,
4812 axis_node_2_index,
4813 base_node_index,
4814 num_rays,
4815 rim_radius,
4816 side != WheelSide::RIGHT,
4817 mesh_name,
4818 mesh_rg,
4819 material_name,
4820 material_rg);
4821 Ogre::SceneNode* scene_node = m_wheels_parent_scenenode->createChildSceneNode(this->ComposeName("meshwheel*", wheel_index));
4822 scene_node->attachObject(flexmesh_wheel->GetTireEntity());
4823
4824 WheelGfx visual_wheel;
4825 visual_wheel.wx_wheel_id = wheel_index;
4826 visual_wheel.wx_flex_mesh = flexmesh_wheel;
4827 visual_wheel.wx_scenenode = scene_node;
4828 visual_wheel.wx_side = side;
4829 visual_wheel.wx_rim_mesh_name = mesh_name;
4830 m_actor->m_gfx_actor->m_wheels.push_back(visual_wheel);
4831 }
4832 catch (Ogre::Exception& e)
4833 {
4834 this->AddMessage(Message::TYPE_ERROR, "Failed to create meshwheel visuals: " + e.getDescription());
4835 return;
4836 }
4837}
4838
4840 WheelID_t wheel_id,
4841 unsigned int num_rays,
4842 node_t *axis_node_1,
4843 node_t *axis_node_2,
4844 node_t *reference_arm_node,
4845 unsigned int reserve_nodes,
4846 unsigned int reserve_beams,
4847 float wheel_radius,
4848 WheelPropulsion propulsion,
4849 WheelBraking braking,
4850 std::shared_ptr<RigDef::NodeDefaults> node_defaults,
4851 float wheel_mass,
4852 float wheel_width /* Default: -1.f */
4853)
4854{
4855 wheel_t & wheel = m_actor->ar_wheels[wheel_id];
4856
4857 /* Axis */
4858 Ogre::Vector3 axis_vector = axis_node_2->RelPosition - axis_node_1->RelPosition;
4859 float axis_length = axis_vector.length();
4860 axis_vector.normalise();
4861
4862 /* Wheel object */
4863 wheel.wh_braking = braking;
4864 wheel.wh_propulsed = propulsion;
4865 wheel.wh_num_nodes = 2 * num_rays;
4866 wheel.wh_axis_node_0 = axis_node_1;
4867 wheel.wh_axis_node_1 = axis_node_2;
4868 wheel.wh_radius = wheel_radius;
4869 wheel.wh_width = (wheel_width < 0) ? axis_length : wheel_width;
4870 wheel.wh_arm_node = reference_arm_node;
4871
4872 /* Find near attach */
4873 Ogre::Real length_1 = (axis_node_1->RelPosition - wheel.wh_arm_node->RelPosition).length();
4874 Ogre::Real length_2 = (axis_node_2->RelPosition - wheel.wh_arm_node->RelPosition).length();
4875 wheel.wh_near_attach_node = (length_1 < length_2) ? axis_node_1 : axis_node_2;
4876
4877 if (propulsion != WheelPropulsion::NONE)
4878 {
4879 /* for inter-differential locking */
4882 }
4883
4884 /* Nodes */
4885 Ogre::Vector3 ray_vector = axis_vector.perpendicular() * wheel_radius;
4886 Ogre::Quaternion ray_rotator = Ogre::Quaternion(Ogre::Degree(-360.0 / (num_rays * 2)), axis_vector);
4887
4888 for (unsigned int i = 0; i < num_rays; i++)
4889 {
4890 /* Outer ring */
4891 Ogre::Vector3 ray_point = axis_node_1->RelPosition + ray_vector;
4892 Ogre::Vector3 ray_spawnpoint = m_actor->ar_nodes_spawn_offsets[axis_node_1->pos] + ray_vector;
4893 ray_vector = ray_rotator * ray_vector;
4894
4895 node_t & outer_node = GetFreeNode();
4896 InitNode(outer_node, ray_point, node_defaults);
4897 outer_node.mass = wheel_mass / (2.f * num_rays);
4898 outer_node.nd_contacter = true;
4899 outer_node.nd_tyre_node = true;
4900 AdjustNodeBuoyancy(outer_node, node_defaults);
4901
4902 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
4903 m_actor->ar_nodes_spawn_offsets[outer_node.pos] = ray_spawnpoint;
4904
4905 /* Inner ring */
4906 ray_point = axis_node_2->RelPosition + ray_vector;
4907 ray_spawnpoint = m_actor->ar_nodes_spawn_offsets[axis_node_2->pos] + ray_vector;
4908 ray_vector = ray_rotator * ray_vector;
4909
4910 node_t & inner_node = GetFreeNode();
4911 InitNode(inner_node, ray_point, node_defaults);
4912 inner_node.mass = wheel_mass / (2.f * num_rays);
4913 inner_node.nd_contacter = true;
4914 inner_node.nd_tyre_node = true;
4915 AdjustNodeBuoyancy(inner_node, node_defaults);
4916
4917 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
4918 m_actor->ar_nodes_spawn_offsets[inner_node.pos] = ray_spawnpoint;
4919
4920 /* Wheel object */
4921 wheel.wh_nodes[i * 2] = & outer_node;
4922 wheel.wh_nodes[(i * 2) + 1] = & inner_node;
4923 }
4924}
4925
4926void ActorSpawner::AdjustNodeBuoyancy(node_t & node, RigDef::Node & node_def, std::shared_ptr<RigDef::NodeDefaults> defaults)
4927{
4928 unsigned int options = (defaults->options | node_def.options); // Merge flags
4930}
4931
4932void ActorSpawner::AdjustNodeBuoyancy(node_t & node, std::shared_ptr<RigDef::NodeDefaults> defaults)
4933{
4934 node.buoyancy = BITMASK_IS_1(defaults->options, RigDef::Node::OPTION_b_EXTRA_BUOYANCY) ? 10000.f : m_actor->ar_dry_mass/15.f;
4935}
4936
4938 unsigned int num_rays,
4939 NodeNum_t base_node_index,
4940 node_t *axis_node_1,
4941 node_t *axis_node_2,
4942 float tyre_spring,
4943 float tyre_damping,
4944 float rim_spring,
4945 float rim_damping,
4946 std::shared_ptr<RigDef::BeamDefaults> beam_defaults,
4947 RigDef::Node::Ref const & rigidity_node_id,
4948 float max_extension // = 0.f
4949)
4950{
4951 /* Find out where to connect rigidity node */
4952 bool rigidity_beam_side_1 = false;
4953 node_t *rigidity_node = nullptr;
4954 if (rigidity_node_id.IsValidAnyState())
4955 {
4956 rigidity_node = GetNodePointerOrThrow(rigidity_node_id);
4957 float distance_1 = rigidity_node->RelPosition.distance(axis_node_1->RelPosition);
4958 float distance_2 = rigidity_node->RelPosition.distance(axis_node_2->RelPosition);
4959 rigidity_beam_side_1 = distance_1 < distance_2;
4960 }
4961
4962 for (unsigned int i = 0; i < num_rays; i++)
4963 {
4964 /* Bounded */
4965 unsigned int outer_ring_node_index = base_node_index + (i * 2);
4966 node_t *outer_ring_node = & m_actor->ar_nodes[outer_ring_node_index];
4967 node_t *inner_ring_node = & m_actor->ar_nodes[outer_ring_node_index + 1];
4968
4969 AddWheelBeam(axis_node_1, outer_ring_node, tyre_spring, tyre_damping, beam_defaults, 0.66f, max_extension);
4970 AddWheelBeam(axis_node_2, inner_ring_node, tyre_spring, tyre_damping, beam_defaults, 0.66f, max_extension);
4971 AddWheelBeam(axis_node_2, outer_ring_node, tyre_spring, tyre_damping, beam_defaults);
4972 AddWheelBeam(axis_node_1, inner_ring_node, tyre_spring, tyre_damping, beam_defaults);
4973
4974 /* Reinforcement */
4975 unsigned int next_outer_ring_node_index = base_node_index + (((i + 1) % num_rays) * 2);
4976 node_t *next_outer_ring_node = & m_actor->ar_nodes[next_outer_ring_node_index];
4977 node_t *next_inner_ring_node = & m_actor->ar_nodes[next_outer_ring_node_index + 1];
4978
4979 AddWheelBeam(outer_ring_node, inner_ring_node, rim_spring, rim_damping, beam_defaults);
4980 AddWheelBeam(outer_ring_node, next_outer_ring_node, rim_spring, rim_damping, beam_defaults);
4981 AddWheelBeam(inner_ring_node, next_inner_ring_node, rim_spring, rim_damping, beam_defaults);
4982 AddWheelBeam(inner_ring_node, next_outer_ring_node, rim_spring, rim_damping, beam_defaults);
4983
4984 /* Rigidity beams */
4985 if (rigidity_node != nullptr)
4986 {
4987 node_t *target_node = (rigidity_beam_side_1) ? outer_ring_node : inner_ring_node;
4988 unsigned int beam_index = AddWheelBeam(rigidity_node, target_node, tyre_spring, tyre_damping, beam_defaults, -1.f, -1.f, BEAM_VIRTUAL);
4989 m_actor->ar_beams[beam_index].bm_type = BEAM_VIRTUAL;
4990 }
4991 }
4992}
4993
4995{
4996 WheelID_t wheel_id = m_actor->ar_num_wheels;
4997
4998 node_t* axis_node_1 = nullptr;
4999 node_t* axis_node_2 = nullptr;
5000 this->GetWheelAxisNodes(wheel_def, axis_node_1, axis_node_2);
5001
5002 NodeNum_t base_node_index = (NodeNum_t)m_actor->ar_num_nodes;
5003
5005 wheel_id,
5006 wheel_def.num_rays,
5007 axis_node_1,
5008 axis_node_2,
5010 wheel_def.num_rays * 2,
5011 wheel_def.num_rays * 8,
5013 wheel_def.propulsion,
5014 wheel_def.braking,
5015 wheel_def.node_defaults,
5016 wheel_def.mass,
5017 -1.f // Set width to axis length (width in definition is ignored)
5018 );
5019
5021 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = wheel_def.num_rays;
5022 m_actor->ar_wheels[wheel_id].wh_arg_rigidity_node = this->ResolveNodeRef(wheel_def.rigidity_node, /* optional: */ true);
5023 m_actor->ar_wheels[wheel_id].wh_arg_simple_spring = wheel_def.springiness;
5024 m_actor->ar_wheels[wheel_id].wh_arg_simple_damping = wheel_def.damping;
5025 m_actor->ar_wheels[wheel_id].wh_arg_media1 = wheel_def.face_material_name;
5026 m_actor->ar_wheels[wheel_id].wh_arg_media2 = wheel_def.band_material_name;
5028
5029 this->BuildWheelBeams(
5030 wheel_def.num_rays,
5031 base_node_index,
5032 axis_node_1,
5033 axis_node_2,
5034 wheel_def.springiness, /* Tyre */
5035 wheel_def.damping, /* Tyre */
5036 wheel_def.springiness, /* Rim */
5037 wheel_def.damping, /* Rim */
5038 wheel_def.beam_defaults,
5039 wheel_def.rigidity_node
5040 );
5041
5042 this->CreateWheelVisuals(
5043 wheel_id,
5044 base_node_index,
5045 wheel_def.num_rays,
5050 /*separate_rim:*/false
5051 );
5052
5053 CreateWheelSkidmarks(wheel_id);
5054
5056}
5057
5059{
5060 // Always create, even if disabled by config
5061 m_actor->m_skid_trails[wheel_index] = new RoR::Skidmark(
5062 RoR::App::GetGfxScene()->GetSkidmarkConf(), &m_actor->ar_wheels[wheel_index], m_particles_parent_scenenode, 300, 20);
5063}
5064
5066{
5067 WheelID_t wheel_id = m_actor->ar_num_wheels;
5068
5069 node_t* axis_node_1 = nullptr;
5070 node_t* axis_node_2 = nullptr;
5071 this->GetWheelAxisNodes(wheel_2_def, axis_node_1, axis_node_2);
5072
5073 NodeNum_t base_node_index = (NodeNum_t)m_actor->ar_num_nodes;
5074
5075 /* Find out where to connect rigidity node */
5076 bool rigidity_beam_side_1 = false;
5077 if (wheel_2_def.rigidity_node.IsValidAnyState())
5078 {
5079 node_t & rigidity_node = m_actor->ar_nodes[this->GetNodeIndexOrThrow(wheel_2_def.rigidity_node)];
5080 Ogre::Real distance_1 = (rigidity_node.RelPosition - axis_node_1->RelPosition).length();
5081 Ogre::Real distance_2 = (rigidity_node.RelPosition - axis_node_2->RelPosition).length();
5082 rigidity_beam_side_1 = distance_1 < distance_2;
5083 }
5084
5085 // Tweaks
5086 float override_rim_radius = TuneupUtil::getTweakedWheelRimRadius(m_actor->getWorkingTuneupDef(), wheel_id, wheel_2_def.rim_radius);
5087 float override_tire_radius = TuneupUtil::getTweakedWheelTireRadius(m_actor->getWorkingTuneupDef(), wheel_id, wheel_2_def.tyre_radius);
5088
5089 /* Node&beam generation */
5090 wheel_t& wheel = m_actor->ar_wheels[wheel_id];
5091 Ogre::Vector3 axis_vector = axis_node_2->RelPosition - axis_node_1->RelPosition;
5092 axis_vector.normalise();
5093 Ogre::Vector3 rim_ray_vector = Ogre::Vector3(0, override_rim_radius, 0);
5094 Ogre::Quaternion rim_ray_rotator = Ogre::Quaternion(Ogre::Degree(-360.f / wheel_2_def.num_rays), axis_vector);
5095
5096 /* Width */
5097 wheel.wh_width = axis_vector.length(); /* wheel_def.width is ignored. */
5098
5099 /* Rim nodes */
5100 for (unsigned int i = 0; i < wheel_2_def.num_rays; i++)
5101 {
5102 float node_mass = wheel_2_def.mass / (4.f * wheel_2_def.num_rays);
5103
5104 /* Outer ring */
5105 Ogre::Vector3 ray_point = axis_node_1->RelPosition + rim_ray_vector;
5106
5107 node_t & outer_node = GetFreeNode();
5108 InitNode(outer_node, ray_point, wheel_2_def.node_defaults);
5109 outer_node.mass = node_mass;
5110 outer_node.nd_rim_node = true;
5111
5113
5114 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
5115
5116 /* Inner ring */
5117 ray_point = axis_node_2->RelPosition + rim_ray_vector;
5118
5119 node_t & inner_node = GetFreeNode();
5120 InitNode(inner_node, ray_point, wheel_2_def.node_defaults);
5121 inner_node.mass = node_mass;
5122 inner_node.nd_rim_node = true;
5123
5125
5126 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
5127
5128 /* Wheel object */
5129 wheel.wh_rim_nodes[i * 2] = & outer_node;
5130 wheel.wh_rim_nodes[(i * 2) + 1] = & inner_node;
5131
5132 rim_ray_vector = rim_ray_rotator * rim_ray_vector;
5133 }
5134
5135 Ogre::Vector3 tyre_ray_vector = Ogre::Vector3(0, override_tire_radius, 0);
5136 Ogre::Quaternion tyre_ray_rotator = Ogre::Quaternion(Ogre::Degree(-180.f / wheel_2_def.num_rays), axis_vector);
5137 tyre_ray_vector = tyre_ray_rotator * tyre_ray_vector;
5138
5139 /* Tyre nodes */
5140 for (unsigned int i = 0; i < wheel_2_def.num_rays; i++)
5141 {
5142 /* Outer ring */
5143 Ogre::Vector3 ray_point = axis_node_1->RelPosition + tyre_ray_vector;
5144
5145 node_t & outer_node = GetFreeNode();
5146 InitNode(outer_node, ray_point);
5147 outer_node.mass = (0.67f * wheel_2_def.mass) / (2.f * wheel_2_def.num_rays);
5148 outer_node.friction_coef = wheel.wh_width * WHEEL_FRICTION_COEF;
5149 outer_node.volume_coef = wheel_2_def.node_defaults->volume;
5150 outer_node.surface_coef = wheel_2_def.node_defaults->surface;
5151 outer_node.nd_contacter = true;
5152 outer_node.nd_tyre_node = true;
5153
5154 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
5155
5156 /* Inner ring */
5157 ray_point = axis_node_2->RelPosition + tyre_ray_vector;
5158
5159 node_t & inner_node = GetFreeNode();
5160 InitNode(inner_node, ray_point);
5161 inner_node.mass = (0.33f * wheel_2_def.mass) / (2.f * wheel_2_def.num_rays);
5162 inner_node.friction_coef = wheel.wh_width * WHEEL_FRICTION_COEF;
5163 inner_node.volume_coef = wheel_2_def.node_defaults->volume;
5164 inner_node.surface_coef = wheel_2_def.node_defaults->surface;
5165 inner_node.nd_contacter = true;
5166 inner_node.nd_tyre_node = true;
5167
5168 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
5169
5170 /* Wheel object */
5171 wheel.wh_nodes[i * 2] = & outer_node;
5172 wheel.wh_nodes[(i * 2) + 1] = & inner_node;
5173
5174 tyre_ray_vector = rim_ray_rotator * tyre_ray_vector; // This is OK
5175 }
5176
5177 // ~~~ Args ~~~
5179 m_actor->ar_wheels[wheel_id].wh_radius = wheel_2_def.tyre_radius;
5180 m_actor->ar_wheels[wheel_id].wh_rim_radius = wheel_2_def.rim_radius;
5181 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = wheel_2_def.num_rays;
5182 m_actor->ar_wheels[wheel_id].wh_arg_rigidity_node = this->ResolveNodeRef(wheel_2_def.rigidity_node, /* optional: */ true);
5183 m_actor->ar_wheels[wheel_id].wh_arg_rim_spring = wheel_2_def.rim_springiness;
5184 m_actor->ar_wheels[wheel_id].wh_arg_rim_damping = wheel_2_def.rim_damping;
5185 m_actor->ar_wheels[wheel_id].wh_arg_simple_spring = wheel_2_def.tyre_springiness;
5186 m_actor->ar_wheels[wheel_id].wh_arg_simple_damping = wheel_2_def.tyre_damping;
5187 m_actor->ar_wheels[wheel_id].wh_arg_media1 = wheel_2_def.face_material_name;
5188 m_actor->ar_wheels[wheel_id].wh_arg_media2 = wheel_2_def.band_material_name;
5190
5191 /* Beams */
5192 for (unsigned int i = 0; i < wheel_2_def.num_rays; i++)
5193 {
5194 /* --- Rim --- */
5195
5196 /* Bounded */
5197 unsigned int rim_outer_node_index = base_node_index + (i * 2);
5198 node_t *rim_outer_node = & m_actor->ar_nodes[rim_outer_node_index];
5199 node_t *rim_inner_node = & m_actor->ar_nodes[rim_outer_node_index + 1];
5200
5201 unsigned int beam_index;
5202 beam_index = AddWheelRimBeam(wheel_2_def, axis_node_1, rim_outer_node);
5203 GetBeam(beam_index).shortbound = 0.66;
5204 beam_index = AddWheelRimBeam(wheel_2_def, axis_node_2, rim_inner_node);
5205 GetBeam(beam_index).shortbound = 0.66;
5206 AddWheelRimBeam(wheel_2_def, axis_node_2, rim_outer_node);
5207 AddWheelRimBeam(wheel_2_def, axis_node_1, rim_inner_node);
5208
5209 /* Reinforcement */
5210 unsigned int rim_next_outer_node_index = base_node_index + (((i + 1) % wheel_2_def.num_rays) * 2);
5211 node_t *rim_next_outer_node = & m_actor->ar_nodes[rim_next_outer_node_index];
5212 node_t *rim_next_inner_node = & m_actor->ar_nodes[rim_next_outer_node_index + 1];
5213
5214 AddWheelRimBeam(wheel_2_def, axis_node_1, rim_outer_node);
5215 AddWheelRimBeam(wheel_2_def, rim_outer_node, rim_inner_node);
5216 AddWheelRimBeam(wheel_2_def, rim_outer_node, rim_next_outer_node);
5217 AddWheelRimBeam(wheel_2_def, rim_inner_node, rim_next_inner_node);
5218 AddWheelRimBeam(wheel_2_def, rim_outer_node, rim_next_inner_node);
5219 AddWheelRimBeam(wheel_2_def, rim_inner_node, rim_next_outer_node);
5220
5221 /* -- Rigidity -- */
5222 if (wheel_2_def.rigidity_node.IsValidAnyState())
5223 {
5224 unsigned int rig_beam_index = AddWheelRimBeam(wheel_2_def,
5225 GetNodePointer(wheel_2_def.rigidity_node),
5226 (rigidity_beam_side_1) ? rim_outer_node : rim_inner_node
5227 );
5228 m_actor->ar_beams[rig_beam_index].bm_type = BEAM_VIRTUAL;
5229 }
5230
5231 /* --- Tyre --- */
5232
5233 unsigned int tyre_node_index = rim_outer_node_index + (2 * wheel_2_def.num_rays);
5234 node_t *tyre_outer_node = & m_actor->ar_nodes[tyre_node_index];
5235 node_t *tyre_inner_node = & m_actor->ar_nodes[tyre_node_index + 1];
5236 unsigned int tyre_next_node_index = rim_next_outer_node_index + (2 * wheel_2_def.num_rays);
5237 node_t *tyre_next_outer_node = & m_actor->ar_nodes[tyre_next_node_index];
5238 node_t *tyre_next_inner_node = & m_actor->ar_nodes[tyre_next_node_index + 1];
5239
5240 /* Tyre band */
5241 AddTyreBeam(wheel_2_def, tyre_outer_node, tyre_next_outer_node);
5242 AddTyreBeam(wheel_2_def, tyre_outer_node, tyre_next_inner_node);
5243 AddTyreBeam(wheel_2_def, tyre_inner_node, tyre_next_outer_node);
5244 AddTyreBeam(wheel_2_def, tyre_inner_node, tyre_next_inner_node);
5245 /* Tyre sidewalls */
5246 AddTyreBeam(wheel_2_def, tyre_outer_node, rim_outer_node);
5247 AddTyreBeam(wheel_2_def, tyre_outer_node, rim_next_outer_node);
5248 AddTyreBeam(wheel_2_def, tyre_inner_node, rim_inner_node);
5249 AddTyreBeam(wheel_2_def, tyre_inner_node, rim_next_inner_node);
5250 /* Reinforcement */
5251 AddTyreBeam(wheel_2_def, tyre_outer_node, rim_inner_node);
5252 AddTyreBeam(wheel_2_def, tyre_outer_node, rim_next_inner_node);
5253 AddTyreBeam(wheel_2_def, tyre_inner_node, rim_outer_node);
5254 AddTyreBeam(wheel_2_def, tyre_inner_node, rim_next_outer_node);
5255 /* Backpressure, bounded */
5256 AddTyreBeam(wheel_2_def, axis_node_1, tyre_outer_node);
5257 AddTyreBeam(wheel_2_def, axis_node_2, tyre_inner_node);
5258 }
5259
5260 /* Wheel object */
5261 wheel.wh_braking = wheel_2_def.braking;
5262 wheel.wh_propulsed = wheel_2_def.propulsion;
5263 wheel.wh_num_nodes = 2 * wheel_2_def.num_rays;
5264 wheel.wh_num_rim_nodes = wheel.wh_num_nodes;
5265 wheel.wh_axis_node_0 = axis_node_1;
5266 wheel.wh_axis_node_1 = axis_node_2;
5267 wheel.wh_radius = override_tire_radius;
5268 wheel.wh_rim_radius = override_rim_radius;
5269 wheel.wh_arm_node = this->GetNodePointer(wheel_2_def.reference_arm_node);
5270
5271 if (wheel_2_def.propulsion != WheelPropulsion::NONE)
5272 {
5273 /* for inter-differential locking */
5276 }
5277
5278 /* Find near attach */
5279 Ogre::Real length_1 = (axis_node_1->RelPosition - wheel.wh_arm_node->RelPosition).length();
5280 Ogre::Real length_2 = (axis_node_2->RelPosition - wheel.wh_arm_node->RelPosition).length();
5281 wheel.wh_near_attach_node = (length_1 < length_2) ? axis_node_1 : axis_node_2;
5282
5283 CreateWheelSkidmarks(static_cast<unsigned>(m_actor->ar_num_wheels));
5284
5285 this->CreateWheelVisuals(
5286 wheel_id,
5287 base_node_index,
5288 wheel_2_def.num_rays,
5293 /*separate_rim:*/true,
5294 /*rim_ratio:*/override_rim_radius / override_tire_radius
5295 );
5296
5298}
5299
5301 WheelID_t wheel_index,
5302 NodeNum_t node_base_index,
5303 unsigned int num_rays,
5304 Ogre::String const& face_material_name,
5305 Ogre::String const& face_material_rg,
5306 Ogre::String const& band_material_name,
5307 Ogre::String const& band_material_rg,
5308 bool separate_rim,
5309 float rim_ratio
5310)
5311{
5312 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
5313
5314 wheel_t & wheel = m_actor->ar_wheels[wheel_index];
5315
5316 try
5317 {
5318 WheelGfx visual_wheel;
5319
5320 const std::string wheel_mesh_name = this->ComposeName("mesh @ wheel*", wheel_index);
5321 visual_wheel.wx_flex_mesh = new FlexMesh(
5322 wheel_mesh_name,
5323 m_actor->m_gfx_actor.get(),
5324 wheel.wh_axis_node_0->pos,
5325 wheel.wh_axis_node_1->pos,
5326 static_cast<NodeNum_t>(node_base_index), // FIXME - node_base_index should be also NodeNum_t
5327 num_rays,
5328 face_material_name, face_material_rg,
5329 band_material_name, band_material_rg,
5330 separate_rim,
5331 rim_ratio
5332 );
5333
5334 const std::string instance_name = this->ComposeName("entity @ wheel*", wheel_index);
5335 Ogre::Entity *ec = App::GetGfxScene()->GetSceneManager()->createEntity(instance_name, wheel_mesh_name);
5336 this->SetupNewEntity(ec, Ogre::ColourValue(0, 0.5, 0.5));
5337 visual_wheel.wx_scenenode = m_wheels_parent_scenenode->createChildSceneNode(this->ComposeName("wheel", wheel_index));
5338 m_actor->m_deletion_entities.emplace_back(ec);
5339 visual_wheel.wx_scenenode->attachObject(ec);
5340 m_actor->m_gfx_actor->m_wheels.push_back(visual_wheel);
5341 }
5342 catch (Ogre::Exception& e)
5343 {
5344 AddMessage(Message::TYPE_ERROR, "Failed to create wheel visuals: " + e.getFullDescription());
5345 }
5346}
5347
5349 WheelID_t wheel_id,
5350 NodeNum_t node_base_index,
5351 NodeNum_t axis_node_1,
5352 NodeNum_t axis_node_2,
5353 int num_rays,
5354 float radius,
5355 WheelSide side,
5356 std::string rim_mesh_name,
5357 std::string rim_mesh_rg,
5358 std::string tire_mesh_name,
5359 std::string tire_mesh_rg)
5360{
5361 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
5362
5364 wheel_id,
5365 node_base_index,
5366 axis_node_1,
5367 axis_node_2,
5368 num_rays,
5369 side,
5370 rim_mesh_name,
5371 rim_mesh_rg,
5372 "tracks/trans", // Use a builtin transparent material for the generated tire mesh, to effectively disable it.
5374 radius
5375 );
5376
5377 int num_nodes = num_rays * 4;
5378 std::vector<unsigned int> node_indices;
5379 node_indices.reserve(num_nodes);
5380 for (int i = 0; i < num_nodes; ++i)
5381 {
5382 node_indices.push_back( node_base_index + i );
5383 }
5384 std::vector<ForvertTempData> forverts;
5385
5386 try
5387 {
5388 auto* flexbody = m_flex_factory.CreateFlexBody(
5389 (FlexbodyID_t)m_actor->m_gfx_actor->m_flexbodies.size(),
5390 node_base_index,
5391 axis_node_1,
5392 axis_node_2,
5393 Ogre::Vector3(0.5f, 0.5f, 0.f),
5394 Ogre::Vector3(0.f, 0.f, 0.f),
5395 node_indices,
5396 forverts,
5397 tire_mesh_name,
5398 tire_mesh_rg
5399 );
5400
5401 if (flexbody == nullptr)
5402 return; // Error already logged
5403
5404 this->CreateWheelSkidmarks(wheel_id);
5405
5406 m_actor->m_gfx_actor->m_flexbodies.push_back(flexbody);
5407 }
5408 catch (Ogre::Exception& e)
5409 {
5411 "Failed to create flexbodywheel visuals '" + tire_mesh_name + "', reason:" + e.getDescription());
5412 }
5413}
5414
5416 node_t *node_1,
5417 node_t *node_2,
5418 float spring,
5419 float damping,
5420 std::shared_ptr<RigDef::BeamDefaults> beam_defaults,
5421 float max_contraction, /* Default: -1.f */
5422 float max_extension, /* Default: -1.f */
5423 BeamType type /* Default: BEAM_INVISIBLE */
5424)
5425{
5426 unsigned int index = m_actor->ar_num_beams;
5427 beam_t & beam = AddBeam(*node_1, *node_2, beam_defaults, DEFAULT_DETACHER_GROUP);
5428 beam.bm_type = type;
5429 beam.k = spring;
5430 beam.d = damping;
5431 if (max_contraction > 0.f)
5432 {
5433 beam.shortbound = max_contraction;
5434 beam.longbound = max_extension;
5435 beam.bounded = SHOCK1;
5436 }
5437 CalculateBeamLength(beam);
5438
5439 return index;
5440}
5441
5442unsigned int ActorSpawner::AddWheelRimBeam(RigDef::Wheel2 & wheel_2_def, node_t *node_1, node_t *node_2)
5443{
5444 unsigned int beam_index = _SectionWheels2AddBeam(wheel_2_def, node_1, node_2);
5445 beam_t & beam = GetBeam(beam_index);
5446 beam.k = wheel_2_def.rim_springiness;
5447 beam.d = wheel_2_def.rim_damping;
5448 return beam_index;
5449}
5450
5451unsigned int ActorSpawner::AddTyreBeam(RigDef::Wheel2 & wheel_2_def, node_t *node_1, node_t *node_2)
5452{
5453 unsigned int beam_index = _SectionWheels2AddBeam(wheel_2_def, node_1, node_2);
5454 beam_t & beam = GetBeam(beam_index);
5455 beam.k = wheel_2_def.tyre_springiness;
5456 beam.d = wheel_2_def.tyre_damping;
5457
5458 m_actor->getTyrePressure().AddBeam((int)beam_index);
5459
5460 return beam_index;
5461}
5462
5463unsigned int ActorSpawner::_SectionWheels2AddBeam(RigDef::Wheel2 & wheel_2_def, node_t *node_1, node_t *node_2)
5464{
5465 unsigned int index = m_actor->ar_num_beams;
5466 beam_t & beam = GetFreeBeam();
5467 InitBeam(beam, node_1, node_2);
5468 beam.bm_type = BEAM_NORMAL;
5469 SetBeamStrength(beam, wheel_2_def.beam_defaults->breaking_threshold);
5470 SetBeamDeformationThreshold(beam, wheel_2_def.beam_defaults);
5471 return index;
5472}
5473
5475{
5476 if (def.wheel_id > m_actor->ar_num_wheels - 1)
5477 {
5478 AddMessage(Message::TYPE_ERROR, std::string("Invalid wheel_id: ") + TOSTRING(def.wheel_id));
5479 return;
5480 }
5481
5482 wheeldetacher_t obj;
5483 obj.wd_wheel_id = def.wheel_id;
5485 m_actor->ar_wheeldetachers.push_back(obj);
5486};
5487
5489{
5490 /* #1: regulating_force */
5491 float force = def.regulation_force;
5492 if (force < 1.f || force > 20.f)
5493 {
5494 std::stringstream msg;
5495 msg << "Clamping 'regulating_force' value '" << force << "' to allowed range <1 - 20>";
5496 AddMessage(Message::TYPE_INFO, msg.str());
5497 force = (force < 1.f) ? 1.f : 20.f;
5498 }
5499 m_actor->tc_ratio = force;
5500
5501 /* #2: wheelslip */
5502 // no longer needed
5503
5504 /* #3: fade_speed */
5505 // no longer needed
5506
5507 /* #4: pulse/sec */
5508 float pulse = def.pulse_per_sec;
5509 if (pulse <= 1.0f || pulse >= 2000.0f)
5510 {
5511 pulse = 2000.0f;
5512 }
5513 m_actor->tc_pulse_time = 1 / pulse;
5514
5515 /* #4: mode */
5516 m_actor->tc_mode = def.attr_is_on;
5519};
5520
5522{
5523 /* #1: regulating_force */
5524 float force = def.regulation_force;
5525 if (force < 1.f || force > 20.f)
5526 {
5527 std::stringstream msg;
5528 msg << "Clamping 'regulating_force' value '" << force << "' to allowed range <1 - 20>";
5529 AddMessage(Message::TYPE_INFO, msg.str());
5530 force = (force < 1.f) ? 1.f : 20.f;
5531 }
5532 m_actor->alb_ratio = force;
5533
5534 /* #2: min_speed */
5535 /* Wheelspeed adaption: 60 sec * 60 mins / 1000(kilometer) = 3.6 to get meter per sec */
5536 float min_speed = def.min_speed / 3.6f;
5537 m_actor->alb_minspeed = std::max(0.5f, min_speed);
5538
5539 /* #3: pulse_per_sec */
5540 float pulse = def.pulse_per_sec;
5541 if (pulse <= 1.0f || pulse >= 2000.0f)
5542 {
5543 pulse = 2000.0f;
5544 }
5545 m_actor->alb_pulse_time = 1 / pulse;
5546
5547 /* #4: mode */
5551}
5552
5562
5564{
5565 /* Is this a land vehicle? */
5566 if (m_actor->ar_engine == nullptr)
5567 {
5568 AddMessage(Message::TYPE_WARNING, "Section 'engturbo' found but no engine defined. Skipping ...");
5569 return;
5570 }
5571
5572 m_actor->ar_engine->SetTurboOptions(def.version, def.tinertiaFactor, def.nturbos, def.param1, def.param2, def.param3, def.param4, def.param5, def.param6, def.param7, def.param8, def.param9, def.param10, def.param11);
5573};
5574
5576{
5577 /* Is this a land vehicle? */
5578 if (m_actor->ar_engine == nullptr)
5579 {
5580 AddMessage(Message::TYPE_WARNING, "Section 'engoption' found but no engine defined. Skipping ...");
5581 return;
5582 }
5583
5584 if (def.idle_rpm > 0 && def.stall_rpm > 0 && def.stall_rpm > def.idle_rpm)
5585 {
5586 AddMessage(Message::TYPE_WARNING, "Stall RPM is set higher than Idle RPM.");
5587 }
5588
5589 /* Process it */
5591 def.inertia,
5592 (char)def.type,
5593 def.clutch_force,
5594 def.shift_time,
5595 def.clutch_time,
5596 def.post_shift_time,
5597 def.idle_rpm,
5598 def.stall_rpm,
5599 def.max_idle_mixture,
5600 def.min_idle_mixture,
5601 def.braking_torque
5602 );
5603};
5604
5606{
5607 /* This is a land vehicle */
5609
5610 /* Process it */
5611 m_actor->ar_engine = new Engine(
5612 def.shift_down_rpm,
5613 def.shift_up_rpm,
5614 def.torque,
5617 def.gear_ratios,
5619 m_actor
5620 );
5621
5622 /* Apply game configuration */
5623 m_actor->ar_engine->setAutoMode(App::sim_gearbox_mode->getEnum<SimGearboxMode>());
5624};
5625
5630
5632{
5633 authorinfo_t author;
5634 author.type = def.type;
5635 author.name = def.name;
5636 author.email = def.email;
5637 if (def._has_forum_account)
5638 {
5639 author.id = def.forum_account_id;
5640 }
5641 m_actor->authors.push_back(author);
5642};
5643
5645{
5646 NodeNum_t node = this->ResolveNodeRef(node_ref);
5647 if (node == NODENUM_INVALID)
5648 {
5649 std::stringstream msg;
5650 msg << "Failed to retrieve required node: " << node_ref.ToString();
5651 throw Exception(msg.str());
5652 }
5653 return node;
5654}
5655
5675
5677{
5678 // Nodes
5679 const NodeNum_t n1 = this->ResolveNodeRef(def.nodes[0]);
5680 const NodeNum_t n2 = this->ResolveNodeRef(def.nodes[1]);
5681 if (n1 == NODENUM_INVALID || n2 == NODENUM_INVALID)
5682 {
5683 AddMessage(Message::TYPE_WARNING, "Skipping beam, some nodes not found."); // Node IDs already logged by `ResolveNodeRef()`
5684 return;
5685 }
5686
5687 // Beam
5688 int beam_index = m_actor->ar_num_beams;
5689 m_actor->ar_beams_user_defined[beam_index] = true;
5690 beam_t & beam = AddBeam(m_actor->ar_nodes[n1], m_actor->ar_nodes[n2], def.defaults, def.detacher_group);
5691 beam.bm_type = BEAM_NORMAL;
5692 beam.k = def.defaults->GetScaledSpringiness();
5693 beam.d = def.defaults->GetScaledDamping();
5694 beam.bounded = NOSHOCK; // Orig: if (shortbound) ... hardcoded in BTS_BEAMS
5695
5696 /* Calculate length */
5697 // orig = precompression hardcoded to 1
5698 CalculateBeamLength(beam);
5699
5700 /* Strength */
5701 float beam_strength = def.defaults->GetScaledBreakingThreshold();
5702 beam.strength = beam_strength;
5703
5704 /* Options */
5706 {
5707 beam.bounded = ROPE;
5708 }
5710 {
5711 beam.bounded = SUPPORTBEAM;
5713 }
5714
5716 {
5717 this->CreateBeamVisuals(beam, beam_index, true, def.defaults);
5718 }
5719 else
5720 {
5721 m_actor->ar_beams_invisible[beam_index] = true;
5722 }
5723}
5724
5725void ActorSpawner::SetBeamDeformationThreshold(beam_t & beam, std::shared_ptr<RigDef::BeamDefaults> beam_defaults)
5726{
5727 /*
5728 ---------------------------------------------------------------------------
5729 Old parser logic
5730 ---------------------------------------------------------------------------
5731
5732 VAR default_deform = BEAM_DEFORM (400,000)
5733 VAR default_deform_scale = 1
5734 VAR beam_creak = BEAM_CREAK_DEFAULT (100,000)
5735 VAR enable_advanced_deformation = false
5736
5737
5738 add_beam()
5739 IF default_deform < beam_creak
5740 default_deform = beam_creak
5741 END IF
5742
5743 VAR beam;
5744 beam.default_deform = default_deform * default_deform_scale
5745 END
5746
5747
5748 enable_advanced_deformation:
5749 READ enable_advanced_deformation
5750
5751
5752 set_beam_defaults:
5753 READ default_deform
5754 VAR default_deform_user_defined
5755 READ default_deform_scale
5756 VAR plastic_coef_user_defined
5757
5758 IF (!enable_advanced_deformation && default_deform < BEAM_DEFORM)
5759 default_deform = BEAM_DEFORM;
5760 END IF
5761
5762 IF (plastic_coef_user_defined)
5763 beam_creak = 0
5764 END IF
5765
5766 ---------------------------------------------------------------------------
5767 TruckParser2013
5768 ---------------------------------------------------------------------------
5769
5770 VAR beam_defaults
5771 {
5772 default_deform = BEAM_DEFORM
5773 scale.default_deform = 1
5774 _enable_advanced_deformation = false
5775 _user_defined = false
5776 _default_deform_set = false
5777 _plastic_coef_user_defined = false
5778 }
5779
5780
5781 set_beam_defaults:
5782 READ beam_defaults
5783
5784
5785 add_beam:
5786
5787 // Init
5788
5789 VAR default_deform = BEAM_DEFORM;
5790 VAR beam_creak = BEAM_CREAK_DEFAULT;
5791
5792 // Old 'set_beam_defaults'
5793
5794 IF (beam_defaults._is_user_defined)
5795
5796 default_deform = beam_defaults.default_deform
5797 IF (!beam_defaults._enable_advanced_deformation && default_deform < BEAM_DEFORM)
5798 default_deform = BEAM_DEFORM;
5799 END IF
5800
5801 IF (beam_defaults._plastic_coef_user_defined && beam_defaults.plastic_coef >= 0)
5802 beam_creak = 0
5803 END IF
5804
5805 END IF
5806
5807 // Old 'add_beam'
5808
5809 IF default_deform < beam_creak
5810 default_deform = beam_creak
5811 END IF
5812
5813 VAR beam;
5814 beam.default_deform = default_deform * beam_defaults.scale.default_deform
5815
5816 ---------------------------------------------------------------------------
5817 */
5818
5819 // Old init
5820 float default_deform = BEAM_DEFORM;
5821 float beam_creak = BEAM_CREAK_DEFAULT;
5822
5823 // Old 'set_beam_defaults'
5824 if (beam_defaults->_is_user_defined)
5825 {
5826 default_deform = beam_defaults->deformation_threshold;
5827 if (!beam_defaults->_enable_advanced_deformation && default_deform < BEAM_DEFORM)
5828 {
5829 default_deform = BEAM_DEFORM;
5830 }
5831
5832 if (beam_defaults->_is_plastic_deform_coef_user_defined && beam_defaults->plastic_deform_coef >= BEAM_PLASTIC_COEF_DEFAULT)
5833 {
5834 beam_creak = 0.f;
5835 }
5836 }
5837
5838 // Old 'add_beam'
5839 if (default_deform < beam_creak)
5840 {
5841 default_deform = beam_creak;
5842 }
5843
5844 float deformation_threshold = default_deform * beam_defaults->scale.deformation_threshold_constant;
5845
5846 beam.minmaxposnegstress = deformation_threshold;
5847 beam.maxposstress = deformation_threshold;
5848 beam.maxnegstress = -(deformation_threshold);
5849}
5850
5851void ActorSpawner::CreateBeamVisuals(beam_t & beam, int beam_index, bool visible, std::shared_ptr<RigDef::BeamDefaults> const& beam_defaults, std::string material_override)
5852{
5853 std::string material_name = material_override;
5854 if (material_name == "")
5855 {
5856 if (beam.bm_type == BEAM_HYDRO)
5857 {
5858 material_name = "tracks/Chrome";
5859 }
5860 else
5861 {
5862 material_name = beam_defaults->beam_material_name;
5863 }
5864 }
5865
5866 if (m_actor->m_gfx_actor->m_gfx_beams_parent_scenenode == nullptr)
5867 {
5868 m_actor->m_gfx_actor->m_gfx_beams_parent_scenenode = m_actor_grouping_scenenode->createChildSceneNode(this->ComposeName("beams"));
5869 }
5870
5871 try
5872 {
5873 Ogre::Entity* entity = App::GetGfxScene()->GetSceneManager()->createEntity(this->ComposeName("beam", beam_index), "beam.mesh");
5874 Ogre::MaterialPtr mat = this->FindOrCreateCustomizedMaterial(material_name, m_custom_resource_group);
5875 entity->setMaterial(mat);
5876
5877 BeamGfx beamx;
5878 beamx.rod_diameter = beam_defaults->visual_beam_diameter;
5879 beam.default_beam_diameter = beam_defaults->visual_beam_diameter; // Hack for ActorExport.cpp
5880 beamx.rod_beam_index = static_cast<uint16_t>(beam_index);
5881 beamx.rod_node1 = beam.p1->pos;
5882 beamx.rod_node2 = beam.p2->pos;
5883 beamx.rod_target_actor = m_actor;
5884 beamx.rod_is_visible = false;
5885
5886 beamx.rod_scenenode = m_actor->m_gfx_actor->m_gfx_beams_parent_scenenode->createChildSceneNode(this->ComposeName("beam", beam_index));
5887 beamx.rod_scenenode->attachObject(entity);
5888 beamx.rod_scenenode->setVisible(visible, /*cascade:*/ false);
5889 beamx.rod_scenenode->setScale(beam_defaults->visual_beam_diameter, -1, beam_defaults->visual_beam_diameter);
5890
5891 m_actor->m_gfx_actor->m_gfx_beams.push_back(beamx);
5892 }
5893 catch (Ogre::Exception& e)
5894 {
5895 this->AddMessage(Message::TYPE_WARNING, fmt::format("Could not create beam visuals: {}", e.getFullDescription()));
5896 }
5897}
5898
5900{
5901 float beam_length = (beam.p1->RelPosition - beam.p2->RelPosition).length();
5902 beam.L = beam_length;
5903 beam.refL = beam_length;
5904}
5905
5906void ActorSpawner::InitBeam(beam_t & beam, node_t *node_1, node_t *node_2)
5907{
5908 beam.p1 = node_1;
5909 beam.p2 = node_2;
5910
5911 /* Length */
5912 CalculateBeamLength(beam);
5913}
5914
5915void ActorSpawner::AddMessage(ActorSpawner::Message type, Ogre::String const & text)
5916{
5917 Str<4000> txt;
5918 if (m_file)
5919 {
5920 txt << m_file->name;
5921 }
5923 {
5924 txt << " (" << RigDef::KeywordToString(m_current_keyword) << ")";
5925 }
5926 txt << ": " << text;
5928 switch (type)
5929 {
5932 break;
5933
5936 break;
5937
5938 default:
5940 break;
5941 }
5942
5944}
5945
5946NodeNum_t ActorSpawner::ResolveNodeRef(RigDef::Node::Ref const & node_ref, bool optional /* = false */)
5947{
5948 if (!node_ref.IsValidAnyState())
5949 {
5950 if (!optional)
5951 {
5952 AddMessage(Message::TYPE_ERROR, std::string("Attempt to resolve invalid node reference: ") + node_ref.ToString());
5953 }
5954 return NODENUM_INVALID;
5955 }
5956 bool is_imported = node_ref.GetImportState_IsValid();
5957 bool is_named = (is_imported ? node_ref.GetImportState_IsResolvedNamed() : node_ref.GetRegularState_IsNamed());
5958 if (is_named)
5959 {
5960 auto result = m_named_nodes.find(node_ref.Str());
5961 if (result != m_named_nodes.end())
5962 {
5963 return (NodeNum_t)result->second;
5964 }
5965
5966 std::stringstream msg;
5967 msg << "Failed to resolve node-ref (node not found):" << node_ref.ToString();
5968 AddMessage(Message::TYPE_ERROR, msg.str());
5969
5970 return NODENUM_INVALID;
5971 }
5972 else
5973 {
5974 // Imported nodes pass without check
5975 if (!is_imported && (node_ref.Num() >= static_cast<unsigned int>(m_actor->ar_num_nodes)))
5976 {
5977
5978 std::stringstream msg;
5979 msg << "Failed to resolve node-ref (node index too big, node count is: "<<m_actor->ar_num_nodes<<"): " << node_ref.ToString();
5980 AddMessage(Message::TYPE_ERROR, msg.str());
5981
5982 return NODENUM_INVALID;
5983 }
5984 return (NodeNum_t)node_ref.Num();
5985 }
5986}
5987
5989{
5990 NodeNum_t node = ResolveNodeRef(node_ref);
5991 if (node != NODENUM_INVALID)
5992 {
5993 return & m_actor->ar_nodes[node];
5994 }
5995 else
5996 {
5997 return nullptr;
5998 }
5999}
6000
6002{
6003 node_t *node = GetNodePointer(node_ref);
6004 if (node == nullptr)
6005 {
6006 std::stringstream msg;
6007 msg << "Required node not found: " << node_ref.ToString();
6008 throw Exception(msg.str());
6009 }
6010 return node;
6011}
6012
6014{
6015 if (!id.IsValid())
6016 {
6017 std::stringstream msg;
6018 msg << "Attempt to add node with 'INVALID' flag: " << id.ToString() << " (number of nodes at this point: " << m_actor->ar_num_nodes << ")";
6019 this->AddMessage(Message::TYPE_ERROR, msg.str());
6020 return NODENUM_INVALID;
6021 }
6022
6023 if (id.IsTypeNamed())
6024 {
6025 unsigned int new_index = static_cast<unsigned int>(m_actor->ar_num_nodes);
6026 auto insert_result = m_named_nodes.insert(std::make_pair(id.Str(), new_index));
6027 if (! insert_result.second)
6028 {
6029 std::stringstream msg;
6030 msg << "Ignoring named node! Duplicate name: " << id.Str() << " (number of nodes at this point: " << m_actor->ar_num_nodes << ")";
6031 this->AddMessage(Message::TYPE_ERROR, msg.str());
6032 return NODENUM_INVALID;
6033 }
6034 m_actor->ar_nodes_name[new_index] = id.Str();
6035 m_actor->ar_nodes_id[new_index] = m_actor->ar_num_nodes;
6036 m_actor->ar_nodes_name_top_length = std::max(m_actor->ar_nodes_name_top_length, (int)id.Str().length());
6038 return (NodeNum_t)new_index;
6039 }
6040 if (id.IsTypeNumbered())
6041 {
6042 if (id.Num() < static_cast<unsigned int>(m_actor->ar_num_nodes))
6043 {
6044 std::stringstream msg;
6045 msg << "Duplicate node number, previous definition will be overriden! - " << id.ToString() << " (number of nodes at this point: " << m_actor->ar_num_nodes << ")";
6046 this->AddMessage(Message::TYPE_WARNING, msg.str());
6047 }
6048 unsigned int new_index = static_cast<unsigned int>(m_actor->ar_num_nodes);
6049 m_actor->ar_nodes_id[new_index] = id.Num();
6051 return (NodeNum_t)new_index;
6052 }
6053 // Invalid node ID without type flag!
6054 throw Exception("Invalid Node::Id without type flags!");
6055}
6056
6058{
6059 const NodeNum_t nodeid = RegisterNode(def.id);
6060 if (nodeid == NODENUM_INVALID)
6061 {
6062 return; // Error already logged
6063 }
6064
6065 node_t & node = m_actor->ar_nodes[nodeid];
6066 node.pos = nodeid;
6067
6068 /* Positioning */
6069 const Ogre::Vector3 spawn_offset = TuneupUtil::getTweakedNodePosition(m_actor->getWorkingTuneupDef(), node.pos, def.position);
6070 m_actor->ar_nodes_spawn_offsets[nodeid] = spawn_offset;
6071
6072 Ogre::Vector3 node_position = m_spawn_position + spawn_offset;
6073 ROR_ASSERT(!std::isnan(node_position.x));
6074 ROR_ASSERT(!std::isnan(node_position.y));
6075 ROR_ASSERT(!std::isnan(node_position.z));
6076 node.AbsPosition = node_position;
6077 node.RelPosition = node_position - m_actor->ar_origin;
6078
6079 node.friction_coef = def.node_defaults->friction;
6080 node.volume_coef = def.node_defaults->volume;
6081 node.surface_coef = def.node_defaults->surface;
6082
6083 /* Mass */
6084 if (def.default_minimass)
6085 {
6086 m_actor->ar_minimass[nodeid] = def.default_minimass->min_mass_Kg;
6087 }
6088 else
6089 {
6091 }
6092
6093 if (def.node_defaults->load_weight >= 0.f) // The `>=` operator is intentional (negative value => use default).
6094 {
6095 node.mass = def.node_defaults->load_weight;
6096 node.nd_override_mass = true;
6097 node.nd_loaded_mass = true;
6098 m_actor->ar_nodes_override_loadweights[nodeid] = def.node_defaults->load_weight;
6099 }
6100 else
6101 {
6103 node.nd_loaded_mass = false;
6104 m_actor->ar_nodes_override_loadweights[nodeid] = -1.f;
6105 }
6106 m_actor->ar_nodes_default_loadweights[nodeid] = def.node_defaults->load_weight;
6107
6108 /* Lockgroup */
6110
6111 /* Options */
6112 unsigned int options = def.options | def.node_defaults->options; /* Merge bit flags */
6113 m_actor->ar_nodes_options[nodeid] = options;
6115 {
6116 node.nd_loaded_mass = true;
6118 {
6119 node.nd_override_mass = true;
6120 node.mass = def.load_weight_override;
6122 }
6123 else
6124 {
6126 }
6127 }
6129 {
6130 /* Link [current-node] -> [node-0] */
6131 /* If current node is 0, link [node-0] -> [node-1] */
6132 node_t & node_2 = m_actor->ar_nodes[((node.pos == 0) ? 1 : 0)];
6133 unsigned int beam_index = m_actor->ar_num_beams;
6134
6135 beam_t & beam = AddBeam(node, node_2, def.beam_defaults, def.detacher_group);
6136 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold() * 100.f);
6137 beam.bm_type = BEAM_HYDRO;
6138 beam.d = def.beam_defaults->GetScaledDamping() * 0.1f;
6139 beam.k = def.beam_defaults->GetScaledSpringiness();
6140 beam.bounded = ROPE;
6141 beam.bm_disabled = true;
6142 beam.L = HOOK_RANGE_DEFAULT;
6143 beam.refL = HOOK_RANGE_DEFAULT;
6145 CreateBeamVisuals(beam, beam_index, false, def.beam_defaults);
6146
6147 // Logic cloned from SerializedRig.cpp, section BTS_NODES
6148 hook_t hook;
6149 hook.hk_hook_node = & node;
6150 hook.hk_group = -1;
6151 hook.hk_locked = UNLOCKED;
6152 hook.hk_lock_node = nullptr;
6153 hook.hk_locked_actor = nullptr;
6154 hook.hk_lockgroup = -1;
6155 hook.hk_beam = & beam;
6159 hook.hk_selflock = false;
6160 hook.hk_nodisable = false;
6161 hook.hk_timer = 0.0f;
6163 hook.hk_autolock = false;
6164 hook.hk_min_length = 0.f;
6165 m_actor->ar_hooks.push_back(hook);
6166 }
6167 AdjustNodeBuoyancy(node, def, def.node_defaults);
6170
6173
6174 // Update "fusedrag" autocalc y & z span
6175 if (def.position.z < m_fuse_z_min) { m_fuse_z_min = def.position.z; }
6176 if (def.position.z > m_fuse_z_max) { m_fuse_z_max = def.position.z; }
6177 if (def.position.y < m_fuse_y_min) { m_fuse_y_min = def.position.y; }
6178 if (def.position.y > m_fuse_y_max) { m_fuse_y_max = def.position.y; }
6179
6180 // GFX
6181 NodeGfx nfx(node.pos);
6185 m_actor->m_gfx_actor->m_gfx_nodes.push_back(nfx);
6186}
6187
6189 NodeNum_t emitter_node_idx,
6190 NodeNum_t direction_node_idx
6191 )
6192{
6193 const ExhaustID_t exhaust_id = (ExhaustID_t)m_actor->m_gfx_actor->m_exhausts.size();
6194 Exhaust exhaust;
6195 exhaust.emitterNode = emitter_node_idx;
6196 exhaust.directionNode = direction_node_idx;
6197
6198 exhaust.smoker = App::GetGfxScene()->GetSceneManager()->createParticleSystem(
6199 this->ComposeName("exhaust", exhaust_id),
6200 /*quota=*/500, // Default value
6202
6203 if (exhaust.smoker == nullptr)
6204 {
6205 AddMessage(Message::TYPE_ERROR, "Failed to create exhaust");
6206 return;
6207 }
6208 exhaust.smoker->setVisibilityFlags(DEPTHMAP_DISABLED); // Disable particles in depthmap
6209
6210 Ogre::MaterialPtr mat = this->FindOrCreateCustomizedMaterial("tracks/Smoke", m_custom_resource_group);
6211 exhaust.smoker->setMaterialName(mat->getName(), mat->getGroup());
6212
6213 exhaust.smokeNode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("exhaust", exhaust_id));
6214 exhaust.smokeNode->attachObject(exhaust.smoker);
6215 exhaust.smokeNode->setPosition(m_actor->ar_nodes[exhaust.emitterNode].AbsPosition);
6216
6217 m_actor->m_gfx_actor->SetNodeHot(exhaust.emitterNode, true);
6218 m_actor->m_gfx_actor->SetNodeHot(exhaust.directionNode, true);
6219
6220 m_actor->m_gfx_actor->m_exhausts.push_back(exhaust);
6221}
6222
6224{
6225 // Node
6227 node_t & camera_node = GetAndInitFreeNode(node_pos);
6228 m_actor->ar_nodes_spawn_offsets[camera_node.pos] = def.position;
6229 camera_node.nd_no_ground_contact = true; // Orig: hardcoded in BTS_CINECAM
6230 camera_node.nd_cinecam_node = true;
6231 camera_node.friction_coef = NODE_FRICTION_COEF_DEFAULT; // Node defaults are ignored here.
6232 AdjustNodeBuoyancy(camera_node, def.node_defaults);
6233 camera_node.volume_coef = def.node_defaults->volume;
6234 camera_node.surface_coef = def.node_defaults->surface;
6235 // NOTE: Not applying the 'node_mass' value here for backwards compatibility - this node must go through initial `Actor::RecalculateNodeMasses()` pass with default weight.
6236
6238
6241
6242 // Beams
6243 for (unsigned int i = 0; i < 8; i++)
6244 {
6245 int beam_index = m_actor->ar_num_beams;
6246 node_t& node = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[i])];
6247 beam_t & beam = AddBeam(camera_node, node, def.beam_defaults, DEFAULT_DETACHER_GROUP);
6248 beam.bm_type = BEAM_NORMAL;
6249 CalculateBeamLength(beam);
6250 beam.k = def.spring;
6251 beam.d = def.damping;
6252 }
6253};
6254
6255void ActorSpawner::InitNode(node_t & node, Ogre::Vector3 const & position)
6256{
6257 /* Position */
6258 node.AbsPosition = position;
6259 node.RelPosition = position - m_actor->ar_origin;
6260}
6261
6263 node_t & node,
6264 Ogre::Vector3 const & position,
6265 std::shared_ptr<RigDef::NodeDefaults> node_defaults
6266)
6267{
6268 InitNode(node, position);
6269 node.friction_coef = node_defaults->friction;
6270 node.volume_coef = node_defaults->volume;
6271 node.surface_coef = node_defaults->surface;
6272}
6273
6275{
6278
6279 // NOTE: Don't do any material pre-processing here; it'll be done on actual entities (via `SetupNewEntity()`).
6280 if (! def.material_name.empty())
6281 {
6282 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(def.material_name); // Check if exists (compatibility)
6283 if (mat)
6284 {
6286 }
6287 else
6288 {
6289 std::stringstream msg;
6290 msg << "Material '" << def.material_name << "' defined in section 'globals' not found. Trying material 'tracks/transred'";
6291 this->AddMessage(Message::TYPE_ERROR, msg.str());
6292
6293 m_cab_material_name = "tracks/transred";
6294 }
6295 }
6296}
6297
6298/* -------------------------------------------------------------------------- */
6299// Limits.
6300/* -------------------------------------------------------------------------- */
6301
6302bool ActorSpawner::CheckAxleLimit(unsigned int count)
6303{
6304 if ((m_actor->m_num_wheel_diffs + count) > MAX_WHEELS/2)
6305 {
6306 std::stringstream msg;
6307 msg << "Axle limit (" << MAX_WHEELS/2 << ") exceeded";
6308 AddMessage(Message::TYPE_ERROR, msg.str());
6309 return false;
6310 }
6311 return true;
6312}
6313
6314bool ActorSpawner::CheckSubmeshLimit(unsigned int count)
6315{
6316 if ((m_oldstyle_cab_submeshes.size() + count) > MAX_SUBMESHES)
6317 {
6318 std::stringstream msg;
6319 msg << "Submesh limit (" << MAX_SUBMESHES << ") exceeded";
6320 AddMessage(Message::TYPE_ERROR, msg.str());
6321 return false;
6322 }
6323 return true;
6324}
6325
6326bool ActorSpawner::CheckTexcoordLimit(unsigned int count)
6327{
6328 if ((m_oldstyle_cab_texcoords.size() + count) > MAX_TEXCOORDS)
6329 {
6330 std::stringstream msg;
6331 msg << "Texcoord limit (" << MAX_TEXCOORDS << ") exceeded";
6332 AddMessage(Message::TYPE_ERROR, msg.str());
6333 return false;
6334 }
6335 return true;
6336}
6337
6338/* Static version */
6339bool ActorSpawner::CheckSoundScriptLimit(ActorPtr const& vehicle, unsigned int count)
6340{
6341 if ((vehicle->ar_num_soundsources + count) > MAX_SOUNDSCRIPTS_PER_TRUCK)
6342 {
6343 std::stringstream msg;
6344 msg << "SoundScript limit (" << MAX_SOUNDSCRIPTS_PER_TRUCK << ") exceeded";
6345 LOG(msg.str());
6346 return false;
6347 }
6348 return true;
6349}
6350
6351bool ActorSpawner::CheckCabLimit(unsigned int count)
6352{
6353 if ((m_actor->ar_num_cabs + count) > MAX_CABS)
6354 {
6355 std::stringstream msg;
6356 msg << "Cab limit (" << MAX_CABS << ") exceeded";
6357 AddMessage(Message::TYPE_ERROR, msg.str());
6358 return false;
6359 }
6360 return true;
6361}
6362
6364{
6366 {
6367 std::stringstream msg;
6368 msg << "CameraRail limit (" << MAX_CAMERARAIL << ") exceeded";
6369 AddMessage(Message::TYPE_ERROR, msg.str());
6370 return false;
6371 }
6372 return true;
6373}
6374
6376{
6378 {
6379 std::stringstream msg;
6380 msg << "AeroEngine limit (" << MAX_AEROENGINES << ") exceeded";
6381 AddMessage(Message::TYPE_ERROR, msg.str());
6382 return false;
6383 }
6384 return true;
6385}
6386
6388{
6389 if ((m_actor->ar_num_screwprops + count) > MAX_SCREWPROPS)
6390 {
6391 std::stringstream msg;
6392 msg << "Screwprop limit (" << MAX_SCREWPROPS << ") exceeded";
6393 AddMessage(Message::TYPE_ERROR, msg.str());
6394 return false;
6395 }
6396 return true;
6397}
6398
6399void ActorSpawner::InitNode(unsigned int node_index, Ogre::Vector3 const & position)
6400{
6401 InitNode(m_actor->ar_nodes[node_index], position);
6402}
6403
6404beam_t & ActorSpawner::GetBeam(unsigned int index)
6405{
6406 return m_actor->ar_beams[index];
6407}
6408
6410{
6412 node.pos = m_actor->ar_num_nodes;
6414 return node;
6415}
6416
6418{
6421 return beam;
6422}
6423
6425{
6428 return shock;
6429}
6430
6432{
6433 beam_t & beam = GetFreeBeam();
6434 beam.p1 = & node_1;
6435 beam.p2 = & node_2;
6436 return beam;
6437}
6438
6439node_t & ActorSpawner::GetAndInitFreeNode(Ogre::Vector3 const & position)
6440{
6441 node_t & node = GetFreeNode();
6442 InitNode(node, position);
6443 return node;
6444}
6445
6446void ActorSpawner::SetBeamSpring(beam_t & beam, float spring)
6447{
6448 beam.k = spring;
6449}
6450
6451void ActorSpawner::SetBeamDamping(beam_t & beam, float damping)
6452{
6453 beam.d = damping;
6454}
6455
6457{
6458 int trucknum = vehicle->ar_instance_id;
6459 int ar_exhaust_pos_node = vehicle->ar_exhaust_pos_node;
6460
6461#ifdef USE_OPENAL
6462 if (App::GetSoundScriptManager()->isDisabled())
6463 {
6464 return;
6465 }
6466
6467 //engine
6468 if (vehicle->ar_engine != nullptr) /* Land vehicle */
6469 {
6470 if (vehicle->ar_engine->m_engine_type == 't')
6471 {
6472 AddSoundSourceInstance(vehicle, "tracks/default_diesel", ar_exhaust_pos_node);
6473 AddSoundSourceInstance(vehicle, "tracks/default_force", ar_exhaust_pos_node);
6474 AddSoundSourceInstance(vehicle, "tracks/default_brakes", 0);
6475 AddSoundSourceInstance(vehicle, "tracks/default_parkbrakes", 0);
6476 AddSoundSourceInstance(vehicle, "tracks/default_reverse_beep", 0);
6477 }
6478 if (vehicle->ar_engine->m_engine_type == 'c')
6479 AddSoundSourceInstance(vehicle, "tracks/default_car", ar_exhaust_pos_node);
6480 if (vehicle->ar_engine->hasTurbo())
6481 {
6482 if (vehicle->ar_engine->m_turbo_inertia_factor >= 3)
6483 AddSoundSourceInstance(vehicle, "tracks/default_turbo_big", ar_exhaust_pos_node);
6484 else if (vehicle->ar_engine->m_turbo_inertia_factor <= 0.5)
6485 AddSoundSourceInstance(vehicle, "tracks/default_turbo_small", ar_exhaust_pos_node);
6486 else
6487 AddSoundSourceInstance(vehicle, "tracks/default_turbo_mid", ar_exhaust_pos_node);
6488
6489 AddSoundSourceInstance(vehicle, "tracks/default_turbo_bov", ar_exhaust_pos_node);
6490 AddSoundSourceInstance(vehicle, "tracks/default_wastegate_flutter", ar_exhaust_pos_node);
6491 }
6492
6493 if (vehicle->ar_engine->m_engine_has_air)
6494 AddSoundSourceInstance(vehicle, "tracks/default_air_purge", 0);
6495 //starter
6496 AddSoundSourceInstance(vehicle, "tracks/default_starter", 0);
6497 // turn signals
6498 AddSoundSourceInstance(vehicle, "tracks/default_turn_signal", 0);
6499 }
6500 if (vehicle->ar_driveable==TRUCK)
6501 {
6502 //horn
6503 if (vehicle->ar_is_police)
6504 AddSoundSourceInstance(vehicle, "tracks/default_police", 0);
6505 else
6506 AddSoundSourceInstance(vehicle, "tracks/default_horn", 0);
6507 //shift
6508 AddSoundSourceInstance(vehicle, "tracks/default_shift", 0);
6509 }
6510 //pump
6511 if (vehicle->m_has_command_beams)
6512 {
6513 AddSoundSourceInstance(vehicle, "tracks/default_pump", 0);
6514 }
6515 //antilock brake
6516 if (vehicle->alb_mode || !vehicle->alb_notoggle)
6517 {
6518 AddSoundSourceInstance(vehicle, "tracks/default_antilock", 0);
6519 }
6520 //tractioncontrol
6521 if (vehicle->tc_mode || !vehicle->tc_notoggle)
6522 {
6523 AddSoundSourceInstance(vehicle, "tracks/default_tractioncontrol", 0);
6524 }
6525 //screetch
6526 if ((vehicle->ar_driveable==TRUCK || vehicle->ar_driveable==AIRPLANE) && vehicle->ar_num_wheels != 0)
6527 {
6528 AddSoundSourceInstance(vehicle, "tracks/default_screetch", 0);
6529 }
6530 //break & creak
6531 AddSoundSourceInstance(vehicle, "tracks/default_break", 0);
6532 AddSoundSourceInstance(vehicle, "tracks/default_creak", 0);
6533 //boat engine
6534 if (vehicle->ar_driveable==BOAT)
6535 {
6536 if (vehicle->ar_total_mass>50000.0)
6537 AddSoundSourceInstance(vehicle, "tracks/default_marine_large", ar_exhaust_pos_node);
6538 else
6539 AddSoundSourceInstance(vehicle, "tracks/default_marine_small", ar_exhaust_pos_node);
6540 //no start/stop engine for boats, so set sound always on!
6541 SOUND_START(trucknum, SS_TRIG_ENGINE);
6542 SOUND_MODULATE(trucknum, SS_MOD_ENGINE, 0.5);
6543 }
6544 //airplane warnings
6545 if (vehicle->ar_driveable==AIRPLANE)
6546 {
6547 AddSoundSourceInstance(vehicle, "tracks/default_gpws_10", 0);
6548 AddSoundSourceInstance(vehicle, "tracks/default_gpws_20", 0);
6549 AddSoundSourceInstance(vehicle, "tracks/default_gpws_30", 0);
6550 AddSoundSourceInstance(vehicle, "tracks/default_gpws_40", 0);
6551 AddSoundSourceInstance(vehicle, "tracks/default_gpws_50", 0);
6552 AddSoundSourceInstance(vehicle, "tracks/default_gpws_100", 0);
6553
6554 AddSoundSourceInstance(vehicle, "tracks/default_gpws_pullup", 0);
6555 AddSoundSourceInstance(vehicle, "tracks/default_gpws_minimums", 0);
6556 AddSoundSourceInstance(vehicle, "tracks/default_gpws_apdisconnect", 0);
6557 AddSoundSourceInstance(vehicle, "tracks/default_aoa_warning", 0);
6558
6559 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat01", 0);
6560 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat02", 0);
6561 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat03", 0);
6562 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat04", 0);
6563 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat05", 0);
6564 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat06", 0);
6565 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat07", 0);
6566 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat08", 0);
6567 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat09", 0);
6568 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat10", 0);
6569 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat11", 0);
6570 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat12", 0);
6571 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat13", 0);
6572 }
6573 //airplane engines
6574 for (int i=0; i<vehicle->ar_num_aeroengines && i<8; i++)
6575 {
6576 int turbojet_node = vehicle->ar_aeroengines[i]->getNoderef();
6577 Ogre::String index_str = TOSTRING(i+1);
6578
6580 {
6581 AddSoundSourceInstance(vehicle, "tracks/default_turbojet_start" + index_str, turbojet_node);
6582 AddSoundSourceInstance(vehicle, "tracks/default_turbojet_lopower" + index_str, turbojet_node);
6583 AddSoundSourceInstance(vehicle, "tracks/default_turbojet_hipower" + index_str, turbojet_node);
6584 if (((Turbojet*)(vehicle->ar_aeroengines[i].GetRef()))->tjet_afterburnable)
6585 {
6586 AddSoundSourceInstance(vehicle, "tracks/default_turbojet_afterburner" + index_str, turbojet_node);
6587 }
6588 }
6589 else if (vehicle->ar_aeroengines[i]->getType() == AeroEngineType::AE_XPROP)
6590 {
6591 if (((Turboprop*)vehicle->ar_aeroengines[i].GetRef())->is_piston)
6592 {
6593 AddSoundSourceInstance(vehicle, "tracks/default_pistonprop_start" + index_str, turbojet_node);
6594 AddSoundSourceInstance(vehicle, "tracks/default_pistonprop_lopower" + index_str, turbojet_node);
6595 AddSoundSourceInstance(vehicle, "tracks/default_pistonprop_hipower" + index_str, turbojet_node);
6596 }
6597 else
6598 {
6599 AddSoundSourceInstance(vehicle, "tracks/default_turboprop_start" + index_str, turbojet_node);
6600 AddSoundSourceInstance(vehicle, "tracks/default_turboprop_lopower" + index_str, turbojet_node);
6601 AddSoundSourceInstance(vehicle, "tracks/default_turboprop_hipower" + index_str, turbojet_node);
6602 }
6603 }
6604 }
6605
6606 // linked sounds
6607 for (int i=0; i<vehicle->m_num_command_beams; i++)
6608 {
6609 AddSoundSource(vehicle, App::GetSoundScriptManager()->createInstance(Ogre::String("tracks/linked/default_command/extend"), trucknum, SL_COMMAND, i), 0);
6610 AddSoundSource(vehicle, App::GetSoundScriptManager()->createInstance(Ogre::String("tracks/linked/default_command/retract"), trucknum, SL_COMMAND, -i), 0);
6611 }
6612
6613#endif //OPENAL
6614}
6615
6617{
6618 for (int i=0; i<m_actor->ar_num_collcabs; i++)
6619 {
6620 int tmpv = m_actor->ar_collcabs[i] * 3;
6621 m_actor->ar_nodes[m_actor->ar_cabs[tmpv]].nd_cab_node = true;
6622 m_actor->ar_nodes[m_actor->ar_cabs[tmpv+1]].nd_cab_node = true;
6623 m_actor->ar_nodes[m_actor->ar_cabs[tmpv+2]].nd_cab_node = true;
6624 }
6625 for (int i = 0; i < m_actor->ar_num_nodes; i++)
6626 {
6628 {
6631 }
6632 else if (!m_actor->ar_nodes[i].nd_no_ground_contact &&
6634 {
6635 m_actor->ar_nodes[i].nd_contactable = true;
6637 }
6638 }
6639}
6640
6642{
6643 for (auto& module: m_selected_modules)
6644 {
6645 for (auto& def: module->materialflarebindings)
6646 {
6647 if (def.material_name == material_name)
6648 {
6649 return &def;
6650 }
6651 }
6652 }
6653 return nullptr;
6654}
6655
6657{
6658 for (auto& module: m_selected_modules)
6659 {
6660 for (auto& def: module->videocameras)
6661 {
6662 if (def.material_name == material_name)
6663 {
6664 return &def;
6665 }
6666 }
6667 }
6668
6669 return nullptr;
6670}
6671
6672Ogre::MaterialPtr ActorSpawner::FindOrCreateCustomizedMaterial(const std::string& mat_lookup_name, const std::string& mat_lookup_rg)
6673{
6674 try
6675 {
6676 // Check for existing substitute
6677 auto lookup_res = m_material_substitutions.find(mat_lookup_name);
6678 if (lookup_res != m_material_substitutions.end())
6679 {
6680 return lookup_res->second.material;
6681 }
6682
6683 CustomMaterial lookup_entry;
6684
6685 // Query old-style mirrors (=special props, hardcoded material name 'mirror')
6686 if (mat_lookup_name == "mirror")
6687 {
6690 lookup_entry.material_flare_def = nullptr;
6691 static int mirror_counter = 0;
6692 const std::string new_mat_name = this->ComposeName("RenderMaterial", mirror_counter);
6693 ++mirror_counter;
6694 lookup_entry.material = Ogre::MaterialManager::getSingleton().getByName("mirror")->clone(new_mat_name, true, mat_lookup_rg);
6695 // Special case - register under generated name. This is because all mirrors use the same material 'mirror'
6696 m_material_substitutions.insert(std::make_pair(new_mat_name, lookup_entry));
6697 return lookup_entry.material; // Done!
6698 }
6699
6700 // Query 'videocameras'
6701 RigDef::VideoCamera* videocam_def = this->FindVideoCameraByMaterial(mat_lookup_name);
6702 if (videocam_def != nullptr)
6703 {
6704 Ogre::MaterialPtr video_mat_shared;
6705 auto found_managedmat = m_managed_materials.find(mat_lookup_name);
6706 if (found_managedmat != m_managed_materials.end())
6707 {
6708 video_mat_shared = found_managedmat->second;
6709 }
6710 else
6711 {
6712 video_mat_shared = Ogre::MaterialManager::getSingleton().getByName(mat_lookup_name);
6713 }
6714
6715 if (video_mat_shared)
6716 {
6717 lookup_entry.video_camera_def = videocam_def;
6718 const std::string video_mat_name = this->ComposeName(videocam_def->material_name);
6719 lookup_entry.material = video_mat_shared->clone(video_mat_name, true, mat_lookup_rg);
6720 m_material_substitutions.insert(std::make_pair(mat_lookup_name, lookup_entry));
6721 return lookup_entry.material; // Done!
6722 }
6723 else
6724 {
6725 std::stringstream msg;
6726 msg << "VideoCamera material '" << mat_lookup_name << "' not found! Ignoring videocamera.";
6727 this->AddMessage(Message::TYPE_WARNING, msg.str());
6728 }
6729 }
6730
6731 // Resolve 'materialflarebindings'.
6732 RigDef::MaterialFlareBinding* mat_flare_def = this->FindFlareBindingForMaterial(mat_lookup_name);
6733 if (mat_flare_def != nullptr)
6734 {
6735 lookup_entry.material_flare_def = mat_flare_def;
6736 }
6737
6738 // Query .skin material replacements
6739 if (m_actor->m_used_skin_entry != nullptr)
6740 {
6742
6743 auto skin_res = skin_def->replace_materials.find(mat_lookup_name);
6744 if (skin_res != skin_def->replace_materials.end())
6745 {
6746 Ogre::MaterialPtr skin_mat = Ogre::MaterialManager::getSingleton().getByName(
6747 skin_res->second, m_actor->m_used_skin_entry->resource_group);
6748 if (skin_mat)
6749 {
6750 lookup_entry.material = skin_mat->clone(this->ComposeName(skin_mat->getName()), /*changeGroup=*/true, mat_lookup_rg);
6751 m_material_substitutions.insert(std::make_pair(mat_lookup_name, lookup_entry));
6752 return lookup_entry.material;
6753 }
6754 else
6755 {
6756 std::stringstream buf;
6757 buf << "Material '" << skin_res->second << "' from skin '" << m_actor->m_used_skin_entry->dname
6758 << "' not found (filename: '" << m_actor->m_used_skin_entry->fname
6759 << "', resource group: '"<< m_actor->m_used_skin_entry->resource_group
6760 <<"')! Ignoring it...";
6761 this->AddMessage(Message::TYPE_ERROR, buf.str());
6762 }
6763 }
6764 }
6765
6766 // Acquire substitute - either use managedmaterial or generate new by cloning.
6767 auto mmat_res = m_managed_materials.find(mat_lookup_name);
6768 if (mmat_res != m_managed_materials.end())
6769 {
6770 // Use managedmaterial as substitute
6771 lookup_entry.material = mmat_res->second;
6772 }
6773 else
6774 {
6775 // Generate new substitute
6776 Ogre::MaterialPtr orig_mat = Ogre::MaterialManager::getSingleton().getByName(mat_lookup_name, mat_lookup_rg);
6777 if (!orig_mat)
6778 {
6779 std::stringstream buf;
6780 buf << "Material doesn't exist:" << mat_lookup_name;
6781 this->AddMessage(Message::TYPE_ERROR, buf.str());
6782 return Ogre::MaterialPtr(); // NULL
6783 }
6784
6785 lookup_entry.material = orig_mat->clone(this->ComposeName(orig_mat->getName()), true, mat_lookup_rg);
6786 /*
6787 02:53:47: [RoR|Actor|Error] (Keyword: managedmaterials) Ogre::ItemIdentityException::ItemIdentityException: Texture Pointer is empty. in TextureUnitState::setTexture at C:\Users\Petr\.conan2\p\b\ogre3ff3740bbb9785\b\OgreMain\src\OgreTextureUnitState.cpp (line 271)
6788 02:55:17: [RoR|ContentManager] Skipping resource with duplicate name: 'pushbar1 (gavrilmv4.truck [Instance ID 1])' (origin: '')
6789 */
6790 ROR_ASSERT(lookup_entry.material);
6791 }
6792
6793 // Register the substitute
6794 m_material_substitutions.insert(std::make_pair(mat_lookup_name, lookup_entry));
6795
6796 // Finally, query texture replacements - .skin and dashboards.
6797 for (auto& technique: lookup_entry.material->getTechniques())
6798 {
6799 for (auto& pass: technique->getPasses())
6800 {
6801 for (auto& tex_unit: pass->getTextureUnitStates())
6802 {
6803 // The built-in 'renderdash' material and associated 'dashtexture'.
6804 if (tex_unit->getTextureName() == "dashtexture")
6805 {
6806 ROR_ASSERT(m_renderdash_rtt_layer && "RenderDash RTT layer not created");
6807 Ogre::TexturePtr dash_tex = Ogre::TextureManager::getSingleton().getByName(m_renderdash_rtt_layer->getTextureName());
6808 ROR_ASSERT(dash_tex && "could not find renderdash's render texture");
6809 tex_unit->setTexture(dash_tex);
6810 }
6811 // The placeholder 'RTTTexture#'-s specified by 'guisettings/texturedashboard' entries in truck file.
6812 else if (Ogre::StringUtil::startsWith(tex_unit->getTextureName(), "RTTTexture"))
6813 {
6814 int rtt_index = std::stoi(tex_unit->getTextureName().substr(std::string("RTTTexture").length()));
6815 if (rtt_index > 0 && rtt_index <= (int)m_texturedashboard_rtt_layers.size())
6816 {
6817 const std::string texname = m_texturedashboard_rtt_layers[rtt_index - 1]->getTextureName();
6818 LOG(fmt::format("[RoR|DashBoard] Swapping placeholder '{}' for '{}'", tex_unit->getTextureName(), texname));
6819 const Ogre::TexturePtr dash_tex = Ogre::TextureManager::getSingleton().getByName(texname);
6820 tex_unit->setTexture(dash_tex);
6821 }
6822 else
6823 {
6825 fmt::format("Invalid dashboard RTT texture {} in material '{}', number of available RTTTextures: {}",
6826 tex_unit->getTextureName(), lookup_entry.material->getName(), m_texturedashboard_rtt_layers.size()));
6827 }
6828 }
6829 // .skin
6830 else if (m_actor->m_used_skin_entry != nullptr)
6831 {
6832 const size_t num_frames = tex_unit->getNumFrames();
6833 for (size_t i = 0; i < num_frames; ++i)
6834 {
6835 const auto end = m_actor->m_used_skin_entry->skin_def->replace_textures.end();
6836 const auto query = m_actor->m_used_skin_entry->skin_def->replace_textures.find(tex_unit->getFrameTextureName((unsigned int)i));
6837 if (query != end)
6838 {
6839 // Skin has replacement for this texture
6840 if (m_actor->m_used_skin_entry->resource_group != mat_lookup_rg) // The skin comes from a SkinZip bundle (different resource group)
6841 {
6842 Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName(
6843 query->second, m_actor->m_used_skin_entry->resource_group);
6844 if (!tex)
6845 {
6846 // `Ogre::TextureManager` doesn't automatically register all images in resource groups,
6847 // it waits for `Ogre::Resource`s to be created explicitly.
6848 // Normally this is done by `Ogre::MaterialManager` when loading a material.
6849 // In this case we must do it manually
6850 tex = Ogre::TextureManager::getSingleton().create(
6851 query->second, m_actor->m_used_skin_entry->resource_group);
6852 }
6853 tex_unit->_setTexturePtr(tex, i);
6854 }
6855 else // The skin lives in the vehicle bundle (same resource group)
6856 {
6857 tex_unit->setFrameTextureName(query->second, (unsigned int)i);
6858 }
6859 }
6860 }
6861 }
6862 } // texture unit states
6863 } // passes
6864 } // techniques
6865
6866
6867 return lookup_entry.material;
6868 }
6869 catch (Ogre::Exception& e)
6870 {
6871 std::stringstream msg;
6872 msg << "Exception while customizing material \"" << mat_lookup_name << "\", message: " << e.getFullDescription();
6873 this->AddMessage(Message::TYPE_ERROR, msg.str());
6874 }
6875 return Ogre::MaterialPtr(); // NULL
6876}
6877
6878Ogre::MaterialPtr ActorSpawner::CreateSimpleMaterial(Ogre::ColourValue color)
6879{
6881
6882 static unsigned int simple_mat_counter = 0;
6883 Ogre::MaterialPtr newmat = m_simple_material_base->clone(this->ComposeName("simple material", simple_mat_counter++));
6884 newmat->getTechnique(0)->getPass(0)->setAmbient(color);
6885
6886 return newmat;
6887}
6888
6889void ActorSpawner::SetupNewEntity(Ogre::Entity* ent, Ogre::ColourValue simple_color)
6890{
6891 // RULE: Each actor must have it's own material instances (a lookup table is kept for OrigName->CustomName)
6892 //
6893 // Setup routine:
6894 //
6895 // 1. If "SimpleMaterials" (plain color surfaces denoting component type) are enabled in config file,
6896 // material is generated (not saved to lookup table) and processing ends.
6897 // 2. If the material name is 'mirror', it's a special prop - rear view mirror.
6898 // material is generated, added to lookup table under generated name (special case) and processing ends.
6899 // 3. If the material is a 'videocamera' of any subtype, material is created, added to lookup table and processing ends.
6900 // 4 'materialflarebindngs' are resolved -> binding is persisted in lookup table.
6901 // 5 SkinZIP _material replacements_ are queried. If match is found, it's added to lookup table and processing ends.
6902 // 6. ManagedMaterials are queried. If match is found, it's added to lookup table and processing ends.
6903 // 7. Orig. material is cloned to create substitute.
6904 // 8. SkinZIP _texture replacements_ are queried. If match is found, substitute material is updated.
6905 // 9. Material is added to lookup table, processing ends.
6906 // ==========================================================
6907
6908 if (ent == nullptr)
6909 {
6910 // Dirty but I don't see any alternative ... ~ ulteq, 10/2018
6911 AddMessage(Message::TYPE_WARNING, "Failed to create entity: continuing without it ...");
6912 return;
6913 }
6914
6915 // Use simple materials if applicable
6917 {
6918 Ogre::MaterialPtr mat = this->CreateSimpleMaterial(simple_color);
6919
6920 size_t num_sub_entities = ent->getNumSubEntities();
6921 for (size_t i = 0; i < num_sub_entities; i++)
6922 {
6923 Ogre::SubEntity* subent = ent->getSubEntity(i);
6924 subent->setMaterial(mat);
6925 }
6926
6927 return; // Done!
6928 }
6929
6930 // Create unique sub-entity (=instance of submesh) materials
6931 size_t subent_max = ent->getNumSubEntities();
6932 for (size_t i = 0; i < subent_max; ++i)
6933 {
6934 Ogre::SubEntity* subent = ent->getSubEntity(i);
6935
6936 if (subent->getMaterial())
6937 {
6938 Ogre::MaterialPtr own_mat = this->FindOrCreateCustomizedMaterial(subent->getMaterialName(), subent->getSubMesh()->parent->getGroup());
6939 if (own_mat)
6940 {
6941 subent->setMaterial(own_mat);
6942 }
6943 }
6944 }
6945}
6946
6948{
6949 // Check and warn if there are unclaimed managed materials
6950 // TODO &*&*
6951
6952 // Process special materials
6953 for (auto& entry: m_material_substitutions)
6954 {
6955 if (entry.second.material_flare_def != nullptr) // 'materialflarebindings'
6956 {
6957 this->CreateMaterialFlare(
6958 entry.second.material_flare_def->flare_number, entry.second.material);
6959 }
6960 else if (entry.second.mirror_prop_type != CustomMaterial::MirrorPropType::MPROP_NONE) // special 'prop' - rear view mirror
6961 {
6963 entry.second.material, entry.second.mirror_prop_type, entry.second.mirror_prop_scenenode);
6964 }
6965 else if (entry.second.video_camera_def != nullptr) // 'videocameras'
6966 {
6968 this->CreateVideoCamera(entry.second.video_camera_def);
6970 }
6971 }
6972
6973 if (!App::gfx_enable_videocams->getBool())
6974 {
6976 }
6977
6978 // Load dashboard layouts
6980 for (auto& module: m_selected_modules)
6981 {
6982 for (auto& gs: module->guisettings)
6983 {
6984 if (gs.key == "dashboard")
6985 {
6986 m_actor->ar_dashboard->loadDashBoard(gs.value, LOADDASHBOARD_SCREEN_HUD, nullptr);
6987 }
6988 else if (gs.key == "texturedashboard")
6989 {
6991 {
6992 this->AddMessage(Message::TYPE_ERROR, "Internal error - missing pre-allocated RTT layer");
6993 }
6994 else
6995 {
6996 const std::string rtt_name = this->ComposeName("dashboard_rtt", m_actor->ar_instance_id);
6997 RTTLayer* rtt_layer = m_texturedashboard_rtt_layers.front();
7000 }
7001 }
7002 }
7003 }
7004 this->SetCurrentKeyword(RigDef::Keyword::INVALID); // Logging
7005
7006 // If none specified, load default dashboard layouts
7007 BitMask_t defaultdash_flags = 0; // this means 'do nothing'
7008 RTTLayer* defaultdash_rtt_layer = nullptr;
7009 BITMASK_SET_1(defaultdash_flags, m_actor->ar_dashboard->wasDashboardHudLoaded() ? 0 : LOADDASHBOARD_SCREEN_HUD);
7010
7011 switch (m_actor->ar_driveable)
7012 {
7013 case TRUCK:
7014 m_actor->ar_dashboard->loadDashBoard(App::ui_default_truck_dash->getStr(), defaultdash_flags, defaultdash_rtt_layer);
7015 m_actor->ar_dashboard->loadDashBoard(App::ui_legacy_truck_renderdash->getStr(), LOADDASHBOARD_RENDERDASH, m_renderdash_rtt_layer);
7016 m_actor->ar_dashboard->setVisible(false);
7017 break;
7018 case BOAT:
7019 m_actor->ar_dashboard->loadDashBoard(App::ui_default_boat_dash->getStr(), defaultdash_flags, defaultdash_rtt_layer);
7020 m_actor->ar_dashboard->setVisible(false);
7021 break;
7022 default:
7023 break;
7024 }
7025
7026 if (m_actor->ar_guisettings_help_material != "")
7027 {
7028 try
7029 {
7030 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(m_actor->ar_guisettings_help_material, m_custom_resource_group);
7031 m_actor->m_gfx_actor->m_help_mat = mat;
7032 if (mat &&
7033 mat->getNumTechniques() > 0 &&
7034 mat->getTechnique(0)->getNumPasses() > 0 &&
7035 mat->getTechnique(0)->getPass(0)->getNumTextureUnitStates() > 0 &&
7036 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getNumFrames() > 0)
7037 {
7038 m_actor->m_gfx_actor->m_help_tex =
7039 Ogre::TextureManager::getSingleton().getByName(
7040 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getFrameTextureName(0), m_custom_resource_group);
7041 }
7042 }
7043 catch (Ogre::Exception& e)
7044 {
7045 this->AddMessage(Message::TYPE_ERROR,
7046 "Failed to load `help` material '" + m_actor->ar_guisettings_help_material + "', message:" + e.getFullDescription());
7047 }
7048 }
7049
7050 m_actor->ar_managed_materials = m_managed_materials;
7051}
7052
7053void ActorSpawner::ValidateRotator(int id, int axis1, int axis2, NodeNum_t *nodes1, NodeNum_t *nodes2)
7054{
7055 const float eps = 0.001f;
7056 const Ogre::Vector3 ax1 = m_actor->ar_nodes[axis1].AbsPosition;
7057 const Ogre::Vector3 ax2 = m_actor->ar_nodes[axis2].AbsPosition;
7058 Ogre::Plane pl = Ogre::Plane((ax1 - ax2).normalisedCopy(), 0);
7059
7060 Ogre::Vector3 a1 = pl.projectVector(ax1 - m_actor->ar_nodes[nodes1[0]].AbsPosition);
7061 Ogre::Vector3 a2 = pl.projectVector(ax1 - m_actor->ar_nodes[nodes1[1]].AbsPosition);
7062 Ogre::Vector3 a3 = pl.projectVector(ax1 - m_actor->ar_nodes[nodes1[2]].AbsPosition);
7063 Ogre::Vector3 a4 = pl.projectVector(ax1 - m_actor->ar_nodes[nodes1[3]].AbsPosition);
7064 float a1len = a1.normalise();
7065 float a2len = a2.normalise();
7066 float a3len = a3.normalise();
7067 float a4len = a4.normalise();
7068 if ((std::max(a1len, a3len) / std::min(a1len, a3len) > 1.f + eps) ||
7069 (std::max(a2len, a4len) / std::min(a2len, a4len) > 1.f + eps))
7070 {
7071 Ogre::String msg = Ogre::StringUtil::format("Off-centered axis on base plate of rotator %d", id);
7073 }
7074
7075 Ogre::Vector3 b1 = pl.projectVector(ax2 - m_actor->ar_nodes[nodes2[0]].AbsPosition);
7076 Ogre::Vector3 b2 = pl.projectVector(ax2 - m_actor->ar_nodes[nodes2[1]].AbsPosition);
7077 Ogre::Vector3 b3 = pl.projectVector(ax2 - m_actor->ar_nodes[nodes2[2]].AbsPosition);
7078 Ogre::Vector3 b4 = pl.projectVector(ax2 - m_actor->ar_nodes[nodes2[3]].AbsPosition);
7079 float b1len = b1.normalise();
7080 float b2len = b2.normalise();
7081 float b3len = b3.normalise();
7082 float b4len = b4.normalise();
7083 if ((std::max(b1len, b3len) / std::min(b1len, b3len) > 1.f + eps) ||
7084 (std::max(b2len, b4len) / std::min(b2len, b4len) > 1.f + eps))
7085 {
7086 Ogre::String msg = Ogre::StringUtil::format("Off-centered axis on rotating plate of rotator %d", id);
7088 }
7089
7090 float rot1 = a1.dotProduct(b1);
7091 float rot2 = a2.dotProduct(b2);
7092 float rot3 = a3.dotProduct(b3);
7093 float rot4 = a4.dotProduct(b4);
7094 if ((std::max(rot1, rot2) / std::min(rot1, rot2) > 1.f + eps) ||
7095 (std::max(rot2, rot3) / std::min(rot2, rot3) > 1.f + eps) ||
7096 (std::max(rot3, rot4) / std::min(rot3, rot4) > 1.f + eps) ||
7097 (std::max(rot4, rot1) / std::min(rot4, rot1) > 1.f + eps))
7098 {
7099 Ogre::String msg = Ogre::StringUtil::format("Misaligned plates on rotator %d", id);
7101 }
7102}
7103
7104Ogre::ManualObject* CreateVideocameraDebugMesh()
7105{
7106 // Create material
7107 static size_t counter = 0;
7108 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(
7109 "VideoCamDebugMat-" + TOSTRING(counter), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
7110 ++counter;
7111 mat->getTechnique(0)->getPass(0)->createTextureUnitState();
7112 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
7113 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureAnisotropy(3);
7114 mat->setLightingEnabled(false);
7115 mat->setReceiveShadows(false);
7116 // Create mesh
7117 Ogre::ManualObject* mo = App::GetGfxScene()->GetSceneManager()->createManualObject(); // TODO: Eliminate gEnv
7118 mo->begin(mat->getName(), Ogre::RenderOperation::OT_LINE_LIST);
7119 Ogre::ColourValue pos_mark_col(1.f, 0.82f, 0.26f);
7120 Ogre::ColourValue dir_mark_col(0.f, 1.f, 1.f); // TODO: This comes out green in simulation - why? ~ only_a_ptr, 05/2017
7121 const float pos_mark_len = 0.8f;
7122 const float dir_mark_len = 4.f;
7123 // X
7124 mo->position(pos_mark_len,0,0);
7125 mo->colour(pos_mark_col);
7126 mo->position(-pos_mark_len,0,0);
7127 mo->colour(pos_mark_col);
7128 // Y
7129 mo->position(0,pos_mark_len,0);
7130 mo->colour(pos_mark_col);
7131 mo->position(0,-pos_mark_len,0);
7132 mo->colour(pos_mark_col);
7133 // +Z
7134 mo->position(0,0,pos_mark_len);
7135 mo->colour(pos_mark_col);
7136 mo->position(0,0,0);
7137 mo->colour(pos_mark_col);
7138 // -Z = the direction
7139 mo->position(0,0,-dir_mark_len);
7140 mo->colour(dir_mark_col);
7141 mo->position(0,0,0);
7142 mo->colour(dir_mark_col);
7143 mo->end(); // Don't forget this!
7144
7145 return mo;
7146}
7147
7149{
7150 try
7151 {
7152 auto videocameraid = (VideoCameraID_t)m_actor->m_gfx_actor->m_videocameras.size();
7153 RoR::VideoCamera vcam;
7154
7156 if (vcam.vcam_role == VCAM_ROLE_INVALID)
7157 {
7158 this->AddMessage(Message::TYPE_ERROR, fmt::format("Skipping VideoCamera (mat: {}) with invalid 'role' ({})", def->material_name, (int)vcam.vcam_role));
7159 return;
7160 }
7161
7164 if (!vcam.vcam_material)
7165 {
7166 this->AddMessage(Message::TYPE_ERROR, "Failed to create VideoCamera with material: " + def->material_name);
7167 return;
7168 }
7169
7173 vcam.vcam_pos_offset = def->offset;
7174
7175 //rotate camera picture 180 degrees, skip for mirrors
7176 float rotation_z = def->rotation.z + 180;
7179 {
7180 rotation_z += 180.0f;
7181 }
7182 vcam.vcam_rotation
7183 = Ogre::Quaternion(Ogre::Degree(rotation_z), Ogre::Vector3::UNIT_Z)
7184 * Ogre::Quaternion(Ogre::Degree(def->rotation.y), Ogre::Vector3::UNIT_Y)
7185 * Ogre::Quaternion(Ogre::Degree(def->rotation.x), Ogre::Vector3::UNIT_X);
7186
7187 // set alternative camposition (optional)
7189 {
7191 }
7192 else
7193 {
7195 }
7196
7197 // set alternative lookat position (optional)
7199 {
7200 // This is a tracker camera
7201 switch (vcam.vcam_role)
7202 {
7206 default: break; // Assume the TRACKING_* role is already set by the tuning system.
7207 }
7209 }
7210
7211 vcam.vcam_ogre_camera = App::GetGfxScene()->GetSceneManager()->createCamera(vcam.vcam_material->getName() + "_camera");
7212
7213 if (!App::gfx_window_videocams->getBool())
7214 {
7215 vcam.vcam_render_tex = Ogre::TextureManager::getSingleton().createManual(
7216 vcam.vcam_material->getName() + "_texture",
7217 Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
7218 Ogre::TEX_TYPE_2D,
7219 def->texture_width,
7220 def->texture_height,
7221 0, // no mip maps
7222 Ogre::PF_R8G8B8,
7223 Ogre::TU_RENDERTARGET);
7224 vcam.vcam_render_target = vcam.vcam_render_tex->getBuffer()->getRenderTarget();
7225 vcam.vcam_render_target->setAutoUpdated(false);
7226 }
7227 else
7228 {
7229 const std::string window_name = (!def->camera_name.empty()) ? def->camera_name : def->material_name;
7231 vcam.vcam_render_window->setAutoUpdated(false);
7232 vcam.vcam_render_window->setDeactivateOnFocusChange(false);
7233
7234 // TODO: disable texture mirrors
7235 }
7236
7237 vcam.vcam_ogre_camera->setNearClipDistance(def->min_clip_distance);
7238 vcam.vcam_ogre_camera->setFarClipDistance(def->max_clip_distance);
7239 vcam.vcam_ogre_camera->setFOVy(Ogre::Degree(def->field_of_view));
7240 const float aspect_ratio = static_cast<float>(def->texture_width) / static_cast<float>(def->texture_height);
7241 vcam.vcam_ogre_camera->setAspectRatio(aspect_ratio);
7242 vcam.vcam_material->getTechnique(0)->getPass(0)->setLightingEnabled(false);
7243 vcam.vcam_off_tex_name = "Chrome.dds"; // Built-in gray texture
7244
7245 if (vcam.vcam_render_target)
7246 {
7247 Ogre::Viewport* vp = vcam.vcam_render_target->addViewport(vcam.vcam_ogre_camera);
7248 vp->setClearEveryFrame(true);
7249 vp->setBackgroundColour(App::GetCameraManager()->GetCamera()->getViewport()->getBackgroundColour());
7250 vp->setVisibilityMask(~HIDE_MIRROR);
7251 vp->setVisibilityMask(~DEPTHMAP_DISABLED);
7252 vp->setOverlaysEnabled(false);
7253
7254 vcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(vcam.vcam_render_tex->getName());
7255
7256 // this is a mirror, flip the image left<>right to have a mirror and not a cameraimage
7258 vcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureUScale(-1);
7259 }
7260
7261 if (vcam.vcam_render_window)
7262 {
7263 Ogre::Viewport* vp = vcam.vcam_render_window->addViewport(vcam.vcam_ogre_camera);
7264 vp->setClearEveryFrame(true);
7265 vp->setBackgroundColour(App::GetCameraManager()->GetCamera()->getViewport()->getBackgroundColour());
7266 vp->setVisibilityMask(~HIDE_MIRROR);
7267 vp->setVisibilityMask(~DEPTHMAP_DISABLED);
7268 vp->setOverlaysEnabled(false);
7269 vcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(vcam.vcam_off_tex_name);
7270 }
7271
7272 if (App::diag_videocameras->getBool())
7273 {
7274 Ogre::ManualObject* mo = CreateVideocameraDebugMesh(); // local helper function
7275 vcam.vcam_debug_node = App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->createChildSceneNode(
7276 this->ComposeName("debug @ videocamera", (int)m_actor->m_gfx_actor->m_videocameras.size()));
7277 vcam.vcam_debug_node->attachObject(mo);
7278 }
7279
7280 m_actor->m_gfx_actor->m_videocameras.push_back(vcam);
7281 }
7282 catch (std::exception & ex)
7283 {
7284 this->AddMessage(Message::TYPE_ERROR, ex.what());
7285 }
7286 catch (...)
7287 {
7288 this->AddMessage(Message::TYPE_ERROR, "An unknown exception has occured");
7289 }
7290}
7291
7293 Ogre::MaterialPtr custom_mat, CustomMaterial::MirrorPropType type, Ogre::SceneNode* prop_scenenode)
7294{
7295 static int mprop_counter = 0;
7296 try
7297 {
7298 // Prepare videocamera entry
7299 RoR::VideoCamera vcam;
7300 vcam.vcam_off_tex_name = "mirror.dds";
7301 vcam.vcam_prop_scenenode = prop_scenenode;
7302 switch (type)
7303 {
7306 break;
7307
7310 break;
7311
7312 default:
7313 this->AddMessage(Message::TYPE_ERROR, "Cannot create mirror prop of type 'MPROP_NONE'");
7314 return;
7315 }
7316
7317 // Create rendering texture
7318 vcam.vcam_render_tex = Ogre::TextureManager::getSingleton().createManual(
7319 this->ComposeName("texture @ mirror", mprop_counter)
7321 , Ogre::TEX_TYPE_2D
7322 , 128
7323 , 256
7324 , 0
7325 , Ogre::PF_R8G8B8
7326 , Ogre::TU_RENDERTARGET);
7327
7328 // Create OGRE camera
7329 vcam.vcam_ogre_camera = App::GetGfxScene()->GetSceneManager()->createCamera(this->ComposeName("camera @ mirror prop", mprop_counter));
7330 vcam.vcam_ogre_camera->setNearClipDistance(0.2f);
7331 vcam.vcam_ogre_camera->setFarClipDistance(App::GetCameraManager()->GetCamera()->getFarClipDistance());
7332 vcam.vcam_ogre_camera->setFOVy(Ogre::Degree(50));
7333 vcam.vcam_ogre_camera->setAspectRatio(
7334 (App::GetCameraManager()->GetCamera()->getViewport()->getActualWidth() / App::GetCameraManager()->GetCamera()->getViewport()->getActualHeight()) / 2.0f);
7335
7336 // Setup rendering
7337 vcam.vcam_render_target = vcam.vcam_render_tex->getBuffer()->getRenderTarget();
7338 vcam.vcam_render_target->setActive(true);
7339 Ogre::Viewport* v = vcam.vcam_render_target->addViewport(vcam.vcam_ogre_camera);
7340 v->setClearEveryFrame(true);
7341 v->setBackgroundColour(App::GetCameraManager()->GetCamera()->getViewport()->getBackgroundColour());
7342 v->setOverlaysEnabled(false);
7343
7344 // Setup material
7345 vcam.vcam_material = custom_mat;
7346 vcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(vcam.vcam_render_tex->getName());
7347 vcam.vcam_material->getTechnique(0)->getPass(0)->setLightingEnabled(false);
7348
7349 // Submit the videocamera
7350 m_actor->m_gfx_actor->m_videocameras.push_back(vcam);
7351 }
7352 catch (std::exception & ex)
7353 {
7354 this->AddMessage(Message::TYPE_ERROR, ex.what());
7355 }
7356 catch (...)
7357 {
7358 this->AddMessage(Message::TYPE_ERROR, "An unknown exception has occured");
7359 }
7360 ++mprop_counter;
7361}
7362
7364{
7365 try { throw; } // Rethrow
7366
7367 catch (Ogre::Exception& ogre_e)
7368 {
7369 // Add the message silently, OGRE already printed it to RoR.log
7370 RoR::Str<2000> txt;
7371 txt << "(Keyword: " << RigDef::KeywordToString(m_current_keyword)
7372 << ") " << ogre_e.getFullDescription();
7375 }
7376 catch (std::exception& std_e)
7377 {
7378 this->AddMessage(Message::TYPE_ERROR, std_e.what());
7379 }
7380 catch (...)
7381 {
7382 this->AddMessage(Message::TYPE_ERROR, "An unknown exception has occurred");
7383 }
7384}
7385
7386Ogre::ParticleSystem* ActorSpawner::CreateParticleSystem(std::string const & name, std::string const & template_name)
7387{
7388 // None of `Ogre::SceneManager::createParticleSystem()` overloads
7389 // lets us specify both resource group and template name.
7390
7391 Ogre::NameValuePairList params;
7392 params["resourceGroup"] = m_custom_resource_group;
7393 params["templateName"] = template_name;
7394
7395 Ogre::MovableObject* obj = App::GetGfxScene()->GetSceneManager()->createMovableObject(
7396 name, Ogre::ParticleSystemFactory::FACTORY_TYPE_NAME, &params);
7397 Ogre::ParticleSystem* psys = static_cast<Ogre::ParticleSystem*>(obj);
7398 psys->setVisibilityFlags(DEPTHMAP_DISABLED); // disable particles in depthmap
7399
7400 // Shut down the emitters
7401 for (size_t i = 0; i < psys->getNumEmitters(); i++)
7402 {
7403 psys->getEmitter(i)->setEnabled(false);
7404 }
7405
7406 return psys;
7407}
7408
7410{
7413
7414 //the cab materials are as follow:
7415 //texname: base texture with emissive(2 pass) or without emissive if none available(1 pass), alpha cutting
7416 //texname-trans: transparency texture (1 pass)
7417 //texname-back: backface texture: black+alpha cutting (1 pass)
7418 //texname-noem: base texture without emissive (1 pass), alpha cutting
7419
7420 //material passes must be:
7421 //0: normal texture
7422 //1: transparent (windows)
7423 //2: emissive
7424
7425 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(m_cab_material_name);
7426 if (!mat)
7427 {
7428 Ogre::String msg = "Material '"+m_cab_material_name+"' missing!";
7430 return;
7431 }
7432
7433 //-trans
7434 char transmatname[256];
7435 static int trans_counter = 0;
7436 sprintf(transmatname, "%s-trans-%d", m_cab_material_name.c_str(), trans_counter++);
7437 Ogre::MaterialPtr transmat=mat->clone(transmatname);
7438 if (mat->getTechnique(0)->getNumPasses()>1) // If there's the "emissive pass", remove it from the 'transmat'
7439 {
7440 transmat->getTechnique(0)->removePass(1);
7441 }
7442 transmat->getTechnique(0)->getPass(0)->setAlphaRejectSettings(Ogre::CMPF_LESS_EQUAL, 128);
7443 transmat->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
7444 if (transmat->getTechnique(0)->getPass(0)->getNumTextureUnitStates()>0)
7445 {
7446 transmat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureFiltering(Ogre::TFO_NONE);
7447 }
7448 transmat->compile();
7449 m_cab_trans_material = transmat;
7450
7451 //-back
7452 char backmatname[256];
7453 static int back_counter = 0;
7454 sprintf(backmatname, "%s-back-%d", m_cab_material_name.c_str(), back_counter++);
7455 Ogre::MaterialPtr backmat=mat->clone(backmatname);
7456 if (mat->getTechnique(0)->getNumPasses()>1)// If there's the "emissive pass", remove it from the 'transmat'
7457 {
7458 backmat->getTechnique(0)->removePass(1);
7459 }
7460 if (transmat->getTechnique(0)->getPass(0)->getNumTextureUnitStates()>0)
7461 {
7462 backmat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setColourOperationEx(
7463 Ogre::LBX_SOURCE1,
7464 Ogre::LBS_MANUAL,
7465 Ogre::LBS_MANUAL,
7466 Ogre::ColourValue(0,0,0),
7467 Ogre::ColourValue(0,0,0)
7468 );
7469 }
7470 if (App::gfx_reduce_shadows->getBool())
7471 {
7472 backmat->setReceiveShadows(false);
7473 }
7474 backmat->compile();
7475
7476 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
7477
7478 char cab_material_name_cstr[1000] = {};
7479 strncpy(cab_material_name_cstr, m_cab_material_name.c_str(), 999);
7480 std::string mesh_name = this->ComposeName("mesh @ cab");
7481 FlexObj* cab_mesh =new FlexObj(
7482 m_actor->m_gfx_actor.get(),
7488 cab_material_name_cstr,
7489 mesh_name.c_str(),
7490 backmatname,
7491 transmatname
7492 );
7493
7494 Ogre::SceneNode* cab_scene_node = m_actor_grouping_scenenode->createChildSceneNode(this->ComposeName("cab"));
7495 Ogre::Entity *ec = nullptr;
7496 try
7497 {
7498 ec = App::GetGfxScene()->GetSceneManager()->createEntity(this->ComposeName("entity @ cab"), mesh_name);
7499 this->SetupNewEntity(ec, Ogre::ColourValue(0.5, 1, 0.5));
7500 if (ec)
7501 {
7502 cab_scene_node->attachObject(ec);
7503 }
7504
7505 // Process "emissive cab" materials
7506 auto search_itor = m_material_substitutions.find(m_cab_material_name);
7507 if (search_itor != m_material_substitutions.end())
7508 {
7509 m_actor->m_gfx_actor->RegisterCabMaterial(search_itor->second.material, m_cab_trans_material);
7510 }
7511 m_actor->m_gfx_actor->SetCabLightsActive(false); // Reset emissive lights to "off" state
7512
7513 m_actor->GetGfxActor()->RegisterCabMesh(ec, cab_scene_node, cab_mesh);
7514 }
7515 catch (Ogre::Exception& e)
7516 {
7517 this->AddMessage(Message::TYPE_ERROR, "error creating cab mesh: "+e.getFullDescription());
7518 if (ec)
7519 {
7520 App::GetGfxScene()->GetSceneManager()->destroyEntity(ec);
7521 }
7522 }
7523}
7524
7525void ActorSpawner::CreateMaterialFlare(int flareid, Ogre::MaterialPtr m)
7526{
7527 RoR::FlareMaterial binding;
7528 binding.flare_index = flareid;
7529 binding.mat_instance = m;
7530
7531 if (!m)
7532 return;
7533 Ogre::Technique* tech = m->getTechnique(0);
7534 if (!tech)
7535 return;
7536 Ogre::Pass* p = tech->getPass(0);
7537 if (!p)
7538 return;
7539 // save emissive colour and then set to zero (light disabled by default)
7540 binding.emissive_color = p->getSelfIllumination();
7541 p->setSelfIllumination(Ogre::ColourValue::ZERO);
7542
7543 m_actor->m_gfx_actor->m_flare_materials.push_back(binding);
7544}
7545
7547{
7548 // Media (Textures/Materials/meshes) can be either in AddonPart bundle or the vehicle bundle.
7549 // =========================================================================================
7550
7551 if (m_current_module->origin_addonpart)
7552 {
7553 return m_current_module->origin_addonpart->resource_group;
7554 }
7555 else
7556 {
7558 }
7559}
7560
7561void ActorSpawner::AssignManagedMaterialTexture(Ogre::TextureUnitState* tus, const std::string & mm_name, int media_id, const std::string& tex_name)
7562{
7563 // Helper for `ProcessManagedMaterial()`, resolves tweaks
7564 // ======================================================
7565
7566 try
7567 {
7568 ROR_ASSERT(tus);
7569 if (tus)
7570 {
7571 Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().load(
7572 TuneupUtil::getTweakedManagedMatMedia(m_actor->getWorkingTuneupDef(), mm_name, media_id, tex_name),
7573 TuneupUtil::getTweakedManagedMatMediaRG(m_actor->getWorkingTuneupDef(), mm_name, media_id, this->GetCurrentElementMediaRG()));
7574
7575 if (tex)
7576 {
7577 tus->setTexture(tex);
7578 }
7579 }
7580 }
7581 catch (...) // Exception is already logged by OGRE
7582 {
7583 }
7584}
7585
7587{
7588 // Create 'RTTTexture#' textures for 'guisettings/texturedashboard' (# is number starting with 1).
7589 // The first texturedashboard is rendered to RTTTexture1, the second one to RTTTexture2, and so on.
7590 // This must be done before loading the meshes (usually 'props') that use these textures.
7591 // -----------------------------------------------------------------------------------------------
7592
7593 for (auto& module: m_selected_modules)
7594 {
7595 for (auto& gs: module->guisettings)
7596 {
7597 if (gs.key == "texturedashboard")
7598 {
7599 RTTLayer* rttLayer = App::GetGuiManager()->GetRttLayerManager().CreateOrReuseRttLayer();
7600 rttLayer->setTextureSize(MyGUI::IntSize(1024, 1024));
7601 rttLayer->setTextureName(this->ComposeName("RTTTexture", (int)m_texturedashboard_rtt_layers.size() + 1));
7602 rttLayer->createRttTexture();
7603 m_texturedashboard_rtt_layers.push_back(rttLayer);
7604 }
7605 }
7606 }
7607}
7608
7610{
7611 // Set up the built-in "renderdash" material for use in meshes.
7612 // Must be done before 'props' are processed because those traditionally use it.
7613
7614 // Example content: https://github.com/RigsOfRods/rigs-of-rods/files/3044343/45fc291a9d2aa5faaa36cca6df9571cd6d1f1869_Actros_8x8-englisch.zip
7615 // ----------------------------------------------------------------------------------------------------------------------------------
7616
7617 // NOTE: The 'renderdash' material is defined as a dummy in file 'ror.material' which is loaded into every actor's resource group.
7618 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName("renderdash", m_custom_resource_group);
7619 ROR_ASSERT(mat && "could not find 'renderdash' material");
7620
7621 // Create a rendertarget-texture for the renderdash, but don't load the dashboard layout yet - to determine type and RPM, `ar_engine` and `ar_driveable` must be set up first.
7623 m_renderdash_rtt_layer->setTextureSize(MyGUI::IntSize(2048, 1024));
7626
7627 // Attach the generated RTT texture to the material
7628 ROR_ASSERT(m_renderdash_rtt_layer && "failed to create 'renderdash' RTT layer");
7629 Ogre::TexturePtr renderdash_tex = Ogre::TextureManager::getSingleton().getByName(m_renderdash_rtt_layer->getTextureName());
7630 ROR_ASSERT(renderdash_tex && "failed to find 'renderdash' texture");
7631 LOG(fmt::format("[RoR|DashBoard] Swapping placeholder 'dashtexture' for '{}'", m_renderdash_rtt_layer->getTextureName()));
7632 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTexture(renderdash_tex);
7633}
Ogre::ManualObject * CreateVideocameraDebugMesh()
Vehicle spawning logic.
System integration layer; inspired by OgreBites::ApplicationContext.
Central state/object manager and communications hub.
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
#define PARSEREAL(x)
Definition Application.h:60
#define TOSTRING(x)
Definition Application.h:58
void LOG(const char *msg)
Legacy alias - formerly a macro.
float frand()
Definition ApproxMath.h:31
Bit operations.
#define BITMASK_SET_0(VAR, FLAGS)
Definition BitFlags.h:16
#define BITMASK_IS_1(VAR, FLAGS)
Definition BitFlags.h:14
#define BITMASK_IS_0(VAR, FLAGS)
Definition BitFlags.h:13
#define BITMASK_SET_1(VAR, FLAGS)
Definition BitFlags.h:17
uint32_t BitMask_t
Definition BitFlags.h:7
A database of user-installed content alias 'mods' (vehicles, terrains...)
#define _L
Game state manager and message-queue provider.
Manager for all visuals belonging to a single actor.
Handles controller inputs from player.
static const int MAX_CABS
maximum number of cabs per actor
static const int MAX_CLIGHTS
See RoRnet::Lightmask and enum events in InputEngine.h.
static const int MAX_CAMERAS
maximum number of cameras per actor
static const int MAX_AEROENGINES
maximum number of aero engines per actor
static const int MAX_WHEELS
maximum number of wheels per actor
static const int MAX_SUBMESHES
maximum number of submeshes per actor
static const int MAX_COMMANDS
maximum number of commands per actor
static const int MAX_SOUNDSCRIPTS_PER_TRUCK
maximum number of soundsscripts per actor
static const int MAX_TEXCOORDS
maximum number of texture coordinates per actor
static const int MAX_CAMERARAIL
maximum number of camera rail points
static const int MAX_SCREWPROPS
maximum number of boat screws per actor
#define SOUND_START(_ACTOR_, _TRIG_)
#define SOUND_MODULATE(_ACTOR_, _MOD_, _VALUE_)
static int counter
torquecurve loader.
The vehicle tuning system; applies addonparts and user overrides to vehicles.
Simple waypoint AI.
void setCastShadows(bool b)
Ogre::SceneNode * GetSceneNode()
Definition MeshObject.h:44
void setMaterialName(Ogre::String m)
Ogre::Entity * getEntity()
Definition MeshObject.h:43
Abstract node ID (numbered or named) Node name is always available.
Definition RigDef_Node.h:45
Legacy parser resolved references on-the-fly and the condition to check named nodes was "are there an...
Definition RigDef_Node.h:78
bool IsValidAnyState() const
unsigned int Num() const
Definition RigDef_Node.h:95
std::string ToString() const
std::string const & Str() const
Definition RigDef_Node.h:94
bool alb_mode
Anti-lock brake state; Enabled? {1/0}.
Definition Actor.h:445
float m_odometer_user
GUI state.
Definition Actor.h:703
wing_t * ar_wings
Definition Actor.h:395
int ar_num_screwprops
Definition Actor.h:426
int ar_num_wings
Definition Actor.h:396
int m_num_command_beams
TODO: Remove! Spawner context only; likely unused feature.
Definition Actor.h:704
EnginePtr ar_engine
Definition Actor.h:467
float ar_collision_range
Physics attr.
Definition Actor.h:526
std::vector< Ogre::AxisAlignedBox > ar_predicted_coll_bounding_boxes
Definition Actor.h:413
std::vector< float > ar_minimass
minimum node mass in Kg - can be scaled in-game via NBUtil
Definition Actor.h:372
TransferCase * m_transfer_case
Physics.
Definition Actor.h:694
float ar_guisettings_speedo_max_kph
Definition Actor.h:555
float ar_dry_mass
User-defined (editable via NBUtil); from 'globals' arg#1 - default for all nodes.
Definition Actor.h:355
int ar_airbrake_intensity
Physics state; values 0-5.
Definition Actor.h:520
int m_proped_wheel_pairs[MAX_WHEELS]
Physics attr; For inter-differential locking.
Definition Actor.h:666
NodeNum_t ar_camera_node_dir[MAX_CAMERAS]
Physics attr; 'camera' = frame of reference; back node.
Definition Actor.h:480
collcab_rate_t ar_intra_collcabrate[MAX_CABS]
Definition Actor.h:433
std::vector< float > ar_nodes_override_loadweights
'nodes': 'l' flag and number.
Definition Actor.h:369
std::vector< bool > ar_beams_invisible
Used only by the exporter (for rendering, invisible beams simply get no mesh).
Definition Actor.h:386
std::vector< ropable_t > ar_ropables
Definition Actor.h:399
CmdKeyArray ar_command_key
BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
Definition Actor.h:404
wheel_t ar_wheels[MAX_WHEELS]
Definition Actor.h:419
std::vector< rope_t > ar_ropes
Definition Actor.h:398
DashBoardManagerPtr ar_dashboard
Gfx state; Dashboard manager (HUD element manager)
Definition Actor.h:563
float m_odometer_total
GUI state.
Definition Actor.h:702
std::vector< wheeldetacher_t > ar_wheeldetachers
Definition Actor.h:409
bool ar_camera_node_roll_inv[MAX_CAMERAS]
Physics attr; 'camera' = frame of reference; indicates roll node is right instead of left.
Definition Actor.h:482
int ar_num_camera_rails
Definition Actor.h:439
float tc_pulse_time
Definition Actor.h:546
node_t * ar_nodes
Definition Actor.h:365
int ar_buoycabs[MAX_CABS]
Definition Actor.h:435
GfxActor * GetGfxActor()
Definition Actor.h:344
std::vector< BitMask_t > ar_nodes_options
merged options from 'nodes' and 'set_node_defaults'
Definition Actor.h:371
int m_num_wheel_diffs
Physics attr.
Definition Actor.h:693
std::vector< bool > ar_beams_user_defined
True for 'beams', false for wheels/cinecam/hooknode/wings/rotators etc...
Definition Actor.h:387
int m_num_proped_wheels
Physics attr, filled at spawn - Number of propelled wheels.
Definition Actor.h:667
std::vector< Ogre::SceneNode * > m_deletion_scene_nodes
For unloading vehicle; filled at spawn.
Definition Actor.h:665
std::vector< tie_t > ar_ties
Definition Actor.h:400
int m_num_axle_diffs
Physics attr.
Definition Actor.h:691
float ar_posnode_spawn_height
Definition Actor.h:484
ground_model_t * ar_submesh_ground_model
Definition Actor.h:508
std::vector< SlideNode > m_slidenodes
all the SlideNodes available on this actor
Definition Actor.h:662
int * ar_nodes_id
Number in truck file, -1 for nodes generated by wheels/cinecam.
Definition Actor.h:366
NodeNum_t ar_camera_node_roll[MAX_CAMERAS]
Physics attr; 'camera' = frame of reference; left node.
Definition Actor.h:481
Differential * m_axle_diffs[1+MAX_WHEELS/2]
Physics.
Definition Actor.h:690
ScrewpropPtr ar_screwprops[MAX_SCREWPROPS]
Definition Actor.h:425
bool m_disable_smoke
Stops/starts smoke particles (i.e. exhausts, turbojets).
Definition Actor.h:759
NodeNum_t ar_main_camera_node_pos
Sim attr; ar_camera_node_pos[0] >= 0 ? ar_camera_node_pos[0] : 0.
Definition Actor.h:476
bool ar_is_police
Gfx/sfx attr.
Definition Actor.h:603
std::vector< Airbrake * > ar_airbrakes
Definition Actor.h:403
bool m_beam_break_debug_enabled
Logging state.
Definition Actor.h:755
std::vector< float > ar_nodes_default_loadweights
'set_node_defaults': load weight.
Definition Actor.h:368
NodeNum_t ar_camera_rail[MAX_CAMERARAIL]
Nodes defining camera-movement spline.
Definition Actor.h:438
int ar_buoycab_types[MAX_CABS]
Definition Actor.h:436
float m_fusealge_width
Physics attr; defined in truckfile.
Definition Actor.h:701
Airfoil * m_fusealge_airfoil
Physics attr; defined in truckfile.
Definition Actor.h:698
std::vector< Ogre::AxisAlignedBox > ar_collision_bounding_boxes
smart bounding boxes, used for determining the state of an actor (every box surrounds only a subset o...
Definition Actor.h:412
soundsource_t ar_soundsources[MAX_SOUNDSCRIPTS_PER_TRUCK]
Definition Actor.h:421
node_t * m_fusealge_front
Physics attr; defined in truckfile.
Definition Actor.h:699
std::vector< flare_t > ar_flares
Definition Actor.h:402
bool tc_mode
Enabled?
Definition Actor.h:545
std::vector< Ogre::Entity * > m_deletion_entities
For unloading vehicle; filled at spawn.
Definition Actor.h:664
std::unique_ptr< Buoyance > m_buoyance
Definition Actor.h:531
int ar_num_cabs
Definition Actor.h:429
bool alb_nodash
Anti-lock brake attribute: Hide the dashboard indicator?
Definition Actor.h:448
bool sl_enabled
Speed limiter;.
Definition Actor.h:458
ExtCameraMode ar_extern_camera_mode
Definition Actor.h:460
float ar_load_mass
User-defined (editable via NBUtil); from 'globals' arg#2 - only applies to nodes with 'l' flag.
Definition Actor.h:357
node_t * m_fusealge_back
Physics attr; defined in truckfile.
Definition Actor.h:700
NodeNum_t ar_exhaust_dir_node
Old-format exhaust (one per vehicle) backwards direction node.
Definition Actor.h:463
int ar_masscount
Calculated; Number of nodes loaded with l option.
Definition Actor.h:359
PointColDetector * m_inter_point_col_detector
Physics.
Definition Actor.h:670
BitMask_t m_flaregroups_no_import
RoRnet::Lightmask.
Definition Actor.h:742
std::vector< RailGroup * > m_railgroups
all the available RailGroups for this actor
Definition Actor.h:663
float m_handbrake_force
Physics attr; defined in truckfile.
Definition Actor.h:697
NodeNum_t ar_extern_camera_node
Definition Actor.h:461
Ogre::Vector3 ar_origin
Physics state; base position for softbody nodes.
Definition Actor.h:473
float ar_guisettings_shifter_anim_time
Definition Actor.h:556
int getInstanceId()
Definition Actor.h:306
ActorState ar_state
Definition Actor.h:567
float ar_brake_force
Physics attr; filled at spawn.
Definition Actor.h:471
std::unique_ptr< GfxActor > m_gfx_actor
Definition Actor.h:661
NodeNum_t ar_exhaust_pos_node
Old-format exhaust (one per vehicle) emitter node.
Definition Actor.h:462
float tc_timer
Definition Actor.h:550
AeroEnginePtr ar_aeroengines[MAX_AEROENGINES]
Definition Actor.h:423
std::string getTruckFileName()
Definition Actor.h:302
int ar_num_shocks
Number of shock absorbers.
Definition Actor.h:391
float ar_total_mass
Calculated; total mass in Kg.
Definition Actor.h:360
int ar_num_aeroengines
Definition Actor.h:424
float ar_anim_shift_timer
For 'animator' with flag 'shifter'.
Definition Actor.h:451
float alb_ratio
Anti-lock brake attribute: Regulating force.
Definition Actor.h:443
float cc_target_speed_lower_limit
Cruise Control.
Definition Actor.h:456
int ar_num_beams
Definition Actor.h:384
std::vector< authorinfo_t > authors
Definition Actor.h:397
bool m_beam_deform_debug_enabled
Logging state.
Definition Actor.h:756
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition Actor.h:464
bool alb_notoggle
Anti-lock brake attribute: Disable in-game toggle?
Definition Actor.h:449
std::vector< PropAnimKeyState > m_prop_anim_key_states
Definition Actor.h:712
bool m_has_axles_section
Temporary (legacy parsing helper) until central diffs are implemented.
Definition Actor.h:709
rotator_t * ar_rotators
Definition Actor.h:393
bool ar_minimass_skip_loaded_nodes
Definition Actor.h:378
std::string * ar_nodes_name
Name in truck file, only if defined with 'nodes2'.
Definition Actor.h:367
float cc_target_rpm
Cruise Control.
Definition Actor.h:454
NodeNum_t ar_camera_node_pos[MAX_CAMERAS]
Physics attr; 'camera' = frame of reference; origin node.
Definition Actor.h:479
int ar_num_cinecams
Sim attr;.
Definition Actor.h:469
GfxFlaresMode m_flares_mode
Snapshot of cvar 'gfx_flares_mode' on spawn.
Definition Actor.h:740
collcab_rate_t ar_inter_collcabrate[MAX_CABS]
Definition Actor.h:432
TyrePressure & getTyrePressure()
Definition Actor.h:294
bool cc_mode
Cruise Control.
Definition Actor.h:452
Differential * m_wheel_diffs[MAX_WHEELS/2]
Physics.
Definition Actor.h:692
bool ar_hide_in_actor_list
Hide in list of spawned actors (available in top menubar). Useful for fixed-place machinery,...
Definition Actor.h:440
float alb_pulse_time
Anti-lock brake attribute;.
Definition Actor.h:446
int ar_cabs[MAX_CABS *3]
Definition Actor.h:427
int ar_num_collcabs
Definition Actor.h:434
int ar_num_nodes
Definition Actor.h:380
Skidmark * m_skid_trails[MAX_WHEELS *2]
Definition Actor.h:706
bool tc_pulse_state
Definition Actor.h:547
float alb_minspeed
Anti-lock brake attribute;.
Definition Actor.h:444
Ogre::Quaternion ar_main_camera_dir_corr
Sim attr;.
Definition Actor.h:475
std::string ar_guisettings_speedo_material
Used on 'renderdash'.
Definition Actor.h:558
bool m_has_command_beams
Physics attr;.
Definition Actor.h:753
std::vector< std::string > m_description
Definition Actor.h:711
std::string ar_guisettings_tacho_material
Used on 'renderdash'.
Definition Actor.h:557
VehicleAIPtr ar_vehicle_ai
Definition Actor.h:485
float ar_anim_previous_crank
For 'animator' with flag 'torque'.
Definition Actor.h:442
std::string ar_guisettings_help_material
Used on 'renderdash'.
Definition Actor.h:559
std::string getTruckFileResourceGroup()
Definition Actor.cpp:5020
bool tc_notoggle
Disable in-game toggle?
Definition Actor.h:549
AutopilotPtr ar_autopilot
Definition Actor.h:470
float tc_ratio
Regulating force.
Definition Actor.h:544
float alb_timer
Anti-lock brake state;.
Definition Actor.h:450
int ar_num_soundsources
Definition Actor.h:422
int ar_num_contacters
Total number of nodes which can selfcontact cabs.
Definition Actor.h:418
std::vector< hydrobeam_t > ar_hydros
Definition Actor.h:430
int ar_nodes_name_top_length
For nicely formatted diagnostic output.
Definition Actor.h:379
NodeNum_t ar_main_camera_node_dir
Sim attr; ar_camera_node_dir[0] >= 0 ? ar_camera_node_dir[0] : 0.
Definition Actor.h:477
int ar_num_cameras
Definition Actor.h:474
std::vector< hook_t > ar_hooks
Definition Actor.h:401
bool ar_collision_relevant
Physics state;.
Definition Actor.h:602
PointColDetector * m_intra_point_col_detector
Physics.
Definition Actor.h:671
int ar_num_wheels
Definition Actor.h:420
CacheEntryPtr m_used_skin_entry
Optional, only graphics.
Definition Actor.h:720
shock_t * ar_shocks
Shock absorbers.
Definition Actor.h:390
bool tc_nodash
Hide the dashboard indicator?
Definition Actor.h:548
NodeNum_t ar_cinecam_node[MAX_CAMERAS]
Sim attr; Cine-camera node indexes.
Definition Actor.h:468
Ogre::Vector3 * ar_nodes_spawn_offsets
Relative positions (incl. Tuning system tweaks) from the definition file, for spawn-like resetting (i...
Definition Actor.h:370
bool alb_pulse_state
Anti-lock brake state;.
Definition Actor.h:447
CacheEntryPtrVec m_used_addonpart_entries
Optional, assigned by player via Tuning menu (.tuneup files).
Definition Actor.h:721
float m_avg_proped_wheel_radius
Physics attr, filled at spawn - Average proped wheel radius.
Definition Actor.h:668
bool m_trigger_debug_enabled
Logging state.
Definition Actor.h:757
beam_t * ar_beams
Definition Actor.h:383
int ar_num_buoycabs
Definition Actor.h:437
CacheEntryPtrVec m_used_assetpack_entries
Optional, specified by mod author in truck file via 'assetpacks' section.
Definition Actor.h:722
int ar_collcabs[MAX_CABS]
Definition Actor.h:431
bool ar_has_active_shocks
Are there active stabilizer shocks?
Definition Actor.h:392
int ar_num_contactable_nodes
Total number of nodes which can contact ground or cabs.
Definition Actor.h:417
TuneupDefPtr m_working_tuneup_def
Each actor gets unique instance, even if loaded from .tuneup file in modcache.
Definition Actor.h:717
TuneupDefPtr & getWorkingTuneupDef()
Definition Actor.cpp:5045
bool ar_guisettings_use_engine_max_rpm
Definition Actor.h:554
float cc_target_speed
Cruise Control.
Definition Actor.h:455
float sl_speed_limit
Speed limiter;.
Definition Actor.h:459
int ar_num_rotators
Definition Actor.h:394
ActorType ar_driveable
Sim attr; marks vehicle type and features.
Definition Actor.h:466
NodeNum_t ar_main_camera_node_roll
Sim attr; ar_camera_node_roll[0] >= 0 ? ar_camera_node_roll[0] : 0.
Definition Actor.h:478
bool cc_can_brake
Cruise Control.
Definition Actor.h:453
std::vector< UniqueCommandKeyPair > ar_unique_commandkey_pairs
UI helper for displaying command control keys to user (must be built at spawn).
Definition Actor.h:415
RoR::CmdKeyInertiaConfig & GetInertiaConfig()
void ProcessAntiLockBrakes(RigDef::AntiLockBrakes &def)
std::map< std::string, Ogre::MaterialPtr > m_managed_materials
void CreateMeshWheelVisuals(WheelID_t wheel_id, NodeNum_t base_node_index, NodeNum_t axis_node_1_index, NodeNum_t axis_node_2_index, unsigned int num_rays, WheelSide side, Ogre::String mesh_name, Ogre::String mesh_rg, Ogre::String material_name, Ogre::String material_rg, float rim_radius)
static void AddSoundSource(ActorPtr const &vehicle, SoundScriptInstancePtr sound_script, NodeNum_t node_index, int type=-2)
void ConfigureAssetPacks(ActorPtr actor)
void ConfigureAddonParts(ActorPtr actor)
static bool CheckSoundScriptLimit(ActorPtr const &vehicle, unsigned int count)
void InitBeam(beam_t &beam, node_t *node_1, node_t *node_2)
void ProcessFlare2(RigDef::Flare2 &def)
RigDef::MaterialFlareBinding * FindFlareBindingForMaterial(std::string const &material_name)
Returns NULL if none found.
unsigned int AddTyreBeam(RigDef::Wheel2 &wheel_2_def, node_t *node_1, node_t *node_2)
std::list< std::shared_ptr< RigDef::Document::Module > > m_selected_modules
void ProcessScrewprop(RigDef::Screwprop &def)
RTTLayer * m_renderdash_rtt_layer
The built-in 'renderdash' material.
bool CheckTexcoordLimit(unsigned int count)
bool AssignWheelToAxle(int &_out_axle_wheel, node_t *axis_node_1, node_t *axis_node_2)
Finds wheel with given axle nodes and returns it's index.
void ProcessFixedNode(RigDef::Node::Ref node_ref)
void CalculateBeamLength(beam_t &beam)
void SetBeamDeformationThreshold(beam_t &beam, std::shared_ptr< RigDef::BeamDefaults > beam_defaults)
void _ProcessKeyInertia(RigDef::Inertia &inertia, RigDef::Inertia &inertia_defaults, RoR::CmdKeyInertia &contract_key, RoR::CmdKeyInertia &extend_key)
void GetWheelAxisNodes(RigDef::BaseWheel &def, node_t *&out_node_1, node_t *&out_node_2)
void ProcessGlobals(RigDef::Globals &def)
std::vector< CabSubmesh > m_oldstyle_cab_submeshes
void ProcessHydro(RigDef::Hydro &def)
void ValidateRotator(int id, int axis1, int axis2, NodeNum_t *nodes1, NodeNum_t *nodes2)
CustomMaterial::MirrorPropType m_curr_mirror_prop_type
node_t & GetAndInitFreeNode(Ogre::Vector3 const &position)
void UpdateCollcabContacterNodes()
void ProcessRotator2(RigDef::Rotator2 &def)
void ProcessTractionControl(RigDef::TractionControl &def)
static void AddSoundSourceInstance(ActorPtr const &vehicle, Ogre::String const &sound_script_name, int node_index, int type=-2)
void InitNode(node_t &node, Ogre::Vector3 const &position)
void ProcessMeshWheel2(RigDef::MeshWheel2 &def)
void SetBeamSpring(beam_t &beam, float spring)
void ProcessMinimass(RigDef::Minimass &def)
void ProcessEngturbo(RigDef::Engturbo &def)
void CreateWheelSkidmarks(WheelID_t wheel_index)
void ProcessProp(RigDef::Prop &def)
Resource group override is used with addonparts.
bool m_generate_wing_position_lights
node_t * GetNodePointerOrThrow(RigDef::Node::Ref const &node_ref)
void ConfigureSections(Ogre::String const &sectionconfig, RigDef::DocumentPtr def)
void BuildAeroEngine(NodeNum_t ref_node_index, NodeNum_t back_node_index, NodeNum_t blade_1_node_index, NodeNum_t blade_2_node_index, NodeNum_t blade_3_node_index, NodeNum_t blade_4_node_index, NodeNum_t couplenode_index, bool is_turboprops, Ogre::String const &airfoil, float power, float pitch)
void ProcessWheelDetacher(RigDef::WheelDetacher &def)
node_t * GetNodePointer(RigDef::Node::Ref const &node_ref)
void ProcessSlidenode(RigDef::SlideNode &def)
void ProcessSoundSource2(RigDef::SoundSource2 &def)
void ProcessExtCamera(RigDef::ExtCamera &def)
void ProcessSoundSource(RigDef::SoundSource &def)
Ogre::ParticleSystem * CreateParticleSystem(std::string const &name, std::string const &template_name)
Ogre::MaterialPtr m_managedmat_placeholder_template
An 'error marker' material (bright magenta) to generate managedmaterial placeholders from.
void SetBeamDamping(beam_t &beam, float damping)
NodeNum_t RegisterNode(RigDef::Node::Id &id)
void ProcessBeam(RigDef::Beam &def)
void SetCurrentKeyword(RigDef::Keyword keyword)
void ProcessRope(RigDef::Rope &def)
void ProcessParticle(RigDef::Particle &def)
unsigned int _SectionWheels2AddBeam(RigDef::Wheel2 &wheel_2_def, node_t *node_1, node_t *node_2)
void ProcessFlare3(RigDef::Flare3 &def)
std::map< std::string, CustomMaterial > m_material_substitutions
Maps original material names (shared) to their actor-specific substitutes; There's 1 substitute per 1...
void ProcessCruiseControl(RigDef::CruiseControl &def)
void ProcessHook(RigDef::Hook &def)
NodeNum_t GetNodeIndexOrThrow(RigDef::Node::Ref const &id)
void ProcessTurbojet(RigDef::Turbojet &def)
void ProcessExhaust(RigDef::Exhaust &def)
void ProcessFlexbody(RigDef::Flexbody &def)
void ProcessRopable(RigDef::Ropable &def)
void ProcessShock(RigDef::Shock &def)
unsigned int AddWheelBeam(node_t *node_1, node_t *node_2, float spring, float damping, std::shared_ptr< RigDef::BeamDefaults > beam_defaults, float max_contraction=-1.f, float max_extension=-1.f, BeamType type=BEAM_NORMAL)
'wheels', 'meshwheels', 'meshwheels2'
void CreateFlexBodyWheelVisuals(WheelID_t wheel_id, NodeNum_t node_base_index, NodeNum_t axis_node_1, NodeNum_t axis_node_2, int num_rays, float radius, WheelSide side, std::string rim_mesh_name, std::string rim_mesh_rg, std::string tire_mesh_name, std::string tire_mesh_rg)
void ProcessTurboprop2(RigDef::Turboprop2 &def)
void SetupNewEntity(Ogre::Entity *e, Ogre::ColourValue simple_color)
Full texture and material setup.
void ProcessAuthor(RigDef::Author &def)
Ogre::MaterialPtr FindOrCreateCustomizedMaterial(const std::string &mat_lookup_name, const std::string &mat_lookup_rg)
void ProcessAxle(RigDef::Axle &def)
void ProcessWheel(RigDef::Wheel &def)
void AdjustNodeBuoyancy(node_t &node, RigDef::Node &node_def, std::shared_ptr< RigDef::NodeDefaults > defaults)
For user-defined nodes.
Ogre::MaterialPtr m_simple_material_base
void ProcessGuiSettings(RigDef::GuiSettings &def)
std::string GetSubmeshGroundmodelName()
void ProcessTransferCase(RigDef::TransferCase &def)
Ogre::SceneNode * m_flares_parent_scenenode
this isn't used for moving/hiding things, just helps developers inspect the scene graph.
beam_t & GetBeam(unsigned int index)
beam_t * FindBeamInRig(NodeNum_t node_a, NodeNum_t node_b)
void SetBeamStrength(beam_t &beam, float strength)
std::map< Ogre::String, unsigned int > m_named_nodes
void CreateWheelVisuals(WheelID_t wheel_id, NodeNum_t node_base_index, unsigned int def_num_rays, Ogre::String const &face_material_name, Ogre::String const &face_material_rg, Ogre::String const &band_material_name, Ogre::String const &band_material_rg, bool separate_rim, float rim_ratio=1.f)
Ogre::MaterialPtr m_cab_trans_material
void AssignManagedMaterialTexture(Ogre::TextureUnitState *tus, const std::string &mm_name, int media_id, const std::string &tex_name)
Helper for ProcessManagedMaterial()
bool CheckCameraRailLimit(unsigned int count)
void ProcessWheel2(RigDef::Wheel2 &def)
bool CollectNodesFromRanges(std::vector< RigDef::Node::Range > &node_ranges, std::vector< NodeNum_t > &out_node_indices)
Parses list of node-ranges into list of individual nodes.
void ProcessScript(RigDef::Script &def)
unsigned int AddWheelRimBeam(RigDef::Wheel2 &wheel_2_def, node_t *node_1, node_t *node_2)
bool CheckScrewpropLimit(unsigned int count)
void CreateMirrorPropVideoCam(Ogre::MaterialPtr custom_mat, CustomMaterial::MirrorPropType type, Ogre::SceneNode *prop_scenenode)
void ProcessRailGroup(RigDef::RailGroup &def)
float ComputeWingArea(Ogre::Vector3 const &ref, Ogre::Vector3 const &x, Ogre::Vector3 const &y, Ogre::Vector3 const &aref)
Ogre::SceneNode * m_particles_parent_scenenode
this isn't used for moving/hiding things, just helps developers inspect the scene graph.
void ProcessContacter(RigDef::Node::Ref &node_ref)
void ProcessSpeedLimiter(RigDef::SpeedLimiter &def)
void ProcessEngine(RigDef::Engine &def)
ActorSpawnState m_state
void ProcessWing(RigDef::Wing &def)
bool CheckAxleLimit(unsigned int count)
std::shared_ptr< RigDef::Document::Module > m_current_module
For resolving addonparts.
RailGroup * CreateRail(std::vector< RigDef::Node::Range > &node_ranges)
void ProcessSubmesh(RigDef::Submesh &def)
void ProcessAnimator(RigDef::Animator &def)
void ProcessNode(RigDef::Node &def)
RigDef::DocumentPtr m_file
void ProcessCinecam(RigDef::Cinecam &def)
void ProcessFlaregroupNoImport(RigDef::FlaregroupNoImport &def)
void ProcessManagedMaterial(RigDef::ManagedMaterial &def)
void ProcessShock3(RigDef::Shock3 &def)
void CreateBeamVisuals(beam_t &beam, int beam_index, bool visible, std::shared_ptr< RigDef::BeamDefaults > const &beam_defaults, std::string material_override="")
beam_t & GetAndInitFreeBeam(node_t &node_1, node_t &node_2)
void _ProcessSimpleInertia(RigDef::Inertia &def, RoR::SimpleInertia &obj)
void ProcessFusedrag(RigDef::Fusedrag &def)
void CreateMaterialFlare(int flare_index, Ogre::MaterialPtr mat)
void ProcessCommand(RigDef::Command2 &def)
std::string m_custom_resource_group
Ogre::SceneNode * m_wheels_parent_scenenode
this isn't used for moving/hiding things, just helps developers inspect the scene graph.
Ogre::SceneNode * m_props_parent_scenenode
this isn't used for moving/hiding things, just helps developers inspect the scene graph.
ActorMemoryRequirements m_memory_requirements
void ProcessCamera(RigDef::Camera &def)
bool CheckSubmeshLimit(unsigned int count)
beam_t & AddBeam(node_t &node_1, node_t &node_2, std::shared_ptr< RigDef::BeamDefaults > &defaults, int detacher_group)
void ProcessTie(RigDef::Tie &def)
void ProcessFlexBodyWheel(RigDef::FlexBodyWheel &def)
void ProcessPistonprop(RigDef::Pistonprop &def)
void ProcessInterAxle(RigDef::InterAxle &def)
void ProcessBrakes(RigDef::Brakes &def)
void CalcMemoryRequirements(ActorMemoryRequirements &req, RigDef::Document::Module *module_def)
void BuildWheelBeams(unsigned int num_rays, NodeNum_t base_node_index, node_t *axis_node_1, node_t *axis_node_2, float tyre_spring, float tyre_damping, float rim_spring, float rim_damping, std::shared_ptr< RigDef::BeamDefaults > beam_defaults, RigDef::Node::Ref const &rigidity_node_id, float max_extension=0.f)
'wheels', 'meshwheels'
void CreateVideoCamera(RigDef::VideoCamera *def)
void ProcessMeshWheel(RigDef::MeshWheel &def)
void ProcessCustomDashInputs(RigDef::CustomDashboardInput &def)
void ProcessLockgroup(RigDef::Lockgroup &lockgroup)
RoR::FlexFactory m_flex_factory
void ProcessHelp(RigDef::Help &def)
std::string ComposeName(const std::string &object, int number=-1)
Creates name containing actor ID token, i.e. "Object#1 (filename.truck [Instance ID 1])".
void ProcessAirbrake(RigDef::Airbrake &def)
void ProcessRotator(RigDef::Rotator &def)
std::vector< CabTexcoord > m_oldstyle_cab_texcoords
static void SetupDefaultSoundSources(ActorPtr const &actor)
Ogre::MaterialPtr CreateSimpleMaterial(Ogre::ColourValue color)
bool CheckCabLimit(unsigned int count)
void AddBaseFlare(RigDef::FlareBase &flare_def)
void ProcessShock2(RigDef::Shock2 &def)
Ogre::Vector3 m_spawn_position
RigDef::VideoCamera * FindVideoCameraByMaterial(std::string const &material_name)
Returns NULL if none found.
std::string m_cab_material_name
Original name defined in truckfile/globals.
Ogre::MaterialPtr InstantiateManagedMaterial(Ogre::String const &rg_name, Ogre::String const &source_name, Ogre::String const &clone_name)
shock_t & GetFreeShock()
void ProcessTrigger(RigDef::Trigger &def)
void ProcessCameraRail(RigDef::CameraRail &def)
Ogre::SceneNode * m_curr_mirror_prop_scenenode
std::vector< RTTLayer * > m_texturedashboard_rtt_layers
layouts specified in 'guisettings/texturedashboard'.
void ProcessCollisionBox(RigDef::CollisionBox &def)
NodeNum_t ResolveNodeRef(RigDef::Node::Ref const &node_ref, bool optional=false)
void ProcessTorqueCurve(RigDef::TorqueCurve &def)
RigDef::Keyword m_current_keyword
For error reports.
std::string GetCurrentElementMediaRG()
Where to load media from (the addonpart's bundle or vehicle's bundle?)
void ProcessDescription(Ogre::String const &line)
void AddMessage(Message type, Ogre::String const &text)
Maintenance.
void AddExhaust(NodeNum_t emitter_node_idx, NodeNum_t direction_node_idx)
void BuildWheelObjectAndNodes(WheelID_t wheel_id, unsigned int num_rays, node_t *axis_node_1, node_t *axis_node_2, node_t *reference_arm_node, unsigned int reserve_nodes, unsigned int reserve_beams, float wheel_radius, WheelPropulsion propulsion, WheelBraking braking, std::shared_ptr< RigDef::NodeDefaults > node_defaults, float wheel_mass, float wheel_width=-1.f)
Sets up wheel and builds nodes for sections 'wheels', 'meshwheels' and 'meshwheels2'.
void ProcessCollisionRange(RigDef::CollisionRange &def)
Ogre::SceneNode * m_actor_grouping_scenenode
Topmost common parent; this isn't used for moving things, just helps developers inspect the scene gra...
bool CheckAeroEngineLimit(unsigned int count)
void ProcessEngoption(RigDef::Engoption &def)
NOTE: Modcache processes this format directly using RoR::GenericDocument, see RoR::CacheSystem::FillA...
void ResolveUnwantedAndTweakedElements(TuneupDefPtr &tuneup, CacheEntryPtr &addonpart_entry)
Evaluates 'addonpart_unwanted_*' elements, respecting 'protected_*' directives in the tuneup.
static void ResetUnwantedAndTweakedElements(TuneupDefPtr &tuneup)
virtual float getRadius()=0
virtual int getNoderef()=0
virtual AeroEngineType getType()=0
node_t * nodex
Definition AirBrake.h:51
Ogre::MeshPtr msh
Definition AirBrake.h:48
node_t * nodey
Definition AirBrake.h:52
Ogre::SceneNode * snode
Definition AirBrake.h:49
node_t * noderef
Definition AirBrake.h:50
Ogre::Vector3 offset
gfx attribute
Definition AirBrake.h:54
Ogre::Entity * ec
Definition AirBrake.h:59
Represents an airfoil http://en.wikipedia.org/wiki/Airfoil.
Definition Airfoil.h:32
Ogre::RenderWindow * CreateCustomRenderWindow(std::string const &name, int width, int height)
void setInertialReferences(node_t *refl, node_t *refr, node_t *refb, node_t *refc)
Definition AutoPilot.cpp:88
T getEnum() const
Definition CVar.h:99
bool getBool() const
Definition CVar.h:98
int getInt() const
Definition CVar.h:97
Ogre::String fname
filename
Definition CacheSystem.h:67
Ogre::String dname
name parsed from the file
Definition CacheSystem.h:70
SkinDocumentPtr skin_def
Cached skin info, added on first use or during cache rebuild.
Definition CacheSystem.h:92
Ogre::String resource_group
Resource group of the loaded bundle. Empty if not loaded yet.
Definition CacheSystem.h:89
CacheEntryPtr FindEntryByFilename(RoR::LoaderType type, bool partial, const std::string &_filename_maybe_bundlequalified)
Returns NULL if none found; "Bundle-qualified" format also specifies the ZIP/directory in modcache,...
void LoadResource(CacheEntryPtr &t)
Loads the associated resource bundle if not already done.
Ogre::Camera * GetCamera()
Designed to be run in physics loop (2khz)
int SetCmdKeyDelay(RoR::CmdKeyInertiaConfig &cfg, float start_delay, float stop_delay, std::string start_function, std::string stop_function)
ground_model_t * defaultgm
Definition Collisions.h:174
@ CONSOLE_MSGTYPE_ACTOR
Parsing/spawn/simulation messages for actors.
Definition Console.h:63
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_NOTICE
Definition Console.h:51
@ CONSOLE_SYSTEM_WARNING
Definition Console.h:53
int getLinkIDForName(Ogre::String &str)
int registerCustomInput(Ogre::String name, int dataType)
DashBoard * loadDashBoard(const std::string &filename, BitMask_t flags, RTTLayer *rttLayer)
int di_idx_1
array location of wheel / axle 1
int di_idx_2
array location of wheel / axle 2
void AddDifferentialType(DiffType diff)
A land vehicle engine + transmission.
Definition Engine.h:43
char getEngineType() const
't' = truck (default), 'c' = car, 'e' = electric car ('engoption' attr #2)
Definition Engine.h:70
char m_engine_type
't' = truck (default), 'c' = car ('engoption' attr #2)
Definition Engine.h:209
NodeNum_t m_air_intake_node
Node number of the air intake node (if any) - defaults to cinecam ref node (classic behavior) but can...
Definition Engine.h:228
bool m_engine_has_air
Engine attribute.
Definition Engine.h:205
void SetEngineOptions(float einertia, char etype, float eclutch, float ctime, float stime, float pstime, float irpm, float srpm, float maximix, float minimix, float ebraking)
Definition Engine.cpp:196
TorqueCurve * getTorqueCurve()
Definition Engine.h:87
void SetTurboOptions(int type, float tinertiaFactor, int nturbos, float param1, float param2, float param3, float param4, float param5, float param6, float param7, float param8, float param9, float param10, float param11)
Definition Engine.cpp:128
void setAutoMode(SimGearboxMode mode)
Definition Engine.cpp:857
bool hasTurbo() const
Definition Engine.h:73
float m_turbo_inertia_factor
Definition Engine.h:261
void enableInducedDrag(float span, float area, bool l)
void addwash(int propid, float ratio)
Flexbody = A deformable mesh; updated on CPU every frame, then uploaded to video memory.
Definition FlexBody.h:44
CameraMode_t fb_camera_mode_orig
Dynamic visibility mode {0 and higher = cinecam index}.
Definition FlexBody.h:75
CameraMode_t fb_camera_mode_active
Dynamic visibility mode {0 and higher = cinecam index}.
Definition FlexBody.h:74
FlexMeshWheel * CreateFlexMeshWheel(unsigned int wheel_index, int axis_node_1_index, int axis_node_2_index, int nstart, int nrays, float rim_radius, bool rim_reverse, std::string const &rim_mesh_name, std::string const &rim_mesh_rg, std::string const &tire_material_name, std::string const &tire_material_rg)
void SaveFlexbodiesToCache()
FlexBody * CreateFlexBody(FlexbodyID_t flexbody_id, const NodeNum_t ref_node, const NodeNum_t x_node, const NodeNum_t y_node, Ogre::Vector3 offset, Ogre::Vector3 rotation, std::vector< unsigned int > &node_indices, std::vector< ForvertTempData > &forvert_data, const std::string &mesh_name, const std::string &resource_group_name)
void CheckAndLoadFlexbodyCache()
Consists of static mesh, representing the rim, and dynamic mesh, representing the tire.
Ogre::Entity * GetTireEntity()
A visual mesh, forming a chassis for softbody actor At most one instance is created per actor.
Definition FlexObj.h:60
RTTLayerManager & GetRttLayerManager()
Definition GUIManager.h:172
const TerrainPtr & GetTerrain()
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
ActorManager * GetActorManager()
void RegisterCabMesh(Ogre::Entity *ent, Ogre::SceneNode *snode, FlexObj *flexobj)
void UpdateSimDataBuffer()
Copies sim. data from Actor to GfxActor for later update.
void SortFlexbodies()
Ogre::SceneManager * GetSceneManager()
Definition GfxScene.h:83
static int resolveEventName(Ogre::String eventName)
RTT texture 'slot' within MyGUI system; we can only create them, not destroy, so we reuse.
Definition RTTLayer.h:13
void setTextureSize(const MyGUI::IntSize &_size)
Call before createRttTexture()
Definition RTTLayer.cpp:68
const std::string & getTextureName() const
Definition RTTLayer.h:26
void createRttTexture()
Definition RTTLayer.cpp:98
void setTextureName(const std::string &_name)
Call before createRttTexture()
Definition RTTLayer.cpp:80
RTTLayer * CreateOrReuseRttLayer()
Definition RTTLayer.cpp:114
Designed to be run on main/rendering loop (FPS)
void SetSimpleDelay(RoR::CmdKeyInertiaConfig &cfg, float start_delay, float stop_delay, std::string start_function, std::string stop_function)
void SetAttachmentRate(float rate)
How long it will take for springs to fully attach to the Rail.
Definition SlideNode.h:102
void SetBreakForce(float breakRate)
Force required to break the Node from the Rail.
Definition SlideNode.h:101
void SetCorThreshold(float threshold)
Distance from a beam before corrective forces take effect.
Definition SlideNode.h:99
void SetDefaultRail(RailGroup *rail)
Sets rail to initially use when spawned or reset.
Definition SlideNode.h:82
void SetSpringRate(float rate)
Spring force used to calculate corrective forces.
Definition SlideNode.h:100
bool sn_attach_self
Attach/detach to rails on the current vehicle only.
Definition SlideNode.h:106
void SetAttachmentDistance(float dist)
Maximum distance this spring node is allowed to reach out for a Rail.
Definition SlideNode.h:103
bool sn_attach_foreign
Attach/detach to rails only on other vehicles.
Definition SlideNode.h:107
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
Collisions * GetCollisions()
Definition Terrain.h:86
This class loads and processes a torque curve for a vehicle.
Definition TorqueCurve.h:43
Ogre::SimpleSpline * getUsedSpline()
Returns the used spline.
Definition TorqueCurve.h:81
void AddCurveSample(float rpm, float progress, Ogre::String const &model=customModel)
Adds a point to the torque curve graph.
int spaceCurveEvenly(Ogre::SimpleSpline *spline)
Spaces the points of a spline evenly; this is needed for the correct calculation of the Ogre simple s...
int setTorqueModel(Ogre::String name)
Sets the torque model which is used for the vehicle.
bool CreateNewCurve(Ogre::String const &name=customModel)
Creates new torque curve.
bool tr_4wd_mode
Enables 4WD mode.
int tr_ax_2
This axle is only driven in 4WD mode.
int tr_ax_1
This axle is always driven.
static Ogre::Vector3 getTweakedPropRotation(TuneupDefPtr &tuneup_entry, PropID_t prop_id, Ogre::Vector3 orig_val)
static VideoCamRole getTweakedVideoCameraRole(TuneupDefPtr &tuneup_def, VideoCameraID_t camera_id, VideoCamRole orig_val)
static std::string getTweakedPropMedia(TuneupDefPtr &tuneup_entry, PropID_t prop_id, int media_idx, const std::string &orig_val)
static std::string getTweakedPropMediaRG(TuneupDefPtr &tuneup_def, PropID_t prop_id, int media_idx, const std::string &orig_val)
static bool isPropAnyhowRemoved(TuneupDefPtr &tuneup_def, PropID_t prop_id)
static Ogre::Vector3 getTweakedCineCameraPosition(TuneupDefPtr &tuneup_entry, CineCameraID_t cinecamid, Ogre::Vector3 orig_val)
static Ogre::Vector3 getTweakedPropOffset(TuneupDefPtr &tuneup_entry, PropID_t prop_id, Ogre::Vector3 orig_val)
static bool isFlareAnyhowRemoved(TuneupDefPtr &tuneup_def, FlareID_t flare_id)
static std::string getTweakedFlexbodyMediaRG(TuneupDefPtr &tuneup_def, FlexbodyID_t flexbody_id, int media_idx, const std::string &orig_val)
static float getTweakedWheelTireRadius(TuneupDefPtr &tuneup_entry, WheelID_t wheel_id, float orig_val)
static std::string getTweakedFlexbodyMedia(TuneupDefPtr &tuneup_entry, FlexbodyID_t flexbody_id, int media_idx, const std::string &orig_val)
static bool isManagedMatAnyhowRemoved(TuneupDefPtr &tuneup_def, const std::string &matname)
static Ogre::Vector3 getTweakedFlexbodyOffset(TuneupDefPtr &tuneup_entry, FlexbodyID_t flexbody_id, Ogre::Vector3 orig_val)
static float getTweakedWheelRimRadius(TuneupDefPtr &tuneup_entry, WheelID_t wheel_id, float orig_val)
static std::string getTweakedManagedMatMediaRG(TuneupDefPtr &tuneup_def, const std::string &matname, int media_idx, const std::string &orig_val)
static std::string getTweakedManagedMatMedia(TuneupDefPtr &tuneup_def, const std::string &matname, int media_idx, const std::string &orig_val)
static Ogre::Vector3 getTweakedNodePosition(TuneupDefPtr &tuneup_entry, NodeNum_t nodenum, Ogre::Vector3 orig_val)
static Ogre::Vector3 getTweakedFlexbodyRotation(TuneupDefPtr &tuneup_entry, FlexbodyID_t flexbody_id, Ogre::Vector3 orig_val)
static bool isFlexbodyAnyhowRemoved(TuneupDefPtr &tuneup_def, FlexbodyID_t flexbody_id)
static WheelSide getTweakedWheelSide(TuneupDefPtr &tuneup_entry, WheelID_t wheel_id, WheelSide orig_val)
static bool isExhaustAnyhowRemoved(TuneupDefPtr &tuneup_def, ExhaustID_t exhaust_id)
static std::string getTweakedWheelMediaRG(TuneupDefPtr &tuneup_def, WheelID_t wheel_id, int media_idx, const std::string &orig_val)
static std::string getTweakedWheelMedia(TuneupDefPtr &tuneup_entry, WheelID_t wheel_id, int media_idx, const std::string &orig_val)
TurbojetVisual tjet_visual
Definition TurboJet.h:107
void SetupVisuals(RigDef::Turbojet &def, int num, std::string const &propname, Ogre::Entity *nozzle, Ogre::Entity *afterburner_flame)
Definition TurboJet.cpp:73
void SetNodes(NodeNum_t front, NodeNum_t back, NodeNum_t ref)
Definition TurboJet.cpp:104
void SetVisible(bool visible)
Definition TurboJet.cpp:215
void AddBeam(int beam_id)
const char * KeywordToString(RigDef::Keyword keyword)
@ HYDRO_FLAG_DIR
Definition SimData.h:124
@ HYDRO_FLAG_SPEED
Definition SimData.h:123
@ HYDRO_FLAG_RUDDER
Definition SimData.h:126
@ HYDRO_FLAG_REV_ELEVATOR
Definition SimData.h:130
@ HYDRO_FLAG_AILERON
Definition SimData.h:125
@ HYDRO_FLAG_ELEVATOR
Definition SimData.h:127
@ HYDRO_FLAG_REV_AILERON
Definition SimData.h:128
@ ANIM_FLAG_ALTIMETER
Definition SimData.h:137
@ ANIM_FLAG_PBRAKE
Definition SimData.h:150
@ ANIM_FLAG_BRUDDER
Definition SimData.h:163
@ ANIM_FLAG_ROLL
Definition SimData.h:141
@ ANIM_FLAG_BTHROTTLE
Definition SimData.h:164
@ ANIM_FLAG_AEPITCH
Definition SimData.h:154
@ ANIM_FLAG_TACHO
Definition SimData.h:148
@ ANIM_FLAG_RPM
Definition SimData.h:144
@ ANIM_FLAG_AETORQUE
Definition SimData.h:153
@ ANIM_FLAG_SHIFTER
Definition SimData.h:152
@ ANIM_FLAG_AIRSPEED
Definition SimData.h:135
@ ANIM_FLAG_TORQUE
Definition SimData.h:156
@ ANIM_FLAG_CLUTCH
Definition SimData.h:147
@ ANIM_FLAG_VVI
Definition SimData.h:136
@ ANIM_FLAG_FLAP
Definition SimData.h:139
@ ANIM_FLAG_THROTTLE
Definition SimData.h:143
@ ANIM_FLAG_BRAKE
Definition SimData.h:146
@ ANIM_FLAG_AOA
Definition SimData.h:138
@ ANIM_FLAG_TURBO
Definition SimData.h:151
@ ANIM_FLAG_ACCEL
Definition SimData.h:145
@ ANIM_FLAG_PITCH
Definition SimData.h:142
@ ANIM_FLAG_AESTATUS
Definition SimData.h:155
@ ANIM_FLAG_SPEEDO
Definition SimData.h:149
@ ANIM_FLAG_AIRBRAKE
Definition SimData.h:140
@ UNLOCKED
lock not locked
Definition SimData.h:75
@ TRUCK
its a truck (or other land vehicle)
Definition SimData.h:85
@ BOAT
its a boat
Definition SimData.h:87
@ AIRPLANE
its an airplane
Definition SimData.h:86
const PropAnimFlag_t PROP_ANIM_FLAG_ROLL
Definition GfxData.h:50
const PropAnimFlag_t PROP_ANIM_FLAG_AESTATUS
Definition GfxData.h:64
const PropAnimFlag_t PROP_ANIM_FLAG_ARUDDER
Definition GfxData.h:71
const PropAnimFlag_t PROP_ANIM_FLAG_TACHO
Definition GfxData.h:57
const PropAnimFlag_t PROP_ANIM_FLAG_ALTIMETER
Definition GfxData.h:46
const PropAnimFlag_t PROP_ANIM_FLAG_CLUTCH
Definition GfxData.h:56
const PropAnimFlag_t PROP_ANIM_FLAG_PITCH
Definition GfxData.h:51
const PropAnimMode_t PROP_ANIM_MODE_BOUNCE
Definition GfxData.h:91
const PropAnimFlag_t PROP_ANIM_FLAG_TORQUE
Definition GfxData.h:65
const PropAnimFlag_t PROP_ANIM_FLAG_ELEVATORS
Definition GfxData.h:75
const PropAnimMode_t PROP_ANIM_MODE_OFFSET_X
Definition GfxData.h:86
const PropAnimFlag_t PROP_ANIM_FLAG_AIRBRAKE
Definition GfxData.h:49
const PropAnimMode_t PROP_ANIM_MODE_NOFLIP
Definition GfxData.h:90
const PropAnimFlag_t PROP_ANIM_FLAG_SIGNALSTALK
Turn indicator stalk position (-1=left, 0=off, 1=right)
Definition GfxData.h:77
const PropAnimFlag_t PROP_ANIM_FLAG_FLAP
Definition GfxData.h:48
const PropAnimFlag_t PROP_ANIM_FLAG_AILERONS
Definition GfxData.h:70
const PropAnimMode_t PROP_ANIM_MODE_ROTA_Z
Definition GfxData.h:85
const PropAnimFlag_t PROP_ANIM_FLAG_DASHBOARD
Used with dashboard system inputs, see enum DashData in file DashBoardManager.h.
Definition GfxData.h:76
const PropAnimMode_t PROP_ANIM_MODE_OFFSET_Y
Definition GfxData.h:87
const PropAnimFlag_t PROP_ANIM_FLAG_BRUDDER
Definition GfxData.h:72
const PropAnimMode_t PROP_ANIM_MODE_AUTOANIMATE
Definition GfxData.h:89
const PropAnimFlag_t PROP_ANIM_FLAG_AIRSPEED
Definition GfxData.h:44
const PropAnimFlag_t PROP_ANIM_FLAG_TURBO
Definition GfxData.h:60
const PropAnimMode_t PROP_ANIM_MODE_ROTA_Y
Definition GfxData.h:84
const PropAnimFlag_t PROP_ANIM_FLAG_AOA
Definition GfxData.h:47
const PropAnimFlag_t PROP_ANIM_FLAG_VVI
Definition GfxData.h:45
const PropAnimFlag_t PROP_ANIM_FLAG_BTHROTTLE
Definition GfxData.h:73
const PropAnimFlag_t PROP_ANIM_FLAG_THROTTLE
Definition GfxData.h:52
const PropAnimFlag_t PROP_ANIM_FLAG_PBRAKE
Definition GfxData.h:59
const PropAnimFlag_t PROP_ANIM_FLAG_IGNITION
Definition GfxData.h:79
const PropAnimFlag_t PROP_ANIM_FLAG_PERMANENT
Definition GfxData.h:74
const PropAnimFlag_t PROP_ANIM_FLAG_RPM
Definition GfxData.h:53
const PropAnimFlag_t PROP_ANIM_FLAG_HEADING
Definition GfxData.h:66
const PropAnimFlag_t PROP_ANIM_FLAG_AETORQUE
Definition GfxData.h:62
const PropAnimFlag_t PROP_ANIM_FLAG_EVENT
Definition GfxData.h:69
const PropAnimFlag_t PROP_ANIM_FLAG_GEAR
'gearreverse' (animOpt3=-1), 'gearneutral' (animOpt3=0), 'gear#' (animOpt3=#)
Definition GfxData.h:78
const PropAnimFlag_t PROP_ANIM_FLAG_DIFFLOCK
Definition GfxData.h:67
const PropAnimFlag_t PROP_ANIM_FLAG_ACCEL
Definition GfxData.h:54
const PropAnimFlag_t PROP_ANIM_FLAG_STEERING
Definition GfxData.h:68
const PropAnimMode_t PROP_ANIM_MODE_OFFSET_Z
Definition GfxData.h:88
const PropAnimFlag_t PROP_ANIM_FLAG_BRAKE
Definition GfxData.h:55
const PropAnimFlag_t PROP_ANIM_FLAG_SHIFTER
'shifterman1, shifterman2, sequential, shifterlin, autoshifterlin'; animOpt3: see RoR::ShifterPropAni...
Definition GfxData.h:61
const PropAnimFlag_t PROP_ANIM_FLAG_AEPITCH
Definition GfxData.h:63
const PropAnimMode_t PROP_ANIM_MODE_ROTA_X
Definition GfxData.h:83
const PropAnimFlag_t PROP_ANIM_FLAG_SPEEDO
Definition GfxData.h:58
@ SHIFTERMAN1
Definition GfxData.h:125
@ SHIFTERSEQ
Definition GfxData.h:127
@ AUTOSHIFTERLIN
Definition GfxData.h:129
@ SHIFTERMAN2
Definition GfxData.h:126
@ SHIFTERLIN
Definition GfxData.h:128
@ MSG_APP_LOAD_SCRIPT_REQUESTED
Payload = RoR::LoadScriptRequest* (owner)
Definition Application.h:94
BeamType
Definition SimData.h:62
static const int DEFAULT_DETACHER_GROUP
static const float WHEEL_FRICTION_COEF
static const float BEAM_CREAK_DEFAULT
static const float DEFAULT_COLLISION_RANGE
static const float BEAM_DEFORM
static const float DEFAULT_SPEEDO_MAX_KPH
static const float ROTATOR_FORCE_DEFAULT
static const float HOOK_LOCK_TIMER_DEFAULT
static const float BEAM_PLASTIC_COEF_DEFAULT
static const float ROTATOR_TOLERANCE_DEFAULT
static const float NODE_LOADWEIGHT_DEFAULT
static const float HOOK_RANGE_DEFAULT
static const float HOOK_FORCE_DEFAULT
static const float HOOK_SPEED_DEFAULT
static const float NODE_FRICTION_COEF_DEFAULT
@ BEAM_VIRTUAL
Excluded from mass calculations, visuals permanently disabled.
Definition SimData.h:65
@ BEAM_HYDRO
Definition SimData.h:64
@ BEAM_NORMAL
Definition SimData.h:63
@ SHOCK3
shock3
Definition SimData.h:102
@ TRIGGER
trigger
Definition SimData.h:103
@ ROPE
Definition SimData.h:105
@ SUPPORTBEAM
Definition SimData.h:104
@ SHOCK2
shock2
Definition SimData.h:101
@ NOSHOCK
not a shock
Definition SimData.h:99
@ SHOCK1
either 'shock1' (with flag BEAM_HYDRO) or a wheel beam
Definition SimData.h:100
@ LOCAL_SLEEPING
sleeping (local) actor
@ NETWORKED_OK
not simulated (remote) actor
@ SHOCK_FLAG_ISSHOCK3
Definition SimData.h:193
@ SHOCK_FLAG_TRG_CMD_BLOCKER
Definition SimData.h:198
@ SHOCK_FLAG_ISSHOCK2
Definition SimData.h:192
@ SHOCK_FLAG_NORMAL
Definition SimData.h:189
@ SHOCK_FLAG_TRG_HOOK_UNLOCK
Definition SimData.h:200
@ SHOCK_FLAG_LACTIVE
Definition SimData.h:190
@ SHOCK_FLAG_TRG_HOOK_LOCK
Definition SimData.h:201
@ SHOCK_FLAG_TRG_ENGINE
Definition SimData.h:203
@ SHOCK_FLAG_TRG_BLOCKER_A
Definition SimData.h:199
@ SHOCK_FLAG_SOFTBUMP
Definition SimData.h:194
@ SHOCK_FLAG_RACTIVE
Definition SimData.h:191
@ SHOCK_FLAG_TRG_CONTINUOUS
Definition SimData.h:202
@ SHOCK_FLAG_TRG_CMD_SWITCH
Definition SimData.h:197
@ SHOCK_FLAG_ISTRIGGER
Definition SimData.h:195
@ SHOCK_FLAG_TRG_BLOCKER
Definition SimData.h:196
@ ACTOR
Defined in truck file under 'scripts', contains global variable BeamClass@ thisActor.
@ VISCOUS_DIFF
@ SPLIT_DIFF
@ OPEN_DIFF
@ LOCKED_DIFF
DifferentialType
Definition RigDef_File.h:98
@ l_SKIP_LOADED
Only apply minimum mass to nodes without "L" option.
std::shared_ptr< Document > DocumentPtr
AppContext * GetAppContext()
InputEngine * GetInputEngine()
CVar * diag_log_beam_trigger
CameraManager * GetCameraManager()
SoundScriptManager * GetSoundScriptManager()
CVar * gfx_particles_mode
GUIManager * GetGuiManager()
GameContext * GetGameContext()
GfxScene * GetGfxScene()
CVar * diag_log_beam_deform
CVar * sim_no_collisions
CVar * gfx_window_videocams
CVar * sim_no_self_collisions
CVar * ui_legacy_truck_renderdash
string; name of the '.dashboard' file in modcache.
Console * GetConsole()
CVar * gfx_flares_mode
CVar * ui_default_boat_dash
string; name of the '.dashboard' file in modcache.
CVar * ui_default_truck_dash
string; name of the '.dashboard' file in modcache.
CVar * sim_gearbox_mode
CVar * diag_simple_materials
CVar * gfx_alt_actor_materials
CacheSystem * GetCacheSystem()
CVar * gfx_reduce_shadows
CVar * diag_videocameras
CVar * diag_log_beam_break
CVar * gfx_enable_videocams
static const NodeNum_t NODENUM_INVALID
GfxFlaresMode
@ CURR_VEHICLE_HEAD_ONLY
Only current vehicle, main lights.
@ NONE
None (fastest)
@ ALL_VEHICLES_ALL_LIGHTS
All vehicles, all lights.
@ LT_AssetPack
@ LT_AddonPart
@ DD_PARKINGBRAKE
chassis pitch
@ LOADDASHBOARD_RTT_TEXTURE
Will be drawn to texture. Unless STACKABLE, it prevents the default dashboard from loading.
@ LOADDASHBOARD_RENDERDASH
Will be drawn to texture in classic 'renderdash' format. Ignores STACKABLE flag.
@ LOADDASHBOARD_SCREEN_HUD
Will be drawn to screen. Unless STACKABLE, it prevents the default dashboard from loading.
WheelSide
Used by rig-def/addonpart/tuneup formats to specify wheel rim mesh orientation.
@ VCAM_ROLE_MIRROR
Flips the video output and when not in driver cam, acts like a natural mirror, not a screen.
@ VCAM_ROLE_TRACKING_MIRROR_NOFLIP
A MIRROR_NOFLIP(2) with tracking node set.
@ VCAM_ROLE_TRACKING_MIRROR
A MIRROR(1) with tracking node set.
@ VCAM_ROLE_INVALID
@ VCAM_ROLE_MIRROR_PROP_LEFT
The classic 'special prop/rear view mirror'.
@ VCAM_ROLE_TRACKING_VIDEOCAM
@ VCAM_ROLE_MIRROR_PROP_RIGHT
The classic 'special prop/rear view mirror'.
@ VCAM_ROLE_MIRROR_NOFLIP
Same as VCAM_ROLE_MIRROR, but without flipping the texture horizontally (expects texcoords to be alre...
@ VCAM_ROLE_VIDEOCAM
int WheelID_t
Index to Actor::ar_wheels, use RoR::WHEELID_INVALID as empty value.
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
int VideoCameraID_t
Index into GfxActor::m_videocameras, use RoR::VIDEOCAMERAID_INVALID as empty value.
int PropID_t
Index to GfxActor::m_props, use RoR::PROPID_INVALID as empty value.
int FlareID_t
Index into Actor::ar_flares, use RoR::FLAREID_INVALID as empty value.
int ExhaustID_t
Index into GfxActor::m_exhausts, use RoR::EXHAUSTID_INVALID as empty value.
int FlexbodyID_t
Index to GfxActor::m_flexbodies, use RoR::FLEXBODYID_INVALID as empty value.
@ DEPTHMAP_DISABLED
@ HIDE_MIRROR
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
std::shared_ptr< SkinDocument > SkinDocumentPtr
WheelPropulsion
static CameraMode_t CAMERA_MODE_ALWAYS_HIDDEN
int CParticleID_t
Index into GfxActor::m_cparticles, use RoR::CPARTICLEID_INVALID as empty value.
WheelBraking
@ LIGHTMASK_REVERSE
reverse light on
Definition RoRnet.h:121
@ LIGHTMASK_CUSTOM8
custom light 8 on
Definition RoRnet.h:112
@ LIGHTMASK_CUSTOM7
custom light 7 on
Definition RoRnet.h:111
@ LIGHTMASK_CUSTOM3
custom light 3 on
Definition RoRnet.h:107
@ LIGHTMASK_CUSTOM4
custom light 4 on
Definition RoRnet.h:108
@ LIGHTMASK_CUSTOM1
custom light 1 on
Definition RoRnet.h:105
@ LIGHTMASK_BRAKES
brake lights on
Definition RoRnet.h:120
@ LIGHTMASK_CUSTOM2
custom light 2 on
Definition RoRnet.h:106
@ LIGHTMASK_CUSTOM6
custom light 6 on
Definition RoRnet.h:110
@ LIGHTMASK_BLINK_RIGHT
right blinker on
Definition RoRnet.h:124
@ LIGHTMASK_CUSTOM10
custom light 10 on
Definition RoRnet.h:114
@ LIGHTMASK_CUSTOM5
custom light 5 on
Definition RoRnet.h:109
@ LIGHTMASK_FOGLIGHTS
Definition RoRnet.h:118
@ LIGHTMASK_HEADLIGHT
Definition RoRnet.h:116
@ LIGHTMASK_CUSTOM9
custom light 9 on
Definition RoRnet.h:113
@ LIGHTMASK_HIGHBEAMS
Definition RoRnet.h:117
@ LIGHTMASK_SIDELIGHTS
Definition RoRnet.h:119
@ LIGHTMASK_BLINK_LEFT
left blinker on
Definition RoRnet.h:123
static const BitMask_t OPTION_THROTTLE
unsigned int engine_idx
static const BitMask_t OPTION_RPM
static const BitMask_t OPTION_TORQUE
static const BitMask_t OPTION_PITCH
static const BitMask_t OPTION_STATUS
Node::Ref aditional_node
Node::Ref y_axis_node
Node::Ref reference_node
float max_inclination_angle
Node::Ref x_axis_node
Ogre::Vector3 offset
static const BitMask_t SOURCE_AERO_PITCH
static const BitMask_t SOURCE_AERO_STATUS
static const BitMask_t SOURCE_AERO_RPM
static const BitMask_t SOURCE_AERO_TORQUE
static const BitMask_t SOURCE_AERO_THROTTLE
static const BitMask_t SOURCE_GEAR_FORWARD
static const BitMask64_t SOURCE_GEAR_REVERSE
static const BitMask64_t SOURCE_TURBO
static const BitMask64_t SOURCE_SEQUENTIAL_SHIFT
static const BitMask64_t SOURCE_AIR_RUDDER
static const BitMask_t MODE_ROTATION_X
static const BitMask64_t SOURCE_ALTIMETER_1K
static const BitMask64_t SOURCE_AUTOSHIFTERLIN
static const BitMask_t MODE_OFFSET_Y
static const BitMask64_t SOURCE_SHIFT_LEFT_RIGHT
static const BitMask_t MODE_NO_FLIP
static const BitMask64_t SOURCE_FLAP
static const BitMask64_t SOURCE_SPEEDO
static const BitMask64_t SOURCE_DIFFLOCK
static const BitMask64_t SOURCE_ALTIMETER_10K
static const BitMask64_t SOURCE_AIRSPEED
static const BitMask_t MODE_AUTO_ANIMATE
static const BitMask64_t SOURCE_ROLL
static const BitMask64_t SOURCE_BOAT_THROTTLE
static const BitMask64_t SOURCE_CLUTCH
static const BitMask_t MODE_OFFSET_Z
static const BitMask64_t SOURCE_PITCH
static const BitMask64_t SOURCE_TACHO
static const BitMask64_t SOURCE_AILERON
static const BitMask_t MODE_ROTATION_Y
static const BitMask64_t SOURCE_ELEVATOR
static const BitMask64_t SOURCE_PERMANENT
static const BitMask_t MODE_EVENT_LOCK
static const BitMask64_t SOURCE_STEERING_WHEEL
static const BitMask64_t SOURCE_PARKING
static const BitMask64_t SOURCE_SHIFT_BACK_FORTH
static const BitMask64_t SOURCE_ACCEL
static const BitMask64_t SOURCE_IGNITION
static const BitMask64_t SOURCE_AIR_BRAKE
static const BitMask64_t SOURCE_GEAR_NEUTRAL
static const BitMask64_t SOURCE_ANGLE_OF_ATTACK
static const BitMask64_t SOURCE_VERTICAL_VELOCITY
static const BitMask_t MODE_OFFSET_X
static const BitMask_t MODE_BOUNCE
static const BitMask64_t SOURCE_SHIFTERLIN
static const BitMask64_t SOURCE_ALTIMETER_100K
static const BitMask64_t SOURCE_BOAT_RUDDER
static const BitMask64_t SOURCE_BRAKES
static const BitMask64_t SOURCE_TORQUE
static const BitMask_t MODE_ROTATION_Z
static const BitMask64_t SOURCE_DASHBOARD
static const BitMask64_t SOURCE_EVENT
static const BitMask64_t SOURCE_HEADING
static const BitMask64_t SOURCE_SIGNALSTALK
static const BitMask_t OPTION_ALTIMETER_100K
static const BitMask_t OPTION_ALTIMETER_1K
static const BitMask_t OPTION_SPEEDO
static const BitMask_t OPTION_ACCEL
static const BitMask_t OPTION_AIR_BRAKE
static const BitMask_t OPTION_ANGLE_OF_ATTACK
static const BitMask_t OPTION_BOAT_RUDDER
static const BitMask_t OPTION_ROLL
static const BitMask_t OPTION_SEQUENTIAL_SHIFT
static const BitMask_t OPTION_PARKING
static const BitMask_t OPTION_TURBO
static const BitMask_t OPTION_SHIFT_BACK_FORTH
static const BitMask_t OPTION_PITCH
AeroAnimator aero_animator
static const BitMask_t OPTION_AIRSPEED
std::shared_ptr< Inertia > inertia_defaults
static const BitMask_t OPTION_BOAT_THROTTLE
static const BitMask_t OPTION_SHORT_LIMIT
static const BitMask_t OPTION_ALTIMETER_10K
static const BitMask_t OPTION_VERTICAL_VELOCITY
static const BitMask_t OPTION_FLAP
std::shared_ptr< BeamDefaults > beam_defaults
static const BitMask_t OPTION_BRAKES
static const BitMask_t OPTION_TORQUE
static const BitMask_t OPTION_SHIFT_LEFT_RIGHT
static const BitMask_t OPTION_LONG_LIMIT
static const BitMask_t OPTION_INVISIBLE
static const BitMask_t OPTION_CLUTCH
Node::Ref nodes[2]
static const BitMask_t OPTION_GEAR_SELECT
static const BitMask_t OPTION_TACHO
Ogre::String name
unsigned int forum_account_id
bool _has_forum_account
Ogre::String type
Ogre::String email
DifferentialTypeVec options
Order matters!
Node::Ref wheels[2][2]
Ogre::String material_name
RoR::WheelSide side
Ogre::String mesh_name
RoR::WheelBraking braking
unsigned int num_rays
std::shared_ptr< BeamDefaults > beam_defaults
std::shared_ptr< NodeDefaults > node_defaults
Node::Ref rigidity_node
RoR::WheelPropulsion propulsion
Node::Ref nodes[2]
Node::Ref reference_arm_node
static const BitMask_t OPTION_r_ROPE
BitMask_t options
std::shared_ptr< BeamDefaults > defaults
float extension_break_limit
static const BitMask_t OPTION_i_INVISIBLE
Node::Ref nodes[2]
static const BitMask_t OPTION_s_SUPPORT
float default_braking_force
float parking_brake_force
static const BitMask_t OPTION_r_BUOYANT_ONLY_DRAG
static const BitMask_t OPTION_p_10xTOUGHER
static const BitMask_t OPTION_s_BUOYANT_NO_DRAG
static const BitMask_t OPTION_F_10xTOUGHER_BUOYANT
static const BitMask_t OPTION_b_BUOYANT
static const BitMask_t OPTION_D_CONTACT_BUOYANT
static const BitMask_t OPTION_S_INVULNERABLE_BUOYANT
static const BitMask_t OPTION_c_CONTACT
static const BitMask_t OPTION_u_INVULNERABLE
Node::Ref left_node
Node::Ref center_node
Node::Ref back_node
std::vector< Node::Ref > nodes
int mode
0 and higher = cinecam index
Ogre::Vector3 position
std::shared_ptr< BeamDefaults > beam_defaults
Node::Ref nodes[8]
std::shared_ptr< NodeDefaults > node_defaults
std::vector< Node::Ref > nodes
Ogre::String description
std::shared_ptr< Inertia > inertia_defaults
Node::Ref nodes[2]
RoR::CommandkeyID_t extend_key
RoR::CommandkeyID_t contract_key
std::shared_ptr< BeamDefaults > beam_defaults
bool option_o_1press_center
std::vector< Node::Ref > fixes
std::vector< Wheel2 > wheels2
std::vector< Rotator2 > rotators2
std::vector< Rotator > rotators
std::vector< Wing > wings
std::vector< MeshWheel2 > meshwheels2
std::vector< Shock > shocks
std::vector< Shock2 > shocks2
std::vector< Tie > ties
std::vector< MeshWheel > meshwheels
std::vector< Animator > animators
std::vector< Cinecam > cinecam
std::vector< FlexBodyWheel > flexbodywheels
std::vector< Node > nodes
std::vector< Rope > ropes
std::vector< Wheel > wheels
std::vector< Command2 > commands2
std::vector< Trigger > triggers
std::vector< Shock3 > shocks3
std::vector< Hydro > hydros
std::vector< Beam > beams
std::vector< Airbrake > airbrakes
std::vector< float > gear_ratios
float global_gear_ratio
float neutral_gear_ratio
float reverse_gear_ratio
float shift_time
Seconds.
float post_shift_time
Seconds.
float clutch_time
Seconds.
Node::Ref direction_node
Node::Ref reference_node
Ogre::String particle_name
RoR::ExtCameraMode mode
std::shared_ptr< Inertia > inertia_defaults
RoR::FlareType type
Ogre::String material_name
Node::Ref node_axis_x
Node::Ref reference_node
Ogre::Vector3 offset
int control_number
Only 'u' type flares.
std::string dashboard_link
Only 'd' type flares.
Node::Ref node_axis_y
int control_number
Only 'u' type flares.
Ogre::String tyre_mesh_name
RoR::WheelSide side
Ogre::String rim_mesh_name
CameraSettings camera_settings
Ogre::String mesh_name
Node::Ref y_axis_node
Ogre::Vector3 rotation
Ogre::Vector3 offset
Node::Ref reference_node
Node::Ref x_axis_node
std::vector< Node::Ref > node_list
std::vector< Forvert > forvert
Ogre::String airfoil_name
Node::Ref front_node
Ogre::String material_name
std::string material
Node::Ref node
float option_max_force
bool flag_no_disable
bool flag_self_lock
float option_timer
float option_speed_coef
bool flag_auto_lock
float option_hook_range
float option_min_range_meters
static const BitMask_t OPTION_e_INPUT_ELEVATOR
std::shared_ptr< Inertia > inertia_defaults
float lenghtening_factor
static const BitMask_t OPTION_s_DISABLE_ON_HIGH_SPEED
BitMask_t options
static const BitMask_t OPTION_h_INPUT_InvELEVATOR_RUDDER
static const BitMask_t OPTION_g_INPUT_ELEVATOR_RUDDER
static const BitMask_t OPTION_y_INPUT_InvAILERON_RUDDER
Node::Ref nodes[2]
static const BitMask_t OPTION_j_INVISIBLE
static const BitMask_t OPTION_v_INPUT_InvAILERON_ELEVATOR
static const BitMask_t OPTION_a_INPUT_AILERON
static const BitMask_t OPTION_r_INPUT_RUDDER
static const BitMask_t OPTION_n_INPUT_NORMAL
static const BitMask_t OPTION_u_INPUT_AILERON_ELEVATOR
Inertia inertia
static const BitMask_t OPTION_x_INPUT_AILERON_RUDDER
std::shared_ptr< BeamDefaults > beam_defaults
Ogre::String start_function
float start_delay_factor
float stop_delay_factor
Ogre::String stop_function
DifferentialTypeVec options
Order matters!
static const int LOCKGROUP_NOLOCK
std::vector< Node::Ref > nodes
static const int LOCKGROUP_DEFAULT
ManagedMaterialType type
ManagedMaterialsOptions options
Ogre::String specular_map
Ogre::String damaged_diffuse_map
float global_min_mass_Kg
minimum node mass in Kg - only effective where DefaultMinimass was not set.
MinimassOption option
std::shared_ptr< BeamDefaults > beam_defaults
static const BitMask_t OPTION_c_NO_GROUND_CONTACT
static const BitMask_t OPTION_p_NO_PARTICLES
std::shared_ptr< DefaultMinimass > default_minimass
bool _has_load_weight_override
static const BitMask_t OPTION_f_NO_SPARKS
static const BitMask_t OPTION_b_EXTRA_BUOYANCY
BitMask_t options
static const BitMask_t OPTION_x_EXHAUST_POINT
std::shared_ptr< NodeDefaults > node_defaults
float load_weight_override
static const BitMask_t OPTION_h_HOOK_POINT
Ogre::Vector3 position
static const BitMask_t OPTION_m_NO_MOUSE_GRAB
static const BitMask_t OPTION_l_LOAD_WEIGHT
static const BitMask_t OPTION_y_EXHAUST_DIRECTION
Node::Ref emitter_node
Node::Ref reference_node
Ogre::String particle_system_name
Node::Ref reference_node
Ogre::String airfoil
Node::Ref couple_node
Node::Ref blade_tip_nodes[4]
Ogre::ColourValue color
Node::Ref reference_node
Node::Ref x_axis_node
DashboardSpecial special_prop_dashboard
CameraSettings camera_settings
Ogre::Vector3 offset
Ogre::String mesh_name
BeaconSpecial special_prop_beacon
Node::Ref y_axis_node
SpecialProp special
std::list< Animation > animations
Ogre::Vector3 rotation
std::vector< Node::Range > node_list
unsigned int id
std::shared_ptr< BeamDefaults > beam_defaults
Node::Ref end_node
Node::Ref root_node
Ogre::String description
std::shared_ptr< Inertia > inertia_defaults
unsigned int spin_left_key
Node::Ref base_plate_nodes[4]
Node::Ref axis_nodes[2]
Node::Ref rotating_plate_nodes[4]
unsigned int spin_right_key
std::string filename
BitMask_t options
Node::Ref nodes[2]
static const BitMask_t OPTION_i_INVISIBLE
float short_bound
Maximum contraction limit, in percentage ( 1.00 = 100% )
float progress_factor_damp_out
Progression factor dampout, 0 = disabled, 1...x as multipliers, example:maximum dampingrate == spring...
float damp_out
damping value applied when shock extending
float progress_factor_damp_in
Progression factor for dampin. 0 = disabled, 1...x as multipliers, example:maximum dampingrate == spr...
std::shared_ptr< BeamDefaults > beam_defaults
static const BitMask_t OPTION_m_METRIC
float spring_in
Spring value applied when the shock is compressing.
static const BitMask_t OPTION_s_SOFT_BUMP_BOUNDS
float precompression
Changes compression or extension of the suspension when the truck spawns. This can be used to "level"...
float progress_factor_spring_out
Progression factor springout, 0 = disabled, 1...x as multipliers, example:maximum springrate == sprin...
static const BitMask_t OPTION_M_ABSOLUTE_METRIC
float spring_out
spring value applied when shock extending
float long_bound
Maximum extension limit, in percentage ( 1.00 = 100% )
float damp_in
Damping value applied when the shock is compressing.
float progress_factor_spring_in
Progression factor for springin. A value of 0 disables this option. 1...x as multipliers,...
float damp_in_slow
Damping value applied when shock is commpressing slower than split in velocity.
float spring_in
Spring value applied when the shock is compressing.
float spring_out
Spring value applied when shock extending.
static const BitMask_t OPTION_M_ABSOLUTE_METRIC
float damp_in
Damping value applied when the shock is compressing.
float split_vel_in
Split velocity in (m/s) - threshold for slow / fast damping during compression.
BitMask_t options
float precompression
Changes compression or extension of the suspension when the truck spawns. This can be used to "level"...
Node::Ref nodes[2]
static const BitMask_t OPTION_i_INVISIBLE
static const BitMask_t OPTION_m_METRIC
std::shared_ptr< BeamDefaults > beam_defaults
float damp_out
Damping value applied when shock extending.
float long_bound
Maximum extension limit, in percentage ( 1.00 = 100% )
float short_bound
Maximum contraction limit, in percentage ( 1.00 = 100% )
float damp_out_slow
Damping value applied when shock is commpressing slower than split out velocity.
float damp_out_fast
Damping value applied when shock is commpressing faster than split out velocity.
float damp_in_fast
Damping value applied when shock is commpressing faster than split in velocity.
float split_vel_out
Split velocity in (m/s) - threshold for slow / fast damping during extension.
static const BitMask_t OPTION_L_ACTIVE_LEFT
std::shared_ptr< BeamDefaults > beam_defaults
float damping
The 'resistance to motion' of the shock. The best value is given by this equation: 2 * sqrt(suspended...
float spring_rate
The 'stiffness' of the shock. The higher the value, the less the shock will move for a given bump.
static const BitMask_t OPTION_m_METRIC
float precompression
Changes compression or extension of the suspension when the truck spawns. This can be used to "level"...
Node::Ref nodes[2]
float short_bound
Maximum contraction. The shortest length the shock can be, as a proportion of its original length....
float long_bound
Maximum extension. The longest length a shock can be, as a proportion of its original length....
static const BitMask_t OPTION_i_INVISIBLE
static const BitMask_t OPTION_R_ACTIVE_RIGHT
BitMask_t options
static const BitMask_t CONSTRAINT_ATTACH_ALL
static const BitMask_t CONSTRAINT_ATTACH_NONE
static const BitMask_t CONSTRAINT_ATTACH_FOREIGN
static const BitMask_t CONSTRAINT_ATTACH_SELF
BitMask_t constraint_flags
std::vector< Node::Range > rail_node_ranges
int mode
A special constant or cinecam index.
Ogre::String sound_script_name
std::vector< Cab > cab_triangles
std::vector< Texcoord > texcoords
Node::Ref root_node
float auto_shorten_rate
static const BitMask_t OPTION_i_INVISIBLE
static const BitMask_t OPTION_s_DISABLE_SELF_LOCK
std::shared_ptr< BeamDefaults > beam_defaults
BitMask_t options
float max_reach_length
std::vector< Sample > samples
Ogre::String predefined_func_name
std::vector< float > gear_ratios
static const BitMask_t OPTION_B_TRIGGER_BLOCKER
static const BitMask_t OPTION_s_CMD_NUM_SWITCH
static const BitMask_t OPTION_h_UNLOCKS_HOOK_GROUP
static const BitMask_t OPTION_i_INVISIBLE
static const BitMask_t OPTION_t_CONTINUOUS
static const BitMask_t OPTION_E_ENGINE_TRIGGER
static const BitMask_t OPTION_c_COMMAND_STYLE
static const BitMask_t OPTION_b_KEY_BLOCKER
float contraction_trigger_limit
static const BitMask_t OPTION_x_START_DISABLED
std::shared_ptr< BeamDefaults > beam_defaults
BitMask_t options
float expansion_trigger_limit
Node::Ref nodes[2]
static const BitMask_t OPTION_H_LOCKS_HOOK_GROUP
static const BitMask_t OPTION_A_INV_TRIGGER_BLOCKER
int shortbound_trigger_action
Node::Ref front_node
Node::Ref back_node
Node::Ref side_node
Node::Ref blade_tip_nodes[4]
Node::Ref reference_node
Ogre::String airfoil
RoR::VideoCamRole camera_role
Ogre::String material_name
unsigned int texture_width
Ogre::String camera_name
Node::Ref alt_orientation_node
Node::Ref alt_reference_node
unsigned int texture_height
Ogre::Vector3 rotation
Ogre::Vector3 offset
Ogre::String face_material_name
Ogre::String band_material_name
Ogre::String band_material_name
Ogre::String face_material_name
float tex_coords[8]
float min_deflection
float efficacy_coef
WingControlSurface control_surface
Ogre::String airfoil
float max_deflection
Node::Ref nodes[8]
float global_minimass
'minimass' - used where 'set_default_minimass' is not applied.
RigDef::VideoCamera * video_camera_def
RigDef::MaterialFlareBinding * material_flare_def
Ogre::SceneNode * mirror_prop_scenenode
Ogre::MeshPtr abx_mesh
Definition GfxData.h:317
Ogre::Vector3 abx_offset
Definition GfxData.h:320
NodeNum_t abx_y_node
Definition GfxData.h:323
Ogre::SceneNode * abx_scenenode
Definition GfxData.h:318
NodeNum_t abx_ref_node
Definition GfxData.h:321
NodeNum_t abx_x_node
Definition GfxData.h:322
Ogre::Entity * abx_entity
Definition GfxData.h:319
Visuals of softbody beam (beam_t struct); Partially updated along with SimBuffer.
Definition GfxData.h:262
NodeNum_t rod_node2
Node index - may change during simulation!
Definition GfxData.h:272
uint16_t rod_beam_index
Definition GfxData.h:268
float rod_diameter
meters
Definition GfxData.h:269
Ogre::SceneNode * rod_scenenode
Definition GfxData.h:267
ActorPtr rod_target_actor
Definition GfxData.h:273
NodeNum_t rod_node1
Node index - may change during simulation!
Definition GfxData.h:271
bool rod_is_visible
Definition GfxData.h:274
NodeNum_t emitterNode
Definition GfxData.h:344
NodeNum_t directionNode
Definition GfxData.h:345
Ogre::ParticleSystem * psys
Definition GfxData.h:347
Ogre::SceneNode * snode
Definition GfxData.h:346
Submesh for old-style actor body (the "cab")
Definition FlexObj.h:47
@ BACKMESH_TRANSPARENT
Definition FlexObj.h:48
size_t cabs_pos
Definition FlexObj.h:54
BackmeshType backmesh_type
Definition FlexObj.h:52
size_t texcoords_pos
Definition FlexObj.h:53
Texture coordinates for old-style actor body (the "cab")
Definition FlexObj.h:39
float texcoord_u
Definition FlexObj.h:41
float texcoord_v
Definition FlexObj.h:42
Ogre::ParticleSystem * smoker
This remains nullptr if removed via addonpart_unwanted_exhaust or Tuning UI.
Definition GfxData.h:338
NodeNum_t directionNode
Definition GfxData.h:336
Ogre::SceneNode * smokeNode
Definition GfxData.h:337
NodeNum_t emitterNode
Definition GfxData.h:335
std::string particleSystemName
Name in .particle file ~ for display in Tuning UI.
Definition GfxData.h:339
Ogre::ColourValue emissive_color
Definition GfxData.h:330
Ogre::MaterialPtr mat_instance
Definition GfxData.h:329
< Node resolution must be done in ActorSpawner, but vert resolution in FlexBody
Definition Locator_t.h:24
std::string lsr_filename
Load from resource ('.as' file or '.gadget' file); If buffer is supplied, use this as display name on...
ScriptCategory lsr_category
ActorInstanceID_t lsr_associated_actor
For ScriptCategory::ACTOR.
Unified game event system - all requests and state changes are reported using a message.
Definition GameContext.h:52
Gfx attributes/state of a softbody node.
Definition GfxData.h:238
bool nx_no_sparks
User-defined attr;.
Definition GfxData.h:256
bool nx_no_particles
User-defined attr; disable all particles.
Definition GfxData.h:252
bool nx_may_get_wet
Attr; enables water drip and vapour.
Definition GfxData.h:253
float upper_limit
The upper limit for the animation.
Definition GfxData.h:147
float lower_limit
The lower limit for the animation.
Definition GfxData.h:146
float animratio
A coefficient for the animation, prop degree if used with mode: rotation and propoffset if used with ...
Definition GfxData.h:134
PropAnimFlag_t animFlags
Definition GfxData.h:135
PropAnimMode_t animMode
Definition GfxData.h:136
float animOpt3
MULTIPURPOSE.
Definition GfxData.h:144
float animOpt5
Definition GfxData.h:145
User input state for animated props with 'source:event'.
Definition SimData.h:622
A mesh attached to vehicle frame via 3 nodes.
Definition GfxData.h:157
float pp_beacon_rot_angle[4]
Radians.
Definition GfxData.h:189
Ogre::Quaternion pp_rot
Definition GfxData.h:165
MeshObject * pp_wheel_mesh_obj
Definition GfxData.h:178
Ogre::SceneNode * pp_beacon_scene_node[4]
Definition GfxData.h:186
CameraMode_t pp_camera_mode_active
Dynamic visibility mode {0 and higher = cinecam index}.
Definition GfxData.h:173
CameraMode_t pp_camera_mode_orig
Dynamic visibility mode {0 and higher = cinecam index}.
Definition GfxData.h:174
NodeNum_t pp_node_ref
Definition GfxData.h:159
Ogre::Vector3 pp_rota
Definition GfxData.h:164
bool pp_aero_propeller_spin
Special - blurred spinning propeller effect.
Definition GfxData.h:194
NodeNum_t pp_node_y
Definition GfxData.h:161
Ogre::Vector3 pp_offset_orig
Used with ANIM_FLAG_OFFSET*.
Definition GfxData.h:163
Ogre::SceneNode * pp_scene_node
The pivot scene node (parented to root-node).
Definition GfxData.h:166
Ogre::Vector3 pp_offset
Definition GfxData.h:162
PropID_t pp_id
Definition GfxData.h:158
std::vector< PropAnim > pp_animations
Definition GfxData.h:169
std::string pp_media[2]
Redundant, for Tuning UI. Media1 = prop mesh name, Media2 = steeringwheel mesh/beaconprop flare mat.
Definition GfxData.h:168
NodeNum_t pp_node_x
Definition GfxData.h:160
Ogre::BillboardSet * pp_beacon_bbs[4]
Definition GfxData.h:185
char pp_beacon_type
Special prop: beacon {0 = none, 'b' = user-specified, 'r' = red, 'p' = police lightbar,...
Definition GfxData.h:184
bool pp_aero_propeller_blade
Special - single blade mesh.
Definition GfxData.h:193
Ogre::Light * pp_beacon_light[4]
Definition GfxData.h:187
Ogre::Vector3 pp_wheel_pos
Definition GfxData.h:179
Ogre::SceneNode * pp_wheel_scene_node
Definition GfxData.h:180
float pp_wheel_rot_degree
Definition GfxData.h:181
MeshObject * pp_mesh_obj
Optional; NULL if removed via tuneup/addonpart.
Definition GfxData.h:167
float pp_beacon_rot_rate[4]
Radians per second.
Definition GfxData.h:188
A series of RailSegment-s for SlideNode to slide along. Can be closed in a loop.
Definition SlideNode.h:54
std::vector< RailSegment > rg_segments
Definition SlideNode.h:60
int rg_id
Spawn context - matching separately defined rails with slidenodes.
Definition SlideNode.h:61
std::set< std::string > use_addonparts
Addonpart filenames.
UI helper for displaying command control keys to user.
Definition SimData.h:659
CommandkeyID_t uckp_key2
Definition SimData.h:662
std::string uckp_description
Definition SimData.h:660
CommandkeyID_t uckp_key1
Definition SimData.h:661
An Ogre::Camera mounted on the actor and rendering into either in-scene texture or external window.
Definition GfxData.h:216
Ogre::TexturePtr vcam_render_tex
Definition GfxData.h:230
NodeNum_t vcam_node_center
Definition GfxData.h:218
NodeNum_t vcam_node_alt_pos
Definition GfxData.h:221
Ogre::RenderWindow * vcam_render_window
Definition GfxData.h:232
VideoCamRole vcam_role
Definition GfxData.h:217
std::string vcam_off_tex_name
Used when videocamera is offline.
Definition GfxData.h:227
Ogre::RenderTexture * vcam_render_target
Definition GfxData.h:229
std::string vcam_mat_name_orig
For display in Tuning UI: Original material name from rig-def file, without per-actor stamping.
Definition GfxData.h:225
NodeNum_t vcam_node_lookat
Only for VCAM_ROLE_TRACK_CAM.
Definition GfxData.h:222
Ogre::Quaternion vcam_rotation
Definition GfxData.h:223
NodeNum_t vcam_node_dir_y
Definition GfxData.h:219
Ogre::Vector3 vcam_pos_offset
Definition GfxData.h:224
Ogre::SceneNode * vcam_debug_node
Definition GfxData.h:231
Ogre::SceneNode * vcam_prop_scenenode
Only for VCAM_ROLE_MIRROR_PROP_*.
Definition GfxData.h:233
Ogre::MaterialPtr vcam_material
Definition GfxData.h:226
Ogre::Camera * vcam_ogre_camera
Definition GfxData.h:228
NodeNum_t vcam_node_dir_z
Definition GfxData.h:220
Flexable * wx_flex_mesh
Definition GfxData.h:309
WheelID_t wx_wheel_id
Definition GfxData.h:308
std::string wx_rim_mesh_name
Redundant, for Tuning UI. Only for 'meshwheels[2]' and 'flexbodywheels'.
Definition GfxData.h:312
Ogre::SceneNode * wx_scenenode
Definition GfxData.h:310
WheelSide wx_side
Definition GfxData.h:311
Ogre::String email
Definition SimData.h:814
Ogre::String type
Definition SimData.h:812
Ogre::String name
Definition SimData.h:813
Simulation: An edge in the softbody structure.
Definition SimData.h:305
node_t * p1
Definition SimData.h:309
float k
tensile spring
Definition SimData.h:311
BeamType bm_type
Definition SimData.h:322
float refL
reference length
Definition SimData.h:330
float minmaxposnegstress
Definition SimData.h:314
float plastic_coef
Definition SimData.h:319
shock_t * shock
Definition SimData.h:332
float longbound
Definition SimData.h:329
int detacher_group
Attribute: detacher group number (integer)
Definition SimData.h:320
float maxnegstress
Definition SimData.h:316
float shortbound
Definition SimData.h:328
float strength
Definition SimData.h:317
SpecialBeam bounded
Definition SimData.h:321
float d
damping factor
Definition SimData.h:312
float L
length
Definition SimData.h:313
float default_beam_diameter
for export only
Definition SimData.h:336
bool bm_disabled
Definition SimData.h:325
node_t * p2
Definition SimData.h:310
float maxposstress
Definition SimData.h:315
RoR::CmdKeyInertia command_inertia
Definition SimData.h:567
std::vector< commandbeam_t > beams
Definition SimData.h:563
Ogre::String description
Definition SimData.h:565
float cmb_engine_coupling
Attr from truckfile.
Definition SimData.h:539
uint16_t cmb_beam_index
Index to Actor::ar_beams array.
Definition SimData.h:538
bool cmb_is_contraction
Attribute defined at spawn.
Definition SimData.h:545
float cmb_center_length
Attr computed at spawn.
Definition SimData.h:540
bool cmb_needs_engine
Attribute defined in truckfile.
Definition SimData.h:547
bool cmb_is_force_restricted
Attribute defined in truckfile.
Definition SimData.h:546
float cmb_boundary_length
Attr; Maximum/minimum length proportional to orig. len.
Definition SimData.h:542
bool cmb_is_1press
Attribute defined in truckfile.
Definition SimData.h:550
float cmb_speed
Attr; Rate of contraction/extension.
Definition SimData.h:541
bool cmb_is_autocentering
Attribute defined in truckfile.
Definition SimData.h:548
bool cmb_is_1press_center
Attribute defined in truckfile.
Definition SimData.h:551
bool cmb_plays_sound
Attribute defined in truckfile.
Definition SimData.h:549
std::shared_ptr< commandbeam_state_t > cmb_state
Definition SimData.h:553
float blinkdelay_curr
Definition SimData.h:612
float intensity
Definition SimData.h:615
Ogre::Light * light
Definition SimData.h:607
float blinkdelay
Definition SimData.h:611
float offsetx
Definition SimData.h:602
NodeNum_t noderef
Definition SimData.h:599
float offsety
Definition SimData.h:603
Ogre::BillboardSet * bbs
This remains nullptr if removed via addonpart_unwanted_flare or Tuning UI.
Definition SimData.h:606
SimpleInertia inertia
Only 'flares3'.
Definition SimData.h:617
NodeNum_t nodex
Definition SimData.h:600
float size
Definition SimData.h:614
Ogre::SceneNode * snode
Definition SimData.h:605
int dashboard_link
Only 'd' type flares, valid values are DD_*.
Definition SimData.h:610
float offsetz
Definition SimData.h:604
NodeNum_t nodey
Definition SimData.h:601
bool uses_inertia
Only 'flares3'.
Definition SimData.h:616
FlareType fl_type
Definition SimData.h:608
int controlnumber
Only 'u' type flares, valid values 0-9, maps to EV_TRUCK_LIGHTTOGGLE01 to 10.
Definition SimData.h:609
bool blinkdelay_state
Definition SimData.h:613
float hk_timer_preset
Definition SimData.h:471
node_t * hk_lock_node
Definition SimData.h:474
bool hk_selflock
Definition SimData.h:464
float hk_lockspeed
Definition SimData.h:469
int hk_group
Definition SimData.h:462
bool hk_nodisable
Definition SimData.h:466
HookState hk_locked
Definition SimData.h:461
float hk_maxforce
Definition SimData.h:467
float hk_min_length
Absolute value in meters.
Definition SimData.h:472
ActorPtr hk_locked_actor
Definition SimData.h:476
bool hk_autolock
Definition SimData.h:465
float hk_lockrange
Definition SimData.h:468
beam_t * hk_beam
Definition SimData.h:475
node_t * hk_hook_node
Definition SimData.h:473
int hk_lockgroup
Definition SimData.h:463
float hk_timer
Definition SimData.h:470
< beams updating length based on simulation variables, generally known as actuators.
Definition SimData.h:571
BitMask_t hb_flags
Only for 'hydros'.
Definition SimData.h:575
float hb_speed
Rate of change.
Definition SimData.h:574
float hb_anim_param
Only for 'animators'.
Definition SimData.h:577
RoR::CmdKeyInertia hb_inertia
Definition SimData.h:578
uint16_t hb_beam_index
Index to Actor::ar_beams array.
Definition SimData.h:572
BitMask_t hb_anim_flags
Only for 'animators'.
Definition SimData.h:576
float hb_ref_length
Idle length in meters.
Definition SimData.h:573
Physics: A vertex in the softbody structure.
Definition SimData.h:260
bool nd_cinecam_node
Attr; User-defined.
Definition SimData.h:295
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition SimData.h:267
Ogre::Real mass
Definition SimData.h:271
static const int8_t INVALID_BBOX
Definition SimData.h:261
bool nd_no_mouse_grab
Attr; User-defined.
Definition SimData.h:294
bool nd_rim_node
Attr; This node is part of a rim (only wheel types with separate rim nodes)
Definition SimData.h:283
Ogre::Real volume_coef
Definition SimData.h:275
int16_t nd_lockgroup
Optional attribute (-1 = default, 9999 = deny lock) - used in the hook lock logic.
Definition SimData.h:279
bool nd_contacter
Attr; User-defined.
Definition SimData.h:285
Ogre::Real surface_coef
Definition SimData.h:274
Ogre::Vector3 RelPosition
relative to the local physics origin (one origin per actor) (shaky)
Definition SimData.h:266
bool nd_immovable
Attr; User-defined.
Definition SimData.h:289
bool nd_cab_node
Attr; This node is part of collision triangle.
Definition SimData.h:282
bool nd_contactable
Attr; This node will be treated as contacter on inter truck collisions.
Definition SimData.h:286
Ogre::Real buoyancy
Definition SimData.h:272
int16_t nd_coll_bbox_id
Optional attribute (-1 = none) - multiple collision bounding boxes defined in truckfile.
Definition SimData.h:278
bool nd_tyre_node
Attr; This node is part of a tyre (note some wheel types don't use rim nodes at all)
Definition SimData.h:284
Ogre::Real friction_coef
Definition SimData.h:273
bool nd_no_ground_contact
User-defined attr; node ignores contact with ground.
Definition SimData.h:291
bool nd_loaded_mass
User-defined attr; mass is calculated from 'globals/loaded-mass' rather than 'globals/dry-mass' - set...
Definition SimData.h:290
bool nd_override_mass
User defined attr; mass is user-specified rather than calculated (override the calculation)
Definition SimData.h:292
NodeNum_t pos
This node's index in Actor::ar_nodes array.
Definition SimData.h:277
int attached_ties
State.
Definition SimData.h:484
bool multilock
Attribute.
Definition SimData.h:486
int pos
Index into ar_ropables.
Definition SimData.h:482
int attached_ropes
State.
Definition SimData.h:485
node_t * node
Definition SimData.h:481
int rp_group
Definition SimData.h:495
ropable_t * rp_locked_ropable
Definition SimData.h:497
beam_t * rp_beam
Definition SimData.h:496
int rp_locked
Definition SimData.h:494
float engine_coupling
Definition SimData.h:592
NodeNum_t axis2
Definition SimData.h:587
float tolerance
Definition SimData.h:591
NodeNum_t axis1
rot axis
Definition SimData.h:586
bool needs_engine
Definition SimData.h:583
NodeNum_t nodes2[4]
Definition SimData.h:585
NodeNum_t nodes1[4]
Definition SimData.h:584
int trigger_cmdshort
F-key for trigger injection shortbound-check.
Definition SimData.h:354
float sbd_damp
set beam default for damping
Definition SimData.h:375
float sprogin
shocks2
Definition SimData.h:362
float dampin
shocks2 & shocks3
Definition SimData.h:358
float sprogout
shocks2
Definition SimData.h:364
float sbd_break
set beam default for breaking threshold
Definition SimData.h:376
float trigger_switch_state
needed to avoid doubleswitch, bool and timer in one
Definition SimData.h:351
float splitin
shocks3
Definition SimData.h:367
float dfastin
shocks3
Definition SimData.h:369
bool trigger_enabled
general trigger,switch and blocker state
Definition SimData.h:350
int last_debug_state
smart debug output
Definition SimData.h:355
float springout
shocks2 & shocks3
Definition SimData.h:359
float springin
shocks2 & shocks3
Definition SimData.h:357
float trigger_boundary_t
optional value to tune trigger_switch_state autorelease
Definition SimData.h:352
float dampout
shocks2 & shocks3
Definition SimData.h:360
int trigger_cmdlong
F-key for trigger injection longbound-check.
Definition SimData.h:353
float dslowout
shocks3
Definition SimData.h:371
float dprogout
shocks2
Definition SimData.h:365
float splitout
shocks3
Definition SimData.h:370
float dslowin
shocks3
Definition SimData.h:368
float shock_precompression
Only for export.
Definition SimData.h:378
float sbd_spring
set beam default for spring
Definition SimData.h:374
float dfastout
shocks3
Definition SimData.h:372
float dprogin
shocks2
Definition SimData.h:363
NodeNum_t nodenum
Definition SimData.h:395
SoundScriptInstancePtr ssi
Definition SimData.h:394
ActorPtr ti_locked_actor
Definition SimData.h:506
bool ti_tying
State.
Definition SimData.h:516
bool ti_no_self_lock
Attribute.
Definition SimData.h:514
float ti_max_stress
Definition SimData.h:511
int ti_group
Definition SimData.h:509
float ti_min_length
Proportional to orig; length.
Definition SimData.h:512
ropable_t * ti_locked_ropable
Definition SimData.h:508
bool ti_tied
State.
Definition SimData.h:515
float ti_contract_speed
Definition SimData.h:510
beam_t * ti_beam
Definition SimData.h:507
float wh_arg_rim_spring
Not used by 'wheels' (1) and 'meshwheels' (1).
Definition SimData.h:429
float wh_width
Definition SimData.h:422
node_t * wh_axis_node_0
Definition SimData.h:408
node_t * wh_arm_node
Definition SimData.h:406
int wh_num_nodes
Definition SimData.h:401
int wh_arg_num_rays
Definition SimData.h:427
node_t * wh_nodes[50]
Definition SimData.h:402
int wh_num_rim_nodes
Definition SimData.h:403
node_t * wh_axis_node_1
Definition SimData.h:409
WheelPropulsion wh_propulsed
Definition SimData.h:410
Ogre::Real wh_rim_radius
Definition SimData.h:412
node_t * wh_near_attach_node
Definition SimData.h:407
Ogre::Real wh_mass
Total rotational mass of the wheel.
Definition SimData.h:417
float wh_arg_simple_spring
Whole wheel or just tire, depending on type.
Definition SimData.h:431
WheelSide wh_arg_side
Only for 'meshwheels*' and 'flexbodywheels'.
Definition SimData.h:433
node_t * wh_rim_nodes[50]
Definition SimData.h:404
RigDef::Keyword wh_arg_keyword
Definition SimData.h:426
float wh_arg_simple_damping
Whole wheel or just tire, depending on type.
Definition SimData.h:432
NodeNum_t wh_arg_rigidity_node
Definition SimData.h:428
std::string wh_arg_media1
Definition SimData.h:434
Ogre::Real wh_radius
Definition SimData.h:411
float wh_arg_rim_damping
Not used by 'wheels' (1) and 'meshwheels' (1).
Definition SimData.h:430
std::string wh_arg_media2
Definition SimData.h:435
WheelBraking wh_braking
Definition SimData.h:405
int wh_beam_start
BeamID to export 'set_beam_defaults' parameters from.
Definition SimData.h:436
Ogre::SceneNode * cnode
Definition SimData.h:522
FlexAirfoil * fa
Definition SimData.h:521