Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
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 "AirBrake.h"
34#include "Airfoil.h"
35#include "Application.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 "GfxActor.h"
56#include "GfxScene.h"
57#include "Console.h"
58#include "InputEngine.h"
59#include "Language.h"
60#include "MeshObject.h"
61#include "PointColDetector.h"
62#include "Renderdash.h"
63#include "ScrewProp.h"
64#include "Skidmark.h"
65#include "SkinFileFormat.h"
66#include "SlideNode.h"
67#include "SoundScriptManager.h"
68#include "Terrain.h"
69#include "TorqueCurve.h"
70#include "TuneupFileFormat.h"
71#include "TurboJet.h"
72#include "TurboProp.h"
73#include "Utils.h"
74#include "VehicleAI.h"
75
76#include <OgreMaterialManager.h>
77#include <OgreSceneManager.h>
78#include <OgreMovableObject.h>
79#include <OgreParticleSystem.h>
80#include <OgreEntity.h>
81#include <climits>
82#include <fmt/format.h>
83
84using namespace RoR;
85
86/* -------------------------------------------------------------------------- */
87// Prepare for loading
88/* -------------------------------------------------------------------------- */
89
90void ActorSpawner::ConfigureSections(Ogre::String const & sectionconfig, RigDef::DocumentPtr def)
91{
92 m_selected_modules.push_back(def->root_module);
93 if (sectionconfig != "")
94 {
95 auto result = def->user_modules.find(sectionconfig);
96
97 if (result != def->user_modules.end())
98 {
99 m_selected_modules.push_back(result->second);
100 LOG(" == ActorSpawner: Module added to configuration: " + sectionconfig);
101 }
102 else
103 {
104 this->AddMessage(Message::TYPE_WARNING, "Selected module not found: " + sectionconfig);
105 }
106 }
107}
108
110{
111 if (actor->m_working_tuneup_def)
112 {
114
115 for (const std::string& addonpart: actor->m_working_tuneup_def->use_addonparts)
116 {
117 CacheEntryPtr addonpart_entry = App::GetCacheSystem()->FindEntryByFilename(LT_AddonPart, /*partial:*/false, addonpart);
118 if (addonpart_entry)
119 {
120 App::GetCacheSystem()->LoadResource(addonpart_entry);
121
122 AddonPartUtility util;
123 util.ResolveUnwantedAndTweakedElements(actor->m_working_tuneup_def, addonpart_entry);
124 auto module = util.TransformToRigDefModule(addonpart_entry);
125 if (module)
126 {
127 m_selected_modules.push_back(module);
128 actor->m_used_addonpart_entries.push_back(addonpart_entry);
129 LOG(" == ActorSpawner: Addon part added to configuration: " + addonpart);
130 }
131 else
132 {
133 this->AddMessage(Message::TYPE_WARNING, fmt::format(_L("Could not load addon part '{}' (file '{}')"), addonpart_entry->dname, addonpart_entry->fname));
134 }
135 }
136 else
137 {
138 this->AddMessage(Message::TYPE_WARNING, fmt::format(_L("Requested addon part '{}' is not installed"), addonpart));
139 }
140 }
141 }
142}
143
145{
146 for (auto& module: m_selected_modules)
147 {
148 for (RigDef::Assetpack const& assetpack: module->assetpacks)
149 {
150 App::GetCacheSystem()->LoadAssetPack(actor->getUsedActorEntry(), assetpack.filename);
151
152 // Record used assetpack entry
153 CacheEntryPtr assetpack_entry = App::GetCacheSystem()->FindEntryByFilename(LT_AssetPack, /*partial:*/false, assetpack.filename);
154 if (assetpack_entry)
155 {
156 actor->m_used_assetpack_entries.push_back(assetpack_entry);
157 }
158 }
159 }
160}
161
163{
164 // 'nodes'
165 req.num_nodes += module_def->nodes.size();
166 for (auto& def: module_def->nodes)
167 {
169 {
170 req.num_beams += 1;
171 }
172 }
173
174 // 'beams'
175 req.num_beams += module_def->beams.size();
176
177 // 'ties'
178 req.num_beams += module_def->ties.size();
179
180 // 'ropes'
181 req.num_beams += module_def->ropes.size();
182
183 // 'hydros'
184 req.num_beams += module_def->hydros.size();
185
186 // 'triggers'
187 req.num_beams += module_def->triggers.size();
188 req.num_shocks += module_def->triggers.size();
189
190 // 'animators'
191 req.num_beams += module_def->animators.size();
192
193 // 'cinecam'
194 req.num_nodes += module_def->cinecam.size();
195 req.num_beams += module_def->cinecam.size() * 8;
196
197 // 'shocks', 'shocks2', 'shocks3'
198 req.num_beams += module_def->shocks.size();
199 req.num_shocks += module_def->shocks.size();
200 req.num_beams += module_def->shocks2.size();
201 req.num_shocks += module_def->shocks2.size();
202 req.num_beams += module_def->shocks3.size();
203 req.num_shocks += module_def->shocks3.size();
204
205 // 'commands' and 'commands2' (unified)
206 req.num_beams += module_def->commands2.size();
207
208 // 'rotators'
209 req.num_rotators += module_def->rotators.size();
210 req.num_rotators += module_def->rotators2.size();
211
212 // 'wings'
213 req.num_wings += module_def->wings.size();
214
215 // 'wheels'
216 for (RigDef::Wheel& wheel: module_def->wheels)
217 {
218 req.num_nodes += wheel.num_rays * 2; // BuildWheelObjectAndNodes()
219 req.num_beams += wheel.num_rays * ((wheel.rigidity_node.IsValidAnyState()) ? 9 : 8); // BuildWheelBeams()
220 }
221
222 // 'wheels2'
223 for (RigDef::Wheel2& wheel2: module_def->wheels2)
224 {
225 req.num_nodes += wheel2.num_rays * 4;
226 // Rim beams: num_rays*10 (*11 with valid rigidity_node)
227 // Tyre beams: num_rays*14
228 req.num_beams += wheel2.num_rays * ((wheel2.rigidity_node.IsValidAnyState()) ? 25 : 24);
229 }
230
231 // 'meshwheels' & 'meshwheels2'
232 for (RigDef::MeshWheel& meshwheel: module_def->meshwheels)
233 {
234 req.num_nodes += meshwheel.num_rays * 2; // BuildWheelObjectAndNodes()
235 req.num_beams += meshwheel.num_rays * ((meshwheel.rigidity_node.IsValidAnyState()) ? 9 : 8); // BuildWheelBeams()
236 }
237 for (RigDef::MeshWheel2& meshwheel2: module_def->meshwheels2)
238 {
239 req.num_nodes += meshwheel2.num_rays * 2; // BuildWheelObjectAndNodes()
240 req.num_beams += meshwheel2.num_rays * ((meshwheel2.rigidity_node.IsValidAnyState()) ? 9 : 8); // BuildWheelBeams()
241 }
242
243 // 'flexbodywheels'
244 for (RigDef::FlexBodyWheel& flexwheel: module_def->flexbodywheels)
245 {
246 req.num_nodes += flexwheel.num_rays * 4;
247 // Rim beams: num_rays*8
248 // Tyre beams: num_rays*10 (num_rays*11 with valid rigidity_node)
249 // Support beams: num_rays*2
250 req.num_beams += flexwheel.num_rays * ((flexwheel.rigidity_node.IsValidAnyState()) ? 21 : 20);
251 }
252
253 // 'airbrakes'
254 req.num_airbrakes += module_def->airbrakes.size();
255
256 // 'fixes'
257 req.num_fixes += module_def->fixes.size();
258}
259
261{
263 for (auto module: m_selected_modules) // _Root_ module is included
264 {
265 this->CalcMemoryRequirements(req, module.get());
266 }
267
268 // Allocate memory as needed
269 m_actor->ar_beams = new beam_t[req.num_beams];
270 m_actor->ar_beams_invisible.resize(req.num_beams, false);
271 m_actor->ar_beams_user_defined.resize(req.num_beams, false);
272
273 m_actor->ar_nodes = new node_t[req.num_nodes];
274 m_actor->ar_nodes_id = new int[req.num_nodes];
275 for (size_t i = 0; i < req.num_nodes; ++i)
276 {
277 m_actor->ar_nodes_id[i] = -1;
278 }
279 m_actor->ar_nodes_name = new std::string[req.num_nodes];
280 m_actor->ar_nodes_spawn_offsets = new Ogre::Vector3[req.num_nodes];
283 m_actor->ar_nodes_options.resize(req.num_nodes, 0);
284
285 if (req.num_shocks > 0)
287
288 if (req.num_rotators > 0)
290
291 if (req.num_wings > 0)
292 m_actor->ar_wings = new wing_t[req.num_wings];
293
294 m_actor->ar_minimass.resize(req.num_nodes);
295
298 memset(m_actor->ar_collcabs, 0, sizeof(int) * MAX_CABS);
302 memset(m_actor->ar_buoycabs, 0, sizeof(int) * MAX_CABS);
304 memset(m_actor->ar_buoycab_types, 0, sizeof(int) * MAX_CABS);
305 memset(m_actor->m_skid_trails, 0, sizeof(Skidmark *) * (MAX_WHEELS*2));
306
307 m_actor->authors.clear();
308
311
317 m_actor->ar_origin=Ogre::Vector3::ZERO;
318 m_actor->m_slidenodes.clear();
321
323 m_actor->m_fusealge_airfoil = nullptr;
324 m_actor->m_fusealge_front = nullptr;
325 m_actor->m_fusealge_back = nullptr;
327 m_actor->ar_brake_force=30000.0;
329
331
334 for (int i = 0; i < MAX_CAMERAS; ++i)
335 {
339 }
340
341#ifdef USE_ANGELSCRIPT
343#endif // USE_ANGELSCRIPT
344
346 m_actor->alb_minspeed = 0.0f;
347 m_actor->alb_mode = false;
348 m_actor->alb_nodash = true;
349 m_actor->alb_notoggle = false;
350 m_actor->alb_pulse_time = 2000.0f;
351 m_actor->alb_pulse_state = false;
352 m_actor->alb_ratio = 1.0f;
353 m_actor->alb_timer = 0.0f;
355
356 m_actor->cc_mode = false;
357 m_actor->cc_can_brake = false;
358 m_actor->cc_target_rpm = 0.0f;
359 m_actor->cc_target_speed = 0.0f;
361
363
365
367
368 m_actor->sl_enabled = false;
369 m_actor->sl_speed_limit = 0.f;
370
371 m_actor->tc_mode = false;
372 m_actor->tc_nodash = true;
373 m_actor->tc_notoggle = false;
374 m_actor->tc_pulse_time = 2000.0f;
375 m_actor->tc_pulse_state = false;
376 m_actor->tc_ratio = 1.f;
377 m_actor->tc_timer = 0.f;
378
380
381 /* Collisions */
382
383 if (!App::sim_no_collisions->getBool())
384 {
386 }
387
388 if (!App::sim_no_self_collisions->getBool())
389 {
391 }
392
394
395 // Lights mode
397
399
401
402 m_managedmat_placeholder_template = Ogre::MaterialManager::getSingleton().getByName("rigsofrods/managedmaterial-placeholder"); // Built-in
403
406 {
407 m_simple_material_base = Ogre::MaterialManager::getSingleton().getByName("tracks/simple"); // Built-in material
409 {
411 "Failed to load built-in material 'tracks/simple'; disabling 'SimpleMaterials'");
413 }
414 }
415
418}
419
421{
422 // we should post-process the torque curve if existing
423 if (m_actor->ar_engine)
424 {
426 if (result)
427 {
429 if (result == 1)
430 {
431 AddMessage(Message::TYPE_ERROR, "TorqueCurve: Points (rpm) must be in an ascending order. Using default curve");
432 }
433 }
434
435 //Gearbox
436 m_actor->ar_engine->setAutoMode(App::sim_gearbox_mode->getEnum<SimGearboxMode>());
437 }
438
439 // Sanitize trigger_cmdshort and trigger_cmdlong
440 for (int i=0; i<m_actor->ar_num_beams; i++)
441 {
442 shock_t* shock = m_actor->ar_beams[i].shock;
443 if (shock && ((shock->flags & SHOCK_FLAG_TRG_BLOCKER) || (shock->flags & SHOCK_FLAG_TRG_BLOCKER_A)))
444 {
445 shock->trigger_cmdshort = std::min(shock->trigger_cmdshort, m_actor->ar_num_beams - i - 1);
446 shock->trigger_cmdlong = std::min(shock->trigger_cmdlong , m_actor->ar_num_beams - i - 1);
447 }
448 }
449
450 //calculate gwps height offset
451 //get a starting value
453 //start at 0 to avoid a crash whith a 1-node truck
454 for (int i=0; i<m_actor->ar_num_nodes; i++)
455 {
456 // scan and store the y-coord for the lowest node of the truck
458 {
460 }
461 }
462
466
468
469 // Calculate mass of each wheel (without rim)
470 for (int i = 0; i < m_actor->ar_num_wheels; i++)
471 {
472 m_actor->ar_wheels[i].wh_mass = 0.0f;
473 for (int j = 0; j < m_actor->ar_wheels[i].wh_num_nodes; j++)
474 {
476 }
477 }
478
480 {
481 float proped_wheels_radius_sum = 0.0f;
482 for (int i = 0; i < m_actor->ar_num_wheels; i++)
483 {
485 {
486 proped_wheels_radius_sum += m_actor->ar_wheels[i].wh_radius;
487 }
488 }
489 m_actor->m_avg_proped_wheel_radius = proped_wheels_radius_sum / m_actor->m_num_proped_wheels;
490 }
491
492 // Automatically build interwheel differentials from proped wheel pairs
493 if (m_actor->m_num_wheel_diffs == 0)
494 {
495 for (int i = 1; i < m_actor->m_num_proped_wheels; i++)
496 {
497 if (i % 2)
498 {
499 Differential *diff = new Differential();
500
501 diff->di_idx_1 = m_actor->m_proped_wheel_pairs[i - 1];
502 diff->di_idx_2 = m_actor->m_proped_wheel_pairs[i - 0];
503
505
508 }
509 }
510 }
511
512 // Automatically build interaxle differentials from interwheel differentials pairs
513 if (m_actor->m_num_axle_diffs == 0)
514 {
515 for (int i = 1; i < m_actor->m_num_wheel_diffs; i++)
516 {
518 {
521 if ((a1 == i - 1) && (a2 == i - 0))
522 continue;
523 }
524
525 Differential *diff = new Differential();
526
527 diff->di_idx_1 = i - 1;
528 diff->di_idx_2 = i - 0;
529
532 else
534
537 }
538 }
539
540 // Automatically build an additional interaxle differential for the transfer case
542 {
543 Differential *diff = new Differential();
548 }
549
551 {
553 // Step 1: Find a suitable camera node dir
554 float max_dist = 0.0f;
555 NodeNum_t furthest_node = 0;
556 for (int i = 0; i < m_actor->ar_num_nodes; i++)
557 {
558 float dist = m_actor->ar_nodes[i].RelPosition.squaredDistance(ref);
559 if (dist > max_dist)
560 {
561 max_dist = dist;
562 furthest_node = (NodeNum_t)i;
563 }
564 }
565 m_actor->ar_main_camera_node_dir = furthest_node;
566 // Step 2: Correct the misalignment
567 Ogre::Vector3 dir = m_actor->ar_nodes[furthest_node].RelPosition - ref;
568 float offset = atan2(dir.dotProduct(Ogre::Vector3::UNIT_Z), dir.dotProduct(Ogre::Vector3::UNIT_X));
569 m_actor->ar_main_camera_dir_corr = Ogre::Quaternion(Ogre::Radian(offset), Ogre::Vector3::UNIT_Y);
570 }
571
573 {
574 // store the y-difference between the trucks lowest node and the campos-node for the gwps system
576 }
577 else
578 {
579 //this can not be an airplane, just set it to 0.
581 }
582
583 //cameras workaround
584 for (int i=0; i<m_actor->ar_num_cameras; i++)
585 {
588 Ogre::Vector3 cross = dir_node_offset.crossProduct(roll_node_offset);
589
590 m_actor->ar_camera_node_roll_inv[i]=cross.y > 0;
592 {
593 AddMessage(Message::TYPE_WARNING, "camera definition is probably invalid and has been corrected. It should be center, back, left");
594 }
595 }
596
597 //wing closure
598 if (m_first_wing_index!=-1)
599 {
600 if (m_actor->ar_autopilot != nullptr)
601 {
607 );
608 }
609 //inform wing segments
611
614 //wash calculator
616 }
617
619
621
623}
624
625/* -------------------------------------------------------------------------- */
626// Processing functions and utilities.
627/* -------------------------------------------------------------------------- */
628
630{
631 //we will compute wash
632 int w,p;
633 for (p=0; p<m_actor->ar_num_aeroengines; p++)
634 {
635 Ogre::Vector3 prop=m_actor->ar_nodes[m_actor->ar_aeroengines[p]->getNoderef()].RelPosition;
636 float radius=m_actor->ar_aeroengines[p]->getRadius();
637 for (w=0; w<m_actor->ar_num_wings; w++)
638 {
639 //left wash
641 //check if wing is near enough along X (less than 15m back)
642 if (wcent.x>prop.x && wcent.x<prop.x+15.0)
643 {
644 //check if it's okay vertically
645 if (wcent.y>prop.y-radius && wcent.y<prop.y+radius)
646 {
647 //okay, compute wash coverage ratio along Z
648 float wleft=(m_actor->ar_nodes[m_actor->ar_wings[w].fa->nfld].RelPosition).z;
649 float wright=(m_actor->ar_nodes[m_actor->ar_wings[w].fa->nfrd].RelPosition).z;
650 float pleft=prop.z+radius;
651 float pright=prop.z-radius;
652 float aleft=wleft;
653 if (pleft<aleft) aleft=pleft;
654 float aright=wright;
655 if (pright>aright) aright=pright;
656 if (aright<aleft)
657 {
658 //we have a wash
659 float wratio=(aleft-aright)/(wleft-wright);
660 m_actor->ar_wings[w].fa->addwash(p, wratio);
661 Ogre::String msg = "Wing "+TOSTRING(w)+" is washed by prop "+TOSTRING(p)+" at "+TOSTRING((float)(wratio*100.0))+"%";
663 }
664 }
665 }
666 }
667 }
668}
669
671{
672 NodeNum_t front,back,ref;
673 front = GetNodeIndexOrThrow(def.front_node);
674 back = GetNodeIndexOrThrow(def.back_node);
676
677 Turbojet* tj = new Turbojet(m_actor, front, back, ref, def);
678
679 // Visuals
680 std::string nozzle_name = this->ComposeName("nozzle @ turbojet", m_actor->ar_num_aeroengines);
681 Ogre::Entity* nozzle_ent = App::GetGfxScene()->GetSceneManager()->createEntity(nozzle_name, "nozzle.mesh", m_custom_resource_group);
682 this->SetupNewEntity(nozzle_ent, Ogre::ColourValue(1, 0.5, 0.5));
683 Ogre::Entity* afterburn_ent = nullptr;
684 if (def.wet_thrust > 0.f)
685 {
686 std::string flame_name = this->ComposeName("ab flame @ turbojet", m_actor->ar_num_aeroengines);
687 afterburn_ent = App::GetGfxScene()->GetSceneManager()->createEntity(flame_name, "abflame.mesh", m_custom_resource_group);
688 this->SetupNewEntity(afterburn_ent, Ogre::ColourValue(1, 1, 0));
689 }
690 std::string propname = this->ComposeName("turbojet", m_actor->ar_num_aeroengines);
691 tj->tjet_visual.SetNodes(front, back, ref);
693 propname, nozzle_ent, afterburn_ent);
695 {
696 tj->tjet_visual.SetVisible(true);
697 }
698
702 {
704 }
705
707}
708
709std::string ActorSpawner::ComposeName(const std::string& object, int number /* = -1 */)
710{
711 // Under OGRE, each scene node must have a GLOBALLY unique name, even if under different parents.
712 // For that reason the names are intentionally repetitive - the critical part is the Instance ID.
713 // ----------------------------------------------------------------------------------------------
714
715 if (number != -1)
716 {
717 return fmt::format("{}#{} ({} [Instance ID {}])",
718 object, number, m_actor->getTruckFileName(), m_actor->getInstanceId());
719 }
720 else
721 {
722 return fmt::format("{} ({} [Instance ID {}])",
724 }
725}
726
744
746{
747 //parse fusedrag
748 NodeNum_t front_node_idx = GetNodeIndexOrThrow(def.front_node);
749 float width = 1.f;
750 float factor = 1.f;
751 char fusefoil[256];
752 strncpy(fusefoil, def.airfoil_name.c_str(), 255);
753
754 if (def.autocalc)
755 {
756 // fusedrag autocalculation
757
758 // calculate fusedrag by truck size
759 factor = def.area_coefficient;
760 width = (m_fuse_z_max - m_fuse_z_min) * (m_fuse_y_max - m_fuse_y_min) * factor;
761
762 m_actor->m_fusealge_airfoil = new Airfoil(fusefoil);
763
764 m_actor->m_fusealge_front = & m_actor->ar_nodes[front_node_idx];
765 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
766 m_actor->m_fusealge_width = width;
767 AddMessage(Message::TYPE_INFO, "Fusedrag autocalculation size: "+TOSTRING(width)+" m^2");
768 }
769 else
770 {
771 // original fusedrag calculation
772
773 width = def.approximate_width;
774
775 m_actor->m_fusealge_airfoil = new Airfoil(fusefoil);
776
777 m_actor->m_fusealge_front = & m_actor->ar_nodes[front_node_idx];
778 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
779 m_actor->m_fusealge_width = width;
780 }
781}
782
784 NodeNum_t ref_node_index,
785 NodeNum_t back_node_index,
786 NodeNum_t blade_1_node_index,
787 NodeNum_t blade_2_node_index,
788 NodeNum_t blade_3_node_index,
789 NodeNum_t blade_4_node_index,
790 NodeNum_t couplenode_index,
791 bool is_turboprops,
792 Ogre::String const & airfoil,
793 float power,
794 float pitch
795 )
796{
797 int aeroengine_index = m_actor->ar_num_aeroengines;
798
799 Turboprop* turbo_prop = new Turboprop(
800 m_actor,
801 this->ComposeName("turboprop", aeroengine_index).c_str(),
802 ref_node_index,
803 back_node_index,
804 blade_1_node_index,
805 blade_2_node_index,
806 blade_3_node_index,
807 blade_4_node_index,
808 couplenode_index,
809 power,
810 airfoil,
812 ! is_turboprops,
813 pitch
814 );
815
819
820 /* Autopilot */
822 {
824 }
825
826 /* Visuals */
827 float scale = m_actor->ar_nodes[ref_node_index].RelPosition.distance(m_actor->ar_nodes[blade_1_node_index].RelPosition) / 2.25f;
828 for (RoR::Prop& prop: m_actor->m_gfx_actor->m_props)
829 {
830 if ((prop.pp_node_ref == ref_node_index) && (prop.pp_aero_propeller_blade || prop.pp_aero_propeller_spin))
831 {
832 prop.pp_scene_node->scale(scale, scale, scale);
833 prop.pp_aero_engine_idx = aeroengine_index;
834 }
835 }
836}
837
839{
840 const NodeNum_t p3_node_index = (def.blade_tip_nodes[2].IsValidAnyState()) ? GetNodeIndexOrThrow(def.blade_tip_nodes[2]) : -1;
841 const NodeNum_t p4_node_index = (def.blade_tip_nodes[3].IsValidAnyState()) ? GetNodeIndexOrThrow(def.blade_tip_nodes[3]) : -1;
842 const NodeNum_t couple_node_index = (def.couple_node.IsValidAnyState()) ? GetNodeIndexOrThrow(def.couple_node) : -1;
843
849 p3_node_index,
850 p4_node_index,
851 couple_node_index,
852 true,
853 def.airfoil,
855 -10
856 );
857}
858
859void ActorSpawner::ProcessDescription(Ogre::String const& line)
860{
861 m_actor->m_description.push_back(line);
862}
863
865{
866 const NodeNum_t p3_node_index = (def.blade_tip_nodes[2].IsValidAnyState()) ? GetNodeIndexOrThrow(def.blade_tip_nodes[2]) : -1;
867 const NodeNum_t p4_node_index = (def.blade_tip_nodes[3].IsValidAnyState()) ? GetNodeIndexOrThrow(def.blade_tip_nodes[3]) : -1;
868 const NodeNum_t couple_node_index = (def.couple_node.IsValidAnyState()) ? GetNodeIndexOrThrow(def.couple_node) : -1;
869
875 p3_node_index,
876 p4_node_index,
877 couple_node_index,
878 false,
879 def.airfoil,
881 def.pitch
882 );
883}
884
886{
887 const int airbrake_idx = static_cast<int>(m_actor->ar_airbrakes.size());
888 Airbrake* ab = new Airbrake(
889 m_actor,
890 this->ComposeName("airbrake", airbrake_idx).c_str(),
891 airbrake_idx,
896 def.offset,
897 def.width,
898 def.height,
901 def.texcoord_x1,
902 def.texcoord_y1,
903 def.texcoord_x2,
904 def.texcoord_y2,
906 );
907 m_actor->ar_airbrakes.push_back(ab);
908
909 AirbrakeGfx abx;
910 // entity
911 abx.abx_entity = ab->ec;
912 ab->ec = nullptr;
913 // mesh
914 abx.abx_mesh = ab->msh;
915 ab->msh.reset();
916 // scenenode
917 abx.abx_scenenode = ab->snode;
918 ab->snode = nullptr;
919 // offset
920 abx.abx_offset = ab->offset;
921 ab->offset = Ogre::Vector3::ZERO;
922 // Nodes - just copy
923 abx.abx_ref_node = ab->noderef->pos;
924 abx.abx_x_node = ab->nodex->pos;
925 abx.abx_y_node = ab->nodey->pos;
926
927 m_actor->m_gfx_actor->m_gfx_airbrakes.push_back(abx);
928}
929
931{
932 if ((m_first_wing_index != -1) && (m_actor->ar_wings[m_actor->ar_num_wings - 1].fa == nullptr))
933 {
934 this->AddMessage(Message::TYPE_ERROR, "Unable to process wing, previous wing has no Airfoil");
935 return;
936 }
937
938 if (def.airfoil == "") // May happen for malformed `wings` entry in truckfile
939 {
940 this->AddMessage(Message::TYPE_ERROR, "Unable to process wing, no Airfoil defined");
941 return;
942 }
943
944 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
945
946 NodeNum_t node1 = this->GetNodeIndexOrThrow(def.nodes[1]);
947
948 const std::string wing_name = this->ComposeName("wing", m_actor->ar_num_wings);
949 auto flex_airfoil = new FlexAirfoil(
950 wing_name,
951 m_actor,
952 this->GetNodeIndexOrThrow(def.nodes[0]),
953 node1,
954 this->GetNodeIndexOrThrow(def.nodes[2]),
955 this->GetNodeIndexOrThrow(def.nodes[3]),
956 this->GetNodeIndexOrThrow(def.nodes[4]),
957 this->GetNodeIndexOrThrow(def.nodes[5]),
958 this->GetNodeIndexOrThrow(def.nodes[6]),
959 this->GetNodeIndexOrThrow(def.nodes[7]),
961 Ogre::Vector2(def.tex_coords[0], def.tex_coords[1]),
962 Ogre::Vector2(def.tex_coords[2], def.tex_coords[3]),
963 Ogre::Vector2(def.tex_coords[4], def.tex_coords[5]),
964 Ogre::Vector2(def.tex_coords[6], def.tex_coords[7]),
965 (char)def.control_surface,
966 def.chord_point,
967 def.min_deflection,
968 def.max_deflection,
969 def.airfoil,
970 def.efficacy_coef,
972 );
973
974 Ogre::Entity* entity = nullptr;
975 try
976 {
977 const std::string wing_instance_name = this->ComposeName("entity @ wing", m_actor->ar_num_wings);
978 entity = App::GetGfxScene()->GetSceneManager()->createEntity(wing_instance_name, wing_name);
979 m_actor->m_deletion_entities.emplace_back(entity);
980 this->SetupNewEntity(entity, Ogre::ColourValue(0.5, 1, 0));
981 }
982 catch (...)
983 {
984 this->AddMessage(Message::TYPE_ERROR, std::string("Failed to load mesh (flexbody wing): ") + wing_name);
985 delete flex_airfoil;
986 return;
987 }
988
989 // induced drag
990 if (m_first_wing_index == -1)
991 {
994 m_actor->ar_nodes[flex_airfoil->nfld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nfrd].AbsPosition,
995 m_actor->ar_nodes[flex_airfoil->nbld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nbrd].AbsPosition
996 );
997 }
998 else
999 {
1000 wing_t & previous_wing = m_actor->ar_wings[m_actor->ar_num_wings - 1];
1001
1002 if (node1 != previous_wing.fa->nfld)
1003 {
1004 wing_t & start_wing = m_actor->ar_wings[m_first_wing_index];
1005
1006 //discontinuity
1007 //inform wing segments
1008 float span = m_actor->ar_nodes[start_wing.fa->nfrd].RelPosition.distance(m_actor->ar_nodes[previous_wing.fa->nfld].RelPosition );
1009
1010 start_wing.fa->enableInducedDrag(span, m_wing_area, false);
1011 previous_wing.fa->enableInducedDrag(span, m_wing_area, true);
1012
1013 //we want also to add positional lights for first wing
1015 {
1016 //Left green
1017 m_airplane_left_light=previous_wing.fa->nfld;
1018 RoR::Prop left_green_prop;
1019
1020 left_green_prop.pp_node_ref=previous_wing.fa->nfld;
1021 left_green_prop.pp_node_x=previous_wing.fa->nflu;
1022 left_green_prop.pp_node_y=previous_wing.fa->nfld; //ignored
1023 left_green_prop.pp_offset.x=0.5;
1024 left_green_prop.pp_offset.y=0.0;
1025 left_green_prop.pp_offset.z=0.0;
1026 left_green_prop.pp_beacon_rot_angle[0]=0.0;
1027 left_green_prop.pp_beacon_rot_rate[0]=1.0;
1028 left_green_prop.pp_beacon_type='L';
1029 left_green_prop.pp_beacon_light[0]=nullptr; //no light
1030 //the flare billboard
1031 left_green_prop.pp_beacon_scene_node[0] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("left green flare @ wing", m_actor->ar_num_wings));
1032 left_green_prop.pp_beacon_bbs[0]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(this->ComposeName("left green flare bbs @ wing", m_actor->ar_num_wings),1);
1033 left_green_prop.pp_beacon_bbs[0]->createBillboard(0,0,0);
1034 if (left_green_prop.pp_beacon_bbs[0])
1035 {
1036 left_green_prop.pp_beacon_bbs[0]->setVisibilityFlags(DEPTHMAP_DISABLED);
1037 left_green_prop.pp_beacon_bbs[0]->setMaterialName("tracks/greenflare");
1038 left_green_prop.pp_beacon_scene_node[0]->attachObject(left_green_prop.pp_beacon_bbs[0]);
1039 }
1040 left_green_prop.pp_beacon_scene_node[0]->setVisible(false);
1041 left_green_prop.pp_beacon_bbs[0]->setDefaultDimensions(0.5, 0.5);
1042 m_actor->m_gfx_actor->m_props.push_back(left_green_prop);
1043
1044 //Left flash
1045 RoR::Prop left_flash_prop;
1046
1047 left_flash_prop.pp_node_ref=previous_wing.fa->nbld;
1048 left_flash_prop.pp_node_x=previous_wing.fa->nblu;
1049 left_flash_prop.pp_node_y=previous_wing.fa->nbld; //ignored
1050 left_flash_prop.pp_offset.x=0.5;
1051 left_flash_prop.pp_offset.y=0.0;
1052 left_flash_prop.pp_offset.z=0.0;
1053 left_flash_prop.pp_beacon_rot_angle[0]=0.5; //alt
1054 left_flash_prop.pp_beacon_rot_rate[0]=1.0;
1055 left_flash_prop.pp_beacon_type='w';
1056 //light
1057 left_flash_prop.pp_beacon_light[0]=App::GetGfxScene()->GetSceneManager()->createLight(this->ComposeName("left flash light @ wing", m_actor->ar_num_wings));
1058 left_flash_prop.pp_beacon_light[0]->setType(Ogre::Light::LT_POINT);
1059 left_flash_prop.pp_beacon_light[0]->setDiffuseColour( Ogre::ColourValue(1.0, 1.0, 1.0));
1060 left_flash_prop.pp_beacon_light[0]->setSpecularColour( Ogre::ColourValue(1.0, 1.0, 1.0));
1061 left_flash_prop.pp_beacon_light[0]->setAttenuation(50.0, 1.0, 0.3, 0.0);
1062 left_flash_prop.pp_beacon_light[0]->setCastShadows(false);
1063 left_flash_prop.pp_beacon_light[0]->setVisible(false);
1064 //the flare billboard
1065 left_flash_prop.pp_beacon_scene_node[0] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("left flash flare @ wing", m_actor->ar_num_wings));
1066 left_flash_prop.pp_beacon_bbs[0]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(this->ComposeName("left flash flare bbs @ wing", m_actor->ar_num_wings),1);
1067 left_flash_prop.pp_beacon_bbs[0]->createBillboard(0,0,0);
1068 if (left_flash_prop.pp_beacon_bbs[0])
1069 {
1070 left_flash_prop.pp_beacon_bbs[0]->setVisibilityFlags(DEPTHMAP_DISABLED);
1071 left_flash_prop.pp_beacon_bbs[0]->setMaterialName("tracks/flare");
1072 left_flash_prop.pp_beacon_scene_node[0]->attachObject(left_flash_prop.pp_beacon_bbs[0]);
1073 }
1074 left_flash_prop.pp_beacon_scene_node[0]->setVisible(false);
1075 left_flash_prop.pp_beacon_bbs[0]->setDefaultDimensions(1.0, 1.0);
1076 m_actor->m_gfx_actor->m_props.push_back(left_flash_prop);
1077
1078 //Right red
1079 m_airplane_right_light=previous_wing.fa->nfrd;
1080 RoR::Prop right_red_prop;
1081
1082
1083 right_red_prop.pp_node_ref=start_wing.fa->nfrd;
1084 right_red_prop.pp_node_x=start_wing.fa->nfru;
1085 right_red_prop.pp_node_y=start_wing.fa->nfrd; //ignored
1086 right_red_prop.pp_offset.x=0.5;
1087 right_red_prop.pp_offset.y=0.0;
1088 right_red_prop.pp_offset.z=0.0;
1089 right_red_prop.pp_beacon_rot_angle[0]=0.0;
1090 right_red_prop.pp_beacon_rot_rate[0]=1.0;
1091 right_red_prop.pp_beacon_type='R';
1092 right_red_prop.pp_beacon_light[0]=nullptr; /* No light */
1093 //the flare billboard
1094 right_red_prop.pp_beacon_scene_node[0] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("right red flare @ wing", m_actor->ar_num_wings));
1095 right_red_prop.pp_beacon_bbs[0]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(this->ComposeName("right red flare bbs @ wing", m_actor->ar_num_wings),1);
1096 right_red_prop.pp_beacon_bbs[0]->createBillboard(0,0,0);
1097 if (right_red_prop.pp_beacon_bbs[0])
1098 {
1099 right_red_prop.pp_beacon_bbs[0]->setVisibilityFlags(DEPTHMAP_DISABLED);
1100 right_red_prop.pp_beacon_bbs[0]->setMaterialName("tracks/redflare");
1101 right_red_prop.pp_beacon_scene_node[0]->attachObject(right_red_prop.pp_beacon_bbs[0]);
1102 }
1103 right_red_prop.pp_beacon_scene_node[0]->setVisible(false);
1104 right_red_prop.pp_beacon_bbs[0]->setDefaultDimensions(0.5, 0.5);
1105 m_actor->m_gfx_actor->m_props.push_back(right_red_prop);
1106
1107 //Right flash
1108 RoR::Prop right_flash_prop;
1109
1110 right_flash_prop.pp_node_ref=start_wing.fa->nbrd;
1111 right_flash_prop.pp_node_x=start_wing.fa->nbru;
1112 right_flash_prop.pp_node_y=start_wing.fa->nbrd; //ignored
1113 right_flash_prop.pp_offset.x=0.5;
1114 right_flash_prop.pp_offset.y=0.0;
1115 right_flash_prop.pp_offset.z=0.0;
1116 right_flash_prop.pp_beacon_rot_angle[0]=0.5; //alt
1117 right_flash_prop.pp_beacon_rot_rate[0]=1.0;
1118 right_flash_prop.pp_beacon_type='w';
1119 //light
1120 right_flash_prop.pp_beacon_light[0]=App::GetGfxScene()->GetSceneManager()->createLight(this->ComposeName("right flash flare light @ wing", m_actor->ar_num_wings));
1121 right_flash_prop.pp_beacon_light[0]->setType(Ogre::Light::LT_POINT);
1122 right_flash_prop.pp_beacon_light[0]->setDiffuseColour( Ogre::ColourValue(1.0, 1.0, 1.0));
1123 right_flash_prop.pp_beacon_light[0]->setSpecularColour( Ogre::ColourValue(1.0, 1.0, 1.0));
1124 right_flash_prop.pp_beacon_light[0]->setAttenuation(50.0, 1.0, 0.3, 0.0);
1125 right_flash_prop.pp_beacon_light[0]->setCastShadows(false);
1126 right_flash_prop.pp_beacon_light[0]->setVisible(false);
1127 //the flare billboard
1128 right_flash_prop.pp_beacon_scene_node[0] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("right flash flare @ wing", m_actor->ar_num_wings));
1129 right_flash_prop.pp_beacon_bbs[0]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(this->ComposeName("right flash flare bbs @ wing", m_actor->ar_num_wings),1);
1130 right_flash_prop.pp_beacon_bbs[0]->createBillboard(0,0,0);
1131 if (right_flash_prop.pp_beacon_bbs[0] != nullptr)
1132 {
1133 right_flash_prop.pp_beacon_bbs[0]->setVisibilityFlags(DEPTHMAP_DISABLED);
1134 right_flash_prop.pp_beacon_bbs[0]->setMaterialName("tracks/flare");
1135 right_flash_prop.pp_beacon_scene_node[0]->attachObject(right_flash_prop.pp_beacon_bbs[0]);
1136 }
1137 right_flash_prop.pp_beacon_scene_node[0]->setVisible(false);
1138 right_flash_prop.pp_beacon_bbs[0]->setDefaultDimensions(1.0, 1.0);
1139 m_actor->m_gfx_actor->m_props.push_back(right_flash_prop);
1140
1141 m_generate_wing_position_lights = false; // Already done
1142 }
1143
1146 m_actor->ar_nodes[flex_airfoil->nfld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nfrd].AbsPosition,
1147 m_actor->ar_nodes[flex_airfoil->nbld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nbrd].AbsPosition
1148 );
1149 }
1150 else
1151 {
1153 m_actor->ar_nodes[flex_airfoil->nfld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nfrd].AbsPosition,
1154 m_actor->ar_nodes[flex_airfoil->nbld].AbsPosition, m_actor->ar_nodes[flex_airfoil->nbrd].AbsPosition
1155 );
1156 }
1157 }
1158
1159 // Add new wing to rig
1160 m_actor->ar_wings[m_actor->ar_num_wings].fa = flex_airfoil;
1162 m_actor->ar_wings[m_actor->ar_num_wings].cnode->attachObject(entity);
1163
1165}
1166
1167float ActorSpawner::ComputeWingArea(Ogre::Vector3 const & ref, Ogre::Vector3 const & x, Ogre::Vector3 const & y, Ogre::Vector3 const & aref)
1168{
1169 return (((x-ref).crossProduct(y-ref)).length()+((x-aref).crossProduct(y-aref)).length())*0.5f;
1170}
1171
1173{
1174#ifdef USE_OPENAL
1175 NodeNum_t node = ResolveNodeRef(def.node);
1176 if (node == NODENUM_INVALID)
1177 {
1178 return;
1179 }
1181 m_actor,
1183 node,
1184 def.mode
1185 );
1186#endif // USE_OPENAL
1187}
1188
1189void ActorSpawner::AddSoundSourceInstance(ActorPtr const& vehicle, Ogre::String const & sound_script_name, int node_index, int type)
1190{
1191#ifdef USE_OPENAL
1192 AddSoundSource(vehicle, App::GetSoundScriptManager()->createInstance(sound_script_name, vehicle->ar_instance_id), (NodeNum_t)node_index);
1193#endif // USE_OPENAL
1194}
1195
1196void ActorSpawner::AddSoundSource(ActorPtr const& vehicle, SoundScriptInstancePtr sound_script, NodeNum_t node_index, int type)
1197{
1198 if (! CheckSoundScriptLimit(vehicle, 1))
1199 {
1200 return;
1201 }
1202
1203 if (sound_script == nullptr)
1204 {
1205 return;
1206 }
1207
1208 vehicle->ar_soundsources[vehicle->ar_num_soundsources].ssi=sound_script;
1209 vehicle->ar_soundsources[vehicle->ar_num_soundsources].nodenum=node_index;
1210 vehicle->ar_soundsources[vehicle->ar_num_soundsources].type=type;
1211 vehicle->ar_num_soundsources++;
1212}
1213
1215{
1216#ifdef USE_OPENAL
1218 m_actor,
1221 -2
1222 );
1223#endif // USE_OPENAL
1224}
1225
1227{
1228 auto itor = def.nodes.begin();
1229 auto end = def.nodes.end();
1230 for(; itor != end; ++itor)
1231 {
1232 if (! CheckCameraRailLimit(1))
1233 {
1234 return;
1235 }
1238 }
1239}
1240
1249
1254
1256{
1257 if (def.key == "helpMaterial")
1258 {
1260 }
1261 else if (def.key == "speedoMax")
1262 {
1263 float maxKph = PARSEREAL(def.value);
1264 if (maxKph > 10 && maxKph < 32000)
1265 {
1267 }
1268 else
1269 {
1271 fmt::format("Invalid 'speedoMax' ({}), allowed range is <10 -32000>, using default ({})", maxKph, DEFAULT_SPEEDO_MAX_KPH));
1273 }
1274 }
1275 else if (def.key == "useMaxRPM")
1276 {
1278 }
1279 else if (def.key == "shifterAnimTime")
1280 {
1282 }
1283
1284 // NOTE: Dashboard layouts are processed later
1285}
1286
1288{
1289 NodeNum_t node = GetNodeIndexOrThrow(node_ref);
1290 m_actor->ar_nodes[node].nd_immovable = true;
1291}
1292
1294{
1296 {
1297 return;
1298 }
1299
1300 const ExhaustID_t exhaust_id = (ExhaustID_t)m_actor->m_gfx_actor->m_exhausts.size();
1301 Exhaust exhaust;
1302 exhaust.emitterNode = this->GetNodeIndexOrThrow(def.reference_node);
1304
1305 std::string template_name = def.particle_name;
1306 if (template_name == "" || template_name == "default")
1307 {
1308 template_name = "tracks/Smoke"; // defined in `particles/smoke.particle`
1309 }
1310 exhaust.particleSystemName = template_name;
1311
1313 {
1314 std::string name = this->ComposeName(template_name.c_str(), (int)m_actor->m_gfx_actor->m_exhausts.size());
1315 exhaust.smoker = this->CreateParticleSystem(name, template_name);
1316 if (exhaust.smoker == nullptr)
1317 {
1318 std::stringstream msg;
1319 msg << "Failed to create particle system '" << name << "' (template: '" << template_name <<"')";
1320 AddMessage(Message::TYPE_ERROR, msg.str());
1321 return;
1322 }
1323
1324 exhaust.smokeNode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("exhaust", (int)m_actor->m_gfx_actor->m_exhausts.size()));
1325 exhaust.smokeNode->attachObject(exhaust.smoker);
1326 exhaust.smokeNode->setPosition(m_actor->ar_nodes[exhaust.emitterNode].AbsPosition);
1327
1328 m_actor->m_gfx_actor->SetNodeHot(exhaust.emitterNode, true);
1329 m_actor->m_gfx_actor->SetNodeHot(exhaust.directionNode, true);
1330 }
1331
1332 m_actor->m_gfx_actor->m_exhausts.push_back(exhaust);
1333}
1334
1336{
1337 auto module_itor = m_selected_modules.begin();
1338 auto module_end = m_selected_modules.end();
1339 for (; module_itor != module_end; ++module_itor)
1340 {
1341 if (! module_itor->get()->submesh_groundmodel.empty())
1342 {
1343 return module_itor->get()->submesh_groundmodel[0];
1344 }
1345 }
1346 return std::string();
1347};
1348
1350{
1351 if (! CheckSubmeshLimit(1))
1352 {
1353 return;
1354 }
1355
1356 /* TEXCOORDS */
1357
1358 std::vector<RigDef::Texcoord>::iterator texcoord_itor = def.texcoords.begin();
1359 for ( ; texcoord_itor != def.texcoords.end(); texcoord_itor++)
1360 {
1361 if (! CheckTexcoordLimit(1))
1362 {
1363 break;
1364 }
1365
1366 CabTexcoord texcoord;
1367 texcoord.node_id = GetNodeIndexOrThrow(texcoord_itor->node);
1368 texcoord.texcoord_u = texcoord_itor->u;
1369 texcoord.texcoord_v = texcoord_itor->v;
1370 m_oldstyle_cab_texcoords.push_back(texcoord);
1371 }
1372
1373 /* CAB */
1374
1375 auto cab_itor = def.cab_triangles.begin();
1376 auto cab_itor_end = def.cab_triangles.end();
1377 for ( ; cab_itor != cab_itor_end; ++cab_itor)
1378 {
1379 if (! CheckCabLimit(1))
1380 {
1381 return;
1382 }
1383 else if (m_actor->ar_num_collcabs >= MAX_CABS)
1384 {
1385 std::stringstream msg;
1386 msg << "Collcab limit (" << MAX_CABS << ") exceeded";
1387 AddMessage(Message::TYPE_ERROR, msg.str());
1388 return;
1389 }
1390
1391 bool mk_buoyance = false;
1392
1393 m_actor->ar_cabs[m_actor->ar_num_cabs*3]=GetNodeIndexOrThrow(cab_itor->nodes[0]);
1394 m_actor->ar_cabs[m_actor->ar_num_cabs*3+1]=GetNodeIndexOrThrow(cab_itor->nodes[1]);
1395 m_actor->ar_cabs[m_actor->ar_num_cabs*3+2]=GetNodeIndexOrThrow(cab_itor->nodes[2]);
1396
1397 // TODO: Clean this up properly ~ ulteq 10/2018
1398 if (BITMASK_IS_1(cab_itor->options, RigDef::Cab::OPTION_c_CONTACT) ||
1399 BITMASK_IS_1(cab_itor->options, RigDef::Cab::OPTION_p_10xTOUGHER) ||
1401 {
1404 }
1405 if (BITMASK_IS_1(cab_itor->options, RigDef::Cab::OPTION_b_BUOYANT))
1406 {
1410 mk_buoyance = true;
1411 }
1413 {
1417 mk_buoyance = true;
1418 }
1420 {
1424 mk_buoyance = true;
1425 }
1426
1427 if (BITMASK_IS_1(cab_itor->options, RigDef::Cab::OPTION_D_CONTACT_BUOYANT) ||
1430 {
1431
1433 {
1434 std::stringstream msg;
1435 msg << "Collcab limit (" << MAX_CABS << ") exceeded";
1436 AddMessage(Message::TYPE_ERROR, msg.str());
1437 return;
1438 }
1439 else if (m_actor->ar_num_buoycabs >= MAX_CABS)
1440 {
1441 std::stringstream msg;
1442 msg << "Buoycab limit (" << MAX_CABS << ") exceeded";
1443 AddMessage(Message::TYPE_ERROR, msg.str());
1444 return;
1445 }
1446
1452 mk_buoyance = true;
1453 }
1454
1455 if (mk_buoyance && (m_actor->m_buoyance == nullptr))
1456 {
1457 Buoyance* buoy = new Buoyance(App::GetGfxScene()->GetDustPool("splash"), App::GetGfxScene()->GetDustPool("ripple"));
1458 m_actor->m_buoyance.reset(buoy);
1459 }
1461 }
1462
1463 //close the current mesh
1464 CabSubmesh submesh;
1465 submesh.texcoords_pos = m_oldstyle_cab_texcoords.size();
1466 submesh.cabs_pos = static_cast<unsigned int>(m_actor->ar_num_cabs);
1468 m_oldstyle_cab_submeshes.push_back(submesh);
1469
1470 /* BACKMESH */
1471
1472 if (def.backmesh)
1473 {
1474
1475 // Check limit
1476 if (! CheckCabLimit(1))
1477 {
1478 return;
1479 }
1480
1481 // === add an extra front mesh ===
1482 //texcoords
1483 int uv_start = (m_oldstyle_cab_submeshes.size()==1) ? 0 : static_cast<int>((m_oldstyle_cab_submeshes.rbegin()+1)->texcoords_pos);
1484 for (size_t i=uv_start; i<m_oldstyle_cab_submeshes.back().texcoords_pos; i++)
1485 {
1487 }
1488 //cab
1489 int cab_start = (m_oldstyle_cab_submeshes.size()==1) ? 0 : static_cast<int>((m_oldstyle_cab_submeshes.rbegin()+1)->cabs_pos);
1490 for (size_t i=cab_start; i<m_oldstyle_cab_submeshes.back().cabs_pos; i++)
1491 {
1496 }
1497 // Finalize
1498 CabSubmesh submesh;
1500 submesh.texcoords_pos = m_oldstyle_cab_texcoords.size();
1501 submesh.cabs_pos = static_cast<unsigned int>(m_actor->ar_num_cabs);
1502 m_oldstyle_cab_submeshes.push_back(submesh);
1503
1504 // === add an extra back mesh ===
1505 //texcoords
1506 uv_start = (m_oldstyle_cab_submeshes.size()==1) ? 0 : static_cast<int>((m_oldstyle_cab_submeshes.rbegin()+1)->texcoords_pos);
1507 for (size_t i=uv_start; i<m_oldstyle_cab_submeshes.back().texcoords_pos; i++)
1508 {
1510 }
1511
1512 //cab
1513 cab_start = (m_oldstyle_cab_submeshes.size()==1) ? 0 : static_cast<int>((m_oldstyle_cab_submeshes.rbegin()+1)->cabs_pos);
1514 for (size_t i=cab_start; i<m_oldstyle_cab_submeshes.back().cabs_pos; i++)
1515 {
1520 }
1521
1522 //close the current mesh
1523 CabSubmesh submesh2;
1524 submesh2.texcoords_pos = m_oldstyle_cab_texcoords.size();
1525 submesh2.cabs_pos = static_cast<unsigned int>(m_actor->ar_num_cabs);
1527 m_oldstyle_cab_submeshes.push_back(submesh2);
1528 }
1529}
1530
1532{
1533 const FlexbodyID_t flexbody_id = (FlexbodyID_t)m_actor->m_gfx_actor->m_flexbodies.size();
1534
1535 // Check if disabled by .tuneup mod.
1537 {
1538 // Create placeholder
1539 m_actor->m_gfx_actor->m_flexbodies.emplace_back(new FlexBody(FlexBody::PlaceholderType::TUNING_REMOVED_PLACEHOLDER, flexbody_id, def.mesh_name));
1540 return;
1541 }
1542
1543 // Collect 'forset' nodes
1544 std::vector<unsigned int> node_indices;
1545 bool nodes_found = true;
1546 for (auto& node_def: def.node_list)
1547 {
1548 NodeNum_t node = this->ResolveNodeRef(node_def);
1549 if (node == NODENUM_INVALID)
1550 {
1551 nodes_found = false;
1552 break;
1553 }
1554 node_indices.push_back(node);
1555 }
1556
1557 // Resolve 'forvert' nodes
1558 std::vector<ForvertTempData> forverts_tmp;
1559 for (size_t i = 0; i < def.forvert.size(); i++)
1560 {
1561 const auto& forvert_item = def.forvert[i];
1562 ForvertTempData fvtd;
1563 fvtd.vert_index = forvert_item.vert_index;
1564 fvtd.nref = this->ResolveNodeRef(forvert_item.node_ref);
1565 fvtd.nx = this->ResolveNodeRef(forvert_item.node_x);
1566 fvtd.ny = this->ResolveNodeRef(forvert_item.node_y);
1567 if (fvtd.nref == NODENUM_INVALID || fvtd.nx == NODENUM_INVALID || fvtd.ny == NODENUM_INVALID)
1568 {
1569 // Note: although the `RigDef::Node::Ref` was designed to handle numbered/named nodes transparently,
1570 // here we assume nobody will use named nodes with 'forvert' as they're unpopular due to many bugs.
1572 fmt::format("Flexbody {}({}) 'forvert'{}/{}: Some nodes not resolved (nref {}->{}, nx {}->{}, ny {}->{}).",
1573 i, def.forvert.size(), (int)flexbody_id, def.mesh_name,
1574 forvert_item.node_ref.Num(), fvtd.nref != NODENUM_INVALID,
1575 forvert_item.node_x.Num(), fvtd.nx != NODENUM_INVALID,
1576 forvert_item.node_y.Num(), fvtd.ny != NODENUM_INVALID
1577 ));
1578 }
1579 else
1580 {
1581 forverts_tmp.push_back(fvtd);
1582 }
1583 }
1584
1585 if (! nodes_found)
1586 {
1587 this->AddMessage(Message::TYPE_ERROR, "Failed to collect nodes from 'forset' at flexbody: " + def.mesh_name);
1588 // Create placeholder too keep the order of flexbodies (helps Tuning and diagnostics)
1589 m_actor->m_gfx_actor->m_flexbodies.emplace_back(new FlexBody(FlexBody::PlaceholderType::FAULTY_FORSET_PLACEHOLDER, flexbody_id, def.mesh_name));
1590 return;
1591 }
1592
1593 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
1594
1595 try
1596 {
1598 (FlexbodyID_t)m_actor->m_gfx_actor->m_flexbodies.size(),
1599 this->GetNodeIndexOrThrow(def.reference_node),
1600 this->GetNodeIndexOrThrow(def.x_axis_node),
1601 this->GetNodeIndexOrThrow(def.y_axis_node),
1604 node_indices,
1605 forverts_tmp,
1607 TuneupUtil::getTweakedFlexbodyMediaRG(m_actor->getWorkingTuneupDef(), flexbody_id, 0, this->GetCurrentElementMediaRG())
1608 );
1609
1610 // Dynamic visibility - same as with props
1613 m_actor->m_gfx_actor->m_flexbodies.emplace_back(flexbody);
1614 }
1615 catch (Ogre::Exception&)
1616 {
1617 // Ogre::Exception description already logged by OGRE
1618 this->AddMessage(Message::TYPE_ERROR, "Failed to create flexbody '" + def.mesh_name + "'");
1619 // Create placeholder too keep the order of flexbodies (helps Tuning and diagnostics)
1620 m_actor->m_gfx_actor->m_flexbodies.emplace_back(new FlexBody(FlexBody::PlaceholderType::FAULTY_MESH_PLACEHOLDER, flexbody_id, def.mesh_name));
1621 }
1622}
1623
1629
1631{
1632 PropID_t prop_id = static_cast<int>(m_actor->m_gfx_actor->m_props.size());
1633
1634 // Check if removed via .tuneup
1636 {
1637 RoR::Prop pprop; // placeholder
1638 pprop.pp_id = prop_id;
1642 m_actor->m_gfx_actor->m_props.push_back(pprop);
1643 return;
1644 }
1645
1646 RoR::Prop prop;
1647 prop.pp_id = prop_id;
1649 prop.pp_node_x = this->GetNodeIndexOrThrow(def.x_axis_node);
1650 prop.pp_node_y = this->GetNodeIndexOrThrow(def.y_axis_node);
1652 prop.pp_offset_orig = prop.pp_offset;
1654 prop.pp_rot = Ogre::Quaternion(Ogre::Degree(prop.pp_rota.z), Ogre::Vector3::UNIT_Z)
1655 * Ogre::Quaternion(Ogre::Degree(prop.pp_rota.y), Ogre::Vector3::UNIT_Y)
1656 * Ogre::Quaternion(Ogre::Degree(prop.pp_rota.x), Ogre::Vector3::UNIT_X);
1657 prop.pp_camera_mode_active = def.camera_settings.mode; /* Handles default value */
1658 prop.pp_camera_mode_orig = def.camera_settings.mode; /* Handles default value */
1659 prop.pp_wheel_rot_degree = 160.f; // ??
1660
1661 /* SPECIAL PROPS */
1662
1663 /* Rear view mirror (left) */
1665 {
1667 }
1668
1669 /* Rear view mirror (right) */
1671 {
1673 }
1674
1675 /* Custom steering wheel */
1676 Ogre::Vector3 steering_wheel_offset = Ogre::Vector3::ZERO;
1678 {
1679 steering_wheel_offset = Ogre::Vector3(-0.67, -0.61,0.24);
1680 }
1682 {
1683 steering_wheel_offset = Ogre::Vector3(0.67, -0.61,0.24);
1684 }
1685 if (steering_wheel_offset != Ogre::Vector3::ZERO)
1686 {
1688 {
1689 steering_wheel_offset = def.special_prop_dashboard.offset;
1690 }
1692 prop.pp_wheel_scene_node = m_props_parent_scenenode->createChildSceneNode(this->ComposeName("steering wheel @ prop", prop_id));
1693 prop.pp_wheel_pos = steering_wheel_offset;
1695 prop.pp_wheel_mesh_obj = new MeshObject(
1696 prop.pp_media[1],
1697 TuneupUtil::getTweakedPropMediaRG(m_actor->getWorkingTuneupDef(), prop_id, 1, this->GetCurrentElementMediaRG()),
1698 this->ComposeName("steering wheel entity @ prop", prop_id),
1700 );
1701 this->SetupNewEntity(prop.pp_wheel_mesh_obj->getEntity(), Ogre::ColourValue(0, 0.5, 0.5));
1702 }
1703
1704 /* CREATE THE PROP */
1705 prop.pp_scene_node = m_props_parent_scenenode->createChildSceneNode(this->ComposeName("prop", prop_id));
1707 prop.pp_mesh_obj = new MeshObject(
1708 prop.pp_media[0],
1709 TuneupUtil::getTweakedPropMediaRG(m_actor->getWorkingTuneupDef(), prop_id, 0, this->GetCurrentElementMediaRG()),
1710 this->ComposeName("prop entity", prop_id),
1711 prop.pp_scene_node);
1712
1713 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'
1714
1716 {
1717 prop.pp_aero_propeller_spin = true;
1718 prop.pp_mesh_obj->setCastShadows(false);
1719 prop.pp_scene_node->setVisible(false);
1720 }
1722 {
1723 prop.pp_aero_propeller_blade = true;
1724 }
1726 {
1727 //driver seat, used to position the driver and make the seat translucent at times
1728 if (m_actor->m_gfx_actor->m_driverseat_prop_index == -1)
1729 {
1730 m_actor->m_gfx_actor->m_driverseat_prop_index = prop_id;
1731 prop.pp_mesh_obj->setMaterialName("driversseat");
1732 }
1733 else
1734 {
1735 this->AddMessage(Message::TYPE_INFO, "Found more than one 'seat[2]' special props. Only the first one will be the driver's seat.");
1736 }
1737 }
1739 {
1740 // Same as DRIVER_SEAT, except it doesn't force the "driversseat" material
1741 if (m_actor->m_gfx_actor->m_driverseat_prop_index == -1)
1742 {
1743 m_actor->m_gfx_actor->m_driverseat_prop_index = prop_id;
1744 }
1745 else
1746 {
1747 this->AddMessage(Message::TYPE_INFO, "Found more than one 'seat[2]' special props. Only the first one will be the driver's seat.");
1748 }
1749 }
1751 {
1753 {
1754 prop.pp_beacon_type = 'b';
1755 prop.pp_beacon_rot_angle[0] = 2.0 * 3.14 * frand();
1756 prop.pp_beacon_rot_rate[0] = 4.0 * 3.14 + frand() - 0.5;
1757 /* the light */
1758 auto pp_beacon_light = App::GetGfxScene()->GetSceneManager()->createLight();
1759 pp_beacon_light->setType(Ogre::Light::LT_SPOTLIGHT);
1760 pp_beacon_light->setDiffuseColour(def.special_prop_beacon.color);
1761 pp_beacon_light->setSpecularColour(def.special_prop_beacon.color);
1762 pp_beacon_light->setAttenuation(50.0, 1.0, 0.3, 0.0);
1763 pp_beacon_light->setSpotlightRange( Ogre::Degree(35), Ogre::Degree(45) );
1764 pp_beacon_light->setCastShadows(false);
1765 pp_beacon_light->setVisible(false);
1766 /* the flare billboard */
1768 auto flare_scene_node = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("beacon @ prop", prop_id));
1769 auto flare_billboard_sys = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1); //(propname,1);
1770 if (flare_billboard_sys)
1771 {
1772 flare_billboard_sys->createBillboard(0,0,0);
1773 flare_billboard_sys->setMaterialName(prop.pp_media[1]);
1774 flare_billboard_sys->setVisibilityFlags(DEPTHMAP_DISABLED);
1775 flare_scene_node->attachObject(flare_billboard_sys);
1776 }
1777 flare_scene_node->setVisible(false);
1778
1779 // Complete
1780 prop.pp_beacon_scene_node[0] = flare_scene_node;
1781 prop.pp_beacon_bbs[0] = flare_billboard_sys;
1782 prop.pp_beacon_light[0] = pp_beacon_light;
1783 }
1785 {
1786 prop.pp_beacon_rot_angle[0] = 0.f;
1787 prop.pp_beacon_rot_rate[0] = 1.0;
1788 prop.pp_beacon_type = 'r';
1789 //the light
1790 auto pp_beacon_light=App::GetGfxScene()->GetSceneManager()->createLight();//propname);
1791 pp_beacon_light->setType(Ogre::Light::LT_POINT);
1792 pp_beacon_light->setDiffuseColour( Ogre::ColourValue(1.0, 0.0, 0.0));
1793 pp_beacon_light->setSpecularColour( Ogre::ColourValue(1.0, 0.0, 0.0));
1794 pp_beacon_light->setAttenuation(50.0, 1.0, 0.3, 0.0);
1795 pp_beacon_light->setCastShadows(false);
1796 pp_beacon_light->setVisible(false);
1797 //the flare billboard
1798 auto flare_scene_node = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("redbeacon @ prop", prop_id));
1799 auto flare_billboard_sys = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1); //propname,1);
1800 if (flare_billboard_sys)
1801 {
1802 flare_billboard_sys->createBillboard(0,0,0);
1803 flare_billboard_sys->setMaterialName("tracks/redbeaconflare");
1804 flare_billboard_sys->setVisibilityFlags(DEPTHMAP_DISABLED);
1805 flare_billboard_sys->setDefaultDimensions(1.0, 1.0);
1806 flare_scene_node->attachObject(flare_billboard_sys);
1807 }
1808 flare_scene_node->setVisible(false);
1809
1810 // Finalize
1811 prop.pp_beacon_light[0] = pp_beacon_light;
1812 prop.pp_beacon_scene_node[0] = flare_scene_node;
1813 prop.pp_beacon_bbs[0] = flare_billboard_sys;
1814
1815 }
1817 {
1818 m_actor->ar_is_police = true;
1819 prop.pp_beacon_type='p';
1820 for (int k=0; k<4; k++)
1821 {
1822 prop.pp_beacon_rot_angle[k] = 2.0 * 3.14 * frand();
1823 prop.pp_beacon_rot_rate[k] = 4.0 * 3.14 + frand() - 0.5;
1824 prop.pp_beacon_bbs[k] = nullptr;
1825 //the light
1826 prop.pp_beacon_light[k]=App::GetGfxScene()->GetSceneManager()->createLight();
1827 prop.pp_beacon_light[k]->setType(Ogre::Light::LT_SPOTLIGHT);
1828 if (k>1)
1829 {
1830 prop.pp_beacon_light[k]->setDiffuseColour( Ogre::ColourValue(1.0, 0.0, 0.0));
1831 prop.pp_beacon_light[k]->setSpecularColour( Ogre::ColourValue(1.0, 0.0, 0.0));
1832 }
1833 else
1834 {
1835 prop.pp_beacon_light[k]->setDiffuseColour( Ogre::ColourValue(0.0, 0.5, 1.0));
1836 prop.pp_beacon_light[k]->setSpecularColour( Ogre::ColourValue(0.0, 0.5, 1.0));
1837 }
1838 prop.pp_beacon_light[k]->setAttenuation(50.0, 1.0, 0.3, 0.0);
1839 prop.pp_beacon_light[k]->setSpotlightRange( Ogre::Degree(35), Ogre::Degree(45) );
1840 prop.pp_beacon_light[k]->setCastShadows(false);
1841 prop.pp_beacon_light[k]->setVisible(false);
1842 //the flare billboard
1843 prop.pp_beacon_scene_node[k] = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName(fmt::format("lightbar {}/4 @ prop", k), prop_id));
1844 prop.pp_beacon_bbs[k]=App::GetGfxScene()->GetSceneManager()->createBillboardSet(1);
1845 prop.pp_beacon_bbs[k]->createBillboard(0,0,0);
1846 if (prop.pp_beacon_bbs[k])
1847 {
1848 if (k>1)
1849 {
1850 prop.pp_beacon_bbs[k]->setMaterialName("tracks/brightredflare");
1851 }
1852 else
1853 {
1854 prop.pp_beacon_bbs[k]->setMaterialName("tracks/brightblueflare");
1855 }
1856
1857 prop.pp_beacon_bbs[k]->setVisibilityFlags(DEPTHMAP_DISABLED);
1858 prop.pp_beacon_scene_node[k]->attachObject(prop.pp_beacon_bbs[k]);
1859 }
1860 prop.pp_beacon_scene_node[k]->setVisible(false);
1861 }
1862 }
1863
1865 {
1867 }
1868 }
1869
1870 this->SetupNewEntity(prop.pp_mesh_obj->getEntity(), Ogre::ColourValue(1.f, 1.f, 0.f));
1871
1874
1875 /* PROCESS ANIMATIONS */
1876
1877 for (RigDef::Animation& anim_def: def.animations)
1878 {
1879 PropAnim anim;
1880
1881 /* Arg #1: ratio */
1882 anim.animratio = anim_def.ratio;
1883 if (anim_def.ratio == 0)
1884 {
1885 std::stringstream msg;
1886 msg << "Prop (mesh: " << def.mesh_name << ") has invalid animation ratio (0), using it anyway (compatibility)...";
1888 }
1889
1890 /* Arg #2: option1 (lower limit) */
1891 anim.lower_limit = anim_def.lower_limit; /* Handles default */
1892
1893 /* Arg #3: option2 (upper limit) */
1894 anim.upper_limit = anim_def.upper_limit; /* Handles default */
1895
1896 /* Arg #4: source */
1897 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_AIRSPEED)) { /* (NOTE: code formatting relaxed) */
1899 }
1902 }
1905 anim.animOpt3 = 1.f;
1906 }
1909 anim.animOpt3 = 2.f;
1910 }
1913 anim.animOpt3 = 3.f;
1914 }
1917 }
1918 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_FLAP)) {
1920 }
1921 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_AIR_BRAKE)) {
1923 }
1924 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_ROLL)) {
1926 }
1927 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_PITCH)) {
1929 }
1930 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_BRAKES)) {
1932 }
1933 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_ACCEL)) {
1935 }
1936 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_CLUTCH)) {
1938 }
1939 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_SPEEDO)) {
1941 }
1942 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_TACHO)) {
1944 }
1945 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_TURBO)) {
1947 }
1948 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_PARKING)) {
1950 }
1953 anim.animOpt3 = SHIFTERMAN1;
1954 }
1957 anim.animOpt3 = SHIFTERMAN2;
1958 }
1961 anim.animOpt3 = SHIFTERSEQ;
1962 }
1963 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_SHIFTERLIN)) {
1965 anim.animOpt3 = SHIFTERLIN;
1966 }
1969 anim.animOpt3 = AUTOSHIFTERLIN;
1970 }
1971 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_TORQUE)) {
1973 }
1974 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_HEADING)) {
1976 }
1977 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_DIFFLOCK)) {
1979 }
1982 }
1983 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_AILERON)) {
1985 }
1986 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_ELEVATOR)) {
1988 }
1989 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_AIR_RUDDER)) {
1991 }
1994 }
1997 }
1998 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_PERMANENT)) {
2000 }
2003 }
2006 anim.animOpt3 = -1;
2007 }
2010 anim.animOpt3 = 0;
2011 }
2012
2013 /* Motor/Gear-indexed sources */
2014 std::list<RigDef::Animation::MotorSource>::iterator source_itor = anim_def.motor_sources.begin();
2015 for ( ; source_itor != anim_def.motor_sources.end(); source_itor++)
2016 {
2017 // aeroengines
2020 anim.animOpt3 = static_cast<float>(source_itor->motor);
2021 }
2024 anim.animOpt3 = static_cast<float>(source_itor->motor);
2025 }
2028 anim.animOpt3 = static_cast<float>(source_itor->motor);
2029 }
2032 anim.animOpt3 = static_cast<float>(source_itor->motor);
2033 }
2036 anim.animOpt3 = static_cast<float>(source_itor->motor);
2037 }
2038
2039 // gears (hack)
2042 anim.animOpt3 = static_cast<float>(source_itor->motor);
2043 }
2044 }
2045
2046 // Source 'event' - make sure there is parameter 'event:'
2047 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_EVENT) &&
2048 anim_def.event_name != "")
2049 {
2050 int event_id = RoR::App::GetInputEngine()->resolveEventName(anim_def.event_name);
2051 if (event_id == -1)
2052 {
2053 AddMessage(Message::TYPE_ERROR, "Unknown animation event: " + anim_def.event_name);
2054 }
2055 else
2056 {
2057 PropAnimKeyState state;
2059 state.event_id = static_cast<events>(event_id);
2060 m_actor->m_prop_anim_key_states.push_back(state);
2062 }
2063 }
2064
2065 // Source 'dashboard' - make sure there is parameter 'link:'
2066 if (BITMASK_IS_1(anim_def.source, RigDef::Animation::SOURCE_DASHBOARD) &&
2067 anim_def.dash_link_name != "")
2068 {
2069 int link_id = m_actor->ar_dashboard->getLinkIDForName(anim_def.dash_link_name);
2070 if (link_id == -1)
2071 {
2072 AddMessage(Message::TYPE_ERROR, "Unknown animation dashboard link: " + anim_def.dash_link_name);
2073 }
2074 else
2075 {
2077 anim.animOpt3 = static_cast<float>(link_id);
2078 }
2079 }
2080
2081 if (anim.animFlags == 0)
2082 {
2083 AddMessage(Message::TYPE_ERROR, fmt::format("Prop (mesh: '{}') will not be animated, no valid `source` was set.", def.mesh_name));
2084 }
2085
2086 /* Anim modes */
2089 }
2092 }
2095 }
2096 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_X)) {
2098 }
2099 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_Y)) {
2101 }
2102 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_Z)) {
2104 }
2105 if (anim.animMode == 0)
2106 {
2107 AddMessage(Message::TYPE_ERROR, fmt::format("Prop (mesh: '{}') will not be animated, no valid `mode` was set.", def.mesh_name));
2108 }
2109
2111 {
2113
2114 // Flag whether default lower and/or upper animation limit constraints are effective
2115 const bool use_default_lower_limit = (anim_def.lower_limit == 0.f);
2116 const bool use_default_upper_limit = (anim_def.upper_limit == 0.f);
2117
2119 anim.lower_limit = (use_default_lower_limit) ? (-180.f) : (anim_def.lower_limit + prop.pp_rota.x);
2120 anim.upper_limit = (use_default_upper_limit) ? ( 180.f) : (anim_def.upper_limit + prop.pp_rota.x);
2121 }
2123 anim.lower_limit = (use_default_lower_limit) ? (-180.f) : (anim_def.lower_limit + prop.pp_rota.y);
2124 anim.upper_limit = (use_default_upper_limit) ? ( 180.f) : (anim_def.upper_limit + prop.pp_rota.y);
2125 }
2127 anim.lower_limit = (use_default_lower_limit) ? (-180.f) : (anim_def.lower_limit + prop.pp_rota.z);
2128 anim.upper_limit = (use_default_upper_limit) ? ( 180.f) : (anim_def.upper_limit + prop.pp_rota.z);
2129 }
2130 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_X)) {
2131 anim.lower_limit = (use_default_lower_limit) ? (-10.f) : (anim_def.lower_limit + prop.pp_offset_orig.x);
2132 anim.upper_limit = (use_default_upper_limit) ? ( 10.f) : (anim_def.upper_limit + prop.pp_offset_orig.x);
2133 }
2134 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_Y)) {
2135 anim.lower_limit = (use_default_lower_limit) ? (-10.f) : (anim_def.lower_limit + prop.pp_offset_orig.y);
2136 anim.upper_limit = (use_default_upper_limit) ? ( 10.f) : (anim_def.upper_limit + prop.pp_offset_orig.y);
2137 }
2138 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_OFFSET_Z)) {
2139 anim.lower_limit = (use_default_lower_limit) ? (-10.f) : (anim_def.lower_limit + prop.pp_offset_orig.z);
2140 anim.upper_limit = (use_default_upper_limit) ? ( 10.f) : (anim_def.upper_limit + prop.pp_offset_orig.z);
2141 }
2142 }
2143 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_NO_FLIP))
2144 {
2146 }
2147 if (BITMASK_IS_1(anim_def.mode, RigDef::Animation::MODE_BOUNCE))
2148 {
2150 anim.animOpt5 = 1.f;
2151 }
2152
2153 prop.pp_animations.push_back(anim);
2154 }
2155
2156 m_actor->m_gfx_actor->m_props.push_back(prop);
2157}
2158
2160{
2161 // Handles both 'flares' and 'flares2' (both use struct `Flare2`)
2162 // --------------------------------------------------------------
2163
2164 if (m_actor->m_flares_mode == GfxFlaresMode::NONE) { return; }
2165
2166 // Do the common processing
2167 this->AddBaseFlare(def);
2168}
2169
2171{
2172 if (m_actor->m_flares_mode == GfxFlaresMode::NONE) { return; }
2173
2174 // Do the common processing
2175 this->AddBaseFlare(def);
2176
2177 // Now setup the extra inertia feature
2178 flare_t& f = m_actor->ar_flares.back();
2179 f.uses_inertia = true;
2181
2182 // Also create unique copy of the material, so we can adjust opacity via Ogre::Material to simulate incandescence.
2183 f.bbs->setMaterial(f.bbs->getMaterial()->clone(f.snode->getName() + "_mat"));
2184
2185}
2186
2188{
2189 if (m_actor->m_flares_mode == GfxFlaresMode::NONE) { return; }
2190
2191 int blink_delay = def.blink_delay_milis;
2192 float size = def.size;
2193 const FlareID_t flare_id = static_cast<FlareID_t>(m_actor->ar_flares.size());
2194 const bool is_placeholder = TuneupUtil::isFlareAnyhowRemoved(m_actor->getWorkingTuneupDef(), flare_id);
2195
2196 /* Backwards compatibility */
2197 if (blink_delay == -2)
2198 {
2200 {
2201 blink_delay = -1; /* Default blink */
2202 }
2203 else
2204 {
2205 blink_delay = 0; /* Default no blink */
2206 }
2207 }
2208
2209 if (size == -2.f && def.type == FlareType::HEADLIGHT)
2210 {
2211 size = 1.f;
2212 }
2213 else if ((size == -2.f && def.type != FlareType::HEADLIGHT) || size == -1.f)
2214 {
2215 size = 0.5f;
2216 }
2217
2218 flare_t flare;
2219 flare.fl_type = def.type;
2220 flare.controlnumber = -1;
2221 flare.blinkdelay = (blink_delay == -1) ? 0.5f : blink_delay / 1000.f;
2222 flare.blinkdelay_curr = 0.f;
2223 flare.blinkdelay_state = false;
2227 flare.offsetx = def.offset.x;
2228 flare.offsety = def.offset.y;
2229 flare.offsetz = def.offset.z;
2230 flare.size = size;
2231
2232 if (def.type == FlareType::USER)
2233 {
2234 // control number: convert from 1-10 to 0-9
2235 if (def.control_number == 12) // Special case - legacy parking brake indicator
2236 {
2239 }
2240 else if (def.control_number < 1)
2241 {
2243 fmt::format("Bad flare control num {}, must be 1-{}, using 1.",
2245 flare.controlnumber = 0;
2246 }
2247 else if (def.control_number > MAX_CLIGHTS)
2248 {
2250 fmt::format("Bad flare control num {}, must be 1-{}, using {}.",
2252 flare.controlnumber = MAX_CLIGHTS-1;
2253 }
2254 else
2255 {
2256 flare.controlnumber = def.control_number - 1;
2257 }
2258 }
2259
2260 if (def.type == FlareType::DASHBOARD)
2261 {
2263 if (flare.dashboard_link == -1)
2264 {
2266 fmt::format("Skipping 'd' flare, invalid input link '{}'", def.dashboard_link));
2267 return;
2268 }
2269 }
2270
2271 /* Visuals */
2272 std::string flare_name = this->ComposeName("Flare", flare_id);
2273 if (!is_placeholder)
2274 {
2275 flare.snode = m_flares_parent_scenenode->createChildSceneNode(this->ComposeName("flareX", flare_id));
2276 flare.bbs = App::GetGfxScene()->GetSceneManager()->createBillboardSet(flare_name, 1);
2277 }
2278
2279 // Backwards compatibility:
2280 // before 't' (tail light) was introduced in 2022, tail lights were indicated as 'f' (headlight) + custom material.
2281 bool using_default_material = (def.material_name.length() == 0 || def.material_name == "default");
2282 if (flare.fl_type == FlareType::HEADLIGHT && !using_default_material)
2283 {
2285 }
2286
2287 if (flare.bbs == nullptr)
2288 {
2289 AddMessage(Message::TYPE_WARNING, "Failed to create flare: '" + flare_name + "', continuing without it (compatibility)...");
2290 }
2291 else
2292 {
2293 flare.bbs->createBillboard(0,0,0);
2294 flare.bbs->setVisibilityFlags(DEPTHMAP_DISABLED);
2295 std::string material_name = def.material_name;
2296 if (using_default_material)
2297 {
2298 if (flare.fl_type == FlareType::BRAKE_LIGHT)
2299 {
2300 material_name = "tracks/brakeflare";
2301 }
2302 else if (flare.fl_type == FlareType::BLINKER_LEFT || (flare.fl_type == FlareType::BLINKER_RIGHT))
2303 {
2304 material_name = "tracks/blinkflare";
2305 }
2306 else if (flare.fl_type == FlareType::DASHBOARD)
2307 {
2308 material_name = "tracks/greenflare";
2309 }
2310 else if (flare.fl_type == FlareType::TAIL_LIGHT)
2311 {
2312 material_name = "tracks/redflare";
2313 }
2314 else
2315 {
2316 material_name = "tracks/flare";
2317 }
2318 }
2319
2320 Ogre::MaterialPtr material = this->FindOrCreateCustomizedMaterial(material_name, this->GetCurrentElementMediaRG());
2321 if (material)
2322 {
2323 flare.bbs->setMaterial(material);
2324 flare.snode->attachObject(flare.bbs);
2325 }
2326 }
2327 flare.intensity = 1.f;
2328 flare.light = nullptr;
2329
2330 if ((App::gfx_flares_mode->getEnum<GfxFlaresMode>() >= GfxFlaresMode::CURR_VEHICLE_HEAD_ONLY) && size > 0.001 && !is_placeholder)
2331 {
2332 if (flare.fl_type == FlareType::HEADLIGHT)
2333 {
2334 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2335 flare.light->setType(Ogre::Light::LT_SPOTLIGHT);
2336 flare.light->setDiffuseColour( Ogre::ColourValue(1, 1, 1));
2337 flare.light->setSpecularColour( Ogre::ColourValue(1, 1, 1));
2338 flare.light->setAttenuation(200, 0.9, 0, 0);
2339 flare.light->setSpotlightRange( Ogre::Degree(35), Ogre::Degree(45) );
2340 flare.light->setCastShadows(false);
2341 }
2342 else if (flare.fl_type == FlareType::HIGH_BEAM)
2343 {
2344 flare.light = App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2345 flare.light->setType(Ogre::Light::LT_SPOTLIGHT);
2346 flare.light->setDiffuseColour(Ogre::ColourValue(1, 1, 1));
2347 flare.light->setSpecularColour(Ogre::ColourValue(1, 1, 1));
2348 flare.light->setAttenuation(400, 0.9, 0, 0);
2349 flare.light->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(45));
2350 flare.light->setCastShadows(false);
2351 }
2352 else if (flare.fl_type == FlareType::FOG_LIGHT)
2353 {
2354 flare.light = App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2355 flare.light->setType(Ogre::Light::LT_SPOTLIGHT);
2356 flare.light->setDiffuseColour(Ogre::ColourValue(1, 1, 1));
2357 flare.light->setSpecularColour(Ogre::ColourValue(1, 1, 1));
2358 flare.light->setAttenuation(400, 0.9, 0, 0);
2359 flare.light->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(45));
2360 flare.light->setCastShadows(false);
2361 }
2362 }
2363 if ((App::gfx_flares_mode->getEnum<GfxFlaresMode>() >= GfxFlaresMode::ALL_VEHICLES_ALL_LIGHTS) && size > 0.001 && !is_placeholder)
2364 {
2365 if (flare.fl_type == FlareType::TAIL_LIGHT)
2366 {
2367 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2368 flare.light->setDiffuseColour( Ogre::ColourValue(1.0, 0, 0));
2369 flare.light->setSpecularColour( Ogre::ColourValue(1.0, 0, 0));
2370 flare.light->setAttenuation(10.0, 1.0, 0, 0);
2371 }
2372 else if (flare.fl_type == FlareType::REVERSE_LIGHT)
2373 {
2374 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2375 flare.light->setDiffuseColour(Ogre::ColourValue(1, 1, 1));
2376 flare.light->setSpecularColour(Ogre::ColourValue(1, 1, 1));
2377 flare.light->setAttenuation(20.0, 1, 0, 0);
2378 }
2379 else if (flare.fl_type == FlareType::BRAKE_LIGHT)
2380 {
2381 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2382 flare.light->setDiffuseColour( Ogre::ColourValue(1.0, 0, 0));
2383 flare.light->setSpecularColour( Ogre::ColourValue(1.0, 0, 0));
2384 flare.light->setAttenuation(10.0, 1.0, 0, 0);
2385 }
2386 else if (flare.fl_type == FlareType::BLINKER_LEFT || (flare.fl_type == FlareType::BLINKER_RIGHT))
2387 {
2388 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2389 flare.light->setDiffuseColour( Ogre::ColourValue(1, 1, 0));
2390 flare.light->setSpecularColour( Ogre::ColourValue(1, 1, 0));
2391 flare.light->setAttenuation(10.0, 1, 1, 0);
2392 }
2393 else if (flare.fl_type == FlareType::USER)
2394 {
2395 flare.light=App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2396 flare.light->setDiffuseColour( Ogre::ColourValue(1, 1, 1));
2397 flare.light->setSpecularColour( Ogre::ColourValue(1, 1, 1));
2398 flare.light->setAttenuation(1.0, 1.0, 1, 0.2);
2399 }
2400 else if (flare.fl_type == FlareType::SIDELIGHT)
2401 {
2402 flare.light = App::GetGfxScene()->GetSceneManager()->createLight(flare_name);
2403 flare.light->setDiffuseColour(Ogre::ColourValue(1, 1, 1));
2404 flare.light->setSpecularColour(Ogre::ColourValue(1, 1, 1));
2405 flare.light->setAttenuation(5.0, 1.0, 1, 0.2);
2406 }
2407 }
2408
2409 /* Finalize light */
2410 if (flare.light != nullptr)
2411 {
2412 flare.light->setType(Ogre::Light::LT_SPOTLIGHT);
2413 flare.light->setSpotlightRange( Ogre::Degree(35), Ogre::Degree(45) );
2414 flare.light->setCastShadows(false);
2415 }
2416 m_actor->ar_flares.push_back(flare);
2417}
2418
2420{
2421 LOG(fmt::format("[RoR|ActorSpawner] processing FlaregroupNoImport ({} {})", (char)def.type, def.control_number));
2422 switch (def.type)
2423 {
2433 //case FlareType::DASHBOARD: ~ Not subject to syncing between linked actors.
2434 case FlareType::USER:
2435 switch (def.control_number - 1)
2436 {
2447 default: break;
2448 }
2449 break;
2450 default: break;
2451 }
2452}
2453
2454Ogre::MaterialPtr ActorSpawner::InstantiateManagedMaterial(const Ogre::String& rg_name, Ogre::String const & source_name, Ogre::String const & clone_name)
2455{
2456 Ogre::MaterialPtr src_mat = Ogre::MaterialManager::getSingleton().getByName(source_name, rg_name);
2457 if (!src_mat)
2458 {
2459 std::stringstream msg;
2460 msg << "Built-in material '" << source_name << "' missing! Skipping...";
2461 AddMessage(Message::TYPE_ERROR, msg.str());
2462 return Ogre::MaterialPtr();
2463 }
2464
2465 return src_mat->clone(clone_name);
2466}
2467
2469{
2470 // This is how textures map between `RigDef::Document` (*.truck etc...) and `RoR::TuneupDef` (*.tuneup):
2471 // def.diffuse_map ~~ tuneup.media[0]
2472 // def.specular_map ~~ tuneup.media[1]
2473 // def.damaged_diffuse_map ~~ tuneup.media[2]
2474 // ==========================================================================
2475
2476 if (m_managed_materials.find(def.name) != m_managed_materials.end())
2477 {
2478 this->AddMessage(Message::TYPE_ERROR, "Duplicate managed material name: '" + def.name + "'. Ignoring definition...");
2479 return;
2480 }
2481
2482 // Check all textures exist
2483 std::string resource_group = this->GetCurrentElementMediaRG();
2484 if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(resource_group, def.diffuse_map))
2485 {
2486 this->AddMessage(Message::TYPE_WARNING, "Skipping managed material, missing texture file: " + def.diffuse_map);
2487 return;
2488 }
2489
2490 if (def.damaged_diffuse_map != "" &&
2491 !Ogre::ResourceGroupManager::getSingleton().resourceExists(resource_group, def.damaged_diffuse_map))
2492 {
2493 this->AddMessage(Message::TYPE_WARNING, "Damage texture not found: " + def.damaged_diffuse_map);
2494 def.damaged_diffuse_map = "";
2495 }
2496
2497 if (def.specular_map != "" &&
2498 !Ogre::ResourceGroupManager::getSingleton().resourceExists(resource_group, def.specular_map))
2499 {
2500 this->AddMessage(Message::TYPE_WARNING, "Specular texture not found: " + def.specular_map);
2501 def.specular_map = "";
2502 }
2503
2504 // Create fallback placeholders
2505 // This is necessary to load meshes with original material names (= unchanged managed mat names)
2506 // - if not found, OGRE substitutes them with 'BaseWhite' which breaks subsequent processing.
2507 // Note this must be done for all addonparts, too, as any of them can use managed materials defined in the truck file.
2508 for (auto& module: m_selected_modules)
2509 {
2510 std::string module_rg = (module->origin_addonpart) ? module->origin_addonpart->resource_group : m_actor->getTruckFileResourceGroup();
2511 if (!Ogre::MaterialManager::getSingleton().getByName(def.name, module_rg))
2512 {
2513 LOG(fmt::format("[RoR] DBG ActorSpawner::ProcessManagedMaterial(): Creating placeholder for material '{}' in group '{}'", def.name, module_rg));
2514 m_managedmat_placeholder_template->clone(def.name, /*changeGroup=*/true, module_rg);
2515 }
2516 else
2517 {
2518 LOG(fmt::format("[RoR] DBG ActorSpawner::ProcessManagedMaterial(): Placeholder already exists: '{}' in group '{}'", def.name, module_rg));
2519 }
2520 }
2521
2522 std::string custom_name = this->ComposeName(def.name);
2523 Ogre::MaterialPtr material;
2525 {
2526 // Create a placeholder material
2527 material = Ogre::MaterialManager::getSingleton().getByName("tracks/transred")->clone(custom_name, /*changeGroup:*/true, resource_group);
2528 }
2530 {
2531 std::string mat_name_base
2533 ? "managed/flexmesh_standard"
2534 : "managed/flexmesh_transparent";
2535
2536 if (def.damaged_diffuse_map != "")
2537 {
2538 if (def.specular_map != "")
2539 {
2540 /* FLEXMESH, damage, specular */
2541 if (App::gfx_alt_actor_materials->getBool())
2542 {
2543 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/speculardamage", custom_name);
2544 }
2545 else
2546 {
2547 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/speculardamage_nicemetal", custom_name);
2548 }
2549
2550 if (!material)
2551 {
2552 return;
2553 }
2554
2555 if (App::gfx_alt_actor_materials->getBool())
2556 {
2557 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2558 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Dmg_Diffuse_Map"), def.name, 2, def.damaged_diffuse_map);
2559 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("SpecularMapping1")->getTextureUnitState("SpecularMapping1_Tex"), def.name, 1, def.specular_map);
2560 }
2561 else
2562 {
2563 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2564 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Dmg_Diffuse_Map"), def.name, 2, def.damaged_diffuse_map);
2565 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Specular_Map"), def.name, 1, def.specular_map);
2566 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("Specular")->getTextureUnitState("Specular_Map"), def.name, 1, def.specular_map);
2567 }
2568 }
2569 else
2570 {
2571 /* FLEXMESH, damage, no_specular */
2572 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/damageonly", custom_name);
2573 if (!material)
2574 {
2575 return;
2576 }
2577 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2578 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Dmg_Diffuse_Map"), def.name, 2, def.damaged_diffuse_map);
2579 }
2580 }
2581 else
2582 {
2583 if (def.specular_map != "")
2584 {
2585 /* FLEXMESH, no_damage, specular */
2586 if (App::gfx_alt_actor_materials->getBool())
2587 {
2588 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/specularonly", custom_name);
2589 }
2590 else
2591 {
2592 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/specularonly_nicemetal", custom_name);
2593 }
2594
2595 if (!material)
2596 {
2597 return;
2598 }
2599
2600 if (App::gfx_alt_actor_materials->getBool())
2601 {
2602 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2603 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("SpecularMapping1")->getTextureUnitState("SpecularMapping1_Tex"), def.name, 1, def.specular_map);
2604 }
2605 else
2606 {
2607 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map") , def.name, 0, def.diffuse_map);
2608 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Specular_Map"), def.name, 1, def.specular_map);
2609 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("Specular")->getTextureUnitState("Specular_Map") , def.name, 1, def.specular_map);
2610 }
2611 }
2612 else
2613 {
2614 /* FLEXMESH, no_damage, no_specular */
2615 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/simple", custom_name);
2616 if (!material)
2617 {
2618 return;
2619 }
2620 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2621 }
2622 }
2623 }
2625 {
2626 Ogre::String mat_name_base
2628 ? "managed/mesh_standard"
2629 : "managed/mesh_transparent";
2630
2631 if (def.specular_map != "")
2632 {
2633 /* MESH, specular */
2634 if (App::gfx_alt_actor_materials->getBool())
2635 {
2636 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/specular", custom_name);
2637 }
2638 else
2639 {
2640 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/specular_nicemetal", custom_name);
2641 }
2642
2643 if (!material)
2644 {
2645 return;
2646 }
2647
2648 if (App::gfx_alt_actor_materials->getBool())
2649 {
2650 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2651 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("SpecularMapping1")->getTextureUnitState("SpecularMapping1_Tex"), def.name, 1, def.specular_map);
2652 }
2653 else
2654 {
2655 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map") ,def.name, 0, def.diffuse_map);
2656 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Specular_Map"),def.name, 1, def.specular_map);
2657 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("Specular")->getTextureUnitState("Specular_Map") ,def.name, 1, def.specular_map);
2658 }
2659 }
2660 else
2661 {
2662 /* MESH, no_specular */
2663 material = this->InstantiateManagedMaterial(resource_group, mat_name_base + "/simple", custom_name);
2664 if (!material)
2665 {
2666 return;
2667 }
2668 this->AssignManagedMaterialTexture(material->getTechnique("BaseTechnique")->getPass("BaseRender")->getTextureUnitState("Diffuse_Map"), def.name, 0, def.diffuse_map);
2669
2670 }
2671 }
2672
2675 {
2676 if (def.options.double_sided)
2677 {
2678 material->getTechnique("BaseTechnique")->getPass("BaseRender")->setCullingMode(Ogre::CULL_NONE);
2679 if (def.specular_map != "")
2680 {
2681 if (App::gfx_alt_actor_materials->getBool())
2682 {
2683 material->getTechnique("BaseTechnique")->getPass("SpecularMapping1")->setCullingMode(Ogre::CULL_NONE);
2684 }
2685 else
2686 {
2687 material->getTechnique("BaseTechnique")->getPass("Specular")->setCullingMode(Ogre::CULL_NONE);
2688 }
2689 }
2690 }
2691 }
2692
2693 material->compile();
2694 m_managed_materials.insert(std::make_pair(def.name, material));
2695}
2696
2698{
2699 int8_t bbox_id = static_cast<int8_t>(m_actor->ar_collision_bounding_boxes.size());
2700 for (RigDef::Node::Ref& node_ref: def.nodes)
2701 {
2702 NodeNum_t node = this->ResolveNodeRef(node_ref);
2703 if (node == NODENUM_INVALID)
2704 {
2705 RoR::LogFormat("[RoR|Spawner] Collision box: skipping invalid node '%s'", node_ref.ToString().c_str());
2706 continue;
2707 }
2709 {
2710 RoR::LogFormat("[RoR|Spawner] Collision box: re-assigning node '%s' from box ID '%d' to '%d'",
2711 node_ref.ToString().c_str(),
2713 bbox_id);
2714 }
2715 m_actor->ar_nodes[node].nd_coll_bbox_id = bbox_id;
2716 }
2717
2718 m_actor->ar_collision_bounding_boxes.push_back(Ogre::AxisAlignedBox()); // Updated later
2719 m_actor->ar_predicted_coll_bounding_boxes.push_back(Ogre::AxisAlignedBox());
2720}
2721
2729
2730bool ActorSpawner::AssignWheelToAxle(int & _out_axle_wheel, node_t *axis_node_1, node_t *axis_node_2)
2731{
2732 for (int i = 0; i < m_actor->ar_num_wheels; i++)
2733 {
2734 wheel_t & wheel = m_actor->ar_wheels[i];
2735 if ( (wheel.wh_axis_node_0 == axis_node_1 && wheel.wh_axis_node_1 == axis_node_2)
2736 || (wheel.wh_axis_node_0 == axis_node_2 && wheel.wh_axis_node_1 == axis_node_1)
2737 )
2738 {
2739 _out_axle_wheel = i;
2740 return true;
2741 }
2742 }
2743 return false;
2744}
2745
2747{
2748 if (! CheckAxleLimit(1))
2749 {
2750 return;
2751 }
2752
2753 node_t *wheel_1_node_1 = GetNodePointerOrThrow(def.wheels[0][0]);
2754 node_t *wheel_1_node_2 = GetNodePointerOrThrow(def.wheels[0][1]);
2755 node_t *wheel_2_node_1 = GetNodePointerOrThrow(def.wheels[1][0]);
2756 node_t *wheel_2_node_2 = GetNodePointerOrThrow(def.wheels[1][1]);
2757
2758 Differential *diff = new Differential();
2759
2760 if (! AssignWheelToAxle(diff->di_idx_1, wheel_1_node_1, wheel_1_node_2))
2761 {
2762 std::stringstream msg;
2763 msg << "Couldn't find wheel with axis nodes '" << def.wheels[0][0].ToString()
2764 << "' and '" << def.wheels[0][1].ToString() << "'";
2766 }
2767
2768 if (! AssignWheelToAxle(diff->di_idx_2, wheel_2_node_1, wheel_2_node_2))
2769 {
2770 std::stringstream msg;
2771 msg << "Couldn't find wheel with axis nodes '" << def.wheels[1][0].ToString()
2772 << "' and '" << def.wheels[1][1].ToString() << "'";
2774 }
2775
2776 if (def.options.size() == 0)
2777 {
2778 AddMessage(Message::TYPE_INFO, "No differential defined, defaulting to Open & Locked");
2781 }
2782 else
2783 {
2784 auto end = def.options.end();
2785 for (auto itor = def.options.begin(); itor != end; ++itor)
2786 {
2787 switch (*itor)
2788 {
2791 break;
2794 break;
2797 break;
2800 break;
2801 default:
2802 AddMessage(Message::TYPE_WARNING, fmt::format("Unknown differential type: '{}'", (char)*itor));
2803 break;
2804 }
2805 }
2806 }
2807
2810}
2811
2813{
2814 if (def.a1 == def.a2 || std::min(def.a1, def.a2) < 0 || std::max(def.a1 , def.a2) >= m_actor->m_num_wheel_diffs)
2815 {
2816 AddMessage(Message::TYPE_ERROR, "Invalid 'interaxle' axle ids, skipping...");
2817 return;
2818 }
2819
2821 {
2822 if ((m_actor->m_transfer_case->tr_ax_1 == def.a1 && m_actor->m_transfer_case->tr_ax_2 == def.a2) ||
2824 {
2825 AddMessage(Message::TYPE_ERROR, "You cannot have both an inter-axle differential and a transfercase between the same two axles, skipping...");
2826 return;
2827 }
2828 }
2829
2830 Differential *diff = new Differential();
2831
2832 diff->di_idx_1 = def.a1;
2833 diff->di_idx_2 = def.a2;
2834
2835 if (def.options.size() == 0)
2836 {
2837 AddMessage(Message::TYPE_INFO, "No differential defined, defaulting to Locked");
2839 }
2840 else
2841 {
2842 for (RigDef::DifferentialType val: def.options)
2843 {
2844 switch (val)
2845 {
2848 break;
2851 break;
2854 break;
2857 break;
2858 default:
2859 AddMessage(Message::TYPE_WARNING, fmt::format("Unknown differential type: '{}'", (char)val));
2860 break;
2861 }
2862 }
2863 }
2864
2867}
2868
2870{
2871 if (def.a1 == def.a2 || def.a1 < 0 || std::max(def.a1 , def.a2) >= m_actor->m_num_wheel_diffs)
2872 {
2873 AddMessage(Message::TYPE_ERROR, "Invalid 'transfercase' axle ids, skipping...");
2874 return;
2875 }
2876 if (def.a2 < 0) // No 4WD mode
2877 {
2878 if (!def.has_2wd) // No 2WD mode
2879 {
2880 AddMessage(Message::TYPE_ERROR, "Invalid 'transfercase': Define alternate axle or allow 2WD, skipping...");
2881 return;
2882 }
2883 else // Only 2WD
2884 {
2885 AddMessage(Message::TYPE_INFO, "No alternate axle defined, defaulting to 2WD only");
2886 }
2887 }
2888
2889 m_actor->m_transfer_case = new TransferCase(def.a1, def.a2, def.has_2wd, def.has_2wd_lo, def.gear_ratios);
2890
2891 for (int i = 0; i < m_actor->ar_num_wheels; i++)
2892 {
2894 }
2898 if (!def.has_2wd)
2899 {
2904 }
2905}
2906
2908{
2911 {
2912 std::stringstream msg;
2913 msg << "Invalid parameter 'lower_limit' (" << m_actor->cc_target_speed_lower_limit
2914 << ") must be positive nonzero number. Using it anyway (compatibility)";
2915 }
2916 m_actor->cc_can_brake = def.autobrake != 0;
2917}
2918
2924
2926{
2927 if (m_actor->ar_engine == nullptr)
2928 {
2929 AddMessage(Message::TYPE_WARNING, "Section 'torquecurve' found but no 'engine' defined, skipping...");
2930 return;
2931 }
2932
2933 TorqueCurve *target_torque_curve = m_actor->ar_engine->getTorqueCurve();
2934
2935 if (def.predefined_func_name.length() != 0)
2936 {
2937 target_torque_curve->setTorqueModel(def.predefined_func_name);
2938 }
2939 else
2940 {
2941 target_torque_curve->CreateNewCurve(); /* Use default name for custom curve */
2942 std::vector<RigDef::TorqueCurve::Sample>::iterator itor = def.samples.begin();
2943 for ( ; itor != def.samples.end(); itor++)
2944 {
2945 target_torque_curve->AddCurveSample(itor->power, itor->torque_percent);
2946 }
2947 }
2948}
2949
2951{
2952 if (App::gfx_particles_mode->getInt() != 1)
2953 {
2954 return;
2955 }
2956
2957 CParticleID_t particle_id = static_cast<CParticleID_t>(m_actor->m_gfx_actor->m_cparticles.size());
2958 CParticle particle;
2959
2962
2963 std::string name = this->ComposeName(def.particle_system_name.c_str(), particle_id);
2964 particle.psys = this->CreateParticleSystem(name, def.particle_system_name);
2965 if (particle.psys == nullptr)
2966 {
2967 std::stringstream msg;
2968 msg << "Failed to create particle system '" << name << "' (template: '" << def.particle_system_name <<"')";
2969 AddMessage(Message::TYPE_ERROR, msg.str());
2970 return;
2971 }
2972
2973 particle.snode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("cparticles", particle_id));
2974 particle.snode->attachObject(particle.psys);
2975 particle.snode->setPosition(m_actor->ar_nodes[particle.emitterNode].AbsPosition);
2976
2977 m_actor->m_gfx_actor->m_cparticles.push_back(particle);
2978}
2979
2981{
2982 ropable_t ropable;
2983 ropable.node = GetNodePointerOrThrow(def.node);
2984 ropable.pos = static_cast<int>(m_actor->ar_ropables.size());
2985 ropable.group = def.group;
2986 ropable.attached_ties = 0;
2987 ropable.attached_ropes = 0;
2988 ropable.multilock = def.has_multilock;
2989 m_actor->ar_ropables.push_back(ropable);
2990}
2991
2993{
2995 node_t & node_2 = m_actor->ar_nodes[( (node_1.pos == 0) ? 1 : 0 )];
2996
2997 int beam_index = m_actor->ar_num_beams;
2998 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
2999 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold());
3000 beam.k = def.beam_defaults->GetScaledSpringiness();
3001 beam.d = def.beam_defaults->GetScaledDamping();
3002 beam.bm_type = BEAM_HYDRO;
3003 beam.L = def.max_reach_length;
3004 beam.refL = def.max_reach_length;
3005 beam.bounded = ROPE;
3006 beam.bm_disabled = true;
3007
3009 {
3010 this->CreateBeamVisuals(beam, beam_index, false, def.beam_defaults);
3011 }
3012 else
3013 {
3014 m_actor->ar_beams_invisible[beam_index] = true;
3015 }
3016
3017 /* Register tie */
3018 tie_t tie;
3019 tie.ti_group = def.group;
3020 tie.ti_tying = false;
3021 tie.ti_tied = false;
3022 tie.ti_beam = & beam;
3023 tie.ti_locked_actor = nullptr;
3024 tie.ti_locked_ropable = nullptr;
3026 tie.ti_max_stress = def.max_stress;
3027 tie.ti_min_length = def.min_length;
3029 m_actor->ar_ties.push_back(tie);
3030
3032}
3033
3035{
3038
3039 /* Add beam */
3040 int beam_index = m_actor->ar_num_beams;
3041 beam_t & beam = AddBeam(root_node, end_node, def.beam_defaults, def.detacher_group);
3042 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold());
3043 beam.k = def.beam_defaults->GetScaledSpringiness();
3044 beam.d = def.beam_defaults->GetScaledDamping();
3045 beam.bounded = ROPE;
3046 beam.bm_type = BEAM_HYDRO;
3047 beam.L = root_node.AbsPosition.distance(end_node.AbsPosition);
3048 beam.refL = beam.L;
3049
3050 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults, "tracks/beam");
3051
3052 /* Register rope */
3053 rope_t rope;
3054 rope.rp_beam = & beam;
3055 rope.rp_locked = UNLOCKED;
3056 rope.rp_locked_ropable = nullptr;
3057 rope.rp_group = 0; // Orig: hardcoded in BTS_ROPES. TODO: To be used.
3058 m_actor->ar_ropes.push_back(rope);
3059}
3060
3062{
3063 RailGroup* rail_group = this->CreateRail(def.node_list);
3064 rail_group->rg_id = def.id;
3065 m_actor->m_railgroups.push_back(rail_group);
3066}
3067
3069{
3071 SlideNode slide_node(& node, nullptr);
3072
3073 // Optional args
3074 if (def._spring_rate_set) { slide_node.SetSpringRate(def.spring_rate); }
3075 if (def._break_force_set) { slide_node.SetBreakForce(def.break_force); }
3076 if (def._tolerance_set) { slide_node.SetCorThreshold(def.tolerance); }
3077 if (def._attachment_rate_set) { slide_node.SetAttachmentRate(def.attachment_rate); }
3078 if (def._max_attach_dist_set) { slide_node.SetAttachmentDistance(def.max_attach_dist); }
3079
3080 // Constraints
3082 {
3083 slide_node.sn_attach_self = true;
3084 slide_node.sn_attach_foreign = true;
3085 }
3087 {
3088 slide_node.sn_attach_self = true;
3089 slide_node.sn_attach_foreign = false;
3090 }
3092 {
3093 slide_node.sn_attach_self = false;
3094 slide_node.sn_attach_foreign = true;
3095 }
3097 {
3098 slide_node.sn_attach_self = false;
3099 slide_node.sn_attach_foreign = false;
3100 }
3101
3102 // RailGroup
3103 RailGroup *rail_group = nullptr;
3104 if (def._railgroup_id_set)
3105 {
3106 std::vector<RailGroup*>::iterator itor = m_actor->m_railgroups.begin();
3107 for ( ; itor != m_actor->m_railgroups.end(); itor++)
3108 {
3109 if ((*itor)->rg_id == def.railgroup_id)
3110 {
3111 rail_group = *itor;
3112 break;
3113 }
3114 }
3115
3116 if (rail_group == nullptr)
3117 {
3118 std::stringstream msg;
3119 msg << "Specified rail group id '" << def.railgroup_id << "' not found. Ignoring slidenode...";
3120 AddMessage(Message::TYPE_ERROR, msg.str());
3121 return;
3122 }
3123 }
3124 else if (def.rail_node_ranges.size() > 0)
3125 {
3126 rail_group = this->CreateRail(def.rail_node_ranges);
3127 if (rail_group != nullptr)
3128 {
3129 m_actor->m_railgroups.push_back(rail_group);
3130 }
3131 }
3132 else
3133 {
3134 AddMessage(Message::TYPE_ERROR, "No RailGroup available for SlideNode, skipping...");
3135 }
3136
3137 slide_node.SetDefaultRail(rail_group);
3138 m_actor->m_slidenodes.push_back(slide_node);
3139}
3140
3142 std::vector<RigDef::Node::Range> & node_ranges,
3143 std::vector<NodeNum_t> & out_node_indices
3144 )
3145{
3146 std::vector<RigDef::Node::Range>::iterator itor = node_ranges.begin();
3147 for ( ; itor != node_ranges.end(); itor++)
3148 {
3149 if (itor->IsRange())
3150 {
3151 NodeNum_t start = this->ResolveNodeRef(itor->start);
3152 if (start == NODENUM_INVALID)
3153 {
3154 AddMessage(Message::TYPE_WARNING, fmt::format("Invalid start node in range: {}", itor->start.ToString()));
3155 return false;
3156 }
3157
3158 NodeNum_t end = this->ResolveNodeRef(itor->end, /* optional: */ true);
3159 if (end == NODENUM_INVALID)
3160 {
3161 std::stringstream msg;
3162 msg << "Encountered non-existent node '" << itor->end.ToString() << "' in range [" << itor->start.ToString() << " - " << itor->end.ToString() << "], "
3163 << "highest node index is '" << m_actor->ar_num_nodes - 1 << "'.";
3164
3165 if (itor->end.Str().empty()) /* If the node is numeric... */
3166 {
3167 msg << " However, this node must be accepted anyway for backwards compatibility."
3168 << " Please fix this as soon as possible.";
3169 end = itor->end.Num();
3170 AddMessage(Message::TYPE_ERROR, msg.str());
3171 }
3172 else
3173 {
3174 AddMessage(Message::TYPE_ERROR, msg.str());
3175 return false;
3176 }
3177 }
3178
3179 if (end < start)
3180 {
3181 NodeNum_t swap = start;
3182 start = end;
3183 end = swap;
3184 }
3185
3186 for (NodeNum_t i = start; i <= end; i++)
3187 {
3188 out_node_indices.push_back(i);
3189 }
3190 }
3191 else
3192 {
3193 out_node_indices.push_back(GetNodeIndexOrThrow(itor->start));
3194 }
3195 }
3196 return true;
3197}
3198
3199RailGroup *ActorSpawner::CreateRail(std::vector<RigDef::Node::Range> & node_ranges)
3200{
3201 // Collect nodes
3202 std::vector<NodeNum_t> node_indices;
3203 this->CollectNodesFromRanges(node_ranges, node_indices);
3204
3205 // Build the rail
3206 RailGroup* rg = new RailGroup();
3207 for (unsigned int i = 0; i < node_indices.size() - 1; i++)
3208 {
3209 beam_t *beam = FindBeamInRig(node_indices[i], node_indices[i + 1]);
3210 if (beam == nullptr)
3211 {
3212 std::stringstream msg;
3213 msg << "No beam between nodes indexed '" << node_indices[i] << "' and '" << node_indices[i + 1] << "'";
3214 AddMessage(Message::TYPE_ERROR, msg.str());
3215 delete rg;
3216 return nullptr;
3217 }
3218 rg->rg_segments.emplace_back(beam);
3219 }
3220
3221 // Link middle segments
3222 const size_t num_seg = rg->rg_segments.size();
3223 for (size_t i = 1; i < (num_seg - 1); ++i)
3224 {
3225 rg->rg_segments[i].rs_prev = &rg->rg_segments[i - 1];
3226 rg->rg_segments[i].rs_next = &rg->rg_segments[i + 1];
3227 }
3228
3229 // Link end segments
3230 const bool is_loop = (node_indices.front() == node_indices.back());
3231 if (rg->rg_segments.size() > 1)
3232 {
3233 rg->rg_segments[0].rs_next = &rg->rg_segments[1];
3234 rg->rg_segments[num_seg - 1].rs_prev = &rg->rg_segments[num_seg - 2];
3235 if (is_loop)
3236 {
3237 rg->rg_segments[0].rs_prev = &rg->rg_segments[num_seg - 1];
3238 rg->rg_segments[num_seg - 1].rs_next = &rg->rg_segments[0];
3239 }
3240 }
3241
3242 return rg; // Transfers memory ownership
3243}
3244
3246{
3247 node_t *node_a = & m_actor->ar_nodes[node_a_index];
3248 node_t *node_b = & m_actor->ar_nodes[node_b_index];
3249
3250 for (unsigned int i = 0; i < static_cast<unsigned int>(m_actor->ar_num_beams); i++)
3251 {
3252 if (
3253 (GetBeam(i).p1 == node_a && GetBeam(i).p2 == node_b)
3254 || (GetBeam(i).p2 == node_a && GetBeam(i).p1 == node_b)
3255 )
3256 {
3257 return & GetBeam(i);
3258 }
3259 }
3260 return nullptr;
3261}
3262
3264{
3265 /* Find the node */
3266 node_t *node = GetNodePointer(def.node);
3267 if (node == nullptr)
3268 {
3269 return;
3270 }
3271
3272 /* Find the hook */
3273 hook_t *hook = nullptr;
3274 std::vector <hook_t>::iterator itor = m_actor->ar_hooks.begin();
3275 for (; itor != m_actor->ar_hooks.end(); itor++)
3276 {
3277 if (itor->hk_hook_node == node)
3278 {
3279 hook = &*itor;
3280 break;
3281 }
3282 }
3283
3284 if (hook == nullptr)
3285 {
3286 std::stringstream msg;
3287 msg << "Node '" << def.node.ToString() << "' is not a hook-node (not marked with flag 'h'), ignoring...";
3288 AddMessage(Message::TYPE_ERROR, msg.str());
3289 return;
3290 }
3291
3292 /* Process options */
3293 hook->hk_lockrange = def.option_hook_range;
3295 hook->hk_maxforce = def.option_max_force;
3296 hook->hk_group = def.option_hookgroup;
3297 hook->hk_lockgroup = def.option_lockgroup;
3298 hook->hk_timer = 0.f; // Hardcoded in BTS_HOOKS
3299 hook->hk_timer_preset = def.option_timer;
3301 hook->hk_selflock = def.flag_self_lock;
3302 hook->hk_nodisable = def.flag_no_disable;
3303 if (def.flag_auto_lock)
3304 {
3305 hook->hk_autolock = true;
3306 if (hook->hk_group == -1)
3307 {
3308 hook->hk_group = -2; /* only overwrite hgroup when its still default (-1) */
3309 }
3310 }
3311 if (def.flag_no_rope)
3312 {
3313 hook->hk_beam->bounded = NOSHOCK;
3314 }
3315 if (!def.flag_visible) // NOTE: This flag can only hide a visible beam - it won't show a beam defined with 'invisible' flag.
3316 {
3317 // Find beam index
3318 int beam_index = -1;
3319 for (int i = 0; i < m_actor->ar_num_beams; ++i)
3320 {
3321 if (hook->hk_beam == &m_actor->ar_beams[i])
3322 {
3323 beam_index = i;
3324 break;
3325 }
3326 }
3327
3328 // Erase beam visuals (only exist if defined without 'invisible' flag - we don't know at this point)
3329 m_actor->m_gfx_actor->RemoveBeam(beam_index);
3330 }
3331}
3332
3334{
3335 auto itor = lockgroup.nodes.begin();
3336 auto end = lockgroup.nodes.end();
3337 for (; itor != end; ++itor)
3338 {
3339 NodeNum_t node = this->GetNodeIndexOrThrow(*itor);
3340 m_actor->ar_nodes[node].nd_lockgroup = lockgroup.number;
3341 }
3342}
3343
3345{
3346 float sbound = def.contraction_trigger_limit; // shortbound
3347 float lbound = def.expansion_trigger_limit; // longbound
3348
3349 // options
3350 bool invisible = false;
3352 bool shock_trigger_enabled = true;
3353 bool triggerblocker = false;
3354 bool triggerblocker_inverted = false;
3355 bool cmdkeyblock = false;
3356 bool hooktoggle = false;
3357 bool enginetrigger = false;
3358
3359 bool trigger_cmdkeyblock_state_short = false;
3360 bool trigger_cmdkeyblock_state_long = true;
3361 if (def.longbound_trigger_action != -1) trigger_cmdkeyblock_state_long = false;
3362
3363 // now 'parse' the options
3365 {
3366 invisible = true;
3367 }
3368 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_x_START_DISABLED)) // this trigger is disabled on startup, default is enabled
3369 {
3370 shock_trigger_enabled = false;
3371 }
3372 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_B_TRIGGER_BLOCKER)) // Blocker that enable/disable other triggers
3373 {
3374 shockflag |= SHOCK_FLAG_TRG_BLOCKER;
3375 triggerblocker = true;
3376 }
3377 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
3378 {
3379 cmdkeyblock = true;
3380 }
3381 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
3382 {
3383 shockflag |= SHOCK_FLAG_TRG_CMD_SWITCH;
3384 }
3385 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_c_COMMAND_STYLE)) // trigger is set with commandstyle boundaries instead of shocksytle
3386 {
3387 sbound = abs(sbound-1);
3388 lbound = lbound-1;
3389 }
3390 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)
3391 {
3392 shockflag |= SHOCK_FLAG_TRG_BLOCKER_A;
3393 triggerblocker_inverted = true;
3394 }
3396 {
3397 shockflag |= SHOCK_FLAG_TRG_HOOK_UNLOCK;
3398 hooktoggle = true;
3399 }
3401 {
3402 shockflag |= SHOCK_FLAG_TRG_HOOK_LOCK;
3403 hooktoggle = true;
3404 }
3405 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_t_CONTINUOUS)) // this trigger sends values between 0 and 1
3406 {
3407 shockflag |= SHOCK_FLAG_TRG_CONTINUOUS;
3408 }
3409 if (BITMASK_IS_1(def.options, RigDef::Trigger::OPTION_E_ENGINE_TRIGGER)) // this trigger is used to control an engine
3410 {
3411 shockflag |= SHOCK_FLAG_TRG_ENGINE;
3412 enginetrigger = true;
3413 }
3414
3415 if (!triggerblocker && !triggerblocker_inverted && !hooktoggle && !enginetrigger)
3416 {
3417 // make the full check
3419 {
3420 AddMessage(Message::TYPE_ERROR, fmt::format("Wrong 'shortbound_trigger_action': '{}' - must be between 1 and {}. Trigger deactivated.", def.shortbound_trigger_action, MAX_COMMANDS));
3421 return;
3422 }
3423 }
3424 else if (!hooktoggle && !enginetrigger)
3425 {
3426 // this is a Trigger-Blocker, make special check
3428 {
3429 AddMessage(Message::TYPE_ERROR, "Wrong command-eventnumber (Triggers). Trigger-Blocker deactivated.");
3430 return;
3431 }
3432 }
3433 else if (enginetrigger)
3434 {
3435 if (triggerblocker || triggerblocker_inverted || hooktoggle || (shockflag & SHOCK_FLAG_TRG_CMD_SWITCH))
3436 {
3437 AddMessage(Message::TYPE_ERROR, "Error: Wrong command-eventnumber (Triggers). Engine trigger deactivated.");
3438 return;
3439 }
3440 }
3441
3442 // `add_beam()`
3443 const NodeNum_t node_1_index = this->ResolveNodeRef(def.nodes[0]);
3444 const NodeNum_t node_2_index = this->ResolveNodeRef(def.nodes[1]);
3445 if (node_1_index == NODENUM_INVALID || node_2_index == NODENUM_INVALID)
3446 {
3447 this->AddMessage(Message::TYPE_WARNING, "Skipping trigger, some nodes not found");
3448 return;
3449 }
3450 int beam_index = m_actor->ar_num_beams;
3451 beam_t & beam = AddBeam(m_actor->ar_nodes[node_1_index], m_actor->ar_nodes[node_2_index], def.beam_defaults, def.detacher_group);
3452 beam.bm_type = BEAM_HYDRO;
3453 SetBeamStrength(beam, def.beam_defaults->breaking_threshold);
3454 SetBeamSpring(beam, 0.f);
3455 SetBeamDamping(beam, 0.f);
3456 CalculateBeamLength(beam);
3457 beam.shortbound = sbound;
3458 beam.longbound = lbound;
3459 beam.bounded = TRIGGER;
3460
3461 if (!invisible)
3462 {
3463 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
3464 }
3465 else
3466 {
3467 m_actor->ar_beams_invisible[beam_index] = true;
3468 }
3469 // end `add_beam()`
3470
3472 {
3473 LOG("Trigger added. BeamID " + TOSTRING(beam_index));
3474 }
3475
3476 shock_t& shock = this->GetFreeShock();
3477 beam.shock = &shock;
3478 shock.beamid = beam_index;
3479 shock.trigger_switch_state = 0.0f; // used as bool and countdowntimer, dont touch!
3480 if (!triggerblocker && !triggerblocker_inverted) // this is no triggerblocker (A/B)
3481 {
3483 if (def.longbound_trigger_action != -1 || (def.longbound_trigger_action == -1 && hooktoggle)) // this is a trigger or a hooktoggle
3485 else // this is a commandkeyblocker
3486 shockflag |= SHOCK_FLAG_TRG_CMD_BLOCKER;
3487 }
3488 else // this is a triggerblocker
3489 {
3490 if (!triggerblocker_inverted)
3491 {
3492 //normal BLOCKER
3493 shockflag |= SHOCK_FLAG_TRG_BLOCKER;
3496 }
3497 else
3498 {
3499 //inverted BLOCKER
3500 shockflag |= SHOCK_FLAG_TRG_BLOCKER_A;
3503 }
3504 }
3505
3506 if (cmdkeyblock && !triggerblocker)
3507 {
3508 trigger_cmdkeyblock_state_short = true;
3509 if (def.longbound_trigger_action != -1) trigger_cmdkeyblock_state_long = true;
3510 }
3511 if (def.boundary_timer > 0)
3513 else
3514 shock.trigger_boundary_t = 1.0f;
3515
3516 shock.flags = shockflag;
3517 shock.trigger_enabled = shock_trigger_enabled;
3518 shock.sbd_spring = def.beam_defaults->springiness;
3519 shock.sbd_damp = def.beam_defaults->damping_constant;
3520 shock.last_debug_state = 0;
3521
3522 // Note this is a special 'array' object - any negative indices are valid!
3523 m_actor->ar_command_key[def.shortbound_trigger_action].trigger_cmdkeyblock_state = trigger_cmdkeyblock_state_short;
3524 m_actor->ar_command_key[def.longbound_trigger_action].trigger_cmdkeyblock_state = trigger_cmdkeyblock_state_long;
3525}
3526
3528{
3529 unsigned int node_index = GetNodeIndexOrThrow(node_ref);
3530 m_actor->ar_nodes[node_index].nd_contacter = true;
3531};
3532
3534{
3536
3537 rotator.angle = 0;
3538 rotator.rate = def.rate;
3539 rotator.axis1 = GetNodeIndexOrThrow(def.axis_nodes[0]);
3540 rotator.axis2 = GetNodeIndexOrThrow(def.axis_nodes[1]);
3541 rotator.force = ROTATOR_FORCE_DEFAULT;
3543 rotator.engine_coupling = def.engine_coupling;
3544 rotator.needs_engine = def.needs_engine;
3545 for (unsigned int i = 0; i < 4; i++)
3546 {
3547 rotator.nodes1[i] = GetNodeIndexOrThrow(def.base_plate_nodes[i]);
3548 rotator.nodes2[i] = GetNodeIndexOrThrow(def.rotating_plate_nodes[i]);
3549 }
3550
3551 // Validate the reference structure
3552 this->ValidateRotator(m_actor->ar_num_rotators + 1, rotator.axis1, rotator.axis2, rotator.nodes1, rotator.nodes2);
3553
3554 // Rotate left key
3555 m_actor->ar_command_key[def.spin_left_key].rotators.push_back(- (m_actor->ar_num_rotators + 1));
3556 m_actor->ar_command_key[def.spin_left_key].description = "Rotate_Left/Right";
3557
3558 // Rotate right key
3559 m_actor->ar_command_key[def.spin_right_key].rotators.push_back(m_actor->ar_num_rotators + 1);
3560
3562 m_actor->ar_command_key[def.spin_left_key].rotator_inertia,
3563 m_actor->ar_command_key[def.spin_right_key].rotator_inertia);
3564
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 = def.rotating_force; // Default value is set in constructor
3578 rotator.tolerance = def.tolerance; // Default value is set in constructor
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 if (! def.description.empty())
3593 {
3594 m_actor->ar_command_key[def.spin_left_key].description = def.description;
3595 }
3596 else
3597 {
3598 m_actor->ar_command_key[def.spin_left_key].description = "Rotate_Left/Right";
3599 }
3600
3601 // Rotate right key
3602 m_actor->ar_command_key[def.spin_right_key].rotators.push_back(m_actor->ar_num_rotators + 1);
3603
3605 m_actor->ar_command_key[def.spin_left_key].rotator_inertia,
3606 m_actor->ar_command_key[def.spin_right_key].rotator_inertia);
3607
3610}
3611
3613{
3614 // TODO: refactor _ProcessKeyInertia() to use this.
3615
3616 // Handle placeholders
3617 std::string start_function;
3618 std::string stop_function;
3619 if (inertia.start_function != "" && inertia.start_function != "/" && inertia.start_function != "-")
3620 {
3621 start_function = inertia.start_function;
3622 }
3623 if (inertia.stop_function != "" && inertia.stop_function != "/" && inertia.stop_function != "-")
3624 {
3625 stop_function = inertia.stop_function;
3626 }
3627
3628 obj.SetSimpleDelay(
3629 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3630 inertia.start_delay_factor,
3631 inertia.stop_delay_factor,
3632 start_function,
3633 stop_function
3634 );
3635}
3636
3638 RigDef::Inertia & inertia,
3639 RigDef::Inertia & inertia_defaults,
3640 RoR::CmdKeyInertia& contract_cmd,
3641 RoR::CmdKeyInertia& extend_cmd
3642)
3643{
3644 /* Handle placeholders */
3645 Ogre::String start_function;
3646 Ogre::String stop_function;
3647 if (! inertia.start_function.empty() && inertia.start_function != "/" && inertia.start_function != "-")
3648 {
3649 start_function = inertia.start_function;
3650 }
3651 if (! inertia.stop_function.empty() && inertia.stop_function != "/" && inertia.stop_function != "-")
3652 {
3653 stop_function = inertia.stop_function;
3654 }
3655 if (inertia.start_delay_factor != 0.f && inertia.stop_delay_factor != 0.f)
3656 {
3657 contract_cmd.SetCmdKeyDelay(
3658 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3659 inertia.start_delay_factor,
3660 inertia.stop_delay_factor,
3661 start_function,
3662 stop_function
3663 );
3664
3665 extend_cmd.SetCmdKeyDelay(
3666 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3667 inertia.start_delay_factor,
3668 inertia.stop_delay_factor,
3669 start_function,
3670 stop_function
3671 );
3672 }
3673 else if (inertia_defaults.start_delay_factor > 0 || inertia_defaults.stop_delay_factor > 0)
3674 {
3675 contract_cmd.SetCmdKeyDelay(
3676 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3677 inertia_defaults.start_delay_factor,
3678 inertia_defaults.stop_delay_factor,
3679 inertia_defaults.start_function,
3680 inertia_defaults.stop_function
3681 );
3682
3683 extend_cmd.SetCmdKeyDelay(
3684 App::GetGameContext()->GetActorManager()->GetInertiaConfig(),
3685 inertia_defaults.start_delay_factor,
3686 inertia_defaults.stop_delay_factor,
3687 inertia_defaults.start_function,
3688 inertia_defaults.stop_function
3689 );
3690 }
3691}
3692
3694{
3695 const NodeNum_t beam_index = m_actor->ar_num_beams;
3696 const NodeNum_t node_1_index = this->ResolveNodeRef(def.nodes[0]);
3697 const NodeNum_t node_2_index = this->ResolveNodeRef(def.nodes[1]);
3698 if (node_1_index == NODENUM_INVALID || node_2_index == NODENUM_INVALID)
3699 {
3700 AddMessage(Message::TYPE_ERROR, "Skipping command, some nodes not found.");
3701 return;
3702 }
3703 beam_t & beam = AddBeam(m_actor->ar_nodes[node_1_index], m_actor->ar_nodes[node_2_index], def.beam_defaults, def.detacher_group);
3704 CalculateBeamLength(beam);
3705 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold()); /* Override settings from AddBeam() */
3706 SetBeamSpring(beam, def.beam_defaults->GetScaledSpringiness());
3707 SetBeamDamping(beam, def.beam_defaults->GetScaledDamping());
3708 beam.bm_type = BEAM_HYDRO;
3709
3710 /* Options */
3711 if (def.option_r_rope) { beam.bounded = ROPE; }
3712
3713 /* set the middle of the command, so its not required to recalculate this everytime ... */
3714 float center_length = 0.f;
3715 if (def.max_extension > def.max_contraction)
3716 {
3717 center_length = (def.max_extension - def.max_contraction) / 2 + def.max_contraction;
3718 }
3719 else
3720 {
3721 center_length = (def.max_contraction - def.max_extension) / 2 + def.max_extension;
3722 }
3723
3724 /* Add keys */
3725 command_t* contract_command = &m_actor->ar_command_key[def.contract_key];
3726 commandbeam_t cmd_beam;
3727 cmd_beam.cmb_beam_index = static_cast<uint16_t>(beam_index);
3728 cmd_beam.cmb_is_contraction = true;
3729 cmd_beam.cmb_speed = def.shorten_rate;
3730 cmd_beam.cmb_boundary_length = def.max_contraction;
3733 cmd_beam.cmb_needs_engine = def.needs_engine;
3734 cmd_beam.cmb_is_1press = def.option_p_1press;
3736 cmd_beam.cmb_plays_sound = def.plays_sound;
3737 cmd_beam.cmb_engine_coupling = def.affect_engine;
3738 cmd_beam.cmb_center_length = center_length;
3739 cmd_beam.cmb_state = std::shared_ptr<commandbeam_state_t>(new commandbeam_state_t);
3740 contract_command->beams.push_back(cmd_beam);
3741 if (contract_command->description.empty())
3742 {
3743 contract_command->description = def.description;
3744 }
3745
3746 command_t* extend_command = &m_actor->ar_command_key[def.extend_key];
3747 cmd_beam.cmb_is_contraction = false;
3748 cmd_beam.cmb_speed = def.lengthen_rate;
3749 cmd_beam.cmb_boundary_length = def.max_extension;
3750 extend_command->beams.push_back(cmd_beam);
3751 if (extend_command->description.empty())
3752 {
3753 extend_command->description = def.description;
3754 }
3755
3757 contract_command->command_inertia,
3758 extend_command->command_inertia);
3759
3760 if (! def.option_i_invisible)
3761 {
3762 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
3763 }
3764 else
3765 {
3766 m_actor->ar_beams_invisible[beam_index] = true;
3767 }
3768
3771
3772 // Update the unique pair list
3773 bool must_insert_qpair = true;
3775 {
3776 // seek match on both keys (in any order)
3777 if ((def.contract_key == qpair.uckp_key1 && def.extend_key == qpair.uckp_key2)
3778 || (def.contract_key == qpair.uckp_key2 && def.extend_key == qpair.uckp_key1))
3779 {
3780 must_insert_qpair = false;
3781 if (def.description != "")
3782 {
3783 qpair.uckp_description = def.description; // Last description always wins
3784 }
3785 break;
3786 }
3787 }
3788 if (must_insert_qpair)
3789 {
3791 qpair.uckp_key1 = def.contract_key;
3792 qpair.uckp_key2 = def.extend_key;
3793 qpair.uckp_description = def.description;
3794 m_actor->ar_unique_commandkey_pairs.push_back(qpair);
3795 }
3796}
3797
3799{
3800 int anim_flags = 0;
3801 float anim_option = 0;
3802
3803 /* Options. '{' intentionally misplaced. */
3804
3806 BITMASK_SET_1(anim_flags, ANIM_FLAG_AIRSPEED);
3807 }
3809 BITMASK_SET_1(anim_flags, ANIM_FLAG_VVI);
3810 }
3812 BITMASK_SET_1(anim_flags, ANIM_FLAG_AOA);
3813 }
3815 BITMASK_SET_1(anim_flags, ANIM_FLAG_FLAP);
3816 }
3818 BITMASK_SET_1(anim_flags, ANIM_FLAG_AIRBRAKE);
3819 }
3821 BITMASK_SET_1(anim_flags, ANIM_FLAG_ROLL);
3822 }
3824 BITMASK_SET_1(anim_flags, ANIM_FLAG_PITCH);
3825 }
3827 BITMASK_SET_1(anim_flags, ANIM_FLAG_BRAKE);
3828 }
3830 BITMASK_SET_1(anim_flags, ANIM_FLAG_ACCEL);
3831 }
3833 BITMASK_SET_1(anim_flags, ANIM_FLAG_CLUTCH);
3834 }
3836 BITMASK_SET_1(anim_flags, ANIM_FLAG_SPEEDO);
3837 }
3839 BITMASK_SET_1(anim_flags, ANIM_FLAG_TACHO);
3840 }
3842 BITMASK_SET_1(anim_flags, ANIM_FLAG_TURBO);
3843 }
3845 BITMASK_SET_1(anim_flags, ANIM_FLAG_PBRAKE);
3846 }
3848 BITMASK_SET_1(anim_flags, ANIM_FLAG_TORQUE);
3849 }
3852 }
3854 BITMASK_SET_1(anim_flags, ANIM_FLAG_BRUDDER);
3855 }
3857 BITMASK_SET_1(anim_flags, ANIM_FLAG_SHIFTER);
3858 anim_option = 1.f;
3859 }
3861 BITMASK_SET_1(anim_flags, ANIM_FLAG_SHIFTER);
3862 anim_option = 2.f;
3863 }
3865 BITMASK_SET_1(anim_flags, ANIM_FLAG_SHIFTER);
3866 anim_option = 3.f;
3867 }
3869 BITMASK_SET_1(anim_flags, ANIM_FLAG_SHIFTER);
3870 anim_option = 4.f;
3871 }
3874 anim_option = 1.f;
3875 }
3878 anim_option = 2.f;
3879 }
3882 anim_option = 3.f;
3883 }
3884
3885 /* Aerial */
3887 BITMASK_SET_1(anim_flags, ANIM_FLAG_THROTTLE);
3888 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3889 }
3891 BITMASK_SET_1(anim_flags, ANIM_FLAG_RPM);
3892 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3893 }
3895 BITMASK_SET_1(anim_flags, ANIM_FLAG_AETORQUE);
3896 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3897 }
3899 BITMASK_SET_1(anim_flags, ANIM_FLAG_AEPITCH);
3900 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3901 }
3903 BITMASK_SET_1(anim_flags, ANIM_FLAG_AESTATUS);
3904 anim_option = static_cast<float>(def.aero_animator.engine_idx);
3905 }
3906
3907 unsigned int beam_index = m_actor->ar_num_beams;
3908 NodeNum_t n1 = this->GetNodeIndexOrThrow(def.nodes[0]);
3909 NodeNum_t n2 = this->GetNodeIndexOrThrow(def.nodes[1]);
3911 /* set the limits to something with sense by default */
3912 beam.shortbound = 0.99999f;
3913 beam.longbound = 1000000.0f;
3914 beam.bm_type = BEAM_HYDRO;
3915 CalculateBeamLength(beam);
3916 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold());
3917 SetBeamSpring(beam, def.beam_defaults->GetScaledSpringiness());
3918 SetBeamDamping(beam, def.beam_defaults->GetScaledDamping());
3919
3921 {
3922 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
3923 }
3924 else
3925 {
3926 m_actor->ar_beams_invisible[beam_index] = true;
3927 }
3928
3930 {
3931 beam.shortbound = def.short_limit;
3932 }
3934 {
3935 beam.longbound = def.long_limit;
3936 }
3937
3938 hydrobeam_t hb;
3939 hb.hb_beam_index = static_cast<uint16_t>(beam_index);
3941 hb.hb_ref_length = beam.L;
3942 hb.hb_flags = 0;
3943 hb.hb_anim_flags = anim_flags;
3944 hb.hb_anim_param = anim_option;
3945
3946 if (def.inertia_defaults->start_delay_factor > 0 && def.inertia_defaults->stop_delay_factor > 0)
3947 {
3950 def.inertia_defaults->start_delay_factor,
3951 def.inertia_defaults->stop_delay_factor,
3952 def.inertia_defaults->start_function,
3953 def.inertia_defaults->stop_function
3954 );
3955 }
3956
3957 m_actor->ar_hydros.push_back(hb);
3958}
3959
3961 node_t & node_1,
3962 node_t & node_2,
3963 std::shared_ptr<RigDef::BeamDefaults> & beam_defaults,
3964 int detacher_group
3965)
3966{
3967 /* Init */
3968 beam_t & beam = GetAndInitFreeBeam(node_1, node_2);
3969 beam.detacher_group = detacher_group;
3970 beam.bm_disabled = false;
3971
3972 /* Breaking threshold (strength) */
3973 float strength = beam_defaults->breaking_threshold;
3974 beam.strength = strength;
3975
3976 /* Deformation */
3977 SetBeamDeformationThreshold(beam, beam_defaults);
3978
3979 float plastic_coef = beam_defaults->plastic_deform_coef;
3980 beam.plastic_coef = plastic_coef;
3981
3982 return beam;
3983}
3984
3985void ActorSpawner::SetBeamStrength(beam_t & beam, float strength)
3986{
3987 beam.strength = strength;
3988}
3989
3991{
3992 bool invisible = false;
3993 unsigned int hydro_flags = 0;
3994
3996 {
3997 invisible = true;
3998 }
4000 {
4001 hydro_flags |= HYDRO_FLAG_SPEED;
4002 }
4004 {
4005 hydro_flags |= HYDRO_FLAG_AILERON;
4006 }
4008 {
4009 hydro_flags |= HYDRO_FLAG_RUDDER;
4010 }
4012 {
4013 hydro_flags |= HYDRO_FLAG_ELEVATOR;
4014 }
4016 {
4017 hydro_flags |= (HYDRO_FLAG_AILERON | HYDRO_FLAG_ELEVATOR);
4018 }
4020 {
4022 }
4024 {
4025 hydro_flags |= (HYDRO_FLAG_AILERON | HYDRO_FLAG_RUDDER);
4026 }
4028 {
4029 hydro_flags |= (HYDRO_FLAG_REV_AILERON | HYDRO_FLAG_RUDDER);
4030 }
4032 {
4033 hydro_flags |= (HYDRO_FLAG_ELEVATOR | HYDRO_FLAG_RUDDER);
4034 }
4036 {
4037 hydro_flags |= (HYDRO_FLAG_REV_ELEVATOR | HYDRO_FLAG_RUDDER);
4038 }
4040 {
4041 hydro_flags |= HYDRO_FLAG_DIR;
4042 }
4043
4044 node_t & node_1 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4045 node_t & node_2 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4046
4047 int beam_index = m_actor->ar_num_beams;
4048 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
4049 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold());
4050 CalculateBeamLength(beam);
4051 beam.bm_type = BEAM_HYDRO;
4052 beam.k = def.beam_defaults->GetScaledSpringiness();
4053 beam.d = def.beam_defaults->GetScaledDamping();
4054
4055 if (!invisible)
4056 {
4057 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
4058 }
4059 else
4060 {
4061 m_actor->ar_beams_invisible[beam_index] = true;
4062 }
4063
4064 hydrobeam_t hb;
4065 hb.hb_flags = hydro_flags;
4067 hb.hb_beam_index = static_cast<uint16_t>(beam_index);
4068 hb.hb_ref_length = beam.L;
4069 hb.hb_anim_flags = 0;
4070 hb.hb_anim_param = 0.f;
4072
4073 m_actor->ar_hydros.push_back(hb);
4074}
4075
4077{
4078 node_t & node_1 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4079 node_t & node_2 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4080 float short_bound = def.short_bound;
4081 float long_bound = def.long_bound;
4082 unsigned int shock_flags = SHOCK_FLAG_NORMAL | SHOCK_FLAG_ISSHOCK3;
4083
4085 {
4086 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4087 short_bound /= beam_length;
4088 long_bound /= beam_length;
4089 }
4091 {
4092 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4093 short_bound = (beam_length - short_bound) / beam_length;
4094 long_bound = (long_bound - beam_length) / beam_length;
4095
4096 if (long_bound < 0.f)
4097 {
4098 AddMessage(
4100 "Metric shock length calculation failed, 'short_bound' less than beams spawn length. Resetting to beam's spawn length (short_bound = 0)"
4101 );
4102 long_bound = 0.f;
4103 }
4104
4105 if (short_bound > 1.f)
4106 {
4107 AddMessage(
4109 "Metric shock length calculation failed, 'short_bound' less than 0 meters. Resetting to 0 meters (short_bound = 1)"
4110 );
4111 short_bound = 1.f;
4112 }
4113 }
4114
4115 int beam_index = m_actor->ar_num_beams;
4116 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
4117 SetBeamStrength(beam, def.beam_defaults->breaking_threshold * 4.f);
4118 beam.bm_type = BEAM_HYDRO;
4119 beam.bounded = SHOCK3;
4120 beam.k = def.spring_in;
4121 beam.d = def.damp_in;
4122 beam.shortbound = short_bound;
4123 beam.longbound = long_bound;
4124
4125 /* Length + pre-compression */
4126 CalculateBeamLength(beam);
4127 beam.L *= def.precompression;
4128 beam.refL *= def.precompression;
4129
4131 {
4132 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
4133 }
4134 else
4135 {
4136 m_actor->ar_beams_invisible[beam_index] = true;
4137 }
4138
4139 shock_t & shock = GetFreeShock();
4140 shock.flags = shock_flags;
4141 shock.sbd_spring = def.beam_defaults->springiness;
4142 shock.sbd_damp = def.beam_defaults->damping_constant;
4143 shock.sbd_break = def.beam_defaults->breaking_threshold;
4144 shock.springin = def.spring_in;
4145 shock.dampin = def.damp_in;
4146 shock.springout = def.spring_out;
4147 shock.dampout = def.damp_out;
4148 shock.splitin = def.split_vel_in;
4149 shock.dslowin = def.damp_in_slow;
4150 shock.dfastin = def.damp_in_fast;
4151 shock.splitout = def.split_vel_out;
4152 shock.dslowout = def.damp_out_slow;
4153 shock.dfastout = def.damp_out_fast;
4155
4156 beam.shock = & shock;
4157 shock.beamid = beam_index;
4158}
4159
4161{
4162 node_t & node_1 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4163 node_t & node_2 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4164 float short_bound = def.short_bound;
4165 float long_bound = def.long_bound;
4166 unsigned int shock_flags = SHOCK_FLAG_NORMAL | SHOCK_FLAG_ISSHOCK2;
4167
4169 {
4170 BITMASK_SET_0(shock_flags, SHOCK_FLAG_NORMAL); /* Not normal anymore */
4171 BITMASK_SET_1(shock_flags, SHOCK_FLAG_SOFTBUMP);
4172 }
4174 {
4175 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4176 short_bound /= beam_length;
4177 long_bound /= beam_length;
4178 }
4180 {
4181 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4182 short_bound = (beam_length - short_bound) / beam_length;
4183 long_bound = (long_bound - beam_length) / beam_length;
4184
4185 if (long_bound < 0.f)
4186 {
4187 AddMessage(
4189 "Metric shock length calculation failed, 'short_bound' less than beams spawn length. Resetting to beam's spawn length (short_bound = 0)"
4190 );
4191 long_bound = 0.f;
4192 }
4193
4194 if (short_bound > 1.f)
4195 {
4196 AddMessage(
4198 "Metric shock length calculation failed, 'short_bound' less than 0 meters. Resetting to 0 meters (short_bound = 1)"
4199 );
4200 short_bound = 1.f;
4201 }
4202 }
4203
4204 int beam_index = m_actor->ar_num_beams;
4205 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
4206 SetBeamStrength(beam, def.beam_defaults->breaking_threshold * 4.f);
4207 beam.bm_type = BEAM_HYDRO;
4208 beam.bounded = SHOCK2;
4209 beam.k = def.spring_in;
4210 beam.d = def.damp_in;
4211 beam.shortbound = short_bound;
4212 beam.longbound = long_bound;
4213
4214 /* Length + pre-compression */
4215 CalculateBeamLength(beam);
4216 beam.L *= def.precompression;
4217 beam.refL *= def.precompression;
4218
4220 {
4221 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
4222 }
4223 else
4224 {
4225 m_actor->ar_beams_invisible[beam_index] = true;
4226 }
4227
4228 shock_t & shock = GetFreeShock();
4229 shock.flags = shock_flags;
4230 shock.sbd_spring = def.beam_defaults->springiness;
4231 shock.sbd_damp = def.beam_defaults->damping_constant;
4232 shock.sbd_break = def.beam_defaults->breaking_threshold;
4233 shock.springin = def.spring_in;
4234 shock.dampin = def.damp_in;
4235 shock.springout = def.spring_out;
4236 shock.dampout = def.damp_out;
4238 shock.dprogin = def.progress_factor_damp_in;
4242
4243 beam.shock = & shock;
4244 shock.beamid = beam_index;
4245}
4246
4248{
4249 node_t & node_1 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4250 node_t & node_2 = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4251 float short_bound = def.short_bound;
4252 float long_bound = def.long_bound;
4253 unsigned int shock_flags = SHOCK_FLAG_NORMAL;
4254
4256 {
4257 BITMASK_SET_0(shock_flags, SHOCK_FLAG_NORMAL); /* Not normal anymore */
4258 BITMASK_SET_1(shock_flags, SHOCK_FLAG_LACTIVE);
4260 }
4262 {
4263 BITMASK_SET_0(shock_flags, SHOCK_FLAG_NORMAL); /* Not normal anymore */
4264 BITMASK_SET_1(shock_flags, SHOCK_FLAG_RACTIVE);
4266 }
4268 {
4269 float beam_length = node_1.AbsPosition.distance(node_2.AbsPosition);
4270 short_bound /= beam_length;
4271 long_bound /= beam_length;
4272 }
4273
4274 int beam_index = m_actor->ar_num_beams;
4275 beam_t & beam = AddBeam(node_1, node_2, def.beam_defaults, def.detacher_group);
4276 beam.shortbound = short_bound;
4277 beam.longbound = long_bound;
4278 beam.bounded = SHOCK1;
4279 beam.bm_type = BEAM_HYDRO;
4280 beam.k = def.spring_rate;
4281 beam.d = def.damping;
4282 SetBeamStrength(beam, def.beam_defaults->breaking_threshold * 4.f);
4283
4284 /* Length + pre-compression */
4285 CalculateBeamLength(beam);
4286 beam.L *= def.precompression;
4287 beam.refL *= def.precompression;
4288
4289 shock_t & shock = GetFreeShock();
4290 shock.flags = shock_flags;
4291 shock.sbd_spring = def.beam_defaults->springiness;
4292 shock.sbd_damp = def.beam_defaults->damping_constant;
4293 shock.sbd_break = def.beam_defaults->breaking_threshold;
4295
4297 {
4298 this->CreateBeamVisuals(beam, beam_index, true, def.beam_defaults);
4299 }
4300 else
4301 {
4302 m_actor->ar_beams_invisible[beam_index] = true;
4303 }
4304
4305 beam.shock = & shock;
4306 shock.beamid = beam_index;
4307}
4308
4310{
4311 NodeNum_t base_node_index = m_actor->ar_num_nodes;
4312 WheelID_t wheel_id = m_actor->ar_num_wheels;
4313 wheel_t & wheel = m_actor->ar_wheels[wheel_id];
4314
4315 node_t *axis_node_1 = &m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[0])];
4316 node_t *axis_node_2 = &m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[1])];
4317 // Enforce the "second node must have a larger Z coordinate than the first" constraint
4318 if (axis_node_1->AbsPosition.z > axis_node_2->AbsPosition.z)
4319 {
4320 node_t* swap = axis_node_1;
4321 axis_node_1 = axis_node_2;
4322 axis_node_2 = swap;
4323 }
4324
4325 // Rigidity node
4326 node_t *rigidity_node = nullptr;
4327 node_t *axis_node_closest_to_rigidity_node = nullptr;
4329 {
4330 rigidity_node = GetNodePointer(def.rigidity_node);
4331 Ogre::Real distance_1 = (rigidity_node->RelPosition - axis_node_1->RelPosition).length();
4332 Ogre::Real distance_2 = (rigidity_node->RelPosition - axis_node_2->RelPosition).length();
4333 axis_node_closest_to_rigidity_node = ((distance_1 < distance_2)) ? axis_node_1 : axis_node_2;
4334 }
4335
4336 // Tweaks
4337 float override_rim_radius = TuneupUtil::getTweakedWheelRimRadius(m_actor->getWorkingTuneupDef(), wheel_id, def.rim_radius);
4338 float override_tire_radius = TuneupUtil::getTweakedWheelTireRadius(m_actor->getWorkingTuneupDef(), wheel_id, def.tyre_radius);
4339
4340 // Node&beam generation
4341 Ogre::Vector3 axis_vector = axis_node_2->RelPosition - axis_node_1->RelPosition;
4342 wheel.wh_width = axis_vector.length(); // wheel_def.width is ignored.
4343 axis_vector.normalise();
4344 Ogre::Vector3 rim_ray_vector = axis_vector.perpendicular() * override_rim_radius;
4345 Ogre::Quaternion rim_ray_rotator = Ogre::Quaternion(Ogre::Degree(-360.f / (def.num_rays * 2)), axis_vector);
4346
4347 // Rim nodes
4348 for (unsigned int i = 0; i < def.num_rays; i++)
4349 {
4350 float node_mass = def.mass / (4.f * def.num_rays);
4351
4352 // Outer ring
4353 Ogre::Vector3 ray_point = axis_node_1->RelPosition + rim_ray_vector;
4354 rim_ray_vector = rim_ray_rotator * rim_ray_vector;
4355
4356 node_t & outer_node = GetFreeNode();
4357 InitNode(outer_node, ray_point, def.node_defaults);
4358
4359 outer_node.mass = node_mass;
4360 outer_node.friction_coef = def.node_defaults->friction;
4361 outer_node.nd_rim_node = true;
4362 AdjustNodeBuoyancy(outer_node, def.node_defaults);
4364
4365 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
4366
4367 // Inner ring
4368 ray_point = axis_node_2->RelPosition + rim_ray_vector;
4369 rim_ray_vector = rim_ray_rotator * rim_ray_vector;
4370
4371 node_t & inner_node = GetFreeNode();
4372 InitNode(inner_node, ray_point, def.node_defaults);
4373
4374 inner_node.mass = node_mass;
4375 inner_node.friction_coef = def.node_defaults->friction;
4376 inner_node.nd_rim_node = true;
4377 AdjustNodeBuoyancy(inner_node, def.node_defaults);
4379
4380 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
4381
4382 // Wheel object
4383 wheel.wh_rim_nodes[i * 2] = & outer_node;
4384 wheel.wh_rim_nodes[(i * 2) + 1] = & inner_node;
4385 }
4386
4387 Ogre::Vector3 tyre_ray_vector = axis_vector.perpendicular() * override_tire_radius;
4388 Ogre::Quaternion& tyre_ray_rotator = rim_ray_rotator;
4389 tyre_ray_vector = tyre_ray_rotator * tyre_ray_vector;
4390
4391 // Tyre nodes
4392 for (unsigned int i = 0; i < def.num_rays; i++)
4393 {
4394 /* Outer ring */
4395 float node_mass = def.mass / (4.f * def.num_rays);
4396 Ogre::Vector3 ray_point = axis_node_1->RelPosition + tyre_ray_vector;
4397 tyre_ray_vector = tyre_ray_rotator * tyre_ray_vector;
4398
4399 node_t & outer_node = GetFreeNode();
4400 InitNode(outer_node, ray_point);
4401 outer_node.mass = node_mass;
4402 outer_node.friction_coef = def.node_defaults->friction;
4403 outer_node.volume_coef = def.node_defaults->volume;
4404 outer_node.surface_coef = def.node_defaults->surface;
4405 outer_node.nd_contacter = true;
4406 outer_node.nd_tyre_node = true;
4407 AdjustNodeBuoyancy(outer_node, def.node_defaults);
4408
4409 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
4410
4411 // Inner ring
4412 ray_point = axis_node_2->RelPosition + tyre_ray_vector;
4413 tyre_ray_vector = tyre_ray_rotator * tyre_ray_vector;
4414
4415 node_t & inner_node = GetFreeNode();
4416 InitNode(inner_node, ray_point);
4417 inner_node.mass = node_mass;
4418 inner_node.friction_coef = def.node_defaults->friction;
4419 inner_node.volume_coef = def.node_defaults->volume;
4420 inner_node.surface_coef = def.node_defaults->surface;
4421 inner_node.nd_contacter = true;
4422 inner_node.nd_tyre_node = true;
4423 AdjustNodeBuoyancy(inner_node, def.node_defaults);
4424
4425 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
4426
4427 // Wheel object
4428 wheel.wh_nodes[i * 2] = & outer_node;
4429 wheel.wh_nodes[(i * 2) + 1] = & inner_node;
4430 }
4431
4433 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = def.num_rays;
4437
4438 // Beams
4439 float rim_spring = def.rim_springiness;
4440 float rim_damp = def.rim_damping;
4441 float tyre_spring = def.tyre_springiness;
4442 float tyre_damp = def.tyre_damping;
4443 float tread_spring = def.beam_defaults->springiness;
4444 float tread_damp = def.beam_defaults->damping_constant;
4445
4446 for (unsigned int i = 0; i < def.num_rays; i++)
4447 {
4448 // --- Rim ---
4449
4450 // Rim axis to rim ring
4451 unsigned int rim_outer_node_index = base_node_index + (i * 2);
4452 node_t *rim_outer_node = & m_actor->ar_nodes[rim_outer_node_index];
4453 node_t *rim_inner_node = & m_actor->ar_nodes[rim_outer_node_index + 1];
4454
4455 AddWheelBeam(axis_node_1, rim_outer_node, rim_spring, rim_damp, def.beam_defaults);
4456 AddWheelBeam(axis_node_2, rim_inner_node, rim_spring, rim_damp, def.beam_defaults);
4457 AddWheelBeam(axis_node_2, rim_outer_node, rim_spring, rim_damp, def.beam_defaults);
4458 AddWheelBeam(axis_node_1, rim_inner_node, rim_spring, rim_damp, def.beam_defaults);
4459
4460 // Reinforcement rim ring
4461 unsigned int rim_next_outer_node_index = base_node_index + (((i + 1) % def.num_rays) * 2);
4462 node_t *rim_next_outer_node = & m_actor->ar_nodes[rim_next_outer_node_index];
4463 node_t *rim_next_inner_node = & m_actor->ar_nodes[rim_next_outer_node_index + 1];
4464
4465 AddWheelBeam(rim_outer_node, rim_inner_node, rim_spring, rim_damp, def.beam_defaults);
4466 AddWheelBeam(rim_outer_node, rim_next_outer_node, rim_spring, rim_damp, def.beam_defaults);
4467 AddWheelBeam(rim_inner_node, rim_next_inner_node, rim_spring, rim_damp, def.beam_defaults);
4468 AddWheelBeam(rim_inner_node, rim_next_outer_node, rim_spring, rim_damp, def.beam_defaults);
4469 }
4470
4471 // Tyre beams
4472 // Quick&dirty port from original SerializedRig::addWheel3()
4473 for (unsigned int i = 0; i < def.num_rays; i++)
4474 {
4475 int rim_node_index = base_node_index + i*2;
4476 int tyre_node_index = base_node_index + i*2 + def.num_rays*2;
4477 node_t * rim_node = & m_actor->ar_nodes[rim_node_index];
4478
4479 AddWheelBeam(rim_node, & m_actor->ar_nodes[tyre_node_index], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4480
4481 int tyre_base_index = (i == 0) ? tyre_node_index + (def.num_rays * 2) : tyre_node_index;
4482 AddWheelBeam(rim_node, & m_actor->ar_nodes[tyre_base_index - 1], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4483 AddWheelBeam(rim_node, & m_actor->ar_nodes[tyre_base_index - 2], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4484
4485 node_t * next_rim_node = & m_actor->ar_nodes[rim_node_index + 1];
4486 AddWheelBeam(next_rim_node, & m_actor->ar_nodes[tyre_node_index], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4487 AddWheelBeam(next_rim_node, & m_actor->ar_nodes[tyre_node_index + 1], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4488
4489 {
4490 int index = (i == 0) ? tyre_node_index + (def.num_rays * 2) - 1 : tyre_node_index - 1;
4491 node_t * tyre_node = & m_actor->ar_nodes[index];
4492 AddWheelBeam(next_rim_node, tyre_node, tyre_spring/2.f, tyre_damp, def.beam_defaults);
4493 }
4494
4495 //reinforcement (tire tread)
4496 {
4497 // Very messy port :(
4498 // Aliases
4499 int rimnode = rim_node_index;
4500 int rays = def.num_rays;
4501
4502 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);
4503 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);
4504 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);
4505 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);
4506
4507 if (rigidity_node != nullptr)
4508 {
4509 if (axis_node_closest_to_rigidity_node == axis_node_1)
4510 {
4511 axis_node_closest_to_rigidity_node = & m_actor->ar_nodes[base_node_index+i*2+rays*2];
4512 } else
4513 {
4514 axis_node_closest_to_rigidity_node = & m_actor->ar_nodes[base_node_index+i*2+1+rays*2];
4515 };
4516 unsigned int beam_index = AddWheelBeam(rigidity_node, axis_node_closest_to_rigidity_node, tyre_spring, tyre_damp, def.beam_defaults);
4517 GetBeam(beam_index).bm_type = BEAM_VIRTUAL;
4518 }
4519 }
4520 }
4521
4522 // Calculate the point where the support beams get stiff and prevent the tire tread nodes
4523 // bounce into the rim rimradius / tire radius and add 5%, this is a shortbound calc in % !
4524
4525 float support_beams_short_bound = 1.0f - ((override_rim_radius / override_tire_radius) * 0.95f);
4526
4527 for (unsigned int i=0; i<def.num_rays; i++)
4528 {
4529 // tiretread anti collapse reinforcements, using precalced support beams
4530 unsigned int tirenode = base_node_index + i*2 + def.num_rays*2;
4531 unsigned int beam_index;
4532
4533 beam_index = AddWheelBeam(axis_node_1, &m_actor->ar_nodes[tirenode], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4534 GetBeam(beam_index).shortbound = support_beams_short_bound;
4535 GetBeam(beam_index).longbound = 0.f;
4536 GetBeam(beam_index).bounded = SHOCK1;
4537
4538 beam_index = AddWheelBeam(axis_node_2, &m_actor->ar_nodes[tirenode + 1], tyre_spring/2.f, tyre_damp, def.beam_defaults);
4539 GetBeam(beam_index).shortbound = support_beams_short_bound;
4540 GetBeam(beam_index).longbound = 0.f;
4541 GetBeam(beam_index).bounded = SHOCK1;
4542 }
4543
4544 // Wheel object
4545 wheel.wh_braking = def.braking;
4546 wheel.wh_propulsed = def.propulsion;
4547 wheel.wh_num_nodes = 2 * def.num_rays;
4548 wheel.wh_num_rim_nodes = wheel.wh_num_nodes;
4549 wheel.wh_axis_node_0 = axis_node_1;
4550 wheel.wh_axis_node_1 = axis_node_2;
4551 wheel.wh_radius = override_tire_radius;
4552 wheel.wh_rim_radius = override_rim_radius;
4554
4556 {
4557 // for inter-differential locking
4560 }
4561
4562 // Find near attach
4563 Ogre::Real length_1 = (axis_node_1->RelPosition - wheel.wh_arm_node->RelPosition).length();
4564 Ogre::Real length_2 = (axis_node_2->RelPosition - wheel.wh_arm_node->RelPosition).length();
4565 wheel.wh_near_attach_node = (length_1 < length_2) ? axis_node_1 : axis_node_2;
4566
4567 this->CreateFlexBodyWheelVisuals(wheel_id,
4568 base_node_index,
4569 axis_node_1->pos,
4570 axis_node_2->pos,
4571 def.num_rays,
4572 override_rim_radius,
4578 );
4579
4580 // Commit the wheel
4582}
4583
4585{
4586 node_t *def_node_1 = GetNodePointerOrThrow(def.nodes[0]);
4587 node_t *def_node_2 = GetNodePointerOrThrow(def.nodes[1]);
4588
4589 if (def_node_1 == def_node_2)
4590 {
4591 throw Exception("Wheel axis nodes must not be set to single node!");
4592 }
4593
4594 /* Enforce the "second node must have a larger Z coordinate than the first" constraint */
4595 if (def_node_1->AbsPosition.z > def_node_2->AbsPosition.z)
4596 {
4597 out_node_1 = def_node_2;
4598 out_node_2 = def_node_1;
4599 }
4600 else
4601 {
4602 out_node_1 = def_node_1;
4603 out_node_2 = def_node_2;
4604 }
4605}
4606
4608{
4609 WheelID_t wheel_id = m_actor->ar_num_wheels;
4610
4611 node_t* axis_node_1 = nullptr;
4612 node_t* axis_node_2 = nullptr;
4613 this->GetWheelAxisNodes(meshwheel_def, axis_node_1, axis_node_2);
4614
4615 NodeNum_t base_node_index = (NodeNum_t)m_actor->ar_num_nodes;
4617 wheel_id,
4618 meshwheel_def.num_rays,
4619 axis_node_1,
4620 axis_node_2,
4621 GetNodePointer(meshwheel_def.reference_arm_node), /*optional*/
4622 meshwheel_def.num_rays * 2,
4623 meshwheel_def.num_rays * 8,
4625 meshwheel_def.propulsion,
4626 meshwheel_def.braking,
4627 meshwheel_def.node_defaults,
4628 meshwheel_def.mass
4629 );
4630
4632 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = meshwheel_def.num_rays;
4633 m_actor->ar_wheels[wheel_id].wh_arg_rigidity_node = this->ResolveNodeRef(meshwheel_def.rigidity_node, /* optional: */ true);
4634 m_actor->ar_wheels[wheel_id].wh_arg_simple_spring = meshwheel_def.spring;
4635 m_actor->ar_wheels[wheel_id].wh_arg_simple_damping = meshwheel_def.damping;
4636 m_actor->ar_wheels[wheel_id].wh_arg_side = meshwheel_def.side;
4637 m_actor->ar_wheels[wheel_id].wh_arg_media1 = meshwheel_def.mesh_name;
4638 m_actor->ar_wheels[wheel_id].wh_arg_media2 = meshwheel_def.material_name;
4640
4641 this->BuildWheelBeams(
4642 meshwheel_def.num_rays,
4643 base_node_index,
4644 axis_node_1,
4645 axis_node_2,
4646 meshwheel_def.spring, /* Tyre */
4647 meshwheel_def.damping, /* Tyre */
4648 meshwheel_def.spring, /* Rim */
4649 meshwheel_def.damping, /* Rim */
4650 meshwheel_def.beam_defaults,
4651 meshwheel_def.rigidity_node
4652 );
4653
4655 wheel_id,
4656 base_node_index,
4657 axis_node_1->pos,
4658 axis_node_2->pos,
4659 meshwheel_def.num_rays,
4665 meshwheel_def.rim_radius
4666 );
4667
4668 this->CreateWheelSkidmarks(wheel_id);
4669
4671}
4672
4674{
4675 WheelID_t wheel_id = m_actor->ar_num_wheels;
4676
4677 node_t* axis_node_1 = nullptr;
4678 node_t* axis_node_2 = nullptr;
4679 this->GetWheelAxisNodes(def, axis_node_1, axis_node_2);
4680
4681 // --- Nodes ---
4682 NodeNum_t base_node_index = (NodeNum_t)m_actor->ar_num_nodes;
4683
4685 wheel_id,
4686 def.num_rays,
4687 axis_node_1,
4688 axis_node_2,
4690 def.num_rays * 2,
4691 def.num_rays * 8,
4693 def.propulsion,
4694 def.braking,
4695 def.node_defaults,
4696 def.mass
4697 );
4698
4699 // --- Args ---
4701 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = def.num_rays;
4702 m_actor->ar_wheels[wheel_id].wh_arg_rigidity_node = this->ResolveNodeRef(def.rigidity_node, /* optional: */ true);
4705 m_actor->ar_wheels[wheel_id].wh_arg_side = def.side;
4706 m_actor->ar_wheels[wheel_id].wh_arg_rim_spring = def.beam_defaults->springiness;
4707 m_actor->ar_wheels[wheel_id].wh_arg_rim_damping = def.beam_defaults->damping_constant;
4708 m_actor->ar_wheels[wheel_id].wh_arg_media1 = def.mesh_name;
4711
4712 /* --- Beams --- */
4713 /* Use data from directive 'set_beam_defaults' for the tiretread beams */
4714 float tyre_spring = def.spring;
4715 float tyre_damp = def.damping;
4716 float rim_spring = def.beam_defaults->springiness;
4717 float rim_damp = def.beam_defaults->damping_constant;
4718
4720 def.num_rays,
4721 base_node_index,
4722 axis_node_1,
4723 axis_node_2,
4724 tyre_spring,
4725 tyre_damp,
4726 rim_spring,
4727 rim_damp,
4728 def.beam_defaults,
4729 def.rigidity_node,
4730 0.15 // max_extension
4731 );
4732
4734 wheel_id,
4735 base_node_index,
4736 axis_node_1->pos,
4737 axis_node_2->pos,
4738 def.num_rays,
4745 );
4746
4747 CreateWheelSkidmarks(wheel_id);
4748
4750}
4751
4753 WheelID_t wheel_index,
4754 NodeNum_t base_node_index,
4755 NodeNum_t axis_node_1_index,
4756 NodeNum_t axis_node_2_index,
4757 unsigned int num_rays,
4758 WheelSide side,
4759 Ogre::String mesh_name,
4760 Ogre::String mesh_rg,
4761 Ogre::String material_name,
4762 Ogre::String material_rg,
4763 float rim_radius
4764)
4765{
4766 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
4767
4768 try
4769 {
4771 wheel_index,
4772 axis_node_1_index,
4773 axis_node_2_index,
4774 base_node_index,
4775 num_rays,
4776 rim_radius,
4777 side != WheelSide::RIGHT,
4778 mesh_name,
4779 mesh_rg,
4780 material_name,
4781 material_rg);
4782 Ogre::SceneNode* scene_node = m_wheels_parent_scenenode->createChildSceneNode(this->ComposeName("meshwheel*", wheel_index));
4783 scene_node->attachObject(flexmesh_wheel->GetTireEntity());
4784
4785 WheelGfx visual_wheel;
4786 visual_wheel.wx_wheel_id = wheel_index;
4787 visual_wheel.wx_flex_mesh = flexmesh_wheel;
4788 visual_wheel.wx_scenenode = scene_node;
4789 visual_wheel.wx_side = side;
4790 visual_wheel.wx_rim_mesh_name = mesh_name;
4791 m_actor->m_gfx_actor->m_wheels.push_back(visual_wheel);
4792 }
4793 catch (Ogre::Exception& e)
4794 {
4795 this->AddMessage(Message::TYPE_ERROR, "Failed to create meshwheel visuals: " + e.getDescription());
4796 return;
4797 }
4798}
4799
4801 WheelID_t wheel_id,
4802 unsigned int num_rays,
4803 node_t *axis_node_1,
4804 node_t *axis_node_2,
4805 node_t *reference_arm_node,
4806 unsigned int reserve_nodes,
4807 unsigned int reserve_beams,
4808 float wheel_radius,
4809 WheelPropulsion propulsion,
4810 WheelBraking braking,
4811 std::shared_ptr<RigDef::NodeDefaults> node_defaults,
4812 float wheel_mass,
4813 float wheel_width /* Default: -1.f */
4814)
4815{
4816 wheel_t & wheel = m_actor->ar_wheels[wheel_id];
4817
4818 /* Axis */
4819 Ogre::Vector3 axis_vector = axis_node_2->RelPosition - axis_node_1->RelPosition;
4820 float axis_length = axis_vector.length();
4821 axis_vector.normalise();
4822
4823 /* Wheel object */
4824 wheel.wh_braking = braking;
4825 wheel.wh_propulsed = propulsion;
4826 wheel.wh_num_nodes = 2 * num_rays;
4827 wheel.wh_axis_node_0 = axis_node_1;
4828 wheel.wh_axis_node_1 = axis_node_2;
4829 wheel.wh_radius = wheel_radius;
4830 wheel.wh_width = (wheel_width < 0) ? axis_length : wheel_width;
4831 wheel.wh_arm_node = reference_arm_node;
4832
4833 /* Find near attach */
4834 Ogre::Real length_1 = (axis_node_1->RelPosition - wheel.wh_arm_node->RelPosition).length();
4835 Ogre::Real length_2 = (axis_node_2->RelPosition - wheel.wh_arm_node->RelPosition).length();
4836 wheel.wh_near_attach_node = (length_1 < length_2) ? axis_node_1 : axis_node_2;
4837
4838 if (propulsion != WheelPropulsion::NONE)
4839 {
4840 /* for inter-differential locking */
4843 }
4844
4845 /* Nodes */
4846 Ogre::Vector3 ray_vector = axis_vector.perpendicular() * wheel_radius;
4847 Ogre::Quaternion ray_rotator = Ogre::Quaternion(Ogre::Degree(-360.0 / (num_rays * 2)), axis_vector);
4848
4849 for (unsigned int i = 0; i < num_rays; i++)
4850 {
4851 /* Outer ring */
4852 Ogre::Vector3 ray_point = axis_node_1->RelPosition + ray_vector;
4853 Ogre::Vector3 ray_spawnpoint = m_actor->ar_nodes_spawn_offsets[axis_node_1->pos] + ray_vector;
4854 ray_vector = ray_rotator * ray_vector;
4855
4856 node_t & outer_node = GetFreeNode();
4857 InitNode(outer_node, ray_point, node_defaults);
4858 outer_node.mass = wheel_mass / (2.f * num_rays);
4859 outer_node.nd_contacter = true;
4860 outer_node.nd_tyre_node = true;
4861 AdjustNodeBuoyancy(outer_node, node_defaults);
4862
4863 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
4864 m_actor->ar_nodes_spawn_offsets[outer_node.pos] = ray_spawnpoint;
4865
4866 /* Inner ring */
4867 ray_point = axis_node_2->RelPosition + ray_vector;
4868 ray_spawnpoint = m_actor->ar_nodes_spawn_offsets[axis_node_2->pos] + ray_vector;
4869 ray_vector = ray_rotator * ray_vector;
4870
4871 node_t & inner_node = GetFreeNode();
4872 InitNode(inner_node, ray_point, node_defaults);
4873 inner_node.mass = wheel_mass / (2.f * num_rays);
4874 inner_node.nd_contacter = true;
4875 inner_node.nd_tyre_node = true;
4876 AdjustNodeBuoyancy(inner_node, node_defaults);
4877
4878 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
4879 m_actor->ar_nodes_spawn_offsets[inner_node.pos] = ray_spawnpoint;
4880
4881 /* Wheel object */
4882 wheel.wh_nodes[i * 2] = & outer_node;
4883 wheel.wh_nodes[(i * 2) + 1] = & inner_node;
4884 }
4885}
4886
4887void ActorSpawner::AdjustNodeBuoyancy(node_t & node, RigDef::Node & node_def, std::shared_ptr<RigDef::NodeDefaults> defaults)
4888{
4889 unsigned int options = (defaults->options | node_def.options); // Merge flags
4891}
4892
4893void ActorSpawner::AdjustNodeBuoyancy(node_t & node, std::shared_ptr<RigDef::NodeDefaults> defaults)
4894{
4895 node.buoyancy = BITMASK_IS_1(defaults->options, RigDef::Node::OPTION_b_EXTRA_BUOYANCY) ? 10000.f : m_actor->ar_dry_mass/15.f;
4896}
4897
4899 unsigned int num_rays,
4900 NodeNum_t base_node_index,
4901 node_t *axis_node_1,
4902 node_t *axis_node_2,
4903 float tyre_spring,
4904 float tyre_damping,
4905 float rim_spring,
4906 float rim_damping,
4907 std::shared_ptr<RigDef::BeamDefaults> beam_defaults,
4908 RigDef::Node::Ref const & rigidity_node_id,
4909 float max_extension // = 0.f
4910)
4911{
4912 /* Find out where to connect rigidity node */
4913 bool rigidity_beam_side_1 = false;
4914 node_t *rigidity_node = nullptr;
4915 if (rigidity_node_id.IsValidAnyState())
4916 {
4917 rigidity_node = GetNodePointerOrThrow(rigidity_node_id);
4918 float distance_1 = rigidity_node->RelPosition.distance(axis_node_1->RelPosition);
4919 float distance_2 = rigidity_node->RelPosition.distance(axis_node_2->RelPosition);
4920 rigidity_beam_side_1 = distance_1 < distance_2;
4921 }
4922
4923 for (unsigned int i = 0; i < num_rays; i++)
4924 {
4925 /* Bounded */
4926 unsigned int outer_ring_node_index = base_node_index + (i * 2);
4927 node_t *outer_ring_node = & m_actor->ar_nodes[outer_ring_node_index];
4928 node_t *inner_ring_node = & m_actor->ar_nodes[outer_ring_node_index + 1];
4929
4930 AddWheelBeam(axis_node_1, outer_ring_node, tyre_spring, tyre_damping, beam_defaults, 0.66f, max_extension);
4931 AddWheelBeam(axis_node_2, inner_ring_node, tyre_spring, tyre_damping, beam_defaults, 0.66f, max_extension);
4932 AddWheelBeam(axis_node_2, outer_ring_node, tyre_spring, tyre_damping, beam_defaults);
4933 AddWheelBeam(axis_node_1, inner_ring_node, tyre_spring, tyre_damping, beam_defaults);
4934
4935 /* Reinforcement */
4936 unsigned int next_outer_ring_node_index = base_node_index + (((i + 1) % num_rays) * 2);
4937 node_t *next_outer_ring_node = & m_actor->ar_nodes[next_outer_ring_node_index];
4938 node_t *next_inner_ring_node = & m_actor->ar_nodes[next_outer_ring_node_index + 1];
4939
4940 AddWheelBeam(outer_ring_node, inner_ring_node, rim_spring, rim_damping, beam_defaults);
4941 AddWheelBeam(outer_ring_node, next_outer_ring_node, rim_spring, rim_damping, beam_defaults);
4942 AddWheelBeam(inner_ring_node, next_inner_ring_node, rim_spring, rim_damping, beam_defaults);
4943 AddWheelBeam(inner_ring_node, next_outer_ring_node, rim_spring, rim_damping, beam_defaults);
4944
4945 /* Rigidity beams */
4946 if (rigidity_node != nullptr)
4947 {
4948 node_t *target_node = (rigidity_beam_side_1) ? outer_ring_node : inner_ring_node;
4949 unsigned int beam_index = AddWheelBeam(rigidity_node, target_node, tyre_spring, tyre_damping, beam_defaults, -1.f, -1.f, BEAM_VIRTUAL);
4950 m_actor->ar_beams[beam_index].bm_type = BEAM_VIRTUAL;
4951 }
4952 }
4953}
4954
4956{
4957 WheelID_t wheel_id = m_actor->ar_num_wheels;
4958
4959 node_t* axis_node_1 = nullptr;
4960 node_t* axis_node_2 = nullptr;
4961 this->GetWheelAxisNodes(wheel_def, axis_node_1, axis_node_2);
4962
4963 NodeNum_t base_node_index = (NodeNum_t)m_actor->ar_num_nodes;
4964
4966 wheel_id,
4967 wheel_def.num_rays,
4968 axis_node_1,
4969 axis_node_2,
4971 wheel_def.num_rays * 2,
4972 wheel_def.num_rays * 8,
4974 wheel_def.propulsion,
4975 wheel_def.braking,
4976 wheel_def.node_defaults,
4977 wheel_def.mass,
4978 -1.f // Set width to axis length (width in definition is ignored)
4979 );
4980
4982 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = wheel_def.num_rays;
4983 m_actor->ar_wheels[wheel_id].wh_arg_rigidity_node = this->ResolveNodeRef(wheel_def.rigidity_node, /* optional: */ true);
4984 m_actor->ar_wheels[wheel_id].wh_arg_simple_spring = wheel_def.springiness;
4985 m_actor->ar_wheels[wheel_id].wh_arg_simple_damping = wheel_def.damping;
4986 m_actor->ar_wheels[wheel_id].wh_arg_media1 = wheel_def.face_material_name;
4987 m_actor->ar_wheels[wheel_id].wh_arg_media2 = wheel_def.band_material_name;
4989
4990 this->BuildWheelBeams(
4991 wheel_def.num_rays,
4992 base_node_index,
4993 axis_node_1,
4994 axis_node_2,
4995 wheel_def.springiness, /* Tyre */
4996 wheel_def.damping, /* Tyre */
4997 wheel_def.springiness, /* Rim */
4998 wheel_def.damping, /* Rim */
4999 wheel_def.beam_defaults,
5000 wheel_def.rigidity_node
5001 );
5002
5003 this->CreateWheelVisuals(
5004 wheel_id,
5005 base_node_index,
5006 wheel_def.num_rays,
5011 /*separate_rim:*/false
5012 );
5013
5014 CreateWheelSkidmarks(wheel_id);
5015
5017}
5018
5020{
5021 // Always create, even if disabled by config
5022 m_actor->m_skid_trails[wheel_index] = new RoR::Skidmark(
5023 RoR::App::GetGfxScene()->GetSkidmarkConf(), &m_actor->ar_wheels[wheel_index], m_particles_parent_scenenode, 300, 20);
5024}
5025
5027{
5028 WheelID_t wheel_id = m_actor->ar_num_wheels;
5029
5030 node_t* axis_node_1 = nullptr;
5031 node_t* axis_node_2 = nullptr;
5032 this->GetWheelAxisNodes(wheel_2_def, axis_node_1, axis_node_2);
5033
5034 NodeNum_t base_node_index = (NodeNum_t)m_actor->ar_num_nodes;
5035
5036 /* Find out where to connect rigidity node */
5037 bool rigidity_beam_side_1 = false;
5038 if (wheel_2_def.rigidity_node.IsValidAnyState())
5039 {
5040 node_t & rigidity_node = m_actor->ar_nodes[this->GetNodeIndexOrThrow(wheel_2_def.rigidity_node)];
5041 Ogre::Real distance_1 = (rigidity_node.RelPosition - axis_node_1->RelPosition).length();
5042 Ogre::Real distance_2 = (rigidity_node.RelPosition - axis_node_2->RelPosition).length();
5043 rigidity_beam_side_1 = distance_1 < distance_2;
5044 }
5045
5046 // Tweaks
5047 float override_rim_radius = TuneupUtil::getTweakedWheelRimRadius(m_actor->getWorkingTuneupDef(), wheel_id, wheel_2_def.rim_radius);
5048 float override_tire_radius = TuneupUtil::getTweakedWheelTireRadius(m_actor->getWorkingTuneupDef(), wheel_id, wheel_2_def.tyre_radius);
5049
5050 /* Node&beam generation */
5051 wheel_t& wheel = m_actor->ar_wheels[wheel_id];
5052 Ogre::Vector3 axis_vector = axis_node_2->RelPosition - axis_node_1->RelPosition;
5053 axis_vector.normalise();
5054 Ogre::Vector3 rim_ray_vector = Ogre::Vector3(0, override_rim_radius, 0);
5055 Ogre::Quaternion rim_ray_rotator = Ogre::Quaternion(Ogre::Degree(-360.f / wheel_2_def.num_rays), axis_vector);
5056
5057 /* Width */
5058 wheel.wh_width = axis_vector.length(); /* wheel_def.width is ignored. */
5059
5060 /* Rim nodes */
5061 for (unsigned int i = 0; i < wheel_2_def.num_rays; i++)
5062 {
5063 float node_mass = wheel_2_def.mass / (4.f * wheel_2_def.num_rays);
5064
5065 /* Outer ring */
5066 Ogre::Vector3 ray_point = axis_node_1->RelPosition + rim_ray_vector;
5067
5068 node_t & outer_node = GetFreeNode();
5069 InitNode(outer_node, ray_point, wheel_2_def.node_defaults);
5070 outer_node.mass = node_mass;
5071 outer_node.nd_rim_node = true;
5072
5074
5075 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
5076
5077 /* Inner ring */
5078 ray_point = axis_node_2->RelPosition + rim_ray_vector;
5079
5080 node_t & inner_node = GetFreeNode();
5081 InitNode(inner_node, ray_point, wheel_2_def.node_defaults);
5082 inner_node.mass = node_mass;
5083 inner_node.nd_rim_node = true;
5084
5086
5087 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
5088
5089 /* Wheel object */
5090 wheel.wh_rim_nodes[i * 2] = & outer_node;
5091 wheel.wh_rim_nodes[(i * 2) + 1] = & inner_node;
5092
5093 rim_ray_vector = rim_ray_rotator * rim_ray_vector;
5094 }
5095
5096 Ogre::Vector3 tyre_ray_vector = Ogre::Vector3(0, override_tire_radius, 0);
5097 Ogre::Quaternion tyre_ray_rotator = Ogre::Quaternion(Ogre::Degree(-180.f / wheel_2_def.num_rays), axis_vector);
5098 tyre_ray_vector = tyre_ray_rotator * tyre_ray_vector;
5099
5100 /* Tyre nodes */
5101 for (unsigned int i = 0; i < wheel_2_def.num_rays; i++)
5102 {
5103 /* Outer ring */
5104 Ogre::Vector3 ray_point = axis_node_1->RelPosition + tyre_ray_vector;
5105
5106 node_t & outer_node = GetFreeNode();
5107 InitNode(outer_node, ray_point);
5108 outer_node.mass = (0.67f * wheel_2_def.mass) / (2.f * wheel_2_def.num_rays);
5109 outer_node.friction_coef = wheel.wh_width * WHEEL_FRICTION_COEF;
5110 outer_node.volume_coef = wheel_2_def.node_defaults->volume;
5111 outer_node.surface_coef = wheel_2_def.node_defaults->surface;
5112 outer_node.nd_contacter = true;
5113 outer_node.nd_tyre_node = true;
5114
5115 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(outer_node.pos));
5116
5117 /* Inner ring */
5118 ray_point = axis_node_2->RelPosition + tyre_ray_vector;
5119
5120 node_t & inner_node = GetFreeNode();
5121 InitNode(inner_node, ray_point);
5122 inner_node.mass = (0.33f * wheel_2_def.mass) / (2.f * wheel_2_def.num_rays);
5123 inner_node.friction_coef = wheel.wh_width * WHEEL_FRICTION_COEF;
5124 inner_node.volume_coef = wheel_2_def.node_defaults->volume;
5125 inner_node.surface_coef = wheel_2_def.node_defaults->surface;
5126 inner_node.nd_contacter = true;
5127 inner_node.nd_tyre_node = true;
5128
5129 m_actor->m_gfx_actor->m_gfx_nodes.push_back(NodeGfx(inner_node.pos));
5130
5131 /* Wheel object */
5132 wheel.wh_nodes[i * 2] = & outer_node;
5133 wheel.wh_nodes[(i * 2) + 1] = & inner_node;
5134
5135 tyre_ray_vector = rim_ray_rotator * tyre_ray_vector; // This is OK
5136 }
5137
5138 // ~~~ Args ~~~
5140 m_actor->ar_wheels[wheel_id].wh_radius = wheel_2_def.tyre_radius;
5141 m_actor->ar_wheels[wheel_id].wh_rim_radius = wheel_2_def.rim_radius;
5142 m_actor->ar_wheels[wheel_id].wh_arg_num_rays = wheel_2_def.num_rays;
5143 m_actor->ar_wheels[wheel_id].wh_arg_rigidity_node = this->ResolveNodeRef(wheel_2_def.rigidity_node, /* optional: */ true);
5144 m_actor->ar_wheels[wheel_id].wh_arg_rim_spring = wheel_2_def.rim_springiness;
5145 m_actor->ar_wheels[wheel_id].wh_arg_rim_damping = wheel_2_def.rim_damping;
5146 m_actor->ar_wheels[wheel_id].wh_arg_simple_spring = wheel_2_def.tyre_springiness;
5147 m_actor->ar_wheels[wheel_id].wh_arg_simple_damping = wheel_2_def.tyre_damping;
5148 m_actor->ar_wheels[wheel_id].wh_arg_media1 = wheel_2_def.face_material_name;
5149 m_actor->ar_wheels[wheel_id].wh_arg_media2 = wheel_2_def.band_material_name;
5151
5152 /* Beams */
5153 for (unsigned int i = 0; i < wheel_2_def.num_rays; i++)
5154 {
5155 /* --- Rim --- */
5156
5157 /* Bounded */
5158 unsigned int rim_outer_node_index = base_node_index + (i * 2);
5159 node_t *rim_outer_node = & m_actor->ar_nodes[rim_outer_node_index];
5160 node_t *rim_inner_node = & m_actor->ar_nodes[rim_outer_node_index + 1];
5161
5162 unsigned int beam_index;
5163 beam_index = AddWheelRimBeam(wheel_2_def, axis_node_1, rim_outer_node);
5164 GetBeam(beam_index).shortbound = 0.66;
5165 beam_index = AddWheelRimBeam(wheel_2_def, axis_node_2, rim_inner_node);
5166 GetBeam(beam_index).shortbound = 0.66;
5167 AddWheelRimBeam(wheel_2_def, axis_node_2, rim_outer_node);
5168 AddWheelRimBeam(wheel_2_def, axis_node_1, rim_inner_node);
5169
5170 /* Reinforcement */
5171 unsigned int rim_next_outer_node_index = base_node_index + (((i + 1) % wheel_2_def.num_rays) * 2);
5172 node_t *rim_next_outer_node = & m_actor->ar_nodes[rim_next_outer_node_index];
5173 node_t *rim_next_inner_node = & m_actor->ar_nodes[rim_next_outer_node_index + 1];
5174
5175 AddWheelRimBeam(wheel_2_def, axis_node_1, rim_outer_node);
5176 AddWheelRimBeam(wheel_2_def, rim_outer_node, rim_inner_node);
5177 AddWheelRimBeam(wheel_2_def, rim_outer_node, rim_next_outer_node);
5178 AddWheelRimBeam(wheel_2_def, rim_inner_node, rim_next_inner_node);
5179 AddWheelRimBeam(wheel_2_def, rim_outer_node, rim_next_inner_node);
5180 AddWheelRimBeam(wheel_2_def, rim_inner_node, rim_next_outer_node);
5181
5182 /* -- Rigidity -- */
5183 if (wheel_2_def.rigidity_node.IsValidAnyState())
5184 {
5185 unsigned int rig_beam_index = AddWheelRimBeam(wheel_2_def,
5186 GetNodePointer(wheel_2_def.rigidity_node),
5187 (rigidity_beam_side_1) ? rim_outer_node : rim_inner_node
5188 );
5189 m_actor->ar_beams[rig_beam_index].bm_type = BEAM_VIRTUAL;
5190 }
5191
5192 /* --- Tyre --- */
5193
5194 unsigned int tyre_node_index = rim_outer_node_index + (2 * wheel_2_def.num_rays);
5195 node_t *tyre_outer_node = & m_actor->ar_nodes[tyre_node_index];
5196 node_t *tyre_inner_node = & m_actor->ar_nodes[tyre_node_index + 1];
5197 unsigned int tyre_next_node_index = rim_next_outer_node_index + (2 * wheel_2_def.num_rays);
5198 node_t *tyre_next_outer_node = & m_actor->ar_nodes[tyre_next_node_index];
5199 node_t *tyre_next_inner_node = & m_actor->ar_nodes[tyre_next_node_index + 1];
5200
5201 /* Tyre band */
5202 AddTyreBeam(wheel_2_def, tyre_outer_node, tyre_next_outer_node);
5203 AddTyreBeam(wheel_2_def, tyre_outer_node, tyre_next_inner_node);
5204 AddTyreBeam(wheel_2_def, tyre_inner_node, tyre_next_outer_node);
5205 AddTyreBeam(wheel_2_def, tyre_inner_node, tyre_next_inner_node);
5206 /* Tyre sidewalls */
5207 AddTyreBeam(wheel_2_def, tyre_outer_node, rim_outer_node);
5208 AddTyreBeam(wheel_2_def, tyre_outer_node, rim_next_outer_node);
5209 AddTyreBeam(wheel_2_def, tyre_inner_node, rim_inner_node);
5210 AddTyreBeam(wheel_2_def, tyre_inner_node, rim_next_inner_node);
5211 /* Reinforcement */
5212 AddTyreBeam(wheel_2_def, tyre_outer_node, rim_inner_node);
5213 AddTyreBeam(wheel_2_def, tyre_outer_node, rim_next_inner_node);
5214 AddTyreBeam(wheel_2_def, tyre_inner_node, rim_outer_node);
5215 AddTyreBeam(wheel_2_def, tyre_inner_node, rim_next_outer_node);
5216 /* Backpressure, bounded */
5217 AddTyreBeam(wheel_2_def, axis_node_1, tyre_outer_node);
5218 AddTyreBeam(wheel_2_def, axis_node_2, tyre_inner_node);
5219 }
5220
5221 /* Wheel object */
5222 wheel.wh_braking = wheel_2_def.braking;
5223 wheel.wh_propulsed = wheel_2_def.propulsion;
5224 wheel.wh_num_nodes = 2 * wheel_2_def.num_rays;
5225 wheel.wh_num_rim_nodes = wheel.wh_num_nodes;
5226 wheel.wh_axis_node_0 = axis_node_1;
5227 wheel.wh_axis_node_1 = axis_node_2;
5228 wheel.wh_radius = override_tire_radius;
5229 wheel.wh_rim_radius = override_rim_radius;
5230 wheel.wh_arm_node = this->GetNodePointer(wheel_2_def.reference_arm_node);
5231
5232 if (wheel_2_def.propulsion != WheelPropulsion::NONE)
5233 {
5234 /* for inter-differential locking */
5237 }
5238
5239 /* Find near attach */
5240 Ogre::Real length_1 = (axis_node_1->RelPosition - wheel.wh_arm_node->RelPosition).length();
5241 Ogre::Real length_2 = (axis_node_2->RelPosition - wheel.wh_arm_node->RelPosition).length();
5242 wheel.wh_near_attach_node = (length_1 < length_2) ? axis_node_1 : axis_node_2;
5243
5244 CreateWheelSkidmarks(static_cast<unsigned>(m_actor->ar_num_wheels));
5245
5246 this->CreateWheelVisuals(
5247 wheel_id,
5248 base_node_index,
5249 wheel_2_def.num_rays,
5254 /*separate_rim:*/true,
5255 /*rim_ratio:*/override_rim_radius / override_tire_radius
5256 );
5257
5259}
5260
5262 WheelID_t wheel_index,
5263 NodeNum_t node_base_index,
5264 unsigned int num_rays,
5265 Ogre::String const& face_material_name,
5266 Ogre::String const& face_material_rg,
5267 Ogre::String const& band_material_name,
5268 Ogre::String const& band_material_rg,
5269 bool separate_rim,
5270 float rim_ratio
5271)
5272{
5273 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
5274
5275 wheel_t & wheel = m_actor->ar_wheels[wheel_index];
5276
5277 try
5278 {
5279 WheelGfx visual_wheel;
5280
5281 const std::string wheel_mesh_name = this->ComposeName("mesh @ wheel*", wheel_index);
5282 visual_wheel.wx_flex_mesh = new FlexMesh(
5283 wheel_mesh_name,
5284 m_actor->m_gfx_actor.get(),
5285 wheel.wh_axis_node_0->pos,
5286 wheel.wh_axis_node_1->pos,
5287 static_cast<NodeNum_t>(node_base_index), // FIXME - node_base_index should be also NodeNum_t
5288 num_rays,
5289 face_material_name, face_material_rg,
5290 band_material_name, band_material_rg,
5291 separate_rim,
5292 rim_ratio
5293 );
5294
5295 const std::string instance_name = this->ComposeName("entity @ wheel*", wheel_index);
5296 Ogre::Entity *ec = App::GetGfxScene()->GetSceneManager()->createEntity(instance_name, wheel_mesh_name);
5297 this->SetupNewEntity(ec, Ogre::ColourValue(0, 0.5, 0.5));
5298 visual_wheel.wx_scenenode = m_wheels_parent_scenenode->createChildSceneNode(this->ComposeName("wheel", wheel_index));
5299 m_actor->m_deletion_entities.emplace_back(ec);
5300 visual_wheel.wx_scenenode->attachObject(ec);
5301 m_actor->m_gfx_actor->m_wheels.push_back(visual_wheel);
5302 }
5303 catch (Ogre::Exception& e)
5304 {
5305 AddMessage(Message::TYPE_ERROR, "Failed to create wheel visuals: " + e.getFullDescription());
5306 }
5307}
5308
5310 WheelID_t wheel_id,
5311 NodeNum_t node_base_index,
5312 NodeNum_t axis_node_1,
5313 NodeNum_t axis_node_2,
5314 int num_rays,
5315 float radius,
5316 WheelSide side,
5317 std::string rim_mesh_name,
5318 std::string rim_mesh_rg,
5319 std::string tire_mesh_name,
5320 std::string tire_mesh_rg)
5321{
5322 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
5323
5325 wheel_id,
5326 node_base_index,
5327 axis_node_1,
5328 axis_node_2,
5329 num_rays,
5330 side,
5331 rim_mesh_name,
5332 rim_mesh_rg,
5333 "tracks/trans", // Use a builtin transparent material for the generated tire mesh, to effectively disable it.
5335 radius
5336 );
5337
5338 int num_nodes = num_rays * 4;
5339 std::vector<unsigned int> node_indices;
5340 node_indices.reserve(num_nodes);
5341 for (int i = 0; i < num_nodes; ++i)
5342 {
5343 node_indices.push_back( node_base_index + i );
5344 }
5345 std::vector<ForvertTempData> forverts;
5346
5347 try
5348 {
5349 auto* flexbody = m_flex_factory.CreateFlexBody(
5350 (FlexbodyID_t)m_actor->m_gfx_actor->m_flexbodies.size(),
5351 node_base_index,
5352 axis_node_1,
5353 axis_node_2,
5354 Ogre::Vector3(0.5f, 0.5f, 0.f),
5355 Ogre::Vector3(0.f, 0.f, 0.f),
5356 node_indices,
5357 forverts,
5358 tire_mesh_name,
5359 tire_mesh_rg
5360 );
5361
5362 if (flexbody == nullptr)
5363 return; // Error already logged
5364
5365 this->CreateWheelSkidmarks(wheel_id);
5366
5367 m_actor->m_gfx_actor->m_flexbodies.push_back(flexbody);
5368 }
5369 catch (Ogre::Exception& e)
5370 {
5372 "Failed to create flexbodywheel visuals '" + tire_mesh_name + "', reason:" + e.getDescription());
5373 }
5374}
5375
5377 node_t *node_1,
5378 node_t *node_2,
5379 float spring,
5380 float damping,
5381 std::shared_ptr<RigDef::BeamDefaults> beam_defaults,
5382 float max_contraction, /* Default: -1.f */
5383 float max_extension, /* Default: -1.f */
5384 BeamType type /* Default: BEAM_INVISIBLE */
5385)
5386{
5387 unsigned int index = m_actor->ar_num_beams;
5388 beam_t & beam = AddBeam(*node_1, *node_2, beam_defaults, DEFAULT_DETACHER_GROUP);
5389 beam.bm_type = type;
5390 beam.k = spring;
5391 beam.d = damping;
5392 if (max_contraction > 0.f)
5393 {
5394 beam.shortbound = max_contraction;
5395 beam.longbound = max_extension;
5396 beam.bounded = SHOCK1;
5397 }
5398 CalculateBeamLength(beam);
5399
5400 return index;
5401}
5402
5403unsigned int ActorSpawner::AddWheelRimBeam(RigDef::Wheel2 & wheel_2_def, node_t *node_1, node_t *node_2)
5404{
5405 unsigned int beam_index = _SectionWheels2AddBeam(wheel_2_def, node_1, node_2);
5406 beam_t & beam = GetBeam(beam_index);
5407 beam.k = wheel_2_def.rim_springiness;
5408 beam.d = wheel_2_def.rim_damping;
5409 return beam_index;
5410}
5411
5412unsigned int ActorSpawner::AddTyreBeam(RigDef::Wheel2 & wheel_2_def, node_t *node_1, node_t *node_2)
5413{
5414 unsigned int beam_index = _SectionWheels2AddBeam(wheel_2_def, node_1, node_2);
5415 beam_t & beam = GetBeam(beam_index);
5416 beam.k = wheel_2_def.tyre_springiness;
5417 beam.d = wheel_2_def.tyre_damping;
5418
5419 m_actor->getTyrePressure().AddBeam((int)beam_index);
5420
5421 return beam_index;
5422}
5423
5424unsigned int ActorSpawner::_SectionWheels2AddBeam(RigDef::Wheel2 & wheel_2_def, node_t *node_1, node_t *node_2)
5425{
5426 unsigned int index = m_actor->ar_num_beams;
5427 beam_t & beam = GetFreeBeam();
5428 InitBeam(beam, node_1, node_2);
5429 beam.bm_type = BEAM_NORMAL;
5430 SetBeamStrength(beam, wheel_2_def.beam_defaults->breaking_threshold);
5431 SetBeamDeformationThreshold(beam, wheel_2_def.beam_defaults);
5432 return index;
5433}
5434
5436{
5437 if (def.wheel_id > m_actor->ar_num_wheels - 1)
5438 {
5439 AddMessage(Message::TYPE_ERROR, std::string("Invalid wheel_id: ") + TOSTRING(def.wheel_id));
5440 return;
5441 }
5442
5443 wheeldetacher_t obj;
5444 obj.wd_wheel_id = def.wheel_id;
5446 m_actor->ar_wheeldetachers.push_back(obj);
5447};
5448
5450{
5451 /* #1: regulating_force */
5452 float force = def.regulation_force;
5453 if (force < 1.f || force > 20.f)
5454 {
5455 std::stringstream msg;
5456 msg << "Clamping 'regulating_force' value '" << force << "' to allowed range <1 - 20>";
5457 AddMessage(Message::TYPE_INFO, msg.str());
5458 force = (force < 1.f) ? 1.f : 20.f;
5459 }
5460 m_actor->tc_ratio = force;
5461
5462 /* #2: wheelslip */
5463 // no longer needed
5464
5465 /* #3: fade_speed */
5466 // no longer needed
5467
5468 /* #4: pulse/sec */
5469 float pulse = def.pulse_per_sec;
5470 if (pulse <= 1.0f || pulse >= 2000.0f)
5471 {
5472 pulse = 2000.0f;
5473 }
5474 m_actor->tc_pulse_time = 1 / pulse;
5475
5476 /* #4: mode */
5477 m_actor->tc_mode = def.attr_is_on;
5480};
5481
5483{
5484 /* #1: regulating_force */
5485 float force = def.regulation_force;
5486 if (force < 1.f || force > 20.f)
5487 {
5488 std::stringstream msg;
5489 msg << "Clamping 'regulating_force' value '" << force << "' to allowed range <1 - 20>";
5490 AddMessage(Message::TYPE_INFO, msg.str());
5491 force = (force < 1.f) ? 1.f : 20.f;
5492 }
5493 m_actor->alb_ratio = force;
5494
5495 /* #2: min_speed */
5496 /* Wheelspeed adaption: 60 sec * 60 mins / 1000(kilometer) = 3.6 to get meter per sec */
5497 float min_speed = def.min_speed / 3.6f;
5498 m_actor->alb_minspeed = std::max(0.5f, min_speed);
5499
5500 /* #3: pulse_per_sec */
5501 float pulse = def.pulse_per_sec;
5502 if (pulse <= 1.0f || pulse >= 2000.0f)
5503 {
5504 pulse = 2000.0f;
5505 }
5506 m_actor->alb_pulse_time = 1 / pulse;
5507
5508 /* #4: mode */
5512}
5513
5523
5525{
5526 /* Is this a land vehicle? */
5527 if (m_actor->ar_engine == nullptr)
5528 {
5529 AddMessage(Message::TYPE_WARNING, "Section 'engturbo' found but no engine defined. Skipping ...");
5530 return;
5531 }
5532
5533 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);
5534};
5535
5537{
5538 /* Is this a land vehicle? */
5539 if (m_actor->ar_engine == nullptr)
5540 {
5541 AddMessage(Message::TYPE_WARNING, "Section 'engoption' found but no engine defined. Skipping ...");
5542 return;
5543 }
5544
5545 if (def.idle_rpm > 0 && def.stall_rpm > 0 && def.stall_rpm > def.idle_rpm)
5546 {
5547 AddMessage(Message::TYPE_WARNING, "Stall RPM is set higher than Idle RPM.");
5548 }
5549
5550 /* Process it */
5552 def.inertia,
5553 (char)def.type,
5554 def.clutch_force,
5555 def.shift_time,
5556 def.clutch_time,
5557 def.post_shift_time,
5558 def.idle_rpm,
5559 def.stall_rpm,
5560 def.max_idle_mixture,
5561 def.min_idle_mixture,
5562 def.braking_torque
5563 );
5564};
5565
5567{
5568 /* This is a land vehicle */
5570
5571 /* Process it */
5572 m_actor->ar_engine = new Engine(
5573 def.shift_down_rpm,
5574 def.shift_up_rpm,
5575 def.torque,
5578 def.gear_ratios,
5580 m_actor
5581 );
5582
5583 /* Apply game configuration */
5584 m_actor->ar_engine->setAutoMode(App::sim_gearbox_mode->getEnum<SimGearboxMode>());
5585};
5586
5591
5593{
5594 authorinfo_t author;
5595 author.type = def.type;
5596 author.name = def.name;
5597 author.email = def.email;
5598 if (def._has_forum_account)
5599 {
5600 author.id = def.forum_account_id;
5601 }
5602 m_actor->authors.push_back(author);
5603};
5604
5606{
5607 NodeNum_t node = this->ResolveNodeRef(node_ref);
5608 if (node == NODENUM_INVALID)
5609 {
5610 std::stringstream msg;
5611 msg << "Failed to retrieve required node: " << node_ref.ToString();
5612 throw Exception(msg.str());
5613 }
5614 return node;
5615}
5616
5636
5638{
5639 // Nodes
5640 const NodeNum_t n1 = this->ResolveNodeRef(def.nodes[0]);
5641 const NodeNum_t n2 = this->ResolveNodeRef(def.nodes[1]);
5642 if (n1 == NODENUM_INVALID || n2 == NODENUM_INVALID)
5643 {
5644 AddMessage(Message::TYPE_WARNING, "Skipping beam, some nodes not found."); // Node IDs already logged by `ResolveNodeRef()`
5645 return;
5646 }
5647
5648 // Beam
5649 int beam_index = m_actor->ar_num_beams;
5650 m_actor->ar_beams_user_defined[beam_index] = true;
5651 beam_t & beam = AddBeam(m_actor->ar_nodes[n1], m_actor->ar_nodes[n2], def.defaults, def.detacher_group);
5652 beam.bm_type = BEAM_NORMAL;
5653 beam.k = def.defaults->GetScaledSpringiness();
5654 beam.d = def.defaults->GetScaledDamping();
5655 beam.bounded = NOSHOCK; // Orig: if (shortbound) ... hardcoded in BTS_BEAMS
5656
5657 /* Calculate length */
5658 // orig = precompression hardcoded to 1
5659 CalculateBeamLength(beam);
5660
5661 /* Strength */
5662 float beam_strength = def.defaults->GetScaledBreakingThreshold();
5663 beam.strength = beam_strength;
5664
5665 /* Options */
5667 {
5668 beam.bounded = ROPE;
5669 }
5671 {
5672 beam.bounded = SUPPORTBEAM;
5674 }
5675
5677 {
5678 this->CreateBeamVisuals(beam, beam_index, true, def.defaults);
5679 }
5680 else
5681 {
5682 m_actor->ar_beams_invisible[beam_index] = true;
5683 }
5684}
5685
5686void ActorSpawner::SetBeamDeformationThreshold(beam_t & beam, std::shared_ptr<RigDef::BeamDefaults> beam_defaults)
5687{
5688 /*
5689 ---------------------------------------------------------------------------
5690 Old parser logic
5691 ---------------------------------------------------------------------------
5692
5693 VAR default_deform = BEAM_DEFORM (400,000)
5694 VAR default_deform_scale = 1
5695 VAR beam_creak = BEAM_CREAK_DEFAULT (100,000)
5696 VAR enable_advanced_deformation = false
5697
5698
5699 add_beam()
5700 IF default_deform < beam_creak
5701 default_deform = beam_creak
5702 END IF
5703
5704 VAR beam;
5705 beam.default_deform = default_deform * default_deform_scale
5706 END
5707
5708
5709 enable_advanced_deformation:
5710 READ enable_advanced_deformation
5711
5712
5713 set_beam_defaults:
5714 READ default_deform
5715 VAR default_deform_user_defined
5716 READ default_deform_scale
5717 VAR plastic_coef_user_defined
5718
5719 IF (!enable_advanced_deformation && default_deform < BEAM_DEFORM)
5720 default_deform = BEAM_DEFORM;
5721 END IF
5722
5723 IF (plastic_coef_user_defined)
5724 beam_creak = 0
5725 END IF
5726
5727 ---------------------------------------------------------------------------
5728 TruckParser2013
5729 ---------------------------------------------------------------------------
5730
5731 VAR beam_defaults
5732 {
5733 default_deform = BEAM_DEFORM
5734 scale.default_deform = 1
5735 _enable_advanced_deformation = false
5736 _user_defined = false
5737 _default_deform_set = false
5738 _plastic_coef_user_defined = false
5739 }
5740
5741
5742 set_beam_defaults:
5743 READ beam_defaults
5744
5745
5746 add_beam:
5747
5748 // Init
5749
5750 VAR default_deform = BEAM_DEFORM;
5751 VAR beam_creak = BEAM_CREAK_DEFAULT;
5752
5753 // Old 'set_beam_defaults'
5754
5755 IF (beam_defaults._is_user_defined)
5756
5757 default_deform = beam_defaults.default_deform
5758 IF (!beam_defaults._enable_advanced_deformation && default_deform < BEAM_DEFORM)
5759 default_deform = BEAM_DEFORM;
5760 END IF
5761
5762 IF (beam_defaults._plastic_coef_user_defined && beam_defaults.plastic_coef >= 0)
5763 beam_creak = 0
5764 END IF
5765
5766 END IF
5767
5768 // Old 'add_beam'
5769
5770 IF default_deform < beam_creak
5771 default_deform = beam_creak
5772 END IF
5773
5774 VAR beam;
5775 beam.default_deform = default_deform * beam_defaults.scale.default_deform
5776
5777 ---------------------------------------------------------------------------
5778 */
5779
5780 // Old init
5781 float default_deform = BEAM_DEFORM;
5782 float beam_creak = BEAM_CREAK_DEFAULT;
5783
5784 // Old 'set_beam_defaults'
5785 if (beam_defaults->_is_user_defined)
5786 {
5787 default_deform = beam_defaults->deformation_threshold;
5788 if (!beam_defaults->_enable_advanced_deformation && default_deform < BEAM_DEFORM)
5789 {
5790 default_deform = BEAM_DEFORM;
5791 }
5792
5793 if (beam_defaults->_is_plastic_deform_coef_user_defined && beam_defaults->plastic_deform_coef >= BEAM_PLASTIC_COEF_DEFAULT)
5794 {
5795 beam_creak = 0.f;
5796 }
5797 }
5798
5799 // Old 'add_beam'
5800 if (default_deform < beam_creak)
5801 {
5802 default_deform = beam_creak;
5803 }
5804
5805 float deformation_threshold = default_deform * beam_defaults->scale.deformation_threshold_constant;
5806
5807 beam.minmaxposnegstress = deformation_threshold;
5808 beam.maxposstress = deformation_threshold;
5809 beam.maxnegstress = -(deformation_threshold);
5810}
5811
5812void ActorSpawner::CreateBeamVisuals(beam_t & beam, int beam_index, bool visible, std::shared_ptr<RigDef::BeamDefaults> const& beam_defaults, std::string material_override)
5813{
5814 std::string material_name = material_override;
5815 if (material_name.empty())
5816 {
5817 if (beam.bm_type == BEAM_HYDRO)
5818 {
5819 material_name = "tracks/Chrome";
5820 }
5821 else
5822 {
5823 material_name = beam_defaults->beam_material_name;
5824 // Check for existing substitute
5825 auto it = m_managed_materials.find(material_name);
5826 if (it != m_managed_materials.end())
5827 {
5828 auto material = it->second;
5829 if (material)
5830 {
5831 material_name = material->getName();
5832 }
5833 }
5834 }
5835 }
5836
5837 if (m_actor->m_gfx_actor->m_gfx_beams_parent_scenenode == nullptr)
5838 {
5839 m_actor->m_gfx_actor->m_gfx_beams_parent_scenenode = m_actor_grouping_scenenode->createChildSceneNode(this->ComposeName("beams"));
5840 }
5841
5842 try
5843 {
5844 Ogre::Entity* entity = App::GetGfxScene()->GetSceneManager()->createEntity(this->ComposeName("beam", beam_index), "beam.mesh");
5845 entity->setMaterialName(material_name);
5846
5847 BeamGfx beamx;
5848 beamx.rod_diameter = beam_defaults->visual_beam_diameter;
5849 beam.default_beam_diameter = beam_defaults->visual_beam_diameter; // Hack for ActorExport.cpp
5850 beamx.rod_beam_index = static_cast<uint16_t>(beam_index);
5851 beamx.rod_node1 = beam.p1->pos;
5852 beamx.rod_node2 = beam.p2->pos;
5853 beamx.rod_target_actor = m_actor;
5854 beamx.rod_is_visible = false;
5855
5856 beamx.rod_scenenode = m_actor->m_gfx_actor->m_gfx_beams_parent_scenenode->createChildSceneNode(this->ComposeName("beam", beam_index));
5857 beamx.rod_scenenode->attachObject(entity);
5858 beamx.rod_scenenode->setVisible(visible, /*cascade:*/ false);
5859 beamx.rod_scenenode->setScale(beam_defaults->visual_beam_diameter, -1, beam_defaults->visual_beam_diameter);
5860
5861 m_actor->m_gfx_actor->m_gfx_beams.push_back(beamx);
5862 }
5863 catch (Ogre::Exception& e)
5864 {
5865 this->AddMessage(Message::TYPE_WARNING, fmt::format("Could not create beam visuals: {}", e.getFullDescription()));
5866 }
5867}
5868
5870{
5871 float beam_length = (beam.p1->RelPosition - beam.p2->RelPosition).length();
5872 beam.L = beam_length;
5873 beam.refL = beam_length;
5874}
5875
5876void ActorSpawner::InitBeam(beam_t & beam, node_t *node_1, node_t *node_2)
5877{
5878 beam.p1 = node_1;
5879 beam.p2 = node_2;
5880
5881 /* Length */
5882 CalculateBeamLength(beam);
5883}
5884
5885void ActorSpawner::AddMessage(ActorSpawner::Message type, Ogre::String const & text)
5886{
5887 Str<4000> txt;
5888 if (m_file)
5889 {
5890 txt << m_file->name;
5891 }
5893 {
5894 txt << " (" << RigDef::KeywordToString(m_current_keyword) << ")";
5895 }
5896 txt << ": " << text;
5898 switch (type)
5899 {
5902 break;
5903
5906 break;
5907
5908 default:
5910 break;
5911 }
5912
5914}
5915
5916NodeNum_t ActorSpawner::ResolveNodeRef(RigDef::Node::Ref const & node_ref, bool optional /* = false */)
5917{
5918 if (!node_ref.IsValidAnyState())
5919 {
5920 if (!optional)
5921 {
5922 AddMessage(Message::TYPE_ERROR, std::string("Attempt to resolve invalid node reference: ") + node_ref.ToString());
5923 }
5924 return NODENUM_INVALID;
5925 }
5926 bool is_imported = node_ref.GetImportState_IsValid();
5927 bool is_named = (is_imported ? node_ref.GetImportState_IsResolvedNamed() : node_ref.GetRegularState_IsNamed());
5928 if (is_named)
5929 {
5930 auto result = m_named_nodes.find(node_ref.Str());
5931 if (result != m_named_nodes.end())
5932 {
5933 return (NodeNum_t)result->second;
5934 }
5935
5936 std::stringstream msg;
5937 msg << "Failed to resolve node-ref (node not found):" << node_ref.ToString();
5938 AddMessage(Message::TYPE_ERROR, msg.str());
5939
5940 return NODENUM_INVALID;
5941 }
5942 else
5943 {
5944 // Imported nodes pass without check
5945 if (!is_imported && (node_ref.Num() >= static_cast<unsigned int>(m_actor->ar_num_nodes)))
5946 {
5947
5948 std::stringstream msg;
5949 msg << "Failed to resolve node-ref (node index too big, node count is: "<<m_actor->ar_num_nodes<<"): " << node_ref.ToString();
5950 AddMessage(Message::TYPE_ERROR, msg.str());
5951
5952 return NODENUM_INVALID;
5953 }
5954 return (NodeNum_t)node_ref.Num();
5955 }
5956}
5957
5959{
5960 NodeNum_t node = ResolveNodeRef(node_ref);
5961 if (node != NODENUM_INVALID)
5962 {
5963 return & m_actor->ar_nodes[node];
5964 }
5965 else
5966 {
5967 return nullptr;
5968 }
5969}
5970
5972{
5973 node_t *node = GetNodePointer(node_ref);
5974 if (node == nullptr)
5975 {
5976 std::stringstream msg;
5977 msg << "Required node not found: " << node_ref.ToString();
5978 throw Exception(msg.str());
5979 }
5980 return node;
5981}
5982
5984{
5985 if (!id.IsValid())
5986 {
5987 std::stringstream msg;
5988 msg << "Attempt to add node with 'INVALID' flag: " << id.ToString() << " (number of nodes at this point: " << m_actor->ar_num_nodes << ")";
5989 this->AddMessage(Message::TYPE_ERROR, msg.str());
5990 return NODENUM_INVALID;
5991 }
5992
5993 if (id.IsTypeNamed())
5994 {
5995 unsigned int new_index = static_cast<unsigned int>(m_actor->ar_num_nodes);
5996 auto insert_result = m_named_nodes.insert(std::make_pair(id.Str(), new_index));
5997 if (! insert_result.second)
5998 {
5999 std::stringstream msg;
6000 msg << "Ignoring named node! Duplicate name: " << id.Str() << " (number of nodes at this point: " << m_actor->ar_num_nodes << ")";
6001 this->AddMessage(Message::TYPE_ERROR, msg.str());
6002 return NODENUM_INVALID;
6003 }
6004 m_actor->ar_nodes_name[new_index] = id.Str();
6005 m_actor->ar_nodes_id[new_index] = m_actor->ar_num_nodes;
6006 m_actor->ar_nodes_name_top_length = std::max(m_actor->ar_nodes_name_top_length, (int)id.Str().length());
6008 return (NodeNum_t)new_index;
6009 }
6010 if (id.IsTypeNumbered())
6011 {
6012 if (id.Num() < static_cast<unsigned int>(m_actor->ar_num_nodes))
6013 {
6014 std::stringstream msg;
6015 msg << "Duplicate node number, previous definition will be overriden! - " << id.ToString() << " (number of nodes at this point: " << m_actor->ar_num_nodes << ")";
6016 this->AddMessage(Message::TYPE_WARNING, msg.str());
6017 }
6018 unsigned int new_index = static_cast<unsigned int>(m_actor->ar_num_nodes);
6019 m_actor->ar_nodes_id[new_index] = id.Num();
6021 return (NodeNum_t)new_index;
6022 }
6023 // Invalid node ID without type flag!
6024 throw Exception("Invalid Node::Id without type flags!");
6025}
6026
6028{
6029 const NodeNum_t nodeid = RegisterNode(def.id);
6030 if (nodeid == NODENUM_INVALID)
6031 {
6032 return; // Error already logged
6033 }
6034
6035 node_t & node = m_actor->ar_nodes[nodeid];
6036 node.pos = nodeid;
6037
6038 /* Positioning */
6039 const Ogre::Vector3 spawn_offset = TuneupUtil::getTweakedNodePosition(m_actor->getWorkingTuneupDef(), node.pos, def.position);
6040 m_actor->ar_nodes_spawn_offsets[nodeid] = spawn_offset;
6041
6042 Ogre::Vector3 node_position = m_spawn_position + spawn_offset;
6043 ROR_ASSERT(!std::isnan(node_position.x));
6044 ROR_ASSERT(!std::isnan(node_position.y));
6045 ROR_ASSERT(!std::isnan(node_position.z));
6046 node.AbsPosition = node_position;
6047 node.RelPosition = node_position - m_actor->ar_origin;
6048
6049 node.friction_coef = def.node_defaults->friction;
6050 node.volume_coef = def.node_defaults->volume;
6051 node.surface_coef = def.node_defaults->surface;
6052
6053 /* Mass */
6054 if (def.default_minimass)
6055 {
6056 m_actor->ar_minimass[nodeid] = def.default_minimass->min_mass_Kg;
6057 }
6058 else
6059 {
6061 }
6062
6063 if (def.node_defaults->load_weight >= 0.f) // The `>=` operator is intentional (negative value => use default).
6064 {
6065 node.mass = def.node_defaults->load_weight;
6066 node.nd_override_mass = true;
6067 node.nd_loaded_mass = true;
6068 m_actor->ar_nodes_override_loadweights[nodeid] = def.node_defaults->load_weight;
6069 }
6070 else
6071 {
6073 node.nd_loaded_mass = false;
6074 m_actor->ar_nodes_override_loadweights[nodeid] = -1.f;
6075 }
6076 m_actor->ar_nodes_default_loadweights[nodeid] = def.node_defaults->load_weight;
6077
6078 /* Lockgroup */
6080
6081 /* Options */
6082 unsigned int options = def.options | def.node_defaults->options; /* Merge bit flags */
6083 m_actor->ar_nodes_options[nodeid] = options;
6085 {
6086 node.nd_loaded_mass = true;
6088 {
6089 node.nd_override_mass = true;
6090 node.mass = def.load_weight_override;
6092 }
6093 else
6094 {
6096 }
6097 }
6099 {
6100 /* Link [current-node] -> [node-0] */
6101 /* If current node is 0, link [node-0] -> [node-1] */
6102 node_t & node_2 = m_actor->ar_nodes[((node.pos == 0) ? 1 : 0)];
6103 unsigned int beam_index = m_actor->ar_num_beams;
6104
6105 beam_t & beam = AddBeam(node, node_2, def.beam_defaults, def.detacher_group);
6106 SetBeamStrength(beam, def.beam_defaults->GetScaledBreakingThreshold() * 100.f);
6107 beam.bm_type = BEAM_HYDRO;
6108 beam.d = def.beam_defaults->GetScaledDamping() * 0.1f;
6109 beam.k = def.beam_defaults->GetScaledSpringiness();
6110 beam.bounded = ROPE;
6111 beam.bm_disabled = true;
6112 beam.L = HOOK_RANGE_DEFAULT;
6113 beam.refL = HOOK_RANGE_DEFAULT;
6115 CreateBeamVisuals(beam, beam_index, false, def.beam_defaults);
6116
6117 // Logic cloned from SerializedRig.cpp, section BTS_NODES
6118 hook_t hook;
6119 hook.hk_hook_node = & node;
6120 hook.hk_group = -1;
6121 hook.hk_locked = UNLOCKED;
6122 hook.hk_lock_node = nullptr;
6123 hook.hk_locked_actor = nullptr;
6124 hook.hk_lockgroup = -1;
6125 hook.hk_beam = & beam;
6129 hook.hk_selflock = false;
6130 hook.hk_nodisable = false;
6131 hook.hk_timer = 0.0f;
6133 hook.hk_autolock = false;
6134 hook.hk_min_length = 0.f;
6135 m_actor->ar_hooks.push_back(hook);
6136 }
6137 AdjustNodeBuoyancy(node, def, def.node_defaults);
6140
6143
6144 // Update "fusedrag" autocalc y & z span
6145 if (def.position.z < m_fuse_z_min) { m_fuse_z_min = def.position.z; }
6146 if (def.position.z > m_fuse_z_max) { m_fuse_z_max = def.position.z; }
6147 if (def.position.y < m_fuse_y_min) { m_fuse_y_min = def.position.y; }
6148 if (def.position.y > m_fuse_y_max) { m_fuse_y_max = def.position.y; }
6149
6150 // GFX
6151 NodeGfx nfx(node.pos);
6155 m_actor->m_gfx_actor->m_gfx_nodes.push_back(nfx);
6156}
6157
6159 NodeNum_t emitter_node_idx,
6160 NodeNum_t direction_node_idx
6161 )
6162{
6163 const ExhaustID_t exhaust_id = (ExhaustID_t)m_actor->m_gfx_actor->m_exhausts.size();
6164 Exhaust exhaust;
6165 exhaust.emitterNode = emitter_node_idx;
6166 exhaust.directionNode = direction_node_idx;
6167
6168 exhaust.smoker = App::GetGfxScene()->GetSceneManager()->createParticleSystem(
6169 this->ComposeName("exhaust", exhaust_id),
6170 /*quota=*/500, // Default value
6172
6173 if (exhaust.smoker == nullptr)
6174 {
6175 AddMessage(Message::TYPE_ERROR, "Failed to create exhaust");
6176 return;
6177 }
6178 exhaust.smoker->setVisibilityFlags(DEPTHMAP_DISABLED); // Disable particles in depthmap
6179
6180 Ogre::MaterialPtr mat = this->FindOrCreateCustomizedMaterial("tracks/Smoke", m_custom_resource_group);
6181 exhaust.smoker->setMaterialName(mat->getName(), mat->getGroup());
6182
6183 exhaust.smokeNode = m_particles_parent_scenenode->createChildSceneNode(this->ComposeName("exhaust", exhaust_id));
6184 exhaust.smokeNode->attachObject(exhaust.smoker);
6185 exhaust.smokeNode->setPosition(m_actor->ar_nodes[exhaust.emitterNode].AbsPosition);
6186
6187 m_actor->m_gfx_actor->SetNodeHot(exhaust.emitterNode, true);
6188 m_actor->m_gfx_actor->SetNodeHot(exhaust.directionNode, true);
6189
6190 m_actor->m_gfx_actor->m_exhausts.push_back(exhaust);
6191}
6192
6194{
6195 // Node
6197 node_t & camera_node = GetAndInitFreeNode(node_pos);
6198 m_actor->ar_nodes_spawn_offsets[camera_node.pos] = def.position;
6199 camera_node.nd_no_ground_contact = true; // Orig: hardcoded in BTS_CINECAM
6200 camera_node.nd_cinecam_node = true;
6201 camera_node.friction_coef = NODE_FRICTION_COEF_DEFAULT; // Node defaults are ignored here.
6202 AdjustNodeBuoyancy(camera_node, def.node_defaults);
6203 camera_node.volume_coef = def.node_defaults->volume;
6204 camera_node.surface_coef = def.node_defaults->surface;
6205 // NOTE: Not applying the 'node_mass' value here for backwards compatibility - this node must go through initial `Actor::RecalculateNodeMasses()` pass with default weight.
6206
6208
6211
6212 // Beams
6213 for (unsigned int i = 0; i < 8; i++)
6214 {
6215 int beam_index = m_actor->ar_num_beams;
6216 node_t& node = m_actor->ar_nodes[this->GetNodeIndexOrThrow(def.nodes[i])];
6217 beam_t & beam = AddBeam(camera_node, node, def.beam_defaults, DEFAULT_DETACHER_GROUP);
6218 beam.bm_type = BEAM_NORMAL;
6219 CalculateBeamLength(beam);
6220 beam.k = def.spring;
6221 beam.d = def.damping;
6222 }
6223};
6224
6225void ActorSpawner::InitNode(node_t & node, Ogre::Vector3 const & position)
6226{
6227 /* Position */
6228 node.AbsPosition = position;
6229 node.RelPosition = position - m_actor->ar_origin;
6230}
6231
6233 node_t & node,
6234 Ogre::Vector3 const & position,
6235 std::shared_ptr<RigDef::NodeDefaults> node_defaults
6236)
6237{
6238 InitNode(node, position);
6239 node.friction_coef = node_defaults->friction;
6240 node.volume_coef = node_defaults->volume;
6241 node.surface_coef = node_defaults->surface;
6242}
6243
6245{
6248
6249 // NOTE: Don't do any material pre-processing here; it'll be done on actual entities (via `SetupNewEntity()`).
6250 if (! def.material_name.empty())
6251 {
6252 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(def.material_name); // Check if exists (compatibility)
6253 if (mat)
6254 {
6256 }
6257 else
6258 {
6259 std::stringstream msg;
6260 msg << "Material '" << def.material_name << "' defined in section 'globals' not found. Trying material 'tracks/transred'";
6261 this->AddMessage(Message::TYPE_ERROR, msg.str());
6262
6263 m_cab_material_name = "tracks/transred";
6264 }
6265 }
6266}
6267
6268/* -------------------------------------------------------------------------- */
6269// Limits.
6270/* -------------------------------------------------------------------------- */
6271
6272bool ActorSpawner::CheckAxleLimit(unsigned int count)
6273{
6274 if ((m_actor->m_num_wheel_diffs + count) > MAX_WHEELS/2)
6275 {
6276 std::stringstream msg;
6277 msg << "Axle limit (" << MAX_WHEELS/2 << ") exceeded";
6278 AddMessage(Message::TYPE_ERROR, msg.str());
6279 return false;
6280 }
6281 return true;
6282}
6283
6284bool ActorSpawner::CheckSubmeshLimit(unsigned int count)
6285{
6286 if ((m_oldstyle_cab_submeshes.size() + count) > MAX_SUBMESHES)
6287 {
6288 std::stringstream msg;
6289 msg << "Submesh limit (" << MAX_SUBMESHES << ") exceeded";
6290 AddMessage(Message::TYPE_ERROR, msg.str());
6291 return false;
6292 }
6293 return true;
6294}
6295
6296bool ActorSpawner::CheckTexcoordLimit(unsigned int count)
6297{
6298 if ((m_oldstyle_cab_texcoords.size() + count) > MAX_TEXCOORDS)
6299 {
6300 std::stringstream msg;
6301 msg << "Texcoord limit (" << MAX_TEXCOORDS << ") exceeded";
6302 AddMessage(Message::TYPE_ERROR, msg.str());
6303 return false;
6304 }
6305 return true;
6306}
6307
6308/* Static version */
6309bool ActorSpawner::CheckSoundScriptLimit(ActorPtr const& vehicle, unsigned int count)
6310{
6311 if ((vehicle->ar_num_soundsources + count) > MAX_SOUNDSCRIPTS_PER_TRUCK)
6312 {
6313 std::stringstream msg;
6314 msg << "SoundScript limit (" << MAX_SOUNDSCRIPTS_PER_TRUCK << ") exceeded";
6315 LOG(msg.str());
6316 return false;
6317 }
6318 return true;
6319}
6320
6321bool ActorSpawner::CheckCabLimit(unsigned int count)
6322{
6323 if ((m_actor->ar_num_cabs + count) > MAX_CABS)
6324 {
6325 std::stringstream msg;
6326 msg << "Cab limit (" << MAX_CABS << ") exceeded";
6327 AddMessage(Message::TYPE_ERROR, msg.str());
6328 return false;
6329 }
6330 return true;
6331}
6332
6334{
6336 {
6337 std::stringstream msg;
6338 msg << "CameraRail limit (" << MAX_CAMERARAIL << ") exceeded";
6339 AddMessage(Message::TYPE_ERROR, msg.str());
6340 return false;
6341 }
6342 return true;
6343}
6344
6346{
6348 {
6349 std::stringstream msg;
6350 msg << "AeroEngine limit (" << MAX_AEROENGINES << ") exceeded";
6351 AddMessage(Message::TYPE_ERROR, msg.str());
6352 return false;
6353 }
6354 return true;
6355}
6356
6358{
6359 if ((m_actor->ar_num_screwprops + count) > MAX_SCREWPROPS)
6360 {
6361 std::stringstream msg;
6362 msg << "Screwprop limit (" << MAX_SCREWPROPS << ") exceeded";
6363 AddMessage(Message::TYPE_ERROR, msg.str());
6364 return false;
6365 }
6366 return true;
6367}
6368
6369void ActorSpawner::InitNode(unsigned int node_index, Ogre::Vector3 const & position)
6370{
6371 InitNode(m_actor->ar_nodes[node_index], position);
6372}
6373
6374beam_t & ActorSpawner::GetBeam(unsigned int index)
6375{
6376 return m_actor->ar_beams[index];
6377}
6378
6380{
6382 node.pos = m_actor->ar_num_nodes;
6384 return node;
6385}
6386
6388{
6391 return beam;
6392}
6393
6395{
6398 return shock;
6399}
6400
6402{
6403 beam_t & beam = GetFreeBeam();
6404 beam.p1 = & node_1;
6405 beam.p2 = & node_2;
6406 return beam;
6407}
6408
6409node_t & ActorSpawner::GetAndInitFreeNode(Ogre::Vector3 const & position)
6410{
6411 node_t & node = GetFreeNode();
6412 InitNode(node, position);
6413 return node;
6414}
6415
6416void ActorSpawner::SetBeamSpring(beam_t & beam, float spring)
6417{
6418 beam.k = spring;
6419}
6420
6421void ActorSpawner::SetBeamDamping(beam_t & beam, float damping)
6422{
6423 beam.d = damping;
6424}
6425
6427{
6428 int trucknum = vehicle->ar_instance_id;
6429 int ar_exhaust_pos_node = vehicle->ar_exhaust_pos_node;
6430
6431#ifdef USE_OPENAL
6432 if (App::GetSoundScriptManager()->isDisabled())
6433 {
6434 return;
6435 }
6436
6437 //engine
6438 if (vehicle->ar_engine != nullptr) /* Land vehicle */
6439 {
6440 if (vehicle->ar_engine->m_engine_type == 't')
6441 {
6442 AddSoundSourceInstance(vehicle, "tracks/default_diesel", ar_exhaust_pos_node);
6443 AddSoundSourceInstance(vehicle, "tracks/default_force", ar_exhaust_pos_node);
6444 AddSoundSourceInstance(vehicle, "tracks/default_brakes", 0);
6445 AddSoundSourceInstance(vehicle, "tracks/default_parkbrakes", 0);
6446 AddSoundSourceInstance(vehicle, "tracks/default_reverse_beep", 0);
6447 }
6448 if (vehicle->ar_engine->m_engine_type == 'c')
6449 AddSoundSourceInstance(vehicle, "tracks/default_car", ar_exhaust_pos_node);
6450 if (vehicle->ar_engine->hasTurbo())
6451 {
6452 if (vehicle->ar_engine->m_turbo_inertia_factor >= 3)
6453 AddSoundSourceInstance(vehicle, "tracks/default_turbo_big", ar_exhaust_pos_node);
6454 else if (vehicle->ar_engine->m_turbo_inertia_factor <= 0.5)
6455 AddSoundSourceInstance(vehicle, "tracks/default_turbo_small", ar_exhaust_pos_node);
6456 else
6457 AddSoundSourceInstance(vehicle, "tracks/default_turbo_mid", ar_exhaust_pos_node);
6458
6459 AddSoundSourceInstance(vehicle, "tracks/default_turbo_bov", ar_exhaust_pos_node);
6460 AddSoundSourceInstance(vehicle, "tracks/default_wastegate_flutter", ar_exhaust_pos_node);
6461 }
6462
6463 if (vehicle->ar_engine->m_engine_has_air)
6464 AddSoundSourceInstance(vehicle, "tracks/default_air_purge", 0);
6465 //starter
6466 AddSoundSourceInstance(vehicle, "tracks/default_starter", 0);
6467 // turn signals
6468 AddSoundSourceInstance(vehicle, "tracks/default_turn_signal", 0);
6469 }
6470 if (vehicle->ar_driveable==TRUCK)
6471 {
6472 //horn
6473 if (vehicle->ar_is_police)
6474 AddSoundSourceInstance(vehicle, "tracks/default_police", 0);
6475 else
6476 AddSoundSourceInstance(vehicle, "tracks/default_horn", 0);
6477 //shift
6478 AddSoundSourceInstance(vehicle, "tracks/default_shift", 0);
6479 }
6480 //pump
6481 if (vehicle->m_has_command_beams)
6482 {
6483 AddSoundSourceInstance(vehicle, "tracks/default_pump", 0);
6484 }
6485 //antilock brake
6486 if (vehicle->alb_mode || !vehicle->alb_notoggle)
6487 {
6488 AddSoundSourceInstance(vehicle, "tracks/default_antilock", 0);
6489 }
6490 //tractioncontrol
6491 if (vehicle->tc_mode || !vehicle->tc_notoggle)
6492 {
6493 AddSoundSourceInstance(vehicle, "tracks/default_tractioncontrol", 0);
6494 }
6495 //screetch
6496 if ((vehicle->ar_driveable==TRUCK || vehicle->ar_driveable==AIRPLANE) && vehicle->ar_num_wheels != 0)
6497 {
6498 AddSoundSourceInstance(vehicle, "tracks/default_screetch", 0);
6499 }
6500 //break & creak
6501 AddSoundSourceInstance(vehicle, "tracks/default_break", 0);
6502 AddSoundSourceInstance(vehicle, "tracks/default_creak", 0);
6503 //boat engine
6504 if (vehicle->ar_driveable==BOAT)
6505 {
6506 if (vehicle->ar_total_mass>50000.0)
6507 AddSoundSourceInstance(vehicle, "tracks/default_marine_large", ar_exhaust_pos_node);
6508 else
6509 AddSoundSourceInstance(vehicle, "tracks/default_marine_small", ar_exhaust_pos_node);
6510 //no start/stop engine for boats, so set sound always on!
6511 SOUND_START(trucknum, SS_TRIG_ENGINE);
6512 SOUND_MODULATE(trucknum, SS_MOD_ENGINE, 0.5);
6513 }
6514 //airplane warnings
6515 if (vehicle->ar_driveable==AIRPLANE)
6516 {
6517 AddSoundSourceInstance(vehicle, "tracks/default_gpws_10", 0);
6518 AddSoundSourceInstance(vehicle, "tracks/default_gpws_20", 0);
6519 AddSoundSourceInstance(vehicle, "tracks/default_gpws_30", 0);
6520 AddSoundSourceInstance(vehicle, "tracks/default_gpws_40", 0);
6521 AddSoundSourceInstance(vehicle, "tracks/default_gpws_50", 0);
6522 AddSoundSourceInstance(vehicle, "tracks/default_gpws_100", 0);
6523
6524 AddSoundSourceInstance(vehicle, "tracks/default_gpws_pullup", 0);
6525 AddSoundSourceInstance(vehicle, "tracks/default_gpws_minimums", 0);
6526 AddSoundSourceInstance(vehicle, "tracks/default_gpws_apdisconnect", 0);
6527 AddSoundSourceInstance(vehicle, "tracks/default_aoa_warning", 0);
6528
6529 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat01", 0);
6530 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat02", 0);
6531 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat03", 0);
6532 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat04", 0);
6533 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat05", 0);
6534 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat06", 0);
6535 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat07", 0);
6536 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat08", 0);
6537 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat09", 0);
6538 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat10", 0);
6539 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat11", 0);
6540 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat12", 0);
6541 AddSoundSourceInstance(vehicle, "tracks/default_aivionic_chat13", 0);
6542 }
6543 //airplane engines
6544 for (int i=0; i<vehicle->ar_num_aeroengines && i<8; i++)
6545 {
6546 int turbojet_node = vehicle->ar_aeroengines[i]->getNoderef();
6547 Ogre::String index_str = TOSTRING(i+1);
6548
6550 {
6551 AddSoundSourceInstance(vehicle, "tracks/default_turbojet_start" + index_str, turbojet_node);
6552 AddSoundSourceInstance(vehicle, "tracks/default_turbojet_lopower" + index_str, turbojet_node);
6553 AddSoundSourceInstance(vehicle, "tracks/default_turbojet_hipower" + index_str, turbojet_node);
6554 if (((Turbojet*)(vehicle->ar_aeroengines[i].GetRef()))->tjet_afterburnable)
6555 {
6556 AddSoundSourceInstance(vehicle, "tracks/default_turbojet_afterburner" + index_str, turbojet_node);
6557 }
6558 }
6559 else if (vehicle->ar_aeroengines[i]->getType() == AeroEngineType::AE_XPROP)
6560 {
6561 if (((Turboprop*)vehicle->ar_aeroengines[i].GetRef())->is_piston)
6562 {
6563 AddSoundSourceInstance(vehicle, "tracks/default_pistonprop_start" + index_str, turbojet_node);
6564 AddSoundSourceInstance(vehicle, "tracks/default_pistonprop_lopower" + index_str, turbojet_node);
6565 AddSoundSourceInstance(vehicle, "tracks/default_pistonprop_hipower" + index_str, turbojet_node);
6566 }
6567 else
6568 {
6569 AddSoundSourceInstance(vehicle, "tracks/default_turboprop_start" + index_str, turbojet_node);
6570 AddSoundSourceInstance(vehicle, "tracks/default_turboprop_lopower" + index_str, turbojet_node);
6571 AddSoundSourceInstance(vehicle, "tracks/default_turboprop_hipower" + index_str, turbojet_node);
6572 }
6573 }
6574 }
6575
6576 // linked sounds
6577 for (int i=0; i<vehicle->m_num_command_beams; i++)
6578 {
6579 AddSoundSource(vehicle, App::GetSoundScriptManager()->createInstance(Ogre::String("tracks/linked/default_command/extend"), trucknum, SL_COMMAND, i), 0);
6580 AddSoundSource(vehicle, App::GetSoundScriptManager()->createInstance(Ogre::String("tracks/linked/default_command/retract"), trucknum, SL_COMMAND, -i), 0);
6581 }
6582
6583#endif //OPENAL
6584}
6585
6587{
6588 for (int i=0; i<m_actor->ar_num_collcabs; i++)
6589 {
6590 int tmpv = m_actor->ar_collcabs[i] * 3;
6591 m_actor->ar_nodes[m_actor->ar_cabs[tmpv]].nd_cab_node = true;
6592 m_actor->ar_nodes[m_actor->ar_cabs[tmpv+1]].nd_cab_node = true;
6593 m_actor->ar_nodes[m_actor->ar_cabs[tmpv+2]].nd_cab_node = true;
6594 }
6595 for (int i = 0; i < m_actor->ar_num_nodes; i++)
6596 {
6598 {
6601 }
6602 else if (!m_actor->ar_nodes[i].nd_no_ground_contact &&
6604 {
6605 m_actor->ar_nodes[i].nd_contactable = true;
6607 }
6608 }
6609}
6610
6612{
6613 for (auto& module: m_selected_modules)
6614 {
6615 for (auto& def: module->materialflarebindings)
6616 {
6617 if (def.material_name == material_name)
6618 {
6619 return &def;
6620 }
6621 }
6622 }
6623 return nullptr;
6624}
6625
6627{
6628 for (auto& module: m_selected_modules)
6629 {
6630 for (auto& def: module->videocameras)
6631 {
6632 if (def.material_name == material_name)
6633 {
6634 return &def;
6635 }
6636 }
6637 }
6638
6639 return nullptr;
6640}
6641
6642Ogre::MaterialPtr ActorSpawner::FindOrCreateCustomizedMaterial(const std::string& mat_lookup_name, const std::string& mat_lookup_rg)
6643{
6644 try
6645 {
6646 // Check for existing substitute
6647 auto lookup_res = m_material_substitutions.find(mat_lookup_name);
6648 if (lookup_res != m_material_substitutions.end())
6649 {
6650 return lookup_res->second.material;
6651 }
6652
6653 CustomMaterial lookup_entry;
6654
6655 // Query old-style mirrors (=special props, hardcoded material name 'mirror')
6656 if (mat_lookup_name == "mirror")
6657 {
6660 lookup_entry.material_flare_def = nullptr;
6661 static int mirror_counter = 0;
6662 const std::string new_mat_name = this->ComposeName("RenderMaterial", mirror_counter);
6663 ++mirror_counter;
6664 lookup_entry.material = Ogre::MaterialManager::getSingleton().getByName("mirror")->clone(new_mat_name, true, mat_lookup_rg);
6665 // Special case - register under generated name. This is because all mirrors use the same material 'mirror'
6666 m_material_substitutions.insert(std::make_pair(new_mat_name, lookup_entry));
6667 return lookup_entry.material; // Done!
6668 }
6669
6670 // Query 'videocameras'
6671 RigDef::VideoCamera* videocam_def = this->FindVideoCameraByMaterial(mat_lookup_name);
6672 if (videocam_def != nullptr)
6673 {
6674 Ogre::MaterialPtr video_mat_shared;
6675 auto found_managedmat = m_managed_materials.find(mat_lookup_name);
6676 if (found_managedmat != m_managed_materials.end())
6677 {
6678 video_mat_shared = found_managedmat->second;
6679 }
6680 else
6681 {
6682 video_mat_shared = Ogre::MaterialManager::getSingleton().getByName(mat_lookup_name);
6683 }
6684
6685 if (video_mat_shared)
6686 {
6687 lookup_entry.video_camera_def = videocam_def;
6688 const std::string video_mat_name = this->ComposeName(videocam_def->material_name);
6689 lookup_entry.material = video_mat_shared->clone(video_mat_name, true, mat_lookup_rg);
6690 m_material_substitutions.insert(std::make_pair(mat_lookup_name, lookup_entry));
6691 return lookup_entry.material; // Done!
6692 }
6693 else
6694 {
6695 std::stringstream msg;
6696 msg << "VideoCamera material '" << mat_lookup_name << "' not found! Ignoring videocamera.";
6697 this->AddMessage(Message::TYPE_WARNING, msg.str());
6698 }
6699 }
6700
6701 // Resolve 'materialflarebindings'.
6702 RigDef::MaterialFlareBinding* mat_flare_def = this->FindFlareBindingForMaterial(mat_lookup_name);
6703 if (mat_flare_def != nullptr)
6704 {
6705 lookup_entry.material_flare_def = mat_flare_def;
6706 }
6707
6708 // Query .skin material replacements
6709 if (m_actor->m_used_skin_entry != nullptr)
6710 {
6712
6713 auto skin_res = skin_def->replace_materials.find(mat_lookup_name);
6714 if (skin_res != skin_def->replace_materials.end())
6715 {
6716 Ogre::MaterialPtr skin_mat = Ogre::MaterialManager::getSingleton().getByName(
6717 skin_res->second, m_actor->m_used_skin_entry->resource_group);
6718 if (skin_mat)
6719 {
6720 lookup_entry.material = skin_mat->clone(this->ComposeName(skin_mat->getName()), /*changeGroup=*/true, mat_lookup_rg);
6721 m_material_substitutions.insert(std::make_pair(mat_lookup_name, lookup_entry));
6722 return lookup_entry.material;
6723 }
6724 else
6725 {
6726 std::stringstream buf;
6727 buf << "Material '" << skin_res->second << "' from skin '" << m_actor->m_used_skin_entry->dname
6728 << "' not found (filename: '" << m_actor->m_used_skin_entry->fname
6729 << "', resource group: '"<< m_actor->m_used_skin_entry->resource_group
6730 <<"')! Ignoring it...";
6731 this->AddMessage(Message::TYPE_ERROR, buf.str());
6732 }
6733 }
6734 }
6735
6736 // Acquire substitute - either use managedmaterial or generate new by cloning.
6737 auto mmat_res = m_managed_materials.find(mat_lookup_name);
6738 if (mmat_res != m_managed_materials.end())
6739 {
6740 // Use managedmaterial as substitute
6741 lookup_entry.material = mmat_res->second;
6742 }
6743 else
6744 {
6745 // Generate new substitute
6746 Ogre::MaterialPtr orig_mat = Ogre::MaterialManager::getSingleton().getByName(mat_lookup_name, mat_lookup_rg);
6747 if (!orig_mat)
6748 {
6749 std::stringstream buf;
6750 buf << "Material doesn't exist:" << mat_lookup_name;
6751 this->AddMessage(Message::TYPE_ERROR, buf.str());
6752 return Ogre::MaterialPtr(); // NULL
6753 }
6754
6755 lookup_entry.material = orig_mat->clone(this->ComposeName(orig_mat->getName()), true, mat_lookup_rg);
6756 /*
6757 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)
6758 02:55:17: [RoR|ContentManager] Skipping resource with duplicate name: 'pushbar1 (gavrilmv4.truck [Instance ID 1])' (origin: '')
6759 */
6760 ROR_ASSERT(lookup_entry.material);
6761 }
6762
6763 // Register the substitute
6764 m_material_substitutions.insert(std::make_pair(mat_lookup_name, lookup_entry));
6765
6766 // Finally, query texture replacements - .skin and builtins
6767 for (auto& technique: lookup_entry.material->getTechniques())
6768 {
6769 for (auto& pass: technique->getPasses())
6770 {
6771 for (auto& tex_unit: pass->getTextureUnitStates())
6772 {
6773 // Built-ins
6774 if (tex_unit->getTextureName() == "dashtexture")
6775 {
6777 {
6778 // This is technically a bug, but does it matter at all? Let's watch ~ only_a_ptr, 05/2019
6779 std::stringstream msg;
6780 msg << "Warning: '" << mat_lookup_name
6781 << "' references 'dashtexture', but Renderdash isn't created yet! Texture will be blank.";
6782 this->AddMessage(Message::TYPE_WARNING, msg.str());
6783 }
6784 else
6785 {
6786 tex_unit->setTexture(m_oldstyle_renderdash->getTexture());
6787 }
6788 }
6789 // .skin
6790 else if (m_actor->m_used_skin_entry != nullptr)
6791 {
6792 const size_t num_frames = tex_unit->getNumFrames();
6793 for (size_t i = 0; i < num_frames; ++i)
6794 {
6795 const auto end = m_actor->m_used_skin_entry->skin_def->replace_textures.end();
6796 const auto query = m_actor->m_used_skin_entry->skin_def->replace_textures.find(tex_unit->getFrameTextureName((unsigned int)i));
6797 if (query != end)
6798 {
6799 // Skin has replacement for this texture
6800 if (m_actor->m_used_skin_entry->resource_group != mat_lookup_rg) // The skin comes from a SkinZip bundle (different resource group)
6801 {
6802 Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName(
6803 query->second, m_actor->m_used_skin_entry->resource_group);
6804 if (!tex)
6805 {
6806 // `Ogre::TextureManager` doesn't automatically register all images in resource groups,
6807 // it waits for `Ogre::Resource`s to be created explicitly.
6808 // Normally this is done by `Ogre::MaterialManager` when loading a material.
6809 // In this case we must do it manually
6810 tex = Ogre::TextureManager::getSingleton().create(
6811 query->second, m_actor->m_used_skin_entry->resource_group);
6812 }
6813 tex_unit->_setTexturePtr(tex, i);
6814 }
6815 else // The skin lives in the vehicle bundle (same resource group)
6816 {
6817 tex_unit->setFrameTextureName(query->second, (unsigned int)i);
6818 }
6819 }
6820 }
6821 }
6822 } // texture unit states
6823 } // passes
6824 } // techniques
6825
6826
6827 return lookup_entry.material;
6828 }
6829 catch (Ogre::Exception& e)
6830 {
6831 std::stringstream msg;
6832 msg << "Exception while customizing material \"" << mat_lookup_name << "\", message: " << e.getFullDescription();
6833 this->AddMessage(Message::TYPE_ERROR, msg.str());
6834 }
6835 return Ogre::MaterialPtr(); // NULL
6836}
6837
6838Ogre::MaterialPtr ActorSpawner::CreateSimpleMaterial(Ogre::ColourValue color)
6839{
6841
6842 static unsigned int simple_mat_counter = 0;
6843 Ogre::MaterialPtr newmat = m_simple_material_base->clone(this->ComposeName("simple material", simple_mat_counter++));
6844 newmat->getTechnique(0)->getPass(0)->setAmbient(color);
6845
6846 return newmat;
6847}
6848
6849void ActorSpawner::SetupNewEntity(Ogre::Entity* ent, Ogre::ColourValue simple_color)
6850{
6851 // RULE: Each actor must have it's own material instances (a lookup table is kept for OrigName->CustomName)
6852 //
6853 // Setup routine:
6854 //
6855 // 1. If "SimpleMaterials" (plain color surfaces denoting component type) are enabled in config file,
6856 // material is generated (not saved to lookup table) and processing ends.
6857 // 2. If the material name is 'mirror', it's a special prop - rear view mirror.
6858 // material is generated, added to lookup table under generated name (special case) and processing ends.
6859 // 3. If the material is a 'videocamera' of any subtype, material is created, added to lookup table and processing ends.
6860 // 4 'materialflarebindngs' are resolved -> binding is persisted in lookup table.
6861 // 5 SkinZIP _material replacements_ are queried. If match is found, it's added to lookup table and processing ends.
6862 // 6. ManagedMaterials are queried. If match is found, it's added to lookup table and processing ends.
6863 // 7. Orig. material is cloned to create substitute.
6864 // 8. SkinZIP _texture replacements_ are queried. If match is found, substitute material is updated.
6865 // 9. Material is added to lookup table, processing ends.
6866 // ==========================================================
6867
6868 if (ent == nullptr)
6869 {
6870 // Dirty but I don't see any alternative ... ~ ulteq, 10/2018
6871 AddMessage(Message::TYPE_WARNING, "Failed to create entity: continuing without it ...");
6872 return;
6873 }
6874
6875 // Use simple materials if applicable
6877 {
6878 Ogre::MaterialPtr mat = this->CreateSimpleMaterial(simple_color);
6879
6880 size_t num_sub_entities = ent->getNumSubEntities();
6881 for (size_t i = 0; i < num_sub_entities; i++)
6882 {
6883 Ogre::SubEntity* subent = ent->getSubEntity(i);
6884 subent->setMaterial(mat);
6885 }
6886
6887 return; // Done!
6888 }
6889
6890 // Create unique sub-entity (=instance of submesh) materials
6891 size_t subent_max = ent->getNumSubEntities();
6892 for (size_t i = 0; i < subent_max; ++i)
6893 {
6894 Ogre::SubEntity* subent = ent->getSubEntity(i);
6895
6896 if (subent->getMaterial())
6897 {
6898 Ogre::MaterialPtr own_mat = this->FindOrCreateCustomizedMaterial(subent->getMaterialName(), subent->getSubMesh()->parent->getGroup());
6899 if (own_mat)
6900 {
6901 subent->setMaterial(own_mat);
6902 }
6903 }
6904 }
6905}
6906
6908{
6909 // Check and warn if there are unclaimed managed materials
6910 // TODO &*&*
6911
6912 // Process special materials
6913 for (auto& entry: m_material_substitutions)
6914 {
6915 if (entry.second.material_flare_def != nullptr) // 'materialflarebindings'
6916 {
6917 this->CreateMaterialFlare(
6918 entry.second.material_flare_def->flare_number, entry.second.material);
6919 }
6920 else if (entry.second.mirror_prop_type != CustomMaterial::MirrorPropType::MPROP_NONE) // special 'prop' - rear view mirror
6921 {
6923 entry.second.material, entry.second.mirror_prop_type, entry.second.mirror_prop_scenenode);
6924 }
6925 else if (entry.second.video_camera_def != nullptr) // 'videocameras'
6926 {
6928 this->CreateVideoCamera(entry.second.video_camera_def);
6930 }
6931 }
6932
6933 if (!App::gfx_enable_videocams->getBool())
6934 {
6936 }
6937
6938 // Load dashboard layouts
6939 for (auto& module: m_selected_modules)
6940 {
6941 for (auto& gs: module->guisettings)
6942 {
6943 if (gs.key == "dashboard")
6944 {
6945 m_actor->ar_dashboard->loadDashBoard(gs.value, LOADDASHBOARD_SCREEN_HUD);
6946 }
6947 else if (gs.key == "texturedashboard")
6948 {
6950 }
6951 }
6952 }
6953
6954 // If none specified, load default dashboard layouts
6955 BitMask_t defaultdash_flags = 0;
6956 BITMASK_SET_1(defaultdash_flags, m_actor->ar_dashboard->wasDashboardHudLoaded() ? 0 : LOADDASHBOARD_SCREEN_HUD);
6957 BITMASK_SET_1(defaultdash_flags, m_actor->ar_dashboard->wasDashboardRttLoaded() ? 0 : LOADDASHBOARD_RTT_TEXTURE);
6958 switch (m_actor->ar_driveable)
6959 {
6960 case TRUCK:
6961 m_actor->ar_dashboard->loadDashBoard(App::ui_default_truck_dash->getStr(), defaultdash_flags);
6962 m_actor->ar_dashboard->setVisible(false);
6963 break;
6964 case BOAT:
6965 m_actor->ar_dashboard->loadDashBoard(App::ui_default_boat_dash->getStr(), defaultdash_flags);
6966 m_actor->ar_dashboard->setVisible(false);
6967 break;
6968 default:
6969 break;
6970 }
6971
6972 if (!m_help_material_name.empty())
6973 {
6974 try
6975 {
6976 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(m_help_material_name, m_custom_resource_group);
6977 m_actor->m_gfx_actor->m_help_mat = mat;
6978 if (mat &&
6979 mat->getNumTechniques() > 0 &&
6980 mat->getTechnique(0)->getNumPasses() > 0 &&
6981 mat->getTechnique(0)->getPass(0)->getNumTextureUnitStates() > 0 &&
6982 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getNumFrames() > 0)
6983 {
6984 m_actor->m_gfx_actor->m_help_tex =
6985 Ogre::TextureManager::getSingleton().getByName(
6986 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getFrameTextureName(0), m_custom_resource_group);
6987 }
6988 }
6989 catch (Ogre::Exception& e)
6990 {
6991 this->AddMessage(Message::TYPE_ERROR,
6992 "Failed to load `help` material '" + m_help_material_name + "', message:" + e.getFullDescription());
6993 }
6994 }
6995
6996 m_actor->ar_managed_materials = m_managed_materials;
6997}
6998
6999void ActorSpawner::ValidateRotator(int id, int axis1, int axis2, NodeNum_t *nodes1, NodeNum_t *nodes2)
7000{
7001 const float eps = 0.001f;
7002 const Ogre::Vector3 ax1 = m_actor->ar_nodes[axis1].AbsPosition;
7003 const Ogre::Vector3 ax2 = m_actor->ar_nodes[axis2].AbsPosition;
7004 Ogre::Plane pl = Ogre::Plane((ax1 - ax2).normalisedCopy(), 0);
7005
7006 Ogre::Vector3 a1 = pl.projectVector(ax1 - m_actor->ar_nodes[nodes1[0]].AbsPosition);
7007 Ogre::Vector3 a2 = pl.projectVector(ax1 - m_actor->ar_nodes[nodes1[1]].AbsPosition);
7008 Ogre::Vector3 a3 = pl.projectVector(ax1 - m_actor->ar_nodes[nodes1[2]].AbsPosition);
7009 Ogre::Vector3 a4 = pl.projectVector(ax1 - m_actor->ar_nodes[nodes1[3]].AbsPosition);
7010 float a1len = a1.normalise();
7011 float a2len = a2.normalise();
7012 float a3len = a3.normalise();
7013 float a4len = a4.normalise();
7014 if ((std::max(a1len, a3len) / std::min(a1len, a3len) > 1.f + eps) ||
7015 (std::max(a2len, a4len) / std::min(a2len, a4len) > 1.f + eps))
7016 {
7017 Ogre::String msg = Ogre::StringUtil::format("Off-centered axis on base plate of rotator %d", id);
7019 }
7020
7021 Ogre::Vector3 b1 = pl.projectVector(ax2 - m_actor->ar_nodes[nodes2[0]].AbsPosition);
7022 Ogre::Vector3 b2 = pl.projectVector(ax2 - m_actor->ar_nodes[nodes2[1]].AbsPosition);
7023 Ogre::Vector3 b3 = pl.projectVector(ax2 - m_actor->ar_nodes[nodes2[2]].AbsPosition);
7024 Ogre::Vector3 b4 = pl.projectVector(ax2 - m_actor->ar_nodes[nodes2[3]].AbsPosition);
7025 float b1len = b1.normalise();
7026 float b2len = b2.normalise();
7027 float b3len = b3.normalise();
7028 float b4len = b4.normalise();
7029 if ((std::max(b1len, b3len) / std::min(b1len, b3len) > 1.f + eps) ||
7030 (std::max(b2len, b4len) / std::min(b2len, b4len) > 1.f + eps))
7031 {
7032 Ogre::String msg = Ogre::StringUtil::format("Off-centered axis on rotating plate of rotator %d", id);
7034 }
7035
7036 float rot1 = a1.dotProduct(b1);
7037 float rot2 = a2.dotProduct(b2);
7038 float rot3 = a3.dotProduct(b3);
7039 float rot4 = a4.dotProduct(b4);
7040 if ((std::max(rot1, rot2) / std::min(rot1, rot2) > 1.f + eps) ||
7041 (std::max(rot2, rot3) / std::min(rot2, rot3) > 1.f + eps) ||
7042 (std::max(rot3, rot4) / std::min(rot3, rot4) > 1.f + eps) ||
7043 (std::max(rot4, rot1) / std::min(rot4, rot1) > 1.f + eps))
7044 {
7045 Ogre::String msg = Ogre::StringUtil::format("Misaligned plates on rotator %d", id);
7047 }
7048}
7049
7050Ogre::ManualObject* CreateVideocameraDebugMesh()
7051{
7052 // Create material
7053 static size_t counter = 0;
7054 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(
7055 "VideoCamDebugMat-" + TOSTRING(counter), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
7056 ++counter;
7057 mat->getTechnique(0)->getPass(0)->createTextureUnitState();
7058 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
7059 mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureAnisotropy(3);
7060 mat->setLightingEnabled(false);
7061 mat->setReceiveShadows(false);
7062 // Create mesh
7063 Ogre::ManualObject* mo = App::GetGfxScene()->GetSceneManager()->createManualObject(); // TODO: Eliminate gEnv
7064 mo->begin(mat->getName(), Ogre::RenderOperation::OT_LINE_LIST);
7065 Ogre::ColourValue pos_mark_col(1.f, 0.82f, 0.26f);
7066 Ogre::ColourValue dir_mark_col(0.f, 1.f, 1.f); // TODO: This comes out green in simulation - why? ~ only_a_ptr, 05/2017
7067 const float pos_mark_len = 0.8f;
7068 const float dir_mark_len = 4.f;
7069 // X
7070 mo->position(pos_mark_len,0,0);
7071 mo->colour(pos_mark_col);
7072 mo->position(-pos_mark_len,0,0);
7073 mo->colour(pos_mark_col);
7074 // Y
7075 mo->position(0,pos_mark_len,0);
7076 mo->colour(pos_mark_col);
7077 mo->position(0,-pos_mark_len,0);
7078 mo->colour(pos_mark_col);
7079 // +Z
7080 mo->position(0,0,pos_mark_len);
7081 mo->colour(pos_mark_col);
7082 mo->position(0,0,0);
7083 mo->colour(pos_mark_col);
7084 // -Z = the direction
7085 mo->position(0,0,-dir_mark_len);
7086 mo->colour(dir_mark_col);
7087 mo->position(0,0,0);
7088 mo->colour(dir_mark_col);
7089 mo->end(); // Don't forget this!
7090
7091 return mo;
7092}
7093
7095{
7096 try
7097 {
7098 auto videocameraid = (VideoCameraID_t)m_actor->m_gfx_actor->m_videocameras.size();
7099 RoR::VideoCamera vcam;
7100
7102 if (vcam.vcam_role == VCAM_ROLE_INVALID)
7103 {
7104 this->AddMessage(Message::TYPE_ERROR, fmt::format("Skipping VideoCamera (mat: {}) with invalid 'role' ({})", def->material_name, (int)vcam.vcam_role));
7105 return;
7106 }
7107
7110 if (!vcam.vcam_material)
7111 {
7112 this->AddMessage(Message::TYPE_ERROR, "Failed to create VideoCamera with material: " + def->material_name);
7113 return;
7114 }
7115
7119 vcam.vcam_pos_offset = def->offset;
7120
7121 //rotate camera picture 180 degrees, skip for mirrors
7122 float rotation_z = def->rotation.z + 180;
7125 {
7126 rotation_z += 180.0f;
7127 }
7128 vcam.vcam_rotation
7129 = Ogre::Quaternion(Ogre::Degree(rotation_z), Ogre::Vector3::UNIT_Z)
7130 * Ogre::Quaternion(Ogre::Degree(def->rotation.y), Ogre::Vector3::UNIT_Y)
7131 * Ogre::Quaternion(Ogre::Degree(def->rotation.x), Ogre::Vector3::UNIT_X);
7132
7133 // set alternative camposition (optional)
7135 {
7137 }
7138 else
7139 {
7141 }
7142
7143 // set alternative lookat position (optional)
7145 {
7146 // This is a tracker camera
7147 switch (vcam.vcam_role)
7148 {
7152 default: break; // Assume the TRACKING_* role is already set by the tuning system.
7153 }
7155 }
7156
7157 vcam.vcam_ogre_camera = App::GetGfxScene()->GetSceneManager()->createCamera(vcam.vcam_material->getName() + "_camera");
7158
7159 if (!App::gfx_window_videocams->getBool())
7160 {
7161 vcam.vcam_render_tex = Ogre::TextureManager::getSingleton().createManual(
7162 vcam.vcam_material->getName() + "_texture",
7163 Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
7164 Ogre::TEX_TYPE_2D,
7165 def->texture_width,
7166 def->texture_height,
7167 0, // no mip maps
7168 Ogre::PF_R8G8B8,
7169 Ogre::TU_RENDERTARGET);
7170 vcam.vcam_render_target = vcam.vcam_render_tex->getBuffer()->getRenderTarget();
7171 vcam.vcam_render_target->setAutoUpdated(false);
7172 }
7173 else
7174 {
7175 const std::string window_name = (!def->camera_name.empty()) ? def->camera_name : def->material_name;
7177 vcam.vcam_render_window->setAutoUpdated(false);
7178 vcam.vcam_render_window->setDeactivateOnFocusChange(false);
7179
7180 // TODO: disable texture mirrors
7181 }
7182
7183 vcam.vcam_ogre_camera->setNearClipDistance(def->min_clip_distance);
7184 vcam.vcam_ogre_camera->setFarClipDistance(def->max_clip_distance);
7185 vcam.vcam_ogre_camera->setFOVy(Ogre::Degree(def->field_of_view));
7186 const float aspect_ratio = static_cast<float>(def->texture_width) / static_cast<float>(def->texture_height);
7187 vcam.vcam_ogre_camera->setAspectRatio(aspect_ratio);
7188 vcam.vcam_material->getTechnique(0)->getPass(0)->setLightingEnabled(false);
7189 vcam.vcam_off_tex_name = "Chrome.dds"; // Built-in gray texture
7190
7191 if (vcam.vcam_render_target)
7192 {
7193 Ogre::Viewport* vp = vcam.vcam_render_target->addViewport(vcam.vcam_ogre_camera);
7194 vp->setClearEveryFrame(true);
7195 vp->setBackgroundColour(App::GetCameraManager()->GetCamera()->getViewport()->getBackgroundColour());
7196 vp->setVisibilityMask(~HIDE_MIRROR);
7197 vp->setVisibilityMask(~DEPTHMAP_DISABLED);
7198 vp->setOverlaysEnabled(false);
7199
7200 vcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(vcam.vcam_render_tex->getName());
7201
7202 // this is a mirror, flip the image left<>right to have a mirror and not a cameraimage
7204 vcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureUScale(-1);
7205 }
7206
7207 if (vcam.vcam_render_window)
7208 {
7209 Ogre::Viewport* vp = vcam.vcam_render_window->addViewport(vcam.vcam_ogre_camera);
7210 vp->setClearEveryFrame(true);
7211 vp->setBackgroundColour(App::GetCameraManager()->GetCamera()->getViewport()->getBackgroundColour());
7212 vp->setVisibilityMask(~HIDE_MIRROR);
7213 vp->setVisibilityMask(~DEPTHMAP_DISABLED);
7214 vp->setOverlaysEnabled(false);
7215 vcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(vcam.vcam_off_tex_name);
7216 }
7217
7218 if (App::diag_videocameras->getBool())
7219 {
7220 Ogre::ManualObject* mo = CreateVideocameraDebugMesh(); // local helper function
7221 vcam.vcam_debug_node = App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->createChildSceneNode(
7222 this->ComposeName("debug @ videocamera", (int)m_actor->m_gfx_actor->m_videocameras.size()));
7223 vcam.vcam_debug_node->attachObject(mo);
7224 }
7225
7226 m_actor->m_gfx_actor->m_videocameras.push_back(vcam);
7227 }
7228 catch (std::exception & ex)
7229 {
7230 this->AddMessage(Message::TYPE_ERROR, ex.what());
7231 }
7232 catch (...)
7233 {
7234 this->AddMessage(Message::TYPE_ERROR, "An unknown exception has occured");
7235 }
7236}
7237
7239 Ogre::MaterialPtr custom_mat, CustomMaterial::MirrorPropType type, Ogre::SceneNode* prop_scenenode)
7240{
7241 static int mprop_counter = 0;
7242 try
7243 {
7244 // Prepare videocamera entry
7245 RoR::VideoCamera vcam;
7246 vcam.vcam_off_tex_name = "mirror.dds";
7247 vcam.vcam_prop_scenenode = prop_scenenode;
7248 switch (type)
7249 {
7252 break;
7253
7256 break;
7257
7258 default:
7259 this->AddMessage(Message::TYPE_ERROR, "Cannot create mirror prop of type 'MPROP_NONE'");
7260 return;
7261 }
7262
7263 // Create rendering texture
7264 vcam.vcam_render_tex = Ogre::TextureManager::getSingleton().createManual(
7265 this->ComposeName("texture @ mirror", mprop_counter)
7267 , Ogre::TEX_TYPE_2D
7268 , 128
7269 , 256
7270 , 0
7271 , Ogre::PF_R8G8B8
7272 , Ogre::TU_RENDERTARGET);
7273
7274 // Create OGRE camera
7275 vcam.vcam_ogre_camera = App::GetGfxScene()->GetSceneManager()->createCamera(this->ComposeName("camera @ mirror prop", mprop_counter));
7276 vcam.vcam_ogre_camera->setNearClipDistance(0.2f);
7277 vcam.vcam_ogre_camera->setFarClipDistance(App::GetCameraManager()->GetCamera()->getFarClipDistance());
7278 vcam.vcam_ogre_camera->setFOVy(Ogre::Degree(50));
7279 vcam.vcam_ogre_camera->setAspectRatio(
7280 (App::GetCameraManager()->GetCamera()->getViewport()->getActualWidth() / App::GetCameraManager()->GetCamera()->getViewport()->getActualHeight()) / 2.0f);
7281
7282 // Setup rendering
7283 vcam.vcam_render_target = vcam.vcam_render_tex->getBuffer()->getRenderTarget();
7284 vcam.vcam_render_target->setActive(true);
7285 Ogre::Viewport* v = vcam.vcam_render_target->addViewport(vcam.vcam_ogre_camera);
7286 v->setClearEveryFrame(true);
7287 v->setBackgroundColour(App::GetCameraManager()->GetCamera()->getViewport()->getBackgroundColour());
7288 v->setOverlaysEnabled(false);
7289
7290 // Setup material
7291 vcam.vcam_material = custom_mat;
7292 vcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(vcam.vcam_render_tex->getName());
7293 vcam.vcam_material->getTechnique(0)->getPass(0)->setLightingEnabled(false);
7294
7295 // Submit the videocamera
7296 m_actor->m_gfx_actor->m_videocameras.push_back(vcam);
7297 }
7298 catch (std::exception & ex)
7299 {
7300 this->AddMessage(Message::TYPE_ERROR, ex.what());
7301 }
7302 catch (...)
7303 {
7304 this->AddMessage(Message::TYPE_ERROR, "An unknown exception has occured");
7305 }
7306 ++mprop_counter;
7307}
7308
7310{
7311 try { throw; } // Rethrow
7312
7313 catch (Ogre::Exception& ogre_e)
7314 {
7315 // Add the message silently, OGRE already printed it to RoR.log
7316 RoR::Str<2000> txt;
7317 txt << "(Keyword: " << RigDef::KeywordToString(m_current_keyword)
7318 << ") " << ogre_e.getFullDescription();
7321 }
7322 catch (std::exception& std_e)
7323 {
7324 this->AddMessage(Message::TYPE_ERROR, std_e.what());
7325 }
7326 catch (...)
7327 {
7328 this->AddMessage(Message::TYPE_ERROR, "An unknown exception has occurred");
7329 }
7330}
7331
7332Ogre::ParticleSystem* ActorSpawner::CreateParticleSystem(std::string const & name, std::string const & template_name)
7333{
7334 // None of `Ogre::SceneManager::createParticleSystem()` overloads
7335 // lets us specify both resource group and template name.
7336
7337 Ogre::NameValuePairList params;
7338 params["resourceGroup"] = m_custom_resource_group;
7339 params["templateName"] = template_name;
7340
7341 Ogre::MovableObject* obj = App::GetGfxScene()->GetSceneManager()->createMovableObject(
7342 name, Ogre::ParticleSystemFactory::FACTORY_TYPE_NAME, &params);
7343 Ogre::ParticleSystem* psys = static_cast<Ogre::ParticleSystem*>(obj);
7344 psys->setVisibilityFlags(DEPTHMAP_DISABLED); // disable particles in depthmap
7345
7346 // Shut down the emitters
7347 for (size_t i = 0; i < psys->getNumEmitters(); i++)
7348 {
7349 psys->getEmitter(i)->setEnabled(false);
7350 }
7351
7352 return psys;
7353}
7354
7356{
7359
7360 //the cab materials are as follow:
7361 //texname: base texture with emissive(2 pass) or without emissive if none available(1 pass), alpha cutting
7362 //texname-trans: transparency texture (1 pass)
7363 //texname-back: backface texture: black+alpha cutting (1 pass)
7364 //texname-noem: base texture without emissive (1 pass), alpha cutting
7365
7366 //material passes must be:
7367 //0: normal texture
7368 //1: transparent (windows)
7369 //2: emissive
7370
7371 Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(m_cab_material_name);
7372 if (!mat)
7373 {
7374 Ogre::String msg = "Material '"+m_cab_material_name+"' missing!";
7376 return;
7377 }
7378
7379 //-trans
7380 char transmatname[256];
7381 static int trans_counter = 0;
7382 sprintf(transmatname, "%s-trans-%d", m_cab_material_name.c_str(), trans_counter++);
7383 Ogre::MaterialPtr transmat=mat->clone(transmatname);
7384 if (mat->getTechnique(0)->getNumPasses()>1) // If there's the "emissive pass", remove it from the 'transmat'
7385 {
7386 transmat->getTechnique(0)->removePass(1);
7387 }
7388 transmat->getTechnique(0)->getPass(0)->setAlphaRejectSettings(Ogre::CMPF_LESS_EQUAL, 128);
7389 transmat->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
7390 if (transmat->getTechnique(0)->getPass(0)->getNumTextureUnitStates()>0)
7391 {
7392 transmat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureFiltering(Ogre::TFO_NONE);
7393 }
7394 transmat->compile();
7395 m_cab_trans_material = transmat;
7396
7397 //-back
7398 char backmatname[256];
7399 static int back_counter = 0;
7400 sprintf(backmatname, "%s-back-%d", m_cab_material_name.c_str(), back_counter++);
7401 Ogre::MaterialPtr backmat=mat->clone(backmatname);
7402 if (mat->getTechnique(0)->getNumPasses()>1)// If there's the "emissive pass", remove it from the 'transmat'
7403 {
7404 backmat->getTechnique(0)->removePass(1);
7405 }
7406 if (transmat->getTechnique(0)->getPass(0)->getNumTextureUnitStates()>0)
7407 {
7408 backmat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setColourOperationEx(
7409 Ogre::LBX_SOURCE1,
7410 Ogre::LBS_MANUAL,
7411 Ogre::LBS_MANUAL,
7412 Ogre::ColourValue(0,0,0),
7413 Ogre::ColourValue(0,0,0)
7414 );
7415 }
7416 if (App::gfx_reduce_shadows->getBool())
7417 {
7418 backmat->setReceiveShadows(false);
7419 }
7420 backmat->compile();
7421
7422 m_actor->GetGfxActor()->UpdateSimDataBuffer(); // fill all current nodes - needed to setup flexing meshes
7423
7424 char cab_material_name_cstr[1000] = {};
7425 strncpy(cab_material_name_cstr, m_cab_material_name.c_str(), 999);
7426 std::string mesh_name = this->ComposeName("mesh @ cab");
7427 FlexObj* cab_mesh =new FlexObj(
7428 m_actor->m_gfx_actor.get(),
7434 cab_material_name_cstr,
7435 mesh_name.c_str(),
7436 backmatname,
7437 transmatname
7438 );
7439
7440 Ogre::SceneNode* cab_scene_node = m_actor_grouping_scenenode->createChildSceneNode(this->ComposeName("cab"));
7441 Ogre::Entity *ec = nullptr;
7442 try
7443 {
7444 ec = App::GetGfxScene()->GetSceneManager()->createEntity(this->ComposeName("entity @ cab"), mesh_name);
7445 this->SetupNewEntity(ec, Ogre::ColourValue(0.5, 1, 0.5));
7446 if (ec)
7447 {
7448 cab_scene_node->attachObject(ec);
7449 }
7450
7451 // Process "emissive cab" materials
7452 auto search_itor = m_material_substitutions.find(m_cab_material_name);
7453 if (search_itor != m_material_substitutions.end())
7454 {
7455 m_actor->m_gfx_actor->RegisterCabMaterial(search_itor->second.material, m_cab_trans_material);
7456 }
7457 m_actor->m_gfx_actor->SetCabLightsActive(false); // Reset emissive lights to "off" state
7458
7459 m_actor->GetGfxActor()->RegisterCabMesh(ec, cab_scene_node, cab_mesh);
7460 }
7461 catch (Ogre::Exception& e)
7462 {
7463 this->AddMessage(Message::TYPE_ERROR, "error creating cab mesh: "+e.getFullDescription());
7464 if (ec)
7465 {
7466 App::GetGfxScene()->GetSceneManager()->destroyEntity(ec);
7467 }
7468 }
7469}
7470
7471void ActorSpawner::CreateMaterialFlare(int flareid, Ogre::MaterialPtr m)
7472{
7473 RoR::FlareMaterial binding;
7474 binding.flare_index = flareid;
7475 binding.mat_instance = m;
7476
7477 if (!m)
7478 return;
7479 Ogre::Technique* tech = m->getTechnique(0);
7480 if (!tech)
7481 return;
7482 Ogre::Pass* p = tech->getPass(0);
7483 if (!p)
7484 return;
7485 // save emissive colour and then set to zero (light disabled by default)
7486 binding.emissive_color = p->getSelfIllumination();
7487 p->setSelfIllumination(Ogre::ColourValue::ZERO);
7488
7489 m_actor->m_gfx_actor->m_flare_materials.push_back(binding);
7490}
7491
7493{
7494 // Media (Textures/Materials/meshes) can be either in AddonPart bundle or the vehicle bundle.
7495 // =========================================================================================
7496
7497 if (m_current_module->origin_addonpart)
7498 {
7499 return m_current_module->origin_addonpart->resource_group;
7500 }
7501 else
7502 {
7504 }
7505}
7506
7507void ActorSpawner::AssignManagedMaterialTexture(Ogre::TextureUnitState* tus, const std::string & mm_name, int media_id, const std::string& tex_name)
7508{
7509 // Helper for `ProcessManagedMaterial()`, resolves tweaks
7510 // ======================================================
7511
7512 try
7513 {
7514 ROR_ASSERT(tus);
7515 if (tus)
7516 {
7517 Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().load(
7518 TuneupUtil::getTweakedManagedMatMedia(m_actor->getWorkingTuneupDef(), mm_name, media_id, tex_name),
7519 TuneupUtil::getTweakedManagedMatMediaRG(m_actor->getWorkingTuneupDef(), mm_name, media_id, this->GetCurrentElementMediaRG()));
7520
7521 if (tex)
7522 {
7523 tus->setTexture(tex);
7524 }
7525 }
7526 }
7527 catch (...) // Exception is already logged by OGRE
7528 {
7529 }
7530}
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:59
#define TOSTRING(x)
Definition Application.h:57
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:410
float m_odometer_user
GUI state.
Definition Actor.h:652
wing_t * ar_wings
Definition Actor.h:360
int ar_num_screwprops
Definition Actor.h:391
int ar_num_wings
Definition Actor.h:361
int m_num_command_beams
TODO: Remove! Spawner context only; likely unused feature.
Definition Actor.h:653
EnginePtr ar_engine
Definition Actor.h:432
float ar_collision_range
Physics attr.
Definition Actor.h:485
std::vector< Ogre::AxisAlignedBox > ar_predicted_coll_bounding_boxes
Definition Actor.h:378
std::vector< float > ar_minimass
minimum node mass in Kg - can be scaled in-game via NBUtil
Definition Actor.h:337
TransferCase * m_transfer_case
Physics.
Definition Actor.h:643
float ar_guisettings_speedo_max_kph
Definition Actor.h:514
float ar_dry_mass
User-defined (editable via NBUtil); from 'globals' arg#1 - default for all nodes.
Definition Actor.h:320
int ar_airbrake_intensity
Physics state; values 0-5.
Definition Actor.h:478
int m_proped_wheel_pairs[MAX_WHEELS]
Physics attr; For inter-differential locking.
Definition Actor.h:615
NodeNum_t ar_camera_node_dir[MAX_CAMERAS]
Physics attr; 'camera' = frame of reference; back node.
Definition Actor.h:445
collcab_rate_t ar_intra_collcabrate[MAX_CABS]
Definition Actor.h:398
std::vector< float > ar_nodes_override_loadweights
'nodes': 'l' flag and number.
Definition Actor.h:334
std::vector< bool > ar_beams_invisible
Used only by the exporter (for rendering, invisible beams simply get no mesh).
Definition Actor.h:351
std::vector< ropable_t > ar_ropables
Definition Actor.h:364
CmdKeyArray ar_command_key
BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
Definition Actor.h:369
wheel_t ar_wheels[MAX_WHEELS]
Definition Actor.h:384
std::vector< rope_t > ar_ropes
Definition Actor.h:363
DashBoardManagerPtr ar_dashboard
Definition Actor.h:484
float m_odometer_total
GUI state.
Definition Actor.h:651
std::vector< wheeldetacher_t > ar_wheeldetachers
Definition Actor.h:374
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:447
int ar_num_camera_rails
Definition Actor.h:404
float tc_pulse_time
Definition Actor.h:505
node_t * ar_nodes
Definition Actor.h:330
int ar_buoycabs[MAX_CABS]
Definition Actor.h:400
GfxActor * GetGfxActor()
Definition Actor.h:309
std::vector< BitMask_t > ar_nodes_options
merged options from 'nodes' and 'set_node_defaults'
Definition Actor.h:336
int m_num_wheel_diffs
Physics attr.
Definition Actor.h:642
std::vector< bool > ar_beams_user_defined
True for 'beams', false for wheels/cinecam/hooknode/wings/rotators etc...
Definition Actor.h:352
int m_num_proped_wheels
Physics attr, filled at spawn - Number of propelled wheels.
Definition Actor.h:616
std::vector< Ogre::SceneNode * > m_deletion_scene_nodes
For unloading vehicle; filled at spawn.
Definition Actor.h:614
std::vector< tie_t > ar_ties
Definition Actor.h:365
int m_num_axle_diffs
Physics attr.
Definition Actor.h:640
float ar_posnode_spawn_height
Definition Actor.h:449
ground_model_t * ar_submesh_ground_model
Definition Actor.h:466
std::vector< SlideNode > m_slidenodes
all the SlideNodes available on this actor
Definition Actor.h:611
int * ar_nodes_id
Number in truck file, -1 for nodes generated by wheels/cinecam.
Definition Actor.h:331
NodeNum_t ar_camera_node_roll[MAX_CAMERAS]
Physics attr; 'camera' = frame of reference; left node.
Definition Actor.h:446
Differential * m_axle_diffs[1+MAX_WHEELS/2]
Physics.
Definition Actor.h:639
ScrewpropPtr ar_screwprops[MAX_SCREWPROPS]
Definition Actor.h:390
bool m_disable_smoke
Stops/starts smoke particles (i.e. exhausts, turbojets).
Definition Actor.h:707
NodeNum_t ar_main_camera_node_pos
Sim attr; ar_camera_node_pos[0] >= 0 ? ar_camera_node_pos[0] : 0.
Definition Actor.h:441
bool ar_is_police
Gfx/sfx attr.
Definition Actor.h:553
std::vector< Airbrake * > ar_airbrakes
Definition Actor.h:368
bool m_beam_break_debug_enabled
Logging state.
Definition Actor.h:703
std::vector< float > ar_nodes_default_loadweights
'set_node_defaults': load weight.
Definition Actor.h:333
NodeNum_t ar_camera_rail[MAX_CAMERARAIL]
Nodes defining camera-movement spline.
Definition Actor.h:403
int ar_buoycab_types[MAX_CABS]
Definition Actor.h:401
float m_fusealge_width
Physics attr; defined in truckfile.
Definition Actor.h:650
Airfoil * m_fusealge_airfoil
Physics attr; defined in truckfile.
Definition Actor.h:647
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:377
soundsource_t ar_soundsources[MAX_SOUNDSCRIPTS_PER_TRUCK]
Definition Actor.h:386
node_t * m_fusealge_front
Physics attr; defined in truckfile.
Definition Actor.h:648
std::vector< flare_t > ar_flares
Definition Actor.h:367
bool tc_mode
Enabled?
Definition Actor.h:504
std::vector< Ogre::Entity * > m_deletion_entities
For unloading vehicle; filled at spawn.
Definition Actor.h:613
std::unique_ptr< Buoyance > m_buoyance
Definition Actor.h:490
int ar_num_cabs
Definition Actor.h:394
bool alb_nodash
Anti-lock brake attribute: Hide the dashboard indicator?
Definition Actor.h:413
bool sl_enabled
Speed limiter;.
Definition Actor.h:423
ExtCameraMode ar_extern_camera_mode
Definition Actor.h:425
float ar_load_mass
User-defined (editable via NBUtil); from 'globals' arg#2 - only applies to nodes with 'l' flag.
Definition Actor.h:322
node_t * m_fusealge_back
Physics attr; defined in truckfile.
Definition Actor.h:649
NodeNum_t ar_exhaust_dir_node
Old-format exhaust (one per vehicle) backwards direction node.
Definition Actor.h:428
int ar_masscount
Calculated; Number of nodes loaded with l option.
Definition Actor.h:324
PointColDetector * m_inter_point_col_detector
Physics.
Definition Actor.h:619
BitMask_t m_flaregroups_no_import
RoRnet::Lightmask.
Definition Actor.h:690
std::vector< RailGroup * > m_railgroups
all the available RailGroups for this actor
Definition Actor.h:612
float m_handbrake_force
Physics attr; defined in truckfile.
Definition Actor.h:646
NodeNum_t ar_extern_camera_node
Definition Actor.h:426
Ogre::Vector3 ar_origin
Physics state; base position for softbody nodes.
Definition Actor.h:438
float ar_guisettings_shifter_anim_time
Definition Actor.h:515
int getInstanceId()
Definition Actor.h:271
ActorState ar_state
Definition Actor.h:518
float ar_brake_force
Physics attr; filled at spawn.
Definition Actor.h:436
std::unique_ptr< GfxActor > m_gfx_actor
Definition Actor.h:610
NodeNum_t ar_exhaust_pos_node
Old-format exhaust (one per vehicle) emitter node.
Definition Actor.h:427
float tc_timer
Definition Actor.h:509
AeroEnginePtr ar_aeroengines[MAX_AEROENGINES]
Definition Actor.h:388
std::string getTruckFileName()
Definition Actor.h:267
int ar_num_shocks
Number of shock absorbers.
Definition Actor.h:356
float ar_total_mass
Calculated; total mass in Kg.
Definition Actor.h:325
int ar_num_aeroengines
Definition Actor.h:389
float ar_anim_shift_timer
For 'animator' with flag 'shifter'.
Definition Actor.h:416
float alb_ratio
Anti-lock brake attribute: Regulating force.
Definition Actor.h:408
float cc_target_speed_lower_limit
Cruise Control.
Definition Actor.h:421
int ar_num_beams
Definition Actor.h:349
std::vector< authorinfo_t > authors
Definition Actor.h:362
bool m_beam_deform_debug_enabled
Logging state.
Definition Actor.h:704
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition Actor.h:429
bool alb_notoggle
Anti-lock brake attribute: Disable in-game toggle?
Definition Actor.h:414
std::vector< PropAnimKeyState > m_prop_anim_key_states
Definition Actor.h:661
bool m_has_axles_section
Temporary (legacy parsing helper) until central diffs are implemented.
Definition Actor.h:658
rotator_t * ar_rotators
Definition Actor.h:358
bool ar_minimass_skip_loaded_nodes
Definition Actor.h:343
std::string * ar_nodes_name
Name in truck file, only if defined with 'nodes2'.
Definition Actor.h:332
float cc_target_rpm
Cruise Control.
Definition Actor.h:419
NodeNum_t ar_camera_node_pos[MAX_CAMERAS]
Physics attr; 'camera' = frame of reference; origin node.
Definition Actor.h:444
int ar_num_cinecams
Sim attr;.
Definition Actor.h:434
GfxFlaresMode m_flares_mode
Snapshot of cvar 'gfx_flares_mode' on spawn.
Definition Actor.h:688
collcab_rate_t ar_inter_collcabrate[MAX_CABS]
Definition Actor.h:397
TyrePressure & getTyrePressure()
Definition Actor.h:259
bool cc_mode
Cruise Control.
Definition Actor.h:417
Differential * m_wheel_diffs[MAX_WHEELS/2]
Physics.
Definition Actor.h:641
bool ar_hide_in_actor_list
Hide in list of spawned actors (available in top menubar). Useful for fixed-place machinery,...
Definition Actor.h:405
float alb_pulse_time
Anti-lock brake attribute;.
Definition Actor.h:411
int ar_cabs[MAX_CABS *3]
Definition Actor.h:392
int ar_num_collcabs
Definition Actor.h:399
int ar_num_nodes
Definition Actor.h:345
Skidmark * m_skid_trails[MAX_WHEELS *2]
Definition Actor.h:655
bool tc_pulse_state
Definition Actor.h:506
float alb_minspeed
Anti-lock brake attribute;.
Definition Actor.h:409
Ogre::Quaternion ar_main_camera_dir_corr
Sim attr;.
Definition Actor.h:440
bool m_has_command_beams
Physics attr;.
Definition Actor.h:701
std::vector< std::string > m_description
Definition Actor.h:660
VehicleAIPtr ar_vehicle_ai
Definition Actor.h:450
float ar_anim_previous_crank
For 'animator' with flag 'torque'.
Definition Actor.h:407
std::string getTruckFileResourceGroup()
Definition Actor.cpp:4868
bool tc_notoggle
Disable in-game toggle?
Definition Actor.h:508
AutopilotPtr ar_autopilot
Definition Actor.h:435
float tc_ratio
Regulating force.
Definition Actor.h:503
float alb_timer
Anti-lock brake state;.
Definition Actor.h:415
int ar_num_soundsources
Definition Actor.h:387
int ar_num_contacters
Total number of nodes which can selfcontact cabs.
Definition Actor.h:383
std::vector< hydrobeam_t > ar_hydros
Definition Actor.h:395
int ar_nodes_name_top_length
For nicely formatted diagnostic output.
Definition Actor.h:344
NodeNum_t ar_main_camera_node_dir
Sim attr; ar_camera_node_dir[0] >= 0 ? ar_camera_node_dir[0] : 0.
Definition Actor.h:442
int ar_num_cameras
Definition Actor.h:439
std::vector< hook_t > ar_hooks
Definition Actor.h:366
bool ar_collision_relevant
Physics state;.
Definition Actor.h:552
PointColDetector * m_intra_point_col_detector
Physics.
Definition Actor.h:620
int ar_num_wheels
Definition Actor.h:385
CacheEntryPtr m_used_skin_entry
Optional, only graphics.
Definition Actor.h:669
shock_t * ar_shocks
Shock absorbers.
Definition Actor.h:355
bool tc_nodash
Hide the dashboard indicator?
Definition Actor.h:507
NodeNum_t ar_cinecam_node[MAX_CAMERAS]
Sim attr; Cine-camera node indexes.
Definition Actor.h:433
Ogre::Vector3 * ar_nodes_spawn_offsets
Relative positions (incl. Tuning system tweaks) from the definition file, for spawn-like resetting (i...
Definition Actor.h:335
bool alb_pulse_state
Anti-lock brake state;.
Definition Actor.h:412
CacheEntryPtrVec m_used_addonpart_entries
Optional, assigned by player via Tuning menu (.tuneup files).
Definition Actor.h:670
float m_avg_proped_wheel_radius
Physics attr, filled at spawn - Average proped wheel radius.
Definition Actor.h:617
bool m_trigger_debug_enabled
Logging state.
Definition Actor.h:705
beam_t * ar_beams
Definition Actor.h:348
int ar_num_buoycabs
Definition Actor.h:402
CacheEntryPtrVec m_used_assetpack_entries
Optional, specified by mod author in truck file via 'assetpacks' section.
Definition Actor.h:671
int ar_collcabs[MAX_CABS]
Definition Actor.h:396
bool ar_has_active_shocks
Are there active stabilizer shocks?
Definition Actor.h:357
int ar_num_contactable_nodes
Total number of nodes which can contact ground or cabs.
Definition Actor.h:382
TuneupDefPtr m_working_tuneup_def
Each actor gets unique instance, even if loaded from .tuneup file in modcache.
Definition Actor.h:666
TuneupDefPtr & getWorkingTuneupDef()
Definition Actor.cpp:4893
bool ar_guisettings_use_engine_max_rpm
Definition Actor.h:513
float cc_target_speed
Cruise Control.
Definition Actor.h:420
float sl_speed_limit
Speed limiter;.
Definition Actor.h:424
int ar_num_rotators
Definition Actor.h:359
ActorType ar_driveable
Sim attr; marks vehicle type and features.
Definition Actor.h:431
NodeNum_t ar_main_camera_node_roll
Sim attr; ar_camera_node_roll[0] >= 0 ? ar_camera_node_roll[0] : 0.
Definition Actor.h:443
bool cc_can_brake
Cruise Control.
Definition Actor.h:418
std::vector< UniqueCommandKeyPair > ar_unique_commandkey_pairs
UI helper for displaying command control keys to user (must be built at spawn).
Definition Actor.h:380
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)
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()
RoR::Renderdash * m_oldstyle_renderdash
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.
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
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?)
std::string m_help_material_name
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:82
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)
void loadDashBoard(const std::string &filename, BitMask_t flags)
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:42
char m_engine_type
't' = truck (default), 'c' = car ('engoption' attr #2)
Definition Engine.h:208
bool m_engine_has_air
Engine attribute.
Definition Engine.h:204
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:86
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:847
bool hasTurbo() const
Definition Engine.h:72
float m_turbo_inertia_factor
Definition Engine.h:259
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
const TerrainPtr & GetTerrain()
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)
Ogre::TexturePtr getTexture()
Definition Renderdash.h:41
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:105
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:90
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:85
const PropAnimFlag_t PROP_ANIM_FLAG_AIRBRAKE
Definition GfxData.h:49
const PropAnimMode_t PROP_ANIM_MODE_NOFLIP
Definition GfxData.h:89
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:84
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:86
const PropAnimFlag_t PROP_ANIM_FLAG_BRUDDER
Definition GfxData.h:72
const PropAnimMode_t PROP_ANIM_MODE_AUTOANIMATE
Definition GfxData.h:88
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:83
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_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:87
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:82
const PropAnimFlag_t PROP_ANIM_FLAG_SPEEDO
Definition GfxData.h:58
@ SHIFTERMAN1
Definition GfxData.h:124
@ SHIFTERSEQ
Definition GfxData.h:126
@ AUTOSHIFTERLIN
Definition GfxData.h:128
@ SHIFTERMAN2
Definition GfxData.h:125
@ SHIFTERLIN
Definition GfxData.h:127
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
@ 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
GameContext * GetGameContext()
GfxScene * GetGfxScene()
CVar * diag_log_beam_deform
CVar * sim_no_collisions
CVar * gfx_window_videocams
CVar * sim_no_self_collisions
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_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_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
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:316
Ogre::Vector3 abx_offset
Definition GfxData.h:319
NodeNum_t abx_y_node
Definition GfxData.h:322
Ogre::SceneNode * abx_scenenode
Definition GfxData.h:317
NodeNum_t abx_ref_node
Definition GfxData.h:320
NodeNum_t abx_x_node
Definition GfxData.h:321
Ogre::Entity * abx_entity
Definition GfxData.h:318
Visuals of softbody beam (beam_t struct); Partially updated along with SimBuffer.
Definition GfxData.h:261
NodeNum_t rod_node2
Node index - may change during simulation!
Definition GfxData.h:271
uint16_t rod_beam_index
Definition GfxData.h:267
float rod_diameter
meters
Definition GfxData.h:268
Ogre::SceneNode * rod_scenenode
Definition GfxData.h:266
ActorPtr rod_target_actor
Definition GfxData.h:272
NodeNum_t rod_node1
Node index - may change during simulation!
Definition GfxData.h:270
bool rod_is_visible
Definition GfxData.h:273
NodeNum_t emitterNode
Definition GfxData.h:343
NodeNum_t directionNode
Definition GfxData.h:344
Ogre::ParticleSystem * psys
Definition GfxData.h:346
Ogre::SceneNode * snode
Definition GfxData.h:345
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:337
NodeNum_t directionNode
Definition GfxData.h:335
Ogre::SceneNode * smokeNode
Definition GfxData.h:336
NodeNum_t emitterNode
Definition GfxData.h:334
std::string particleSystemName
Name in .particle file ~ for display in Tuning UI.
Definition GfxData.h:338
Ogre::ColourValue emissive_color
Definition GfxData.h:329
Ogre::MaterialPtr mat_instance
Definition GfxData.h:328
< Node resolution must be done in ActorSpawner, but vert resolution in FlexBody
Definition Locator_t.h:24
Gfx attributes/state of a softbody node.
Definition GfxData.h:237
bool nx_no_sparks
User-defined attr;.
Definition GfxData.h:255
bool nx_no_particles
User-defined attr; disable all particles.
Definition GfxData.h:251
bool nx_may_get_wet
Attr; enables water drip and vapour.
Definition GfxData.h:252
float upper_limit
The upper limit for the animation.
Definition GfxData.h:146
float lower_limit
The lower limit for the animation.
Definition GfxData.h:145
float animratio
A coefficient for the animation, prop degree if used with mode: rotation and propoffset if used with ...
Definition GfxData.h:133
PropAnimFlag_t animFlags
Definition GfxData.h:134
PropAnimMode_t animMode
Definition GfxData.h:135
float animOpt3
MULTIPURPOSE.
Definition GfxData.h:143
float animOpt5
Definition GfxData.h:144
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:156
float pp_beacon_rot_angle[4]
Radians.
Definition GfxData.h:188
Ogre::Quaternion pp_rot
Definition GfxData.h:164
MeshObject * pp_wheel_mesh_obj
Definition GfxData.h:177
Ogre::SceneNode * pp_beacon_scene_node[4]
Definition GfxData.h:185
CameraMode_t pp_camera_mode_active
Dynamic visibility mode {0 and higher = cinecam index}.
Definition GfxData.h:172
CameraMode_t pp_camera_mode_orig
Dynamic visibility mode {0 and higher = cinecam index}.
Definition GfxData.h:173
NodeNum_t pp_node_ref
Definition GfxData.h:158
Ogre::Vector3 pp_rota
Definition GfxData.h:163
bool pp_aero_propeller_spin
Special - blurred spinning propeller effect.
Definition GfxData.h:193
NodeNum_t pp_node_y
Definition GfxData.h:160
Ogre::Vector3 pp_offset_orig
Used with ANIM_FLAG_OFFSET*.
Definition GfxData.h:162
Ogre::SceneNode * pp_scene_node
The pivot scene node (parented to root-node).
Definition GfxData.h:165
Ogre::Vector3 pp_offset
Definition GfxData.h:161
PropID_t pp_id
Definition GfxData.h:157
std::vector< PropAnim > pp_animations
Definition GfxData.h:168
std::string pp_media[2]
Redundant, for Tuning UI. Media1 = prop mesh name, Media2 = steeringwheel mesh/beaconprop flare mat.
Definition GfxData.h:167
NodeNum_t pp_node_x
Definition GfxData.h:159
Ogre::BillboardSet * pp_beacon_bbs[4]
Definition GfxData.h:184
char pp_beacon_type
Special prop: beacon {0 = none, 'b' = user-specified, 'r' = red, 'p' = police lightbar,...
Definition GfxData.h:183
bool pp_aero_propeller_blade
Special - single blade mesh.
Definition GfxData.h:192
Ogre::Light * pp_beacon_light[4]
Definition GfxData.h:186
Ogre::Vector3 pp_wheel_pos
Definition GfxData.h:178
Ogre::SceneNode * pp_wheel_scene_node
Definition GfxData.h:179
float pp_wheel_rot_degree
Definition GfxData.h:180
MeshObject * pp_mesh_obj
Optional; NULL if removed via tuneup/addonpart.
Definition GfxData.h:166
float pp_beacon_rot_rate[4]
Radians per second.
Definition GfxData.h:187
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:215
Ogre::TexturePtr vcam_render_tex
Definition GfxData.h:229
NodeNum_t vcam_node_center
Definition GfxData.h:217
NodeNum_t vcam_node_alt_pos
Definition GfxData.h:220
Ogre::RenderWindow * vcam_render_window
Definition GfxData.h:231
VideoCamRole vcam_role
Definition GfxData.h:216
std::string vcam_off_tex_name
Used when videocamera is offline.
Definition GfxData.h:226
Ogre::RenderTexture * vcam_render_target
Definition GfxData.h:228
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:224
NodeNum_t vcam_node_lookat
Only for VCAM_ROLE_TRACK_CAM.
Definition GfxData.h:221
Ogre::Quaternion vcam_rotation
Definition GfxData.h:222
NodeNum_t vcam_node_dir_y
Definition GfxData.h:218
Ogre::Vector3 vcam_pos_offset
Definition GfxData.h:223
Ogre::SceneNode * vcam_debug_node
Definition GfxData.h:230
Ogre::SceneNode * vcam_prop_scenenode
Only for VCAM_ROLE_MIRROR_PROP_*.
Definition GfxData.h:232
Ogre::MaterialPtr vcam_material
Definition GfxData.h:225
Ogre::Camera * vcam_ogre_camera
Definition GfxData.h:227
NodeNum_t vcam_node_dir_z
Definition GfxData.h:219
Flexable * wx_flex_mesh
Definition GfxData.h:308
WheelID_t wx_wheel_id
Definition GfxData.h:307
std::string wx_rim_mesh_name
Redundant, for Tuning UI. Only for 'meshwheels[2]' and 'flexbodywheels'.
Definition GfxData.h:311
Ogre::SceneNode * wx_scenenode
Definition GfxData.h:309
WheelSide wx_side
Definition GfxData.h:310
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