RigsofRods
Soft-body Physics Simulation
GfxActor.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 2016-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 #include "GfxActor.h"
23 
24 #include "ApproxMath.h"
25 #include "AirBrake.h"
26 #include "Actor.h"
27 #include "Collisions.h"
28 #include "DashBoardManager.h"
29 #include "DustPool.h" // General particle gfx
30 #include "EngineSim.h"
31 #include "GameContext.h"
32 #include "GfxScene.h"
33 #include "GUIManager.h"
34 #include "GUIUtils.h"
35 #include "HydraxWater.h"
36 #include "FlexAirfoil.h"
37 #include "FlexBody.h"
38 #include "FlexMeshWheel.h"
39 #include "FlexObj.h"
40 #include "InputEngine.h" // TODO: Keys shouldn't be queried from here, but buffered in sim. loop ~ only_a_ptr, 06/2018
41 #include "MeshObject.h"
42 #include "MovableText.h"
43 #include "OgreImGui.h"
44 #include "Renderdash.h" // classic 'renderdash' material
45 #include "RoRnet.h"
46 #include "ActorSpawner.h"
47 #include "SlideNode.h"
48 #include "SkyManager.h"
49 #include "SoundScriptManager.h"
50 #include "Terrain.h"
51 #include "TurboJet.h"
52 #include "TurboProp.h"
53 #include "Utils.h"
54 
55 #include "imgui_internal.h"
56 
57 #include <Ogre.h>
58 
59 using namespace RoR;
60 
61 RoR::GfxActor::GfxActor(ActorPtr actor, ActorSpawner* spawner, std::string ogre_resource_group,
62  RoR::Renderdash* renderdash):
63  m_actor(actor),
64  m_custom_resource_group(ogre_resource_group),
65  m_renderdash(renderdash)
66 {
67  // Setup particles
69  m_particles_dust = App::GetGfxScene()->GetDustPool("dust"); // Dust, water vapour, tyre smoke
74 }
75 
76 std::string WhereFrom(GfxActor* gfx_actor, const std::string& doing_what)
77 {
78  return fmt::format("GfxActor::~GfxActor(); instanceID:{}, streamID:{}, filename:{}; {}",
79  gfx_actor->getOwningActor()->ar_instance_id,
80  gfx_actor->getOwningActor()->ar_net_stream_id,
81  gfx_actor->getOwningActor()->ar_filename,
82  doing_what);
83 }
84 
86 {
87  // Dispose videocameras
88  this->SetVideoCamState(VideoCamState::VCSTATE_DISABLED);
89  while (!m_videocameras.empty())
90  {
91  try
92  {
93  VideoCamera& vcam = m_videocameras.back();
94  Ogre::TextureManager::getSingleton().remove(vcam.vcam_render_tex->getHandle());
95  vcam.vcam_render_tex.setNull();
96  vcam.vcam_render_target = nullptr; // Invalidated with parent texture
97  App::GetGfxScene()->GetSceneManager()->destroyCamera(vcam.vcam_ogre_camera);
98  }
99  catch (...)
100  {
101  HandleGenericException(WhereFrom(this, fmt::format("destroying videocamera {}", m_videocameras.size())), HANDLEGENERICEXCEPTION_LOGFILE);
102  }
103 
104  m_videocameras.pop_back();
105  }
106 
107  // Dispose rods
108  if (m_gfx_beams_parent_scenenode != nullptr)
109  {
110  try
111  {
112  for (BeamGfx& rod: m_gfx_beams)
113  {
114  Ogre::MovableObject* ogre_object = rod.rod_scenenode->getAttachedObject(0);
115  rod.rod_scenenode->detachAllObjects();
116  App::GetGfxScene()->GetSceneManager()->destroyEntity(static_cast<Ogre::Entity*>(ogre_object));
117  }
118  m_gfx_beams.clear();
119 
120  m_gfx_beams_parent_scenenode->removeAndDestroyAllChildren();
121  App::GetGfxScene()->GetSceneManager()->destroySceneNode(m_gfx_beams_parent_scenenode);
122  m_gfx_beams_parent_scenenode = nullptr;
123  }
124  catch (...)
125  {
127  }
128  }
129 
130  // delete meshwheels
131  for (size_t i = 0; i < m_wheels.size(); i++)
132  {
133  try
134  {
135  if (m_wheels[i].wx_flex_mesh != nullptr)
136  {
137  delete m_wheels[i].wx_flex_mesh;
138  }
139  if (m_wheels[i].wx_scenenode != nullptr)
140  {
141  m_wheels[i].wx_scenenode->removeAndDestroyAllChildren();
142  App::GetGfxScene()->GetSceneManager()->destroySceneNode(m_wheels[i].wx_scenenode);
143  }
144  }
145  catch (...)
146  {
148  }
149  }
150 
151  // delete airbrakes
152  int i_airbrake = 0;
153  for (AirbrakeGfx& abx: m_gfx_airbrakes)
154  {
155  try
156  {
157  // scene node
158  abx.abx_scenenode->detachAllObjects();
159  App::GetGfxScene()->GetSceneManager()->destroySceneNode(abx.abx_scenenode);
160  // entity
161  App::GetGfxScene()->GetSceneManager()->destroyEntity(abx.abx_entity);
162  // mesh
163  Ogre::MeshManager::getSingleton().remove(abx.abx_mesh);
164 
165  i_airbrake++;
166  }
167  catch (...)
168  {
169  HandleGenericException(WhereFrom(this, fmt::format("destroying airbrake {}", i_airbrake)), HANDLEGENERICEXCEPTION_LOGFILE);
170  }
171  }
172  m_gfx_airbrakes.clear();
173 
174  // Delete props
175  int i_prop = 0;
176  for (Prop & prop: m_props)
177  {
178  try
179  {
180  for (int k = 0; k < 4; ++k)
181  {
182  if (prop.pp_beacon_scene_node[k])
183  {
184  Ogre::SceneNode* scene_node = prop.pp_beacon_scene_node[k];
185  scene_node->removeAndDestroyAllChildren();
186  App::GetGfxScene()->GetSceneManager()->destroySceneNode(scene_node);
187  }
188  if (prop.pp_beacon_light[k])
189  {
190  App::GetGfxScene()->GetSceneManager()->destroyLight(prop.pp_beacon_light[k]);
191  }
192  }
193  }
194  catch (...)
195  {
196  HandleGenericException(WhereFrom(this, fmt::format("destroying beacon {} beacons", i_prop)), HANDLEGENERICEXCEPTION_LOGFILE);
197  }
198 
199  try
200  {
201  if (prop.pp_scene_node)
202  {
203  prop.pp_scene_node->removeAndDestroyAllChildren();
204  App::GetGfxScene()->GetSceneManager()->destroySceneNode(prop.pp_scene_node);
205  }
206  if (prop.pp_wheel_scene_node)
207  {
208  prop.pp_wheel_scene_node->removeAndDestroyAllChildren();
209  App::GetGfxScene()->GetSceneManager()->destroySceneNode(prop.pp_wheel_scene_node);
210  }
211  }
212  catch (...)
213  {
214  HandleGenericException(WhereFrom(this, fmt::format("destroying prop {} scene nodes", i_prop)), HANDLEGENERICEXCEPTION_LOGFILE);
215  }
216 
217  try
218  {
219  if (prop.pp_mesh_obj)
220  {
221  delete prop.pp_mesh_obj;
222  }
223  if (prop.pp_wheel_mesh_obj)
224  {
225  delete prop.pp_wheel_mesh_obj;
226  }
227  }
228  catch (...)
229  {
230  HandleGenericException(WhereFrom(this, fmt::format("destroying prop {} mesh objects", i_prop)), HANDLEGENERICEXCEPTION_LOGFILE);
231  }
232 
233  i_prop++;
234  }
235  m_props.clear();
236 
237  // Delete flexbodies
238  int i_flexbody = 0;
239  for (FlexBody* fb: m_flexbodies)
240  {
241  try
242  {
243  fb->destroyOgreObjects();
244  delete fb;
245  }
246  catch (...)
247  {
248  HandleGenericException(WhereFrom(this, fmt::format("destroying flexbody {}", i_flexbody)), HANDLEGENERICEXCEPTION_LOGFILE);
249  }
250  i_flexbody++;
251  }
252 
253  // Delete old cab mesh
254  if (m_cab_mesh != nullptr)
255  {
256  try
257  {
258  m_cab_scene_node->detachAllObjects();
259  m_cab_scene_node->getParentSceneNode()->removeAndDestroyChild(m_cab_scene_node);
260  m_cab_scene_node = nullptr;
261 
262  m_cab_entity->_getManager()->destroyEntity(m_cab_entity);
263  m_cab_entity = nullptr;
264 
265  delete m_cab_mesh; // Unloads the ManualMesh resource; do this last
266  m_cab_mesh = nullptr;
267  }
268  catch (...)
269  {
270  HandleGenericException(WhereFrom(this, "destroying cab mesh"), HANDLEGENERICEXCEPTION_LOGFILE);
271  }
272  }
273 
274  // Delete old dashboard RTT
275  if (m_renderdash != nullptr)
276  {
277  try
278  {
279  delete m_renderdash;
280  }
281  catch (...)
282  {
283  HandleGenericException(WhereFrom(this, "destroying renderdash"), HANDLEGENERICEXCEPTION_LOGFILE);
284  }
285  }
286 }
287 
289 {
290  return m_actor;
291 }
292 
293 void RoR::GfxActor::SetMaterialFlareOn(int flare_index, bool state_on)
294 {
295  for (FlareMaterial& entry: m_flare_materials)
296  {
297  if (entry.flare_index != flare_index)
298  {
299  continue;
300  }
301 
302  const int num_techniques = static_cast<int>(entry.mat_instance->getNumTechniques());
303  for (int i = 0; i < num_techniques; i++)
304  {
305  Ogre::Technique* tech = entry.mat_instance->getTechnique(i);
306  if (!tech)
307  continue;
308 
309  if (tech->getSchemeName() == "glow")
310  {
311  // glowing technique
312  // set the ambient value as glow amount
313  Ogre::Pass* p = tech->getPass(0);
314  if (!p)
315  continue;
316 
317  if (state_on)
318  {
319  p->setSelfIllumination(entry.emissive_color);
320  p->setAmbient(Ogre::ColourValue::White);
321  p->setDiffuse(Ogre::ColourValue::White);
322  }
323  else
324  {
325  p->setSelfIllumination(Ogre::ColourValue::ZERO);
326  p->setAmbient(Ogre::ColourValue::Black);
327  p->setDiffuse(Ogre::ColourValue::Black);
328  }
329  }
330  else
331  {
332  // normal technique
333  Ogre::Pass* p = tech->getPass(0);
334  if (!p)
335  continue;
336 
337  Ogre::TextureUnitState* tus = p->getTextureUnitState(0);
338  if (!tus)
339  continue;
340 
341  if (tus->getNumFrames() < 2)
342  continue;
343 
344  int frame = state_on ? 1 : 0;
345 
346  tus->setCurrentFrame(frame);
347 
348  if (state_on)
349  p->setSelfIllumination(entry.emissive_color);
350  else
351  p->setSelfIllumination(Ogre::ColourValue::ZERO);
352  }
353  } // for each technique
354  }
355 }
356 
357 void RoR::GfxActor::RegisterCabMaterial(Ogre::MaterialPtr mat, Ogre::MaterialPtr mat_trans)
358 {
359  // Material instances of this actor
360  m_cab_mat_visual = mat;
361  m_cab_mat_visual_trans = mat_trans;
362 
363  if (mat->getTechnique(0)->getNumPasses() == 1)
364  return; // No emissive pass -> nothing to do.
365 
366  m_cab_mat_template_emissive = mat->clone("CabMaterialEmissive-" + mat->getName(), true, m_custom_resource_group);
367 
368  m_cab_mat_template_plain = mat->clone("CabMaterialPlain-" + mat->getName(), true, m_custom_resource_group);
369  m_cab_mat_template_plain->getTechnique(0)->removePass(1);
370  m_cab_mat_template_plain->compile();
371 }
372 
374 {
375  if (m_cab_mat_template_emissive.isNull()) // Both this and '_plain' are only set when emissive pass is present.
376  return;
377 
378  // NOTE: Updating material in-place like this is probably inefficient,
379  // but in order to maintain all the existing material features working together,
380  // we need to avoid any material swapping on runtime. ~ only_a_ptr, 05/2017
381  Ogre::MaterialPtr template_mat = (state_on) ? m_cab_mat_template_emissive : m_cab_mat_template_plain;
382  Ogre::Technique* dest_tech = m_cab_mat_visual->getTechnique(0);
383  Ogre::Technique* templ_tech = template_mat->getTechnique(0);
384  dest_tech->removeAllPasses();
385  for (unsigned short i = 0; i < templ_tech->getNumPasses(); ++i)
386  {
387  Ogre::Pass* templ_pass = templ_tech->getPass(i);
388  Ogre::Pass* dest_pass = dest_tech->createPass();
389  *dest_pass = *templ_pass; // Copy the pass! Reference: http://www.ogre3d.org/forums/viewtopic.php?f=2&t=83453
390  }
391  m_cab_mat_visual->compile();
392 }
393 
395 {
396  if (state == m_vidcam_state)
397  {
398  return; // Nothing to do.
399  }
400 
401  const bool enable = (state == VideoCamState::VCSTATE_ENABLED_ONLINE);
402  for (VideoCamera vidcam: m_videocameras)
403  {
404  if (vidcam.vcam_render_target != nullptr)
405  {
406  vidcam.vcam_render_target->setActive(enable);
407  if (enable)
408  vidcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(vidcam.vcam_render_tex->getName());
409  else
410  vidcam.vcam_material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(vidcam.vcam_off_tex_name);
411  continue;
412  }
413 
414  if (vidcam.vcam_render_window != nullptr)
415  {
416  vidcam.vcam_render_window->setActive(enable);
417  }
418  }
419  m_vidcam_state = state;
420 }
421 
423 {
424  if (m_vidcam_state != VideoCamState::VCSTATE_ENABLED_ONLINE)
425  return;
426 
427  for (VideoCamera& vidcam: m_videocameras)
428  {
429 #ifdef USE_CAELUM
430  // caelum needs to know that we changed the cameras
431  SkyManager* sky = App::GetGameContext()->GetTerrain()->getSkyManager();
432  if ((sky != nullptr) && (RoR::App::app_state->getEnum<AppState>() == RoR::AppState::SIMULATION))
433  {
434  sky->NotifySkyCameraChanged(vidcam.vcam_ogre_camera);
435  }
436 #endif // USE_CAELUM
437 
438  if ((vidcam.vcam_type == VCTYPE_MIRROR_PROP_LEFT)
439  || (vidcam.vcam_type == VCTYPE_MIRROR_PROP_RIGHT))
440  {
441  // Mirror prop - special processing.
442  float mirror_angle = 0.f;
443  Ogre::Vector3 offset(Ogre::Vector3::ZERO);
444  if (vidcam.vcam_type == VCTYPE_MIRROR_PROP_LEFT)
445  {
446  mirror_angle = m_actor->ar_left_mirror_angle;
447  offset = Ogre::Vector3(0.07f, -0.22f, 0);
448  }
449  else
450  {
451  mirror_angle = m_actor->ar_right_mirror_angle;
452  offset = Ogre::Vector3(0.07f, +0.22f, 0);
453  }
454 
455  Ogre::Vector3 normal = vidcam.vcam_prop_scenenode->getOrientation()
456  * Ogre::Vector3(cos(mirror_angle), sin(mirror_angle), 0.0f);
457  Ogre::Vector3 center = vidcam.vcam_prop_scenenode->getPosition()
458  + vidcam.vcam_prop_scenenode->getOrientation() * offset;
459  Ogre::Radian roll = Ogre::Degree(360)
460  - Ogre::Radian(asin(m_actor->getDirection().dotProduct(Ogre::Vector3::UNIT_Y)));
461 
462  Ogre::Plane plane = Ogre::Plane(normal, center);
463  Ogre::Vector3 project = plane.projectVector(App::GetCameraManager()->GetCameraNode()->getPosition() - center);
464 
465  vidcam.vcam_ogre_camera->setPosition(center);
466  vidcam.vcam_ogre_camera->lookAt(App::GetCameraManager()->GetCameraNode()->getPosition() - 2.0f * project);
467  vidcam.vcam_ogre_camera->roll(roll);
468  vidcam.vcam_ogre_camera->setNearClipDistance(1); // fixes Caelum sky rendered black on mirrors
469 
470  continue; // Done processing mirror prop.
471  }
472 
473  // update the texture now, otherwise shuttering
474  if (vidcam.vcam_render_target != nullptr)
475  vidcam.vcam_render_target->update();
476 
477  if (vidcam.vcam_render_window != nullptr)
478  vidcam.vcam_render_window->update();
479 
480  // get the normal of the camera plane now
481  const Ogre::Vector3 abs_pos_center = m_simbuf.simbuf_nodes[vidcam.vcam_node_center].AbsPosition;
482  const Ogre::Vector3 abs_pos_z = m_simbuf.simbuf_nodes[vidcam.vcam_node_dir_z].AbsPosition;
483  const Ogre::Vector3 abs_pos_y = m_simbuf.simbuf_nodes[vidcam.vcam_node_dir_y].AbsPosition;
484  Ogre::Vector3 normal = (-(abs_pos_center - abs_pos_z)).crossProduct(-(abs_pos_center - abs_pos_y));
485  normal.normalise();
486 
487  // add user set offset
488  Ogre::Vector3 pos = m_simbuf.simbuf_nodes[vidcam.vcam_node_alt_pos].AbsPosition +
489  (vidcam.vcam_pos_offset.x * normal) +
490  (vidcam.vcam_pos_offset.y * (abs_pos_center - abs_pos_y)) +
491  (vidcam.vcam_pos_offset.z * (abs_pos_center - abs_pos_z));
492 
493  //avoid the camera roll
494  // camup orientates to frustrum of world by default -> rotating the cam related to trucks yaw, lets bind cam rotation videocamera base (nref,ny,nz) as frustum
495  // could this be done faster&better with a plane setFrustumExtents ?
496  Ogre::Vector3 frustumUP = abs_pos_center - abs_pos_y;
497  frustumUP.normalise();
498  vidcam.vcam_ogre_camera->setFixedYawAxis(true, frustumUP);
499 
500  if (vidcam.vcam_type == VCTYPE_MIRROR)
501  {
502  //rotate the normal of the mirror by user rotation setting so it reflects correct
503  normal = vidcam.vcam_rotation * normal;
504  // merge camera direction and reflect it on our plane
505  vidcam.vcam_ogre_camera->setDirection((pos - App::GetCameraManager()->GetCameraNode()->getPosition()).reflect(normal));
506  }
507  else if (vidcam.vcam_type == VCTYPE_VIDEOCAM)
508  {
509  // rotate the camera according to the nodes orientation and user rotation
510  Ogre::Vector3 refx = abs_pos_z - abs_pos_center;
511  refx.normalise();
512  Ogre::Vector3 refy = abs_pos_center - abs_pos_y;
513  refy.normalise();
514  Ogre::Quaternion rot = Ogre::Quaternion(-refx, -refy, -normal);
515  vidcam.vcam_ogre_camera->setOrientation(rot * vidcam.vcam_rotation); // rotate the camera orientation towards the calculated cam direction plus user rotation
516  }
517  else if (vidcam.vcam_type == VCTYPE_TRACKING_VIDEOCAM)
518  {
519  normal = m_simbuf.simbuf_nodes[vidcam.vcam_node_lookat].AbsPosition - pos;
520  normal.normalise();
521  Ogre::Vector3 refx = abs_pos_z - abs_pos_center;
522  refx.normalise();
523  // why does this flip ~2-3� around zero orientation and only with trackercam. back to slower crossproduct calc, a bit slower but better .. sigh
524  // Ogre::Vector3 refy = abs_pos_center - abs_pos_y;
525  Ogre::Vector3 refy = refx.crossProduct(normal);
526  refy.normalise();
527  Ogre::Quaternion rot = Ogre::Quaternion(-refx, -refy, -normal);
528  vidcam.vcam_ogre_camera->setOrientation(rot * vidcam.vcam_rotation); // rotate the camera orientation towards the calculated cam direction plus user rotation
529  }
530 
531  if (vidcam.vcam_debug_node != nullptr)
532  {
533  vidcam.vcam_debug_node->setPosition(pos);
534  vidcam.vcam_debug_node->setOrientation(vidcam.vcam_ogre_camera->getOrientation());
535  }
536 
537  // set the new position
538  vidcam.vcam_ogre_camera->setPosition(pos);
539  }
540 }
541 
543 {
544  float water_height = 0.f; // Unused if terrain has no water
545  if (App::GetGameContext()->GetTerrain()->getWater() != nullptr)
546  {
548  }
549 
550  for (NodeGfx& nfx: m_gfx_nodes)
551  {
552  const node_t& n = m_actor->ar_nodes[nfx.nx_node_idx];
553 
554  // 'Wet' effects - water dripping and vapour
555  if (nfx.nx_may_get_wet && !nfx.nx_no_particles)
556  {
557  // Getting out of water?
558  if (!n.nd_under_water && nfx.nx_under_water_prev)
559  {
560  nfx.nx_wet_time_sec = 0.f;
561  }
562 
563  // Dripping water?
564  if (nfx.nx_wet_time_sec != -1)
565  {
566  nfx.nx_wet_time_sec += dt_sec;
567  if (nfx.nx_wet_time_sec > 5.f) // Dries off in 5 sec
568  {
569  nfx.nx_wet_time_sec = -1.f;
570  }
571  else if (nfx.nx_may_get_wet)
572  {
573  if (m_particles_drip != nullptr)
574  {
575  m_particles_drip->allocDrip(n.AbsPosition, n.Velocity, nfx.nx_wet_time_sec); // Dripping water particles
576  }
577  if (nfx.nx_is_hot && m_particles_dust != nullptr)
578  {
579  m_particles_dust->allocVapour(n.AbsPosition, n.Velocity, nfx.nx_wet_time_sec); // Water vapour particles
580  }
581  }
582  }
583  }
584 
585  // Water splash and ripple
586  if (n.nd_under_water && !nfx.nx_no_particles)
587  {
588  if ((water_height - n.AbsPosition.y < 0.2f) && (n.Velocity.squaredLength() > 4.f))
589  {
590  if (m_particles_splash)
591  {
592  m_particles_splash->allocSplash(n.AbsPosition, n.Velocity);
593  }
594  if (m_particles_ripple)
595  {
596  m_particles_ripple->allocRipple(n.AbsPosition, n.Velocity);
597  }
598  }
599  }
600 
601  // Ground collision (dust, sparks, tyre smoke, clumps...)
602  if (!nfx.nx_no_particles && n.nd_has_ground_contact && n.nd_last_collision_gm != nullptr)
603  {
604  switch (n.nd_last_collision_gm->fx_type)
605  {
607  if (m_particles_dust != nullptr)
608  {
609  m_particles_dust->malloc(n.AbsPosition, n.Velocity / 2.0, n.nd_last_collision_gm->fx_colour);
610  }
611  break;
612 
614  if (m_particles_clump != nullptr && n.Velocity.squaredLength() > 1.f)
615  {
616  m_particles_clump->allocClump(n.AbsPosition, n.Velocity / 2.0, n.nd_last_collision_gm->fx_colour);
617  }
618  break;
619 
620  case Collisions::FX_HARD:
621  if (n.nd_tyre_node) // skidmarks and tyre smoke
622  {
623  const float SMOKE_THRESHOLD = 8.f;
624  const float SCREECH_THRESHOLD = 5.f;
625  const float slipv = n.nd_last_collision_slip.length();
626  const float screech = std::min(slipv, n.nd_avg_collision_slip) - SCREECH_THRESHOLD;
627  if (screech > 0.0f)
628  {
629  SOUND_MODULATE(m_actor, SS_MOD_SCREETCH, screech / SCREECH_THRESHOLD);
631  }
632  if (m_particles_dust != nullptr && n.nd_avg_collision_slip > SMOKE_THRESHOLD)
633  {
634  m_particles_dust->allocSmoke(n.AbsPosition, n.Velocity);
635  }
636  }
637  else if (!nfx.nx_no_sparks) // Not a wheel => sparks
638  {
639  if (m_particles_sparks != nullptr && n.nd_avg_collision_slip > 5.f)
640  {
641  if (n.nd_last_collision_slip.squaredLength() > 25.f)
642  {
643  m_particles_sparks->allocSparks(n.AbsPosition, n.Velocity);
644  }
645  }
646  }
647  break;
648 
649  default:;
650  }
651  }
652 
653  nfx.nx_under_water_prev = n.nd_under_water;
654  }
655 }
656 
657 const ImU32 BEAM_COLOR (0xff556633); // All colors are in ABGR format (alpha, blue, green, red)
658 const float BEAM_THICKNESS (1.2f);
659 const ImU32 BEAM_BROKEN_COLOR (0xff4466dd);
660 const float BEAM_BROKEN_THICKNESS (1.8f);
661 const ImU32 BEAM_HYDRO_COLOR (0xff55a3e0);
662 const float BEAM_HYDRO_THICKNESS (1.4f);
663 const ImU32 BEAM_STRENGTH_TEXT_COLOR (0xffcfd0cc);
664 const ImU32 BEAM_STRESS_TEXT_COLOR (0xff58bbfc);
665 // TODO: commands cannot be distinguished on runtime
666 
667 const ImU32 NODE_COLOR (0xff44ddff);
668 const float NODE_RADIUS (2.f);
669 const ImU32 NODE_TEXT_COLOR (0xffcccccf); // node ID text color
670 const ImU32 NODE_MASS_TEXT_COLOR (0xff77bb66);
671 const ImU32 NODE_IMMOVABLE_COLOR (0xff0033ff);
672 const float NODE_IMMOVABLE_RADIUS (2.8f);
673 
675 {
676  if (m_debug_view == DebugViewType::DEBUGVIEW_NONE && !m_actor->ar_physics_paused)
677  {
678  return; // Nothing to do
679  }
680 
681  // Var
682  ImVec2 screen_size = ImGui::GetIO().DisplaySize;
683  World2ScreenConverter world2screen(
684  App::GetCameraManager()->GetCamera()->getViewMatrix(true), App::GetCameraManager()->GetCamera()->getProjectionMatrix(), Ogre::Vector2(screen_size.x, screen_size.y));
685 
686  ImDrawList* drawlist = GetImDummyFullscreenWindow();
687 
688  if (m_actor->ar_physics_paused && !App::GetGuiManager()->IsGuiHidden())
689  {
690  // Should we replace this circle with a proper bounding box?
691  Ogre::Vector3 pos_xyz = world2screen.Convert(m_actor->getPosition());
692  if (pos_xyz.z < 0.f)
693  {
694  ImVec2 pos(pos_xyz.x, pos_xyz.y);
695 
696  float radius = 0.0f;
697  for (int i = 0; i < m_actor->ar_num_nodes; ++i)
698  {
699  radius = std::max(radius, pos_xyz.distance(world2screen.Convert(m_actor->ar_nodes[i].AbsPosition)));
700  }
701 
702  drawlist->AddCircleFilled(pos, radius * 1.05f, 0x22222222, 36);
703  }
704  }
705 
706  // Skeleton display. NOTE: Order matters, it determines Z-ordering on render
707  if ((m_debug_view == DebugViewType::DEBUGVIEW_SKELETON) ||
708  (m_debug_view == DebugViewType::DEBUGVIEW_NODES) ||
709  (m_debug_view == DebugViewType::DEBUGVIEW_BEAMS))
710  {
711  // Beams
712  const beam_t* beams = m_actor->ar_beams;
713  const size_t num_beams = static_cast<size_t>(m_actor->ar_num_beams);
714  for (size_t i = 0; i < num_beams; ++i)
715  {
716  if (App::diag_hide_wheels->getBool() &&
717  (beams[i].p1->nd_tyre_node || beams[i].p1->nd_rim_node ||
718  beams[i].p2->nd_tyre_node || beams[i].p2->nd_rim_node))
719  continue;
720 
721  Ogre::Vector3 pos1 = world2screen.Convert(beams[i].p1->AbsPosition);
722  Ogre::Vector3 pos2 = world2screen.Convert(beams[i].p2->AbsPosition);
723 
724  if ((pos1.z < 0.f) && (pos2.z < 0.f))
725  {
726  ImVec2 pos1xy(pos1.x, pos1.y);
727  ImVec2 pos2xy(pos2.x, pos2.y);
728 
729  if (beams[i].bm_broken)
730  {
731  if (!App::diag_hide_broken_beams->getBool())
732  {
733  drawlist->AddLine(pos1xy, pos2xy, BEAM_BROKEN_COLOR, BEAM_BROKEN_THICKNESS);
734  }
735  }
736  else if (beams[i].bm_type == BEAM_HYDRO)
737  {
738  if (!beams[i].bm_disabled)
739  {
740  drawlist->AddLine(pos1xy, pos2xy, BEAM_HYDRO_COLOR, BEAM_HYDRO_THICKNESS);
741  }
742  }
743  else
744  {
745  ImU32 color = BEAM_COLOR;
746  if (!App::diag_hide_beam_stress->getBool())
747  {
748  if (beams[i].stress > 0)
749  {
750  float stress_ratio = pow(beams[i].stress / beams[i].maxposstress, 2.0f);
751  float s = std::min(stress_ratio, 1.0f);
752  color = Ogre::ColourValue(0.2f * (1 + 2.0f * s), 0.4f * (1.0f - s), 0.33f, 1.0f).getAsABGR();
753  }
754  else if (beams[i].stress < 0)
755  {
756  float stress_ratio = pow(beams[i].stress / beams[i].maxnegstress, 2.0f);
757  float s = std::min(stress_ratio, 1.0f);
758  color = Ogre::ColourValue(0.2f, 0.4f * (1.0f - s), 0.33f * (1 + 1.0f * s), 1.0f).getAsABGR();
759  }
760  }
761  drawlist->AddLine(pos1xy, pos2xy, color, BEAM_THICKNESS);
762  }
763  }
764  }
765 
766  if (!App::diag_hide_nodes->getBool())
767  {
768  // Nodes
769  const node_t* nodes = m_actor->ar_nodes;
770  const size_t num_nodes = static_cast<size_t>(m_actor->ar_num_nodes);
771  for (size_t i = 0; i < num_nodes; ++i)
772  {
773  if (App::diag_hide_wheels->getBool() && (nodes[i].nd_tyre_node || nodes[i].nd_rim_node))
774  continue;
775 
776  Ogre::Vector3 pos_xyz = world2screen.Convert(nodes[i].AbsPosition);
777 
778  if (pos_xyz.z < 0.f)
779  {
780  ImVec2 pos(pos_xyz.x, pos_xyz.y);
781  if (nodes[i].nd_immovable)
782  {
783  drawlist->AddCircleFilled(pos, NODE_IMMOVABLE_RADIUS, NODE_IMMOVABLE_COLOR);
784  }
785  else
786  {
787  drawlist->AddCircleFilled(pos, NODE_RADIUS, NODE_COLOR);
788  }
789  }
790  }
791 
792  // Node info; drawn after nodes to have higher Z-order
793  if ((m_debug_view == DebugViewType::DEBUGVIEW_NODES) || (m_debug_view == DebugViewType::DEBUGVIEW_BEAMS))
794  {
795  for (size_t i = 0; i < num_nodes; ++i)
796  {
797  if ((App::diag_hide_wheels->getBool() || App::diag_hide_wheel_info->getBool()) &&
798  (nodes[i].nd_tyre_node || nodes[i].nd_rim_node))
799  continue;
800 
801  Ogre::Vector3 pos = world2screen.Convert(nodes[i].AbsPosition);
802 
803  if (pos.z < 0.f)
804  {
805  ImVec2 pos_xy(pos.x, pos.y);
806  Str<25> id_buf;
807  id_buf << nodes[i].pos;
808  drawlist->AddText(pos_xy, NODE_TEXT_COLOR, id_buf.ToCStr());
809 
810  if (m_debug_view != DebugViewType::DEBUGVIEW_BEAMS)
811  {
812  char mass_buf[50];
813  snprintf(mass_buf, 50, "|%.1fKg", nodes[i].mass);
814  ImVec2 offset = ImGui::CalcTextSize(id_buf.ToCStr());
815  drawlist->AddText(ImVec2(pos.x + offset.x, pos.y), NODE_MASS_TEXT_COLOR, mass_buf);
816  }
817  }
818  }
819  }
820  }
821 
822  // Beam-info: drawn after beams to have higher Z-order
823  if (m_debug_view == DebugViewType::DEBUGVIEW_BEAMS)
824  {
825  for (size_t i = 0; i < num_beams; ++i)
826  {
827  if ((App::diag_hide_wheels->getBool() || App::diag_hide_wheel_info->getBool()) &&
828  (beams[i].p1->nd_tyre_node || beams[i].p1->nd_rim_node ||
829  beams[i].p2->nd_tyre_node || beams[i].p2->nd_rim_node))
830  continue;
831 
832  // Position
833  Ogre::Vector3 world_pos = (beams[i].p1->AbsPosition + beams[i].p2->AbsPosition) / 2.f;
834  Ogre::Vector3 pos_xyz = world2screen.Convert(world_pos);
835  if (pos_xyz.z >= 0.f)
836  {
837  continue; // Behind the camera
838  }
839  ImVec2 pos(pos_xyz.x, pos_xyz.y);
840 
841  // Strength is usually in thousands or millions - we shorten it.
842  const size_t BUF_LEN = 50;
843  char buf[BUF_LEN];
844  if (beams[i].strength >= 1000000000000.f)
845  {
846  snprintf(buf, BUF_LEN, "%.1fT", (beams[i].strength / 1000000000000.f));
847  }
848  else if (beams[i].strength >= 1000000000.f)
849  {
850  snprintf(buf, BUF_LEN, "%.1fG", (beams[i].strength / 1000000000.f));
851  }
852  else if (beams[i].strength >= 1000000.f)
853  {
854  snprintf(buf, BUF_LEN, "%.1fM", (beams[i].strength / 1000000.f));
855  }
856  else if (beams[i].strength >= 1000.f)
857  {
858  snprintf(buf, BUF_LEN, "%.1fK", (beams[i].strength / 1000.f));
859  }
860  else
861  {
862  snprintf(buf, BUF_LEN, "%.1f", beams[i].strength);
863  }
864  const ImVec2 stren_text_size = ImGui::CalcTextSize(buf);
865  drawlist->AddText(ImVec2(pos.x - stren_text_size.x, pos.y), BEAM_STRENGTH_TEXT_COLOR, buf);
866 
867  // Stress
868  snprintf(buf, BUF_LEN, "|%.1f", beams[i].stress);
869  drawlist->AddText(pos, BEAM_STRESS_TEXT_COLOR, buf);
870  }
871  }
872  } else if (m_debug_view == DebugViewType::DEBUGVIEW_WHEELS)
873  {
874  // Wheels
875  const wheel_t* wheels = m_actor->ar_wheels;
876  const size_t num_wheels = static_cast<size_t>(m_actor->ar_num_wheels);
877  for (int i = 0; i < num_wheels; i++)
878  {
879  Ogre::Vector3 axis = wheels[i].wh_axis_node_1->RelPosition - wheels[i].wh_axis_node_0->RelPosition;
880  axis.normalise();
881 
882  // Wheel axle
883  {
884  Ogre::Vector3 pos1_xyz = world2screen.Convert(wheels[i].wh_axis_node_1->AbsPosition);
885  if (pos1_xyz.z < 0.f)
886  {
887  ImVec2 pos(pos1_xyz.x, pos1_xyz.y);
888  drawlist->AddCircleFilled(pos, NODE_IMMOVABLE_RADIUS, NODE_COLOR);
889  }
890  Ogre::Vector3 pos2_xyz = world2screen.Convert(wheels[i].wh_axis_node_0->AbsPosition);
891  if (pos2_xyz.z < 0.f)
892  {
893  ImVec2 pos(pos2_xyz.x, pos2_xyz.y);
894  drawlist->AddCircleFilled(pos, NODE_IMMOVABLE_RADIUS, NODE_COLOR);
895  }
896  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
897  {
898  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
899  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
900  drawlist->AddLine(pos1xy, pos2xy, BEAM_COLOR, BEAM_BROKEN_THICKNESS);
901  }
902 
903  // Id, Speed, Torque
904  Ogre::Vector3 pos_xyz = pos1_xyz.midPoint(pos2_xyz);
905  if (pos_xyz.z < 0.f)
906  {
907  float v = ImGui::GetTextLineHeightWithSpacing();
908  ImVec2 pos(pos_xyz.x, pos_xyz.y);
909  Str<25> wheel_id_buf;
910  wheel_id_buf << "Id: " << (i + 1);
911  float h1 = ImGui::CalcTextSize(wheel_id_buf.ToCStr()).x / 2.0f;
912  drawlist->AddText(ImVec2(pos.x - h1, pos.y), NODE_TEXT_COLOR, wheel_id_buf.ToCStr());
913  Str<25> rpm_buf;
914  rpm_buf << "Speed: " << static_cast<int>(Round(wheels[i].debug_rpm)) << " rpm";
915  float h2 = ImGui::CalcTextSize(rpm_buf.ToCStr()).x / 2.0f;
916  drawlist->AddText(ImVec2(pos.x - h2, pos.y + v), NODE_TEXT_COLOR, rpm_buf.ToCStr());
917  Str<25> torque_buf;
918  torque_buf << "Torque: " << static_cast<int>(Round(wheels[i].debug_torque)) << " Nm";
919  float h3 = ImGui::CalcTextSize(torque_buf.ToCStr()).x / 2.0f;
920  drawlist->AddText(ImVec2(pos.x - h3, pos.y + v + v), NODE_TEXT_COLOR, torque_buf.ToCStr());
921  }
922  }
923 
924  Ogre::Vector3 rradius = wheels[i].wh_arm_node->RelPosition - wheels[i].wh_near_attach_node->RelPosition;
925 
926  // Reference arm
927  {
928  Ogre::Vector3 pos1_xyz = world2screen.Convert(wheels[i].wh_arm_node->AbsPosition);
929  if (pos1_xyz.z < 0.f)
930  {
931  ImVec2 pos(pos1_xyz.x, pos1_xyz.y);
932  drawlist->AddCircleFilled(pos, NODE_IMMOVABLE_RADIUS, NODE_IMMOVABLE_COLOR);
933  }
934  Ogre::Vector3 pos2_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition);
935  if (pos2_xyz.z < 0.f)
936  {
937  ImVec2 pos(pos2_xyz.x, pos2_xyz.y);
938  drawlist->AddCircleFilled(pos, NODE_IMMOVABLE_RADIUS, NODE_COLOR);
939  }
940  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
941  {
942  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
943  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
944  drawlist->AddLine(pos1xy, pos2xy, BEAM_BROKEN_COLOR, BEAM_BROKEN_THICKNESS);
945  }
946  }
947 
948  Ogre::Vector3 radius = Ogre::Plane(axis, wheels[i].wh_near_attach_node->RelPosition).projectVector(rradius);
949 
950  // Projection plane
951 #if 0
952  {
953  Ogre::Vector3 up = axis.crossProduct(radius);
954  Ogre::Vector3 pos1_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition + radius - up);
955  Ogre::Vector3 pos2_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition + radius + up);
956  Ogre::Vector3 pos3_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition - radius + up);
957  Ogre::Vector3 pos4_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition - radius - up);
958  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f) && (pos3_xyz.z < 0.f) && (pos4_xyz.z < 0.f))
959  {
960  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
961  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
962  ImVec2 pos3xy(pos3_xyz.x, pos3_xyz.y);
963  ImVec2 pos4xy(pos4_xyz.x, pos4_xyz.y);
964  drawlist->AddQuadFilled(pos1xy, pos2xy, pos3xy, pos4xy, 0x22888888);
965  }
966  }
967 #endif
968  // Projected reference arm & error arm
969  {
970  Ogre::Vector3 pos1_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition);
971  Ogre::Vector3 pos2_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition + radius);
972  Ogre::Vector3 pos3_xyz = world2screen.Convert(wheels[i].wh_arm_node->AbsPosition);
973  if (pos2_xyz.z < 0.f)
974  {
975  ImVec2 pos(pos2_xyz.x, pos2_xyz.y);
976  drawlist->AddCircleFilled(pos, NODE_IMMOVABLE_RADIUS, 0x660033ff);
977  }
978  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
979  {
980  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
981  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
982  drawlist->AddLine(pos1xy, pos2xy, 0x664466dd, BEAM_BROKEN_THICKNESS);
983  }
984  if ((pos2_xyz.z < 0.f) && (pos3_xyz.z < 0.f))
985  {
986  ImVec2 pos1xy(pos2_xyz.x, pos2_xyz.y);
987  ImVec2 pos2xy(pos3_xyz.x, pos3_xyz.y);
988  drawlist->AddLine(pos1xy, pos2xy, 0x99555555, BEAM_BROKEN_THICKNESS);
989  }
990  }
991  // Reaction torque
992  {
993  Ogre::Vector3 cforce = wheels[i].debug_scaled_cforce;
994  {
995  Ogre::Vector3 pos1_xyz = world2screen.Convert(wheels[i].wh_arm_node->AbsPosition);
996  Ogre::Vector3 pos2_xyz = world2screen.Convert(wheels[i].wh_arm_node->AbsPosition - cforce);
997  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
998  {
999  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1000  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
1001  drawlist->AddLine(pos1xy, pos2xy, 0xffcc4444, BEAM_BROKEN_THICKNESS);
1002  }
1003  }
1004  {
1005  Ogre::Vector3 pos1_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition);
1006  Ogre::Vector3 pos2_xyz = world2screen.Convert(wheels[i].wh_near_attach_node->AbsPosition + cforce);
1007  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
1008  {
1009  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1010  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
1011  drawlist->AddLine(pos1xy, pos2xy, 0xffcc4444, BEAM_BROKEN_THICKNESS);
1012  }
1013  }
1014  }
1015 
1016  // Wheel slip
1017  {
1018  Ogre::Vector3 m = wheels[i].wh_axis_node_0->AbsPosition.midPoint(wheels[i].wh_axis_node_1->AbsPosition);
1019  Ogre::Real w = wheels[i].wh_axis_node_0->AbsPosition.distance(m);
1020  Ogre::Vector3 u = - axis.crossProduct(m_simbuf.simbuf_direction);
1021  if (!wheels[i].debug_force.isZeroLength())
1022  {
1023  u = - wheels[i].debug_force.normalisedCopy();
1024  }
1025  Ogre::Vector3 f = axis.crossProduct(u);
1026  Ogre::Vector3 a = - axis * w + f * std::max(w, wheels[i].wh_radius * 0.5f);
1027  Ogre::Vector3 b = + axis * w + f * std::max(w, wheels[i].wh_radius * 0.5f);
1028  Ogre::Vector3 c = + axis * w - f * std::max(w, wheels[i].wh_radius * 0.5f);
1029  Ogre::Vector3 d = - axis * w - f * std::max(w, wheels[i].wh_radius * 0.5f);
1030  Ogre::Quaternion r = Ogre::Quaternion::IDENTITY;
1031  if (wheels[i].debug_vel.length() > 1.0f)
1032  {
1033  r = Ogre::Quaternion(f.angleBetween(wheels[i].debug_vel), u);
1034  }
1035  Ogre::Vector3 pos1_xyz = world2screen.Convert(m - u * wheels[i].wh_radius + r * a);
1036  Ogre::Vector3 pos2_xyz = world2screen.Convert(m - u * wheels[i].wh_radius + r * b);
1037  Ogre::Vector3 pos3_xyz = world2screen.Convert(m - u * wheels[i].wh_radius + r * c);
1038  Ogre::Vector3 pos4_xyz = world2screen.Convert(m - u * wheels[i].wh_radius + r * d);
1039  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f) && (pos3_xyz.z < 0.f) && (pos4_xyz.z < 0.f))
1040  {
1041  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1042  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
1043  ImVec2 pos3xy(pos3_xyz.x, pos3_xyz.y);
1044  ImVec2 pos4xy(pos4_xyz.x, pos4_xyz.y);
1045  if (!wheels[i].debug_force.isZeroLength())
1046  {
1047  float slipv = wheels[i].debug_slip.length();
1048  float wheelv = wheels[i].debug_vel.length();
1049  float slip_ratio = std::min(slipv, wheelv) / std::max(1.0f, wheelv);
1050  float scale = pow(slip_ratio, 2);
1051  ImU32 col = Ogre::ColourValue(scale, 1.0f - scale, 0.0f, 0.2f).getAsABGR();
1052  drawlist->AddQuadFilled(pos1xy, pos2xy, pos3xy, pos4xy, col);
1053  }
1054  else
1055  {
1056  drawlist->AddQuadFilled(pos1xy, pos2xy, pos3xy, pos4xy, 0x55555555);
1057  }
1058  }
1059  }
1060 
1061  // Slip vector
1062  if (!wheels[i].debug_vel.isZeroLength())
1063  {
1064  Ogre::Vector3 m = wheels[i].wh_axis_node_0->AbsPosition.midPoint(wheels[i].wh_axis_node_1->AbsPosition);
1065  Ogre::Real w = wheels[i].wh_axis_node_0->AbsPosition.distance(m);
1066  Ogre::Vector3 d = axis.crossProduct(m_simbuf.simbuf_direction) * wheels[i].wh_radius;
1067  Ogre::Real slipv = wheels[i].debug_slip.length();
1068  Ogre::Real wheelv = wheels[i].debug_vel.length();
1069  Ogre::Vector3 s = wheels[i].debug_slip * (std::min(slipv, wheelv) / std::max(1.0f, wheelv)) / slipv;
1070  Ogre::Vector3 pos1_xyz = world2screen.Convert(m + d);
1071  Ogre::Vector3 pos2_xyz = world2screen.Convert(m + d + s * std::max(w, wheels[i].wh_radius * 0.5f));
1072  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
1073  {
1074  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1075  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
1076  drawlist->AddLine(pos1xy, pos2xy, 0xbb4466dd, BEAM_BROKEN_THICKNESS);
1077  }
1078  }
1079 
1080  // Down force
1081  {
1082  Ogre::Real f = wheels[i].debug_force.length();
1083  Ogre::Real mass = m_actor->getTotalMass(false) * num_wheels;
1084  Ogre::Vector3 normalised_force = wheels[i].debug_force.normalisedCopy() * std::min(f / mass, 1.0f);
1085  Ogre::Vector3 m = wheels[i].wh_axis_node_0->AbsPosition.midPoint(wheels[i].wh_axis_node_1->AbsPosition);
1086  Ogre::Vector3 pos5_xyz = world2screen.Convert(m);
1087  Ogre::Vector3 pos6_xyz = world2screen.Convert(m + normalised_force * wheels[i].wh_radius);
1088  if ((pos5_xyz.z < 0.f) && (pos6_xyz.z < 0.f))
1089  {
1090  ImVec2 pos1xy(pos5_xyz.x, pos5_xyz.y);
1091  ImVec2 pos2xy(pos6_xyz.x, pos6_xyz.y);
1092  drawlist->AddLine(pos1xy, pos2xy, 0x88888888, BEAM_BROKEN_THICKNESS);
1093  }
1094  }
1095  }
1096  } else if (m_debug_view == DebugViewType::DEBUGVIEW_SHOCKS)
1097  {
1098  // Shocks
1099  const beam_t* beams = m_actor->ar_beams;
1100  const size_t num_beams = static_cast<size_t>(m_actor->ar_num_beams);
1101  std::set<int> node_ids;
1102  for (size_t i = 0; i < num_beams; ++i)
1103  {
1104  if (beams[i].bm_type != BEAM_HYDRO)
1105  continue;
1106  if (!(beams[i].bounded == SHOCK1 || beams[i].bounded == SHOCK2 || beams[i].bounded == SHOCK3))
1107  continue;
1108 
1109  Ogre::Vector3 pos1_xyz = world2screen.Convert(beams[i].p1->AbsPosition);
1110  Ogre::Vector3 pos2_xyz = world2screen.Convert(beams[i].p2->AbsPosition);
1111 
1112  if (pos1_xyz.z < 0.f)
1113  {
1114  node_ids.insert(beams[i].p1->pos);
1115  }
1116  if (pos2_xyz.z < 0.f)
1117  {
1118  node_ids.insert(beams[i].p2->pos);
1119  }
1120 
1121  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
1122  {
1123  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1124  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
1125 
1126  ImU32 beam_color = (beams[i].bounded == SHOCK1) ? BEAM_HYDRO_COLOR : BEAM_BROKEN_COLOR;
1127 
1128  drawlist->AddLine(pos1xy, pos2xy, beam_color, 1.25f * BEAM_BROKEN_THICKNESS);
1129  }
1130  }
1131  for (auto id : node_ids)
1132  {
1133  Ogre::Vector3 pos_xyz = world2screen.Convert(m_actor->ar_nodes[id].AbsPosition);
1134  if (pos_xyz.z < 0.f)
1135  {
1136  ImVec2 pos_xy(pos_xyz.x, pos_xyz.y);
1137  drawlist->AddCircleFilled(pos_xy, NODE_RADIUS, NODE_COLOR);
1138  // Node info
1139  Str<25> id_buf;
1140  id_buf << id;
1141  drawlist->AddText(pos_xy, NODE_TEXT_COLOR, id_buf.ToCStr());
1142  }
1143  }
1144  for (size_t i = 0; i < num_beams; ++i)
1145  {
1146  if (beams[i].bm_type != BEAM_HYDRO)
1147  continue;
1148  if (!(beams[i].bounded == SHOCK1 || beams[i].bounded == SHOCK2 || beams[i].bounded == SHOCK3))
1149  continue;
1150 
1151  Ogre::Vector3 pos1_xyz = world2screen.Convert(beams[i].p1->AbsPosition);
1152  Ogre::Vector3 pos2_xyz = world2screen.Convert(beams[i].p2->AbsPosition);
1153  Ogre::Vector3 pos_xyz = pos1_xyz.midPoint(pos2_xyz);
1154 
1155  if (pos_xyz.z < 0.f)
1156  {
1157  // Shock info
1158  float diff = beams[i].p1->AbsPosition.distance(beams[i].p2->AbsPosition) - beams[i].L;
1159  ImU32 text_color = (diff < 0.0f) ? 0xff66ee66 : 0xff8888ff;
1160  float bound = (diff < 0.0f) ? beams[i].shortbound : beams[i].longbound;
1161  float ratio = Ogre::Math::Clamp(diff / (bound * beams[i].L), -2.0f, +2.0f);
1162 
1163  float v = ImGui::GetTextLineHeightWithSpacing();
1164  ImVec2 pos(pos_xyz.x, pos_xyz.y - v - v);
1165  Str<25> len_buf;
1166  len_buf << "L: " << static_cast<int>(Round(std::abs(ratio) * 100.0f)) << " %";
1167  float h1 = ImGui::CalcTextSize(len_buf.ToCStr()).x / 2.0f;
1168  drawlist->AddText(ImVec2(pos.x - h1, pos.y), text_color, len_buf.ToCStr());
1169  Str<25> spring_buf;
1170  spring_buf << "S: " << static_cast<int>(Round(beams[i].debug_k)) << " N";
1171  float h2 = ImGui::CalcTextSize(spring_buf.ToCStr()).x / 2.0f;
1172  drawlist->AddText(ImVec2(pos.x - h2, pos.y + v), text_color, spring_buf.ToCStr());
1173  Str<25> damp_buf;
1174  damp_buf << "D: " << static_cast<int>(Round(beams[i].debug_d)) << " N";
1175  float h3 = ImGui::CalcTextSize(damp_buf.ToCStr()).x / 2.0f;
1176  drawlist->AddText(ImVec2(pos.x - h3, pos.y + v + v), text_color, damp_buf.ToCStr());
1177  char vel_buf[25];
1178  snprintf(vel_buf, 25, "V: %.2f m/s", beams[i].debug_v);
1179  float h4 = ImGui::CalcTextSize(vel_buf).x / 2.0f;
1180  drawlist->AddText(ImVec2(pos.x - h4, pos.y + v + v + v), text_color, vel_buf);
1181  }
1182  }
1183  } else if (m_debug_view == DebugViewType::DEBUGVIEW_ROTATORS)
1184  {
1185  // Rotators
1186  const node_t* nodes = m_actor->ar_nodes;
1187  const rotator_t* rotators = m_actor->ar_rotators;
1188  const size_t num_rotators = static_cast<size_t>(m_actor->ar_num_rotators);
1189  for (int i = 0; i < num_rotators; i++)
1190  {
1191  Ogre::Vector3 pos1_xyz = world2screen.Convert(nodes[rotators[i].axis1].AbsPosition);
1192  Ogre::Vector3 pos2_xyz = world2screen.Convert(nodes[rotators[i].axis2].AbsPosition);
1193 
1194  // Rotator axle
1195  {
1196  if (pos1_xyz.z < 0.f)
1197  {
1198  ImVec2 pos(pos1_xyz.x, pos1_xyz.y);
1199  drawlist->AddCircleFilled(pos, 1.25f * NODE_IMMOVABLE_RADIUS, NODE_COLOR);
1200  Str<25> id_buf;
1201  id_buf << nodes[rotators[i].axis1].pos;
1202  drawlist->AddText(pos, NODE_TEXT_COLOR, id_buf.ToCStr());
1203  }
1204  if (pos2_xyz.z < 0.f)
1205  {
1206  ImVec2 pos(pos2_xyz.x, pos2_xyz.y);
1207  drawlist->AddCircleFilled(pos, 1.25f * NODE_IMMOVABLE_RADIUS, NODE_COLOR);
1208  Str<25> id_buf;
1209  id_buf << nodes[rotators[i].axis2].pos;
1210  drawlist->AddText(pos, NODE_TEXT_COLOR, id_buf.ToCStr());
1211  }
1212  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
1213  {
1214  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1215  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
1216  drawlist->AddLine(pos1xy, pos2xy, BEAM_COLOR, 1.25f * BEAM_BROKEN_THICKNESS);
1217  }
1218 
1219  // Id, RPM, Error
1220  Ogre::Vector3 pos_xyz = pos1_xyz.midPoint(pos2_xyz);
1221  if (pos_xyz.z < 0.f)
1222  {
1223  float v = ImGui::GetTextLineHeightWithSpacing();
1224  ImVec2 pos(pos_xyz.x, pos_xyz.y);
1225  Str<25> rotator_id_buf;
1226  rotator_id_buf << "Id: " << (i + 1);
1227  float h1 = ImGui::CalcTextSize(rotator_id_buf.ToCStr()).x / 2.0f;
1228  drawlist->AddText(ImVec2(pos.x - h1, pos.y), NODE_TEXT_COLOR, rotator_id_buf.ToCStr());
1229  char angle_buf[25];
1230  snprintf(angle_buf, 25, "Rate: %.1f rpm", 60.0f * rotators[i].debug_rate / Ogre::Math::TWO_PI);
1231  float h2 = ImGui::CalcTextSize(angle_buf).x / 2.0f;
1232  drawlist->AddText(ImVec2(pos.x - h2, pos.y + v), NODE_TEXT_COLOR, angle_buf);
1233  char aerror_buf[25];
1234  snprintf(aerror_buf, 25, "Error: %.1f mrad", 1000.0f * std::abs(rotators[i].debug_aerror));
1235  float h3 = ImGui::CalcTextSize(aerror_buf).x / 2.0f;
1236  drawlist->AddText(ImVec2(pos.x - h3, pos.y + v + v), NODE_TEXT_COLOR, aerror_buf);
1237  }
1238  }
1239 
1240  // Reference arms
1241  for (int j = 0; j < 4; j++)
1242  {
1243  // Base plate
1244  {
1245  ImU32 node_color = Ogre::ColourValue(0.33f, 0.33f, 0.33f, j < 2 ? 1.0f : 0.5f).getAsABGR();
1246  ImU32 beam_color = Ogre::ColourValue(0.33f, 0.33f, 0.33f, j < 2 ? 1.0f : 0.5f).getAsABGR();
1247 
1248  Ogre::Vector3 pos3_xyz = world2screen.Convert(nodes[rotators[i].nodes1[j]].AbsPosition);
1249  if (pos3_xyz.z < 0.f)
1250  {
1251  ImVec2 pos(pos3_xyz.x, pos3_xyz.y);
1252  drawlist->AddCircleFilled(pos, NODE_RADIUS, node_color);
1253  Str<25> id_buf;
1254  id_buf << nodes[rotators[i].nodes1[j]].pos;
1255  drawlist->AddText(pos, NODE_TEXT_COLOR, id_buf.ToCStr());
1256  }
1257  if ((pos1_xyz.z < 0.f) && (pos3_xyz.z < 0.f))
1258  {
1259  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1260  ImVec2 pos2xy(pos3_xyz.x, pos3_xyz.y);
1261  drawlist->AddLine(pos1xy, pos2xy, beam_color, BEAM_BROKEN_THICKNESS);
1262  }
1263  }
1264  // Rotating plate
1265  {
1266  ImU32 node_color = Ogre::ColourValue(1.00f, 0.87f, 0.27f, j < 2 ? 1.0f : 0.5f).getAsABGR();
1267  ImU32 beam_color = Ogre::ColourValue(0.88f, 0.64f, 0.33f, j < 2 ? 1.0f : 0.5f).getAsABGR();
1268 
1269  Ogre::Vector3 pos3_xyz = world2screen.Convert(nodes[rotators[i].nodes2[j]].AbsPosition);
1270  if (pos3_xyz.z < 0.f)
1271  {
1272  ImVec2 pos(pos3_xyz.x, pos3_xyz.y);
1273  drawlist->AddCircleFilled(pos, NODE_RADIUS, node_color);
1274  Str<25> id_buf;
1275  id_buf << nodes[rotators[i].nodes2[j]].pos;
1276  drawlist->AddText(pos, NODE_TEXT_COLOR, id_buf.ToCStr());
1277  }
1278  if ((pos2_xyz.z < 0.f) && (pos3_xyz.z < 0.f))
1279  {
1280  ImVec2 pos1xy(pos2_xyz.x, pos2_xyz.y);
1281  ImVec2 pos2xy(pos3_xyz.x, pos3_xyz.y);
1282  drawlist->AddLine(pos1xy, pos2xy, beam_color, BEAM_BROKEN_THICKNESS);
1283  }
1284  }
1285  }
1286 
1287  // Projection plane
1288  {
1289  Ogre::Vector3 mid = nodes[rotators[i].axis1].AbsPosition.midPoint(nodes[rotators[i].axis2].AbsPosition);
1290  Ogre::Vector3 axis = nodes[rotators[i].axis1].RelPosition - nodes[rotators[i].axis2].RelPosition;
1291  Ogre::Vector3 perp = axis.perpendicular();
1292  axis.normalise();
1293 
1294  const int steps = 64;
1295  Ogre::Plane plane = Ogre::Plane(axis, mid);
1296 
1297  Ogre::Real radius1 = 0.0f;
1298  Ogre::Real offset1 = 0.0f;
1299  for (int k = 0; k < 2; k++)
1300  {
1301  Ogre::Vector3 r1 = nodes[rotators[i].nodes1[k]].RelPosition - nodes[rotators[i].axis1].RelPosition;
1302  Ogre::Real r = plane.projectVector(r1).length();
1303  if (r > radius1)
1304  {
1305  radius1 = r;
1306  offset1 = plane.getDistance(nodes[rotators[i].nodes1[k]].AbsPosition);
1307  }
1308  }
1309  std::vector<ImVec2> pos1_xy;
1310  for (int k = 0; k < steps; k++)
1311  {
1312  Ogre::Quaternion rotation(Ogre::Radian(((float)k / steps) * Ogre::Math::TWO_PI), axis);
1313  Ogre::Vector3 pos_xyz = world2screen.Convert(mid + axis * offset1 + rotation * perp * radius1);
1314  if (pos_xyz.z < 0.f)
1315  {
1316  pos1_xy.push_back(ImVec2(pos_xyz.x, pos_xyz.y));
1317  }
1318  }
1319  if (!pos1_xy.empty())
1320  {
1321  drawlist->AddConvexPolyFilled(pos1_xy.data(), static_cast<int>(pos1_xy.size()), 0x33666666);
1322  }
1323 
1324  Ogre::Real radius2 = 0.0f;
1325  Ogre::Real offset2 = 0.0f;
1326  for (int k = 0; k < 2; k++)
1327  {
1328  Ogre::Vector3 r2 = nodes[rotators[i].nodes2[k]].RelPosition - nodes[rotators[i].axis2].RelPosition;
1329  Ogre::Real r = plane.projectVector(r2).length();
1330  if (r > radius2)
1331  {
1332  radius2 = r;
1333  offset2 = plane.getDistance(nodes[rotators[i].nodes2[k]].AbsPosition);
1334  }
1335  }
1336  std::vector<ImVec2> pos2_xy;
1337  for (int k = 0; k < steps; k++)
1338  {
1339  Ogre::Quaternion rotation(Ogre::Radian(((float)k / steps) * Ogre::Math::TWO_PI), axis);
1340  Ogre::Vector3 pos_xyz = world2screen.Convert(mid + axis * offset2 + rotation * perp * radius2);
1341  if (pos_xyz.z < 0.f)
1342  {
1343  pos2_xy.push_back(ImVec2(pos_xyz.x, pos_xyz.y));
1344  }
1345  }
1346  if (!pos2_xy.empty())
1347  {
1348  drawlist->AddConvexPolyFilled(pos2_xy.data(), static_cast<int>(pos2_xy.size()), 0x1155a3e0);
1349  }
1350 
1351  for (int k = 0; k < 2; k++)
1352  {
1353  // Projected and rotated base plate arms (theory vectors)
1354  Ogre::Vector3 ref1 = plane.projectVector(nodes[rotators[i].nodes1[k]].AbsPosition - mid);
1355  Ogre::Vector3 th1 = Ogre::Quaternion(Ogre::Radian(rotators[i].angle), axis) * ref1;
1356  {
1357  Ogre::Vector3 pos1_xyz = world2screen.Convert(mid + axis * offset1);
1358  Ogre::Vector3 pos2_xyz = world2screen.Convert(mid + axis * offset1 + th1);
1359  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
1360  {
1361  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1362  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
1363  drawlist->AddLine(pos1xy, pos2xy, 0x44888888, BEAM_BROKEN_THICKNESS);
1364  }
1365  }
1366  // Projected rotation plate arms
1367  Ogre::Vector3 ref2 = plane.projectVector(nodes[rotators[i].nodes2[k]].AbsPosition - mid);
1368  {
1369  Ogre::Vector3 pos1_xyz = world2screen.Convert(mid + axis * offset2);
1370  Ogre::Vector3 pos2_xyz = world2screen.Convert(mid + axis * offset2 + ref2);
1371  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f))
1372  {
1373  ImVec2 pos1xy(pos1_xyz.x, pos1_xyz.y);
1374  ImVec2 pos2xy(pos2_xyz.x, pos2_xyz.y);
1375  drawlist->AddLine(pos1xy, pos2xy, 0x4455a3e0, BEAM_BROKEN_THICKNESS);
1376  }
1377  }
1378  // Virtual plate connections
1379  th1.normalise();
1380  Ogre::Real radius = std::min(radius1, radius2);
1381  Ogre::Vector3 pos3_xyz = world2screen.Convert(mid + axis * offset1 + th1 * radius);
1382  Ogre::Vector3 pos4_xyz = world2screen.Convert(mid + axis * offset2 + th1 * radius);
1383  if ((pos3_xyz.z < 0.f) && (pos4_xyz.z < 0.f))
1384  {
1385  ImVec2 pos1xy(pos3_xyz.x, pos3_xyz.y);
1386  ImVec2 pos2xy(pos4_xyz.x, pos4_xyz.y);
1387  drawlist->AddLine(pos1xy, pos2xy, BEAM_COLOR, BEAM_BROKEN_THICKNESS);
1388  }
1389  }
1390  }
1391  }
1392  } else if (m_debug_view == DebugViewType::DEBUGVIEW_SLIDENODES)
1393  {
1394  // Slide nodes
1395  const node_t* nodes = m_actor->ar_nodes;
1396  std::set<int> node_ids;
1397  for (auto railgroup : m_actor->m_railgroups)
1398  {
1399  for (auto railsegment : railgroup->rg_segments)
1400  {
1401  Ogre::Vector3 pos1 = world2screen.Convert(railsegment.rs_beam->p1->AbsPosition);
1402  Ogre::Vector3 pos2 = world2screen.Convert(railsegment.rs_beam->p2->AbsPosition);
1403 
1404  if (pos1.z < 0.f)
1405  {
1406  node_ids.insert(railsegment.rs_beam->p1->pos);
1407  }
1408  if (pos2.z < 0.f)
1409  {
1410  node_ids.insert(railsegment.rs_beam->p2->pos);
1411  }
1412  if ((pos1.z < 0.f) && (pos2.z < 0.f))
1413  {
1414  ImVec2 pos1xy(pos1.x, pos1.y);
1415  ImVec2 pos2xy(pos2.x, pos2.y);
1416 
1417  drawlist->AddLine(pos1xy, pos2xy, BEAM_COLOR, BEAM_BROKEN_THICKNESS);
1418  }
1419  }
1420  }
1421  for (auto id : node_ids)
1422  {
1423  Ogre::Vector3 pos_xyz = world2screen.Convert(nodes[id].AbsPosition);
1424  if (pos_xyz.z < 0.f)
1425  {
1426  ImVec2 pos_xy(pos_xyz.x, pos_xyz.y);
1427  drawlist->AddCircleFilled(pos_xy, NODE_RADIUS, NODE_COLOR);
1428  // Node info
1429  Str<25> id_buf;
1430  id_buf << id;
1431  drawlist->AddText(pos_xy, NODE_TEXT_COLOR, id_buf.ToCStr());
1432  }
1433  }
1434  for (auto slidenode : m_actor->m_slidenodes)
1435  {
1436  auto id = slidenode.GetSlideNodeId();
1437  Ogre::Vector3 pos_xyz = world2screen.Convert(nodes[id].AbsPosition);
1438 
1439  if (pos_xyz.z < 0.f)
1440  {
1441  ImVec2 pos(pos_xyz.x, pos_xyz.y);
1442  drawlist->AddCircleFilled(pos, NODE_IMMOVABLE_RADIUS, NODE_IMMOVABLE_COLOR);
1443  // Node info
1444  Str<25> id_buf;
1445  id_buf << id;
1446  drawlist->AddText(pos, NODE_TEXT_COLOR, id_buf.ToCStr());
1447  }
1448  }
1449  } else if (m_debug_view == DebugViewType::DEBUGVIEW_SUBMESH)
1450  {
1451  // Cabs
1452  const node_t* nodes = m_actor->ar_nodes;
1453  const auto cabs = m_actor->ar_cabs;
1454  const auto num_cabs = m_actor->ar_num_cabs;
1455  const auto buoycabs = m_actor->ar_buoycabs;
1456  const auto num_buoycabs = m_actor->ar_num_buoycabs;
1457  const auto collcabs = m_actor->ar_collcabs;
1458  const auto num_collcabs = m_actor->ar_num_collcabs;
1459 
1460  std::vector<std::pair<float, int>> render_cabs;
1461  for (int i = 0; i < num_cabs; i++)
1462  {
1463  Ogre::Vector3 pos1_xyz = world2screen.Convert(nodes[cabs[i*3+0]].AbsPosition);
1464  Ogre::Vector3 pos2_xyz = world2screen.Convert(nodes[cabs[i*3+1]].AbsPosition);
1465  Ogre::Vector3 pos3_xyz = world2screen.Convert(nodes[cabs[i*3+2]].AbsPosition);
1466  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f) && (pos3_xyz.z < 0.f))
1467  {
1468  float depth = pos1_xyz.z;
1469  depth = std::max(depth, pos2_xyz.z);
1470  depth = std::max(depth, pos3_xyz.z);
1471  render_cabs.push_back({depth, i});
1472  }
1473  }
1474  std::sort(render_cabs.begin(), render_cabs.end());
1475 
1476  // Cabs and contacters (which are part of a cab)
1477  std::vector<int> node_ids;
1478  for (auto render_cab : render_cabs)
1479  {
1480  int i = render_cab.second;
1481  bool coll = std::find(collcabs, collcabs + num_collcabs, i) != (collcabs + num_collcabs);
1482  bool buoy = std::find(buoycabs, buoycabs + num_buoycabs, i) != (buoycabs + num_buoycabs);
1483 
1484  ImU32 fill_color = Ogre::ColourValue(0.5f * coll, 0.5f * !buoy, 0.5f * (coll ^ buoy), 0.27f).getAsABGR();
1485  ImU32 beam_color = Ogre::ColourValue(0.5f * coll, 0.5f * !buoy, 0.5f * (coll ^ buoy), 0.53f).getAsABGR();
1486 
1487  Ogre::Vector3 pos1_xyz = world2screen.Convert(nodes[cabs[i*3+0]].AbsPosition);
1488  Ogre::Vector3 pos2_xyz = world2screen.Convert(nodes[cabs[i*3+1]].AbsPosition);
1489  Ogre::Vector3 pos3_xyz = world2screen.Convert(nodes[cabs[i*3+2]].AbsPosition);
1490  if ((pos1_xyz.z < 0.f) && (pos2_xyz.z < 0.f) && (pos3_xyz.z < 0.f))
1491  {
1492  ImVec2 pos1_xy(pos1_xyz.x, pos1_xyz.y);
1493  ImVec2 pos2_xy(pos2_xyz.x, pos2_xyz.y);
1494  ImVec2 pos3_xy(pos3_xyz.x, pos3_xyz.y);
1495  drawlist->AddTriangleFilled(pos1_xy, pos2_xy, pos3_xy, fill_color);
1496  drawlist->AddTriangle(pos1_xy, pos2_xy, pos3_xy, beam_color, BEAM_THICKNESS);
1497  }
1498  for (int k = 0; k < 3; k++)
1499  {
1500  int id = cabs[i*3+k];
1501  if (std::find(node_ids.begin(), node_ids.end(), id) == node_ids.end())
1502  {
1503  Ogre::Vector3 pos_xyz = world2screen.Convert(nodes[id].AbsPosition);
1504  if (pos_xyz.z < 0.f)
1505  {
1506  ImVec2 pos_xy(pos_xyz.x, pos_xyz.y);
1507  drawlist->AddCircleFilled(pos_xy, NODE_RADIUS, nodes[id].nd_contacter ? 0xbb0033ff : 0x88888888);
1508  // Node info
1509  Str<25> id_buf;
1510  id_buf << id;
1511  drawlist->AddText(pos_xy, NODE_TEXT_COLOR, id_buf.ToCStr());
1512  }
1513  node_ids.push_back(id);
1514  }
1515  }
1516  }
1517  }
1518 }
1519 
1521 {
1522  if (m_debug_view == DebugViewType::DEBUGVIEW_NONE)
1523  m_debug_view = m_last_debug_view;
1524  else
1525  m_debug_view = DebugViewType::DEBUGVIEW_NONE;
1526 }
1527 
1529 {
1530  if (dv == DebugViewType::DEBUGVIEW_WHEELS && m_actor->ar_num_wheels == 0 ||
1531  dv == DebugViewType::DEBUGVIEW_SHOCKS && m_actor->ar_num_shocks == 0 ||
1532  dv == DebugViewType::DEBUGVIEW_ROTATORS && m_actor->ar_num_rotators == 0 ||
1533  dv == DebugViewType::DEBUGVIEW_SLIDENODES && m_actor->hasSlidenodes() == 0 ||
1534  dv == DebugViewType::DEBUGVIEW_SUBMESH && m_actor->ar_num_cabs == 0)
1535  {
1537  }
1538 
1539  m_debug_view = dv;
1541  {
1542  m_last_debug_view = dv;
1543  }
1544 }
1545 
1547 {
1548  switch (m_debug_view)
1549  {
1554  {
1555  if (m_actor->ar_num_wheels) SetDebugView(DebugViewType::DEBUGVIEW_WHEELS);
1556  else if (m_actor->ar_num_shocks) SetDebugView(DebugViewType::DEBUGVIEW_SHOCKS);
1557  else if (m_actor->ar_num_rotators) SetDebugView(DebugViewType::DEBUGVIEW_ROTATORS);
1558  else if (m_actor->hasSlidenodes()) SetDebugView(DebugViewType::DEBUGVIEW_SLIDENODES);
1559  else if (m_actor->ar_num_cabs) SetDebugView(DebugViewType::DEBUGVIEW_SUBMESH);
1560  else SetDebugView(DebugViewType::DEBUGVIEW_SKELETON);
1561  break;
1562  }
1564  {
1565  if (m_actor->ar_num_shocks) SetDebugView(DebugViewType::DEBUGVIEW_SHOCKS);
1566  else if (m_actor->ar_num_rotators) SetDebugView(DebugViewType::DEBUGVIEW_ROTATORS);
1567  else if (m_actor->hasSlidenodes()) SetDebugView(DebugViewType::DEBUGVIEW_SLIDENODES);
1568  else if (m_actor->ar_num_cabs) SetDebugView(DebugViewType::DEBUGVIEW_SUBMESH);
1569  else SetDebugView(DebugViewType::DEBUGVIEW_SKELETON);
1570  break;
1571  }
1573  {
1574  if (m_actor->ar_num_rotators) SetDebugView(DebugViewType::DEBUGVIEW_ROTATORS);
1575  else if (m_actor->hasSlidenodes()) SetDebugView(DebugViewType::DEBUGVIEW_SLIDENODES);
1576  else if (m_actor->ar_num_cabs) SetDebugView(DebugViewType::DEBUGVIEW_SUBMESH);
1577  else SetDebugView(DebugViewType::DEBUGVIEW_SKELETON);
1578  break;
1579  }
1581  {
1582  if (m_actor->hasSlidenodes()) SetDebugView(DebugViewType::DEBUGVIEW_SLIDENODES);
1583  else if (m_actor->ar_num_cabs) SetDebugView(DebugViewType::DEBUGVIEW_SUBMESH);
1584  else SetDebugView(DebugViewType::DEBUGVIEW_SKELETON);
1585  break;
1586  }
1588  {
1589  if (m_actor->ar_num_cabs) SetDebugView(DebugViewType::DEBUGVIEW_SUBMESH);
1590  else SetDebugView(DebugViewType::DEBUGVIEW_SKELETON);
1591  break;
1592  }
1594  default:;
1595  }
1596 }
1597 
1599 {
1600  for (BeamGfx& rod: m_gfx_beams)
1601  {
1602  rod.rod_scenenode->setVisible(rod.rod_is_visible);
1603  if (!rod.rod_is_visible)
1604  continue;
1605 
1606  NodeSB* nodes1 = this->GetSimNodeBuffer();
1607  Ogre::Vector3 pos1 = nodes1[rod.rod_node1].AbsPosition;
1608  NodeSB* nodes2 = rod.rod_target_actor->GetGfxActor()->GetSimNodeBuffer();
1609  Ogre::Vector3 pos2 = nodes2[rod.rod_node2].AbsPosition;
1610 
1611  // Classic method
1612  float beam_diameter = static_cast<float>(rod.rod_diameter_mm) * 0.001;
1613  float beam_length = pos1.distance(pos2);
1614 
1615  rod.rod_scenenode->setPosition(pos1.midPoint(pos2));
1616  rod.rod_scenenode->setScale(beam_diameter, beam_length, beam_diameter);
1617  rod.rod_scenenode->setOrientation(GfxActor::SpecialGetRotationTo(Ogre::Vector3::UNIT_Y, (pos1 - pos2)));
1618  }
1619 }
1620 
1621 Ogre::Quaternion RoR::GfxActor::SpecialGetRotationTo(const Ogre::Vector3& src, const Ogre::Vector3& dest)
1622 {
1623  // Based on Stan Melax's article in Game Programming Gems
1624  Ogre::Quaternion q;
1625  // Copy, since cannot modify local
1626  Ogre::Vector3 v0 = src;
1627  Ogre::Vector3 v1 = dest;
1628  v0.normalise();
1629  v1.normalise();
1630 
1631  // NB if the crossProduct approaches zero, we get unstable because ANY axis will do
1632  // when v0 == -v1
1633  Ogre::Real d = v0.dotProduct(v1);
1634  // If dot == 1, vectors are the same
1635  if (d >= 1.0f)
1636  {
1637  return Ogre::Quaternion::IDENTITY;
1638  }
1639  if (d < (1e-6f - 1.0f))
1640  {
1641  // Generate an axis
1642  Ogre::Vector3 axis = Ogre::Vector3::UNIT_X.crossProduct(src);
1643  if (axis.isZeroLength()) // pick another if colinear
1644  axis = Ogre::Vector3::UNIT_Y.crossProduct(src);
1645  axis.normalise();
1646  q.FromAngleAxis(Ogre::Radian(Ogre::Math::PI), axis);
1647  }
1648  else
1649  {
1650  Ogre::Real s = fast_sqrt((1 + d) * 2);
1651  if (s == 0)
1652  return Ogre::Quaternion::IDENTITY;
1653 
1654  Ogre::Vector3 c = v0.crossProduct(v1);
1655  Ogre::Real invs = 1 / s;
1656 
1657  q.x = c.x * invs;
1658  q.y = c.y * invs;
1659  q.z = c.z * invs;
1660  q.w = s * 0.5;
1661  }
1662  return q;
1663 }
1664 
1665 void RoR::GfxActor::ScaleActor(Ogre::Vector3 relpos, float ratio)
1666 {
1667  for (BeamGfx& rod: m_gfx_beams)
1668  {
1669  float diameter2 = static_cast<float>(rod.rod_diameter_mm) * (ratio*1000.f);
1670  rod.rod_diameter_mm = static_cast<uint16_t>(diameter2);
1671  }
1672 
1673  // props and stuff
1674  // TOFIX: care about prop positions as well!
1675  for (Prop& prop: m_props)
1676  {
1677  if (prop.pp_scene_node)
1678  prop.pp_scene_node->scale(ratio, ratio, ratio);
1679 
1680  if (prop.pp_wheel_scene_node)
1681  {
1682  prop.pp_wheel_scene_node->scale(ratio, ratio, ratio);
1683  prop.pp_wheel_pos = relpos + (prop.pp_wheel_pos - relpos) * ratio;
1684  }
1685 
1686  if (prop.pp_beacon_scene_node[0])
1687  prop.pp_beacon_scene_node[0]->scale(ratio, ratio, ratio);
1688 
1689  if (prop.pp_beacon_scene_node[1])
1690  prop.pp_beacon_scene_node[1]->scale(ratio, ratio, ratio);
1691 
1692  if (prop.pp_beacon_scene_node[2])
1693  prop.pp_beacon_scene_node[2]->scale(ratio, ratio, ratio);
1694 
1695  if (prop.pp_beacon_scene_node[3])
1696  prop.pp_beacon_scene_node[3]->scale(ratio, ratio, ratio);
1697  }
1698 
1699  // Old cab mesh
1700  if (m_cab_mesh)
1701  {
1702  m_cab_mesh->ScaleFlexObj(ratio);
1703  }
1704 }
1705 
1707 {
1708  if (m_gfx_beams_parent_scenenode == nullptr)
1709  {
1710  return; // Vehicle has no visual softbody beams -> nothing to do.
1711  }
1712 
1713  // NOTE: We don't use Ogre::SceneNode::setVisible() for performance reasons:
1714  // 1. That function traverses all attached Entities and updates their visibility - too much overhead
1715  // 2. For OGRE up to 1.9 (I don't know about 1.10+) OGRE developers recommended to detach rather than hide.
1716  // ~ only_a_ptr, 12/2017
1717  if (visible && !m_gfx_beams_parent_scenenode->isInSceneGraph())
1718  {
1719  App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->addChild(m_gfx_beams_parent_scenenode);
1720  }
1721  else if (!visible && m_gfx_beams_parent_scenenode->isInSceneGraph())
1722  {
1723  App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->removeChild(m_gfx_beams_parent_scenenode);
1724  }
1725 }
1726 
1728 {
1729  // PLEASE maintain the same order as in `struct ActorSB`
1730 
1731  // Gameplay state
1732  m_simbuf.simbuf_actor_state = m_actor->ar_state;
1733  m_simbuf.simbuf_physics_paused = m_actor->ar_physics_paused;
1734  m_simbuf.simbuf_cur_cinecam = m_actor->ar_current_cinecam;
1735  m_simbuf.simbuf_net_username = m_actor->m_net_username;
1736  m_simbuf.simbuf_net_colornum = m_actor->m_net_color_num;
1737  m_simbuf.simbuf_driveable = m_actor->ar_driveable;
1738 
1739  // Movement
1740  m_simbuf.simbuf_pos = m_actor->getRotationCenter();
1741  m_simbuf.simbuf_node0_velo = m_actor->ar_nodes[0].Velocity;
1742  m_simbuf.simbuf_rotation = m_actor->getRotation();
1743  m_simbuf.simbuf_direction = m_actor->getDirection();
1744  m_simbuf.simbuf_wheel_speed = m_actor->ar_wheel_speed;
1745  m_simbuf.simbuf_top_speed = m_actor->ar_top_speed;
1746  m_simbuf.simbuf_aabb = m_actor->ar_bounding_box;
1747  if (m_actor->ar_num_cameras > 0)
1748  {
1749  m_simbuf.simbuf_camera0_pos_node = m_actor->ar_camera_node_pos[0];
1750  m_simbuf.simbuf_camera0_roll_node = m_actor->ar_camera_node_roll[0];
1751  }
1752 
1753  // Elements: nodes
1754  m_simbuf.simbuf_nodes.resize(m_actor->ar_num_nodes);
1755  for (int i = 0; i < m_actor->ar_num_nodes; ++i)
1756  {
1757  auto node = m_actor->ar_nodes[i];
1758  m_simbuf.simbuf_nodes[i].AbsPosition = node.AbsPosition;
1759  m_simbuf.simbuf_nodes[i].nd_has_contact = node.nd_has_ground_contact || node.nd_has_mesh_contact;
1760  }
1761 
1762  for (NodeGfx& nx: m_gfx_nodes)
1763  {
1764  m_simbuf.simbuf_nodes[nx.nx_node_idx].nd_is_wet = (nx.nx_wet_time_sec != -1.f);
1765  }
1766 
1767  // Elements: beams
1768  for (BeamGfx& rod: m_gfx_beams)
1769  {
1770  const beam_t& beam = m_actor->ar_beams[rod.rod_beam_index];
1771  rod.rod_node1 = static_cast<uint16_t>(beam.p1->pos);
1772  rod.rod_node2 = static_cast<uint16_t>(beam.p2->pos);
1773  if (beam.bm_inter_actor)
1774  {
1775  rod.rod_target_actor = beam.bm_locked_actor;
1776  }
1777  rod.rod_is_visible = !beam.bm_disabled && !beam.bm_broken;
1778  }
1779 
1780  // Elements: airbrakes
1781  m_simbuf.simbuf_airbrakes.resize(m_actor->ar_airbrakes.size());
1782  for (size_t i=0; i< m_actor->ar_airbrakes.size(); ++i)
1783  {
1784  m_simbuf.simbuf_airbrakes[i].simbuf_ab_ratio = m_actor->ar_airbrakes[i]->getRatio();
1785  }
1786 
1787  // Elements: Command keys
1788  for (int i = 1; i <= MAX_COMMANDS; ++i) // BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
1789  {
1790  m_simbuf.simbuf_commandkey[i].simbuf_cmd_value = m_actor->ar_command_key[i].commandValue;
1791  }
1792 
1793  // Elements: Prop animation keys
1794  m_simbuf.simbuf_prop_anim_keys.resize(m_actor->m_prop_anim_key_states.size());
1795  for (size_t i = 0; i < m_actor->m_prop_anim_key_states.size(); ++i)
1796  {
1797  m_simbuf.simbuf_prop_anim_keys[i].simbuf_anim_active = m_actor->m_prop_anim_key_states[i].anim_active;
1798  }
1799 
1800  // Elements: Aeroengines
1801  m_simbuf.simbuf_aeroengines.resize(m_actor->ar_num_aeroengines);
1802  for (int i = 0; i < m_actor->ar_num_aeroengines; ++i)
1803  {
1804  AeroEngine* src = m_actor->ar_aeroengines[i];
1805  AeroEngineSB& dst = m_simbuf.simbuf_aeroengines[i];
1806 
1807  dst.simbuf_ae_type = src->getType();
1808  dst.simbuf_ae_throttle = src->getThrottle();
1809  dst.simbuf_ae_rpm = src->getRPM();
1810  dst.simbuf_ae_rpmpc = src->getRPMpc();
1811  dst.simbuf_ae_rpm = src->getRPM();
1812  dst.simbuf_ae_ignition = src->getIgnition();
1813  dst.simbuf_ae_failed = src->isFailed();
1814 
1816  {
1817  Turboprop* tp = static_cast<Turboprop*>(src);
1818  dst.simbuf_tp_aetorque = (100.0 * tp->indicated_torque / tp->max_torque); // TODO: Code ported as-is from calcAnimators(); what does it do? ~ only_a_ptr, 06/2018
1819  dst.simbuf_tp_aepitch = tp->pitch;
1820  }
1821  else // turbojet
1822  {
1823  Turbojet* tj = static_cast<Turbojet*>(src);
1824  dst.simbuf_tj_afterburn = tj->getAfterburner() != 0.f;
1827  }
1828  }
1829 
1830  // Engine (+drivetrain)
1831  m_simbuf.simbuf_hydro_dir_state = m_actor->ar_hydro_dir_state;
1832  m_simbuf.simbuf_brake = m_actor->ar_brake;
1833  if (m_actor->ar_engine != nullptr)
1834  {
1835  m_simbuf.simbuf_has_engine = true;
1836  m_simbuf.simbuf_gear = m_actor->ar_engine->GetGear();
1837  m_simbuf.simbuf_autoshift = m_actor->ar_engine->getAutoShift();
1838  m_simbuf.simbuf_engine_rpm = m_actor->ar_engine->GetEngineRpm();
1839  m_simbuf.simbuf_engine_turbo_psi= m_actor->ar_engine->GetTurboPsi();
1840  m_simbuf.simbuf_engine_accel = m_actor->ar_engine->GetAcceleration();
1841  m_simbuf.simbuf_engine_torque = m_actor->ar_engine->GetEngineTorque();
1842  m_simbuf.simbuf_inputshaft_rpm = m_actor->ar_engine->GetInputShaftRpm();
1843  m_simbuf.simbuf_drive_ratio = m_actor->ar_engine->GetDriveRatio();
1844  m_simbuf.simbuf_clutch = m_actor->ar_engine->GetClutch();
1845  m_simbuf.simbuf_num_gears = m_actor->ar_engine->getNumGears();
1846  m_simbuf.simbuf_engine_max_rpm = m_actor->ar_engine->getMaxRPM();
1847  }
1848  if (m_actor->m_num_wheel_diffs > 0)
1849  {
1850  m_simbuf.simbuf_diff_type = m_actor->m_wheel_diffs[0]->GetActiveDiffType();
1851  }
1852 
1853  // Tyre pressure
1854  m_simbuf.simbuf_tyre_pressure = m_actor->getTyrePressure().GetCurPressure();
1855  m_simbuf.simbuf_tyre_pressurizing = m_actor->getTyrePressure().IsPressurizing();
1856 
1857  // Effects
1858  m_simbuf.simbuf_lightmask = m_actor->m_lightmask;
1859  m_simbuf.simbuf_smoke_enabled = m_actor->getSmokeEnabled();
1860  m_simbuf.simbuf_parking_brake = m_actor->ar_parking_brake;
1861 
1862  // Aerial
1863  m_simbuf.simbuf_hydro_aileron_state = m_actor->ar_hydro_aileron_state;
1864  m_simbuf.simbuf_hydro_elevator_state = m_actor->ar_hydro_elevator_state;
1865  m_simbuf.simbuf_hydro_aero_rudder_state = m_actor->ar_hydro_rudder_state;
1866  m_simbuf.simbuf_aero_flap_state = m_actor->ar_aerial_flap;
1867  m_simbuf.simbuf_airbrake_state = m_actor->ar_airbrake_intensity;
1868  if (m_actor->ar_num_wings > 4)
1869  {
1870  m_simbuf.simbuf_wing4_aoa = m_actor->ar_wings[4].fa->aoa;
1871  }
1872 
1873  // Autopilot
1874  if (m_actor->ar_autopilot != nullptr)
1875  {
1876  m_simbuf.simbuf_has_autopilot = true;
1877  m_simbuf.simbuf_ap_heading_mode = m_actor->ar_autopilot->GetHeadingMode();
1878  m_simbuf.simbuf_ap_heading_value = m_actor->ar_autopilot->heading;
1879  m_simbuf.simbuf_ap_alt_mode = m_actor->ar_autopilot->GetAltMode();
1880  m_simbuf.simbuf_ap_alt_value = m_actor->ar_autopilot->GetAltValue();
1881  m_simbuf.simbuf_ap_ias_mode = m_actor->ar_autopilot->GetIasMode();
1882  m_simbuf.simbuf_ap_ias_value = m_actor->ar_autopilot->GetIasValue();
1883  m_simbuf.simbuf_ap_gpws_mode = m_actor->ar_autopilot->GetGpwsMode();
1884  m_simbuf.simbuf_ap_ils_available = m_actor->ar_autopilot->IsIlsAvailable();
1885  m_simbuf.simbuf_ap_ils_vdev = m_actor->ar_autopilot->GetVerticalApproachDeviation();
1886  m_simbuf.simbuf_ap_ils_hdev = m_actor->ar_autopilot->GetHorizontalApproachDeviation();
1887  m_simbuf.simbuf_ap_vs_value = m_actor->ar_autopilot->GetVsValue();
1888  }
1889 
1890  m_simbuf.simbuf_speedo_highest_kph = m_actor->ar_guisettings_speedo_max_kph;
1891  m_simbuf.simbuf_speedo_use_engine_max_rpm = m_actor->ar_guisettings_use_engine_max_rpm;
1892  m_simbuf.simbuf_shifter_anim_time = m_actor->ar_guisettings_shifter_anim_time;
1893 
1894 }
1895 
1897 {
1898  return (m_actor->ar_state < ActorState::LOCAL_SLEEPING);
1899 }
1900 
1902 {
1903  if ((m_cab_entity != nullptr) && (m_cab_mesh != nullptr))
1904  {
1905  m_cab_scene_node->setPosition(m_cab_mesh->UpdateFlexObj());
1906  }
1907 }
1908 
1910 {
1911  m_flexwheel_tasks.clear();
1912 
1913  for (WheelGfx& w: m_wheels)
1914  {
1915  if (w.wx_flex_mesh != nullptr && w.wx_flex_mesh->flexitPrepare())
1916  {
1917  auto func = std::function<void()>([this, w]()
1918  {
1919  w.wx_flex_mesh->flexitCompute();
1920  });
1921  auto task_handle = App::GetThreadPool()->RunTask(func);
1922  m_flexwheel_tasks.push_back(task_handle);
1923  }
1924  }
1925 }
1926 
1928 {
1929  for (auto& task: m_flexwheel_tasks)
1930  {
1931  task->join();
1932  }
1933  for (WheelGfx& w: m_wheels)
1934  {
1935  if (w.wx_scenenode != nullptr && w.wx_flex_mesh != nullptr)
1936  {
1937  w.wx_scenenode->setPosition(w.wx_flex_mesh->flexitFinal());
1938  }
1939  }
1940 }
1941 
1943 {
1944  for (WheelGfx& w: m_wheels)
1945  {
1946  if (w.wx_scenenode != nullptr)
1947  {
1948  w.wx_scenenode->setVisible(value);
1949  }
1950  if (w.wx_flex_mesh != nullptr)
1951  {
1952  w.wx_flex_mesh->setVisible(value);
1953  }
1954  }
1955 }
1956 
1957 
1958 int RoR::GfxActor::GetActorId () const { return m_actor->ar_instance_id; }
1959 int RoR::GfxActor::GetActorState () const { return static_cast<int>(m_actor->ar_state); }
1960 
1962 {
1963  const size_t num_airbrakes = m_gfx_airbrakes.size();
1964  for (size_t i=0; i<num_airbrakes; ++i)
1965  {
1966  AirbrakeGfx abx = m_gfx_airbrakes[i];
1967  const float ratio = m_simbuf.simbuf_airbrakes[i].simbuf_ab_ratio;
1968  const float maxangle = m_actor->ar_airbrakes[i]->getMaxAngle();
1969  Ogre::Vector3 ref_node_pos = m_simbuf.simbuf_nodes[m_gfx_airbrakes[i].abx_ref_node].AbsPosition;
1970  Ogre::Vector3 x_node_pos = m_simbuf.simbuf_nodes[m_gfx_airbrakes[i].abx_x_node].AbsPosition;
1971  Ogre::Vector3 y_node_pos = m_simbuf.simbuf_nodes[m_gfx_airbrakes[i].abx_y_node].AbsPosition;
1972 
1973  // -- Ported from `AirBrake::updatePosition()` --
1974  Ogre::Vector3 normal = (y_node_pos - ref_node_pos).crossProduct(x_node_pos - ref_node_pos);
1975  normal.normalise();
1976  //position
1977  Ogre::Vector3 mposition = ref_node_pos + abx.abx_offset.x * (x_node_pos - ref_node_pos) + abx.abx_offset.y * (y_node_pos - ref_node_pos);
1978  abx.abx_scenenode->setPosition(mposition + normal * abx.abx_offset.z);
1979  //orientation
1980  Ogre::Vector3 refx = x_node_pos - ref_node_pos;
1981  refx.normalise();
1982  Ogre::Vector3 refy = refx.crossProduct(normal);
1983  Ogre::Quaternion orientation = Ogre::Quaternion(Ogre::Degree(-ratio * maxangle), (x_node_pos - ref_node_pos).normalisedCopy()) * Ogre::Quaternion(refx, normal, refy);
1984  abx.abx_scenenode->setOrientation(orientation);
1985 
1986  }
1987 }
1988 
1989 // TODO: Also move the data structure + setup code to GfxActor ~ only_a_ptr, 05/2018
1991 {
1992  //update custom particle systems
1993  for (int i = 0; i < m_actor->ar_num_custom_particles; i++)
1994  {
1995  Ogre::Vector3 pos = m_simbuf.simbuf_nodes[m_actor->ar_custom_particles[i].emitterNode].AbsPosition;
1996  Ogre::Vector3 dir = pos - m_simbuf.simbuf_nodes[m_actor->ar_custom_particles[i].directionNode].AbsPosition;
1997  dir = fast_normalise(dir);
1998  m_actor->ar_custom_particles[i].snode->setPosition(pos);
1999  for (int j = 0; j < m_actor->ar_custom_particles[i].psys->getNumEmitters(); j++)
2000  {
2001  m_actor->ar_custom_particles[i].psys->getEmitter(j)->setDirection(dir);
2002  }
2003  }
2004 }
2005 
2007 {
2008  for (int i = 0; i < m_actor->ar_num_aeroengines; i++)
2009  {
2010  m_actor->ar_aeroengines[i]->updateVisuals(this);
2011  }
2012 }
2013 
2015 {
2016  const bool is_remote =
2017  m_simbuf.simbuf_actor_state == ActorState::NETWORKED_OK ||
2018  m_simbuf.simbuf_actor_state == ActorState::NETWORKED_HIDDEN;
2019 
2020  if (App::mp_hide_net_labels->getBool() || (!is_remote && App::mp_hide_own_net_label->getBool()) || App::mp_state->getEnum<MpState>() != MpState::CONNECTED)
2021  {
2022  return;
2023  }
2024 
2025  float vlen = m_simbuf.simbuf_pos.distance(App::GetCameraManager()->GetCameraNode()->getPosition());
2026 
2027  float y_offset = (m_simbuf.simbuf_aabb.getMaximum().y - m_simbuf.simbuf_pos.y) + (vlen / 100.0);
2028  Ogre::Vector3 scene_pos = m_simbuf.simbuf_pos + Ogre::Vector3::UNIT_Y * y_offset;
2029 
2030  App::GetGfxScene()->DrawNetLabel(scene_pos, vlen, m_simbuf.simbuf_net_username, m_simbuf.simbuf_net_colornum);
2031 
2032 }
2033 
2034 void RoR::GfxActor::CalculateDriverPos(Ogre::Vector3& out_pos, Ogre::Quaternion& out_rot)
2035 {
2036  ROR_ASSERT(m_driverseat_prop_index != -1);
2037  Prop* driverseat_prop = &m_props[m_driverseat_prop_index];
2038 
2039  NodeSB* nodes = this->GetSimNodeBuffer();
2040 
2041  const Ogre::Vector3 x_pos = nodes[driverseat_prop->pp_node_x].AbsPosition;
2042  const Ogre::Vector3 y_pos = nodes[driverseat_prop->pp_node_y].AbsPosition;
2043  const Ogre::Vector3 center_pos = nodes[driverseat_prop->pp_node_ref].AbsPosition;
2044 
2045  const Ogre::Vector3 x_vec = x_pos - center_pos;
2046  const Ogre::Vector3 y_vec = y_pos - center_pos;
2047  const Ogre::Vector3 normal = (y_vec.crossProduct(x_vec)).normalisedCopy();
2048 
2049  // Output position
2050  Ogre::Vector3 pos = center_pos;
2051  pos += (driverseat_prop->pp_offset.x * x_vec);
2052  pos += (driverseat_prop->pp_offset.y * y_vec);
2053  pos += (driverseat_prop->pp_offset.z * normal);
2054  out_pos = pos;
2055 
2056  // Output orientation
2057  const Ogre::Vector3 x_vec_norm = x_vec.normalisedCopy();
2058  const Ogre::Vector3 y_vec_norm = x_vec_norm.crossProduct(normal);
2059  Ogre::Quaternion rot(x_vec_norm, normal, y_vec_norm);
2060  rot = rot * driverseat_prop->pp_rot;
2061  rot = rot * Ogre::Quaternion(Ogre::Degree(180), Ogre::Vector3::UNIT_Y); // rotate towards the driving direction
2062  out_rot = rot;
2063 }
2064 
2065 void RoR::GfxActor::UpdateBeaconFlare(Prop & prop, float dt, bool is_player_actor)
2066 {
2067  // TODO: Quick and dirty port from Beam::updateFlares(), clean it up ~ only_a_ptr, 06/2018
2068  using namespace Ogre;
2069 
2070  bool enableAll = !((App::gfx_flares_mode->getEnum<GfxFlaresMode>() == GfxFlaresMode::CURR_VEHICLE_HEAD_ONLY) && !is_player_actor);
2071  NodeSB* nodes = this->GetSimNodeBuffer();
2072 
2073  if (prop.pp_beacon_type == 'b')
2074  {
2075  // Get data
2076  Ogre::SceneNode* beacon_scene_node = prop.pp_scene_node;
2077  Ogre::Quaternion beacon_orientation = beacon_scene_node->getOrientation();
2078  Ogre::Light* pp_beacon_light = prop.pp_beacon_light[0];
2079  float beacon_rotation_rate = prop.pp_beacon_rot_rate[0];
2080  float beacon_rotation_angle = prop.pp_beacon_rot_angle[0]; // Updated at end of block
2081 
2082  // Transform
2083  pp_beacon_light->setPosition(beacon_scene_node->getPosition() + beacon_orientation * Ogre::Vector3(0, 0, 0.12));
2084  beacon_rotation_angle += dt * beacon_rotation_rate;//rotate baby!
2085  pp_beacon_light->setDirection(beacon_orientation * Ogre::Vector3(cos(beacon_rotation_angle), sin(beacon_rotation_angle), 0));
2086  //billboard
2087  Ogre::Vector3 vdir = pp_beacon_light->getPosition() - App::GetCameraManager()->GetCameraNode()->getPosition(); // TODO: verify the position is already updated here ~ only_a_ptr, 06/2018
2088  float vlen = vdir.length();
2089  if (vlen > 100.0)
2090  {
2091  prop.pp_beacon_scene_node[0]->setVisible(false);
2092  return;
2093  }
2094  //normalize
2095  vdir = vdir / vlen;
2096  prop.pp_beacon_scene_node[0]->setPosition(pp_beacon_light->getPosition() - vdir * 0.1);
2097  float amplitude = pp_beacon_light->getDirection().dotProduct(vdir);
2098  if (amplitude > 0)
2099  {
2100  prop.pp_beacon_scene_node[0]->setVisible(true);
2101  prop.pp_beacon_bbs[0]->setDefaultDimensions(amplitude * amplitude * amplitude, amplitude * amplitude * amplitude);
2102  }
2103  else
2104  {
2105  prop.pp_beacon_scene_node[0]->setVisible(false);
2106  }
2107  pp_beacon_light->setVisible(enableAll);
2108 
2109  // Update
2110  prop.pp_beacon_rot_angle[0] = beacon_rotation_angle;
2111  // NOTE: Light position is not updated here!
2112  }
2113  else if (prop.pp_beacon_type == 'p')
2114  {
2115  for (int k = 0; k < 4; k++)
2116  {
2117  //update light
2118  Quaternion orientation = prop.pp_scene_node->getOrientation();
2119  switch (k)
2120  {
2121  case 0: prop.pp_beacon_light[k]->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(-0.64, 0, 0.14));
2122  break;
2123  case 1: prop.pp_beacon_light[k]->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(-0.32, 0, 0.14));
2124  break;
2125  case 2: prop.pp_beacon_light[k]->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(+0.32, 0, 0.14));
2126  break;
2127  case 3: prop.pp_beacon_light[k]->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(+0.64, 0, 0.14));
2128  break;
2129  }
2130  prop.pp_beacon_rot_angle[k] += dt * prop.pp_beacon_rot_rate[k];//rotate baby!
2131  prop.pp_beacon_light[k]->setDirection(orientation * Vector3(cos(prop.pp_beacon_rot_angle[k]), sin(prop.pp_beacon_rot_angle[k]), 0));
2132  //billboard
2133  Vector3 vdir = prop.pp_beacon_light[k]->getPosition() - App::GetCameraManager()->GetCameraNode()->getPosition();
2134  float vlen = vdir.length();
2135  if (vlen > 100.0)
2136  {
2137  prop.pp_beacon_scene_node[k]->setVisible(false);
2138  continue;
2139  }
2140  //normalize
2141  vdir = vdir / vlen;
2142  prop.pp_beacon_scene_node[k]->setPosition(prop.pp_beacon_light[k]->getPosition() - vdir * 0.2);
2143  float amplitude = prop.pp_beacon_light[k]->getDirection().dotProduct(vdir);
2144  if (amplitude > 0)
2145  {
2146  prop.pp_beacon_scene_node[k]->setVisible(true);
2147  prop.pp_beacon_bbs[k]->setDefaultDimensions(amplitude * amplitude * amplitude, amplitude * amplitude * amplitude);
2148  }
2149  else
2150  {
2151  prop.pp_beacon_scene_node[k]->setVisible(false);
2152  }
2153  prop.pp_beacon_light[k]->setVisible(enableAll);
2154  }
2155  }
2156  else if (prop.pp_beacon_type == 'r')
2157  {
2158  //update light
2159  Quaternion orientation = prop.pp_scene_node->getOrientation();
2160  prop.pp_beacon_light[0]->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(0, 0, 0.06));
2161  prop.pp_beacon_rot_angle[0] += dt * prop.pp_beacon_rot_rate[0];//rotate baby!
2162  //billboard
2163  Vector3 vdir = prop.pp_beacon_light[0]->getPosition() - App::GetCameraManager()->GetCameraNode()->getPosition();
2164  float vlen = vdir.length();
2165  if (vlen > 100.0)
2166  {
2167  prop.pp_beacon_scene_node[0]->setVisible(false);
2168  return;
2169  }
2170  //normalize
2171  vdir = vdir / vlen;
2172  prop.pp_beacon_scene_node[0]->setPosition(prop.pp_beacon_light[0]->getPosition() - vdir * 0.1);
2173  bool visible = false;
2174  if (prop.pp_beacon_rot_angle[0] > 1.0)
2175  {
2176  prop.pp_beacon_rot_angle[0] = 0.0;
2177  visible = true;
2178  }
2179  visible = visible && enableAll;
2180  prop.pp_beacon_light[0]->setVisible(visible);
2181  prop.pp_beacon_scene_node[0]->setVisible(visible);
2182  }
2183  else if (prop.pp_beacon_type == 'R' || prop.pp_beacon_type == 'L') // Avionic navigation lights (red/green)
2184  {
2185  Vector3 mposition = nodes[prop.pp_node_ref].AbsPosition + prop.pp_offset.x * (nodes[prop.pp_node_x].AbsPosition - nodes[prop.pp_node_ref].AbsPosition) + prop.pp_offset.y * (nodes[prop.pp_node_y].AbsPosition - nodes[prop.pp_node_ref].AbsPosition);
2186  //billboard
2187  Vector3 vdir = mposition - App::GetCameraManager()->GetCameraNode()->getPosition();
2188  float vlen = vdir.length();
2189  if (vlen > 100.0)
2190  {
2191  prop.pp_beacon_scene_node[0]->setVisible(false);
2192  return;
2193  }
2194  //normalize
2195  vdir = vdir / vlen;
2196  prop.pp_beacon_scene_node[0]->setPosition(mposition - vdir * 0.1);
2197  }
2198  else if (prop.pp_beacon_type == 'w') // Avionic navigation lights (white rotating beacon)
2199  {
2200  Vector3 mposition = nodes[prop.pp_node_ref].AbsPosition + prop.pp_offset.x * (nodes[prop.pp_node_x].AbsPosition - nodes[prop.pp_node_ref].AbsPosition) + prop.pp_offset.y * (nodes[prop.pp_node_y].AbsPosition - nodes[prop.pp_node_ref].AbsPosition);
2201  prop.pp_beacon_light[0]->setPosition(mposition);
2202  prop.pp_beacon_rot_angle[0] += dt * prop.pp_beacon_rot_rate[0];//rotate baby!
2203  //billboard
2204  Vector3 vdir = mposition - App::GetCameraManager()->GetCameraNode()->getPosition();
2205  float vlen = vdir.length();
2206  if (vlen > 100.0)
2207  {
2208  prop.pp_beacon_scene_node[0]->setVisible(false);
2209  return;
2210  }
2211  //normalize
2212  vdir = vdir / vlen;
2213  prop.pp_beacon_scene_node[0]->setPosition(mposition - vdir * 0.1);
2214  bool visible = false;
2215  if (prop.pp_beacon_rot_angle[0] > 1.0)
2216  {
2217  prop.pp_beacon_rot_angle[0] = 0.0;
2218  visible = true;
2219  }
2220  visible = visible && enableAll;
2221  prop.pp_beacon_light[0]->setVisible(visible);
2222  prop.pp_beacon_scene_node[0]->setVisible(visible);
2223  }
2224 }
2225 
2226 void RoR::GfxActor::UpdateProps(float dt, bool is_player_actor)
2227 {
2228  using namespace Ogre;
2229 
2230  NodeSB* nodes = this->GetSimNodeBuffer();
2231 
2232  // Update prop meshes
2233  for (Prop& prop: m_props)
2234  {
2235  if (prop.pp_scene_node == nullptr) // Wing beacons don't have scenenodes
2236  continue;
2237 
2238  // Update visibility
2239  if (prop.pp_aero_propeller_blade || prop.pp_aero_propeller_spin)
2240  {
2241  const float SPINNER_THRESHOLD = 200.f; // TODO: magic! ~ only_a_ptr, 09/2018
2242  const bool show_spinner = m_simbuf.simbuf_aeroengines[prop.pp_aero_engine_idx].simbuf_ae_rpm > SPINNER_THRESHOLD;
2243  if (prop.pp_aero_propeller_blade)
2244  prop.pp_scene_node->setVisible(!show_spinner);
2245  else if (prop.pp_aero_propeller_spin)
2246  prop.pp_scene_node->setVisible(show_spinner);
2247  }
2248  else
2249  {
2250  const bool mo_visible = (prop.pp_camera_mode_active == CAMERA_MODE_ALWAYS_VISIBLE || prop.pp_camera_mode_active == m_simbuf.simbuf_cur_cinecam);
2251  prop.pp_mesh_obj->setVisible(mo_visible);
2252  if (!mo_visible)
2253  {
2254  continue; // No need to update hidden meshes
2255  }
2256  }
2257 
2258  // Update position and orientation
2259  // -- quick ugly port from `Actor::updateProps()` --- ~ 06/2018
2260  Vector3 diffX = nodes[prop.pp_node_x].AbsPosition - nodes[prop.pp_node_ref].AbsPosition;
2261  Vector3 diffY = nodes[prop.pp_node_y].AbsPosition - nodes[prop.pp_node_ref].AbsPosition;
2262 
2263  Vector3 normal = (diffY.crossProduct(diffX)).normalisedCopy();
2264 
2265  Vector3 mposition = nodes[prop.pp_node_ref].AbsPosition + prop.pp_offset.x * diffX + prop.pp_offset.y * diffY;
2266  prop.pp_scene_node->setPosition(mposition + normal * prop.pp_offset.z);
2267 
2268  Vector3 refx = diffX.normalisedCopy();
2269  Vector3 refy = refx.crossProduct(normal);
2270  Quaternion orientation = Quaternion(refx, normal, refy) * prop.pp_rot;
2271  prop.pp_scene_node->setOrientation(orientation);
2272 
2273  if (prop.pp_wheel_scene_node) // special prop - steering wheel
2274  {
2275  Quaternion brot = Quaternion(Degree(-59.0), Vector3::UNIT_X);
2276  brot = brot * Quaternion(Degree(m_simbuf.simbuf_hydro_dir_state * prop.pp_wheel_rot_degree), Vector3::UNIT_Y);
2277  prop.pp_wheel_scene_node->setPosition(mposition + normal * prop.pp_offset.z + orientation * prop.pp_wheel_pos);
2278  prop.pp_wheel_scene_node->setOrientation(orientation * brot);
2279  }
2280  }
2281 
2282  // Update beacon flares
2283  if (BITMASK_IS_1(m_simbuf.simbuf_lightmask, RoRnet::LIGHTMASK_BEACONS) != m_beaconlight_active)
2284  {
2285  m_beaconlight_active = (m_simbuf.simbuf_lightmask & RoRnet::LIGHTMASK_BEACONS);
2286  this->SetBeaconsEnabled(m_beaconlight_active);
2287  }
2288 
2289  if ((App::gfx_flares_mode->getEnum<GfxFlaresMode>() != GfxFlaresMode::NONE)
2290  && m_beaconlight_active)
2291  {
2292  for (Prop& prop: m_props)
2293  {
2294  if (prop.pp_beacon_type != 0)
2295  {
2296  this->UpdateBeaconFlare(prop, dt, is_player_actor);
2297  }
2298  }
2299  }
2300 }
2301 
2303 {
2304  for (Prop& prop: m_props)
2305  {
2306  prop.setPropMeshesVisible(visible);
2307  }
2308 }
2309 
2311 {
2312  // For turbojets, this hides meshes (nozzle, abflame) and particles
2313  // For turbo/piston-props, this only hides particles, meshes are in props.
2314 
2315  for (int i = 0; i < m_actor->ar_num_aeroengines; i++)
2316  {
2317  m_actor->ar_aeroengines[i]->setVisible(visible);
2318  }
2319 }
2320 
2322 {
2323  if (m_renderdash != nullptr)
2324  {
2325  m_renderdash->setEnable(active);
2326  }
2327 }
2328 
2330 {
2331  if (m_renderdash != nullptr)
2332  {
2333  m_renderdash->getRenderTarget()->update();
2334  }
2335 }
2336 
2337 void RoR::GfxActor::SetBeaconsEnabled(bool beacon_light_is_active)
2338 {
2340 
2341  for (Prop& prop: m_props)
2342  {
2343  char beacon_type = prop.pp_beacon_type;
2344  if (beacon_type == 'b')
2345  {
2346  prop.pp_beacon_light[0]->setVisible(beacon_light_is_active && enableLight);
2347  prop.pp_beacon_scene_node[0]->setVisible(beacon_light_is_active);
2348  if (prop.pp_beacon_bbs[0] && beacon_light_is_active && !prop.pp_beacon_scene_node[0]->numAttachedObjects())
2349  {
2350  prop.pp_beacon_bbs[0]->setVisible(true);
2351  prop.pp_beacon_scene_node[0]->attachObject(prop.pp_beacon_bbs[0]);
2352  }
2353  else if (prop.pp_beacon_bbs[0] && !beacon_light_is_active)
2354  {
2355  prop.pp_beacon_scene_node[0]->detachAllObjects();
2356  prop.pp_beacon_bbs[0]->setVisible(false);
2357  }
2358  }
2359  else if (beacon_type == 'R' || beacon_type == 'L')
2360  {
2361  prop.pp_beacon_scene_node[0]->setVisible(beacon_light_is_active);
2362  if (prop.pp_beacon_bbs[0] && beacon_light_is_active && !prop.pp_beacon_scene_node[0]->numAttachedObjects())
2363  prop.pp_beacon_scene_node[0]->attachObject(prop.pp_beacon_bbs[0]);
2364  else if (prop.pp_beacon_bbs[0] && !beacon_light_is_active)
2365  prop.pp_beacon_scene_node[0]->detachAllObjects();
2366  }
2367  else if (beacon_type == 'p')
2368  {
2369  for (int k = 0; k < 4; k++)
2370  {
2371  prop.pp_beacon_light[k]->setVisible(beacon_light_is_active && enableLight);
2372  prop.pp_beacon_scene_node[k]->setVisible(beacon_light_is_active);
2373  if (prop.pp_beacon_bbs[k] && beacon_light_is_active && !prop.pp_beacon_scene_node[k]->numAttachedObjects())
2374  prop.pp_beacon_scene_node[k]->attachObject(prop.pp_beacon_bbs[k]);
2375  else if (prop.pp_beacon_bbs[k] && !beacon_light_is_active)
2376  prop.pp_beacon_scene_node[k]->detachAllObjects();
2377  }
2378  }
2379  else
2380  {
2381  for (int k = 0; k < 4; k++)
2382  {
2383  if (prop.pp_beacon_light[k])
2384  {
2385  prop.pp_beacon_light[k]->setVisible(beacon_light_is_active && enableLight);
2386  }
2387  if (prop.pp_beacon_scene_node[k])
2388  {
2389  prop.pp_beacon_scene_node[k]->setVisible(beacon_light_is_active);
2390 
2391  if (prop.pp_beacon_bbs[k] && beacon_light_is_active && !prop.pp_beacon_scene_node[k]->numAttachedObjects())
2392  {
2393  prop.pp_beacon_scene_node[k]->attachObject(prop.pp_beacon_bbs[k]);
2394  }
2395  else if (prop.pp_beacon_bbs[k] && !beacon_light_is_active)
2396  {
2397  prop.pp_beacon_scene_node[k]->detachAllObjects();
2398  }
2399  }
2400  }
2401  }
2402  }
2403 }
2404 
2405 // Returns a smoothened `cstate`
2406 float RoR::GfxActor::UpdateSmoothShift(PropAnim& anim, float dt, float new_target_cstate)
2407 {
2408  const float delta_cstate = new_target_cstate - anim.shifterTarget;
2409  if (delta_cstate != 0)
2410  {
2411  anim.shifterStep = delta_cstate;
2412  anim.shifterTarget = new_target_cstate;
2413  }
2414 
2415  if (anim.shifterSmooth != anim.shifterTarget)
2416  {
2417  const float cstate_step = (dt / m_simbuf.simbuf_shifter_anim_time) * anim.shifterStep;
2418  anim.shifterSmooth += cstate_step;
2419  // boundary check
2420  if ((anim.shifterStep < 0.f && anim.shifterSmooth < anim.shifterTarget) // undershot
2421  || (anim.shifterStep > 0.f) && anim.shifterSmooth > anim.shifterTarget) // overshot
2422  {
2423  anim.shifterSmooth = anim.shifterTarget;
2424  }
2425  }
2426 
2427  return anim.shifterSmooth;
2428 }
2429 
2430 void RoR::GfxActor::CalcPropAnimation(PropAnim& anim, float& cstate, int& div, float dt)
2431 {
2432  // Note: This is not the same as 'animators' - those run on physics thread!
2433  // ------------------------------------------------------------------------
2434 
2435  //boat rudder
2436  if (anim.animFlags & PROP_ANIM_FLAG_BRUDDER)
2437  {
2438  size_t spi;
2439  float ctmp = 0.0f;
2440  for (spi = 0; spi < m_simbuf.simbuf_screwprops.size(); spi++)
2441  {
2442  ctmp += m_simbuf.simbuf_screwprops[spi].simbuf_sp_rudder;
2443  }
2444 
2445  if (spi > 0)
2446  ctmp = ctmp / spi;
2447  cstate = ctmp;
2448  div++;
2449  }
2450 
2451  //boat throttle
2453  {
2454  size_t spi;
2455  float ctmp = 0.0f;
2456  for (spi = 0; spi < m_simbuf.simbuf_screwprops.size(); spi++)
2457  {
2458  ctmp += m_simbuf.simbuf_screwprops[spi].simbuf_sp_throttle;
2459  }
2460 
2461  if (spi > 0)
2462  ctmp = ctmp / spi;
2463  cstate = ctmp;
2464  div++;
2465  }
2466 
2467  //differential lock status
2469  {
2470  if (m_actor->m_num_wheel_diffs > 0) // read-only attribute - safe to read from here
2471  {
2472  switch (m_simbuf.simbuf_diff_type)
2473  {
2474  case DiffType::OPEN_DIFF:
2475  cstate = 0.0f;
2476  break;
2477  case DiffType::SPLIT_DIFF:
2478  cstate = 0.5f;
2479  break;
2480  case DiffType::LOCKED_DIFF:
2481  cstate = 1.0f;
2482  break;
2483  default:;
2484  }
2485  }
2486  else // no axles/diffs avail, mode is split by default
2487  cstate = 0.5f;
2488 
2489  div++;
2490  }
2491 
2492  //heading
2493  if (anim.animFlags & PROP_ANIM_FLAG_HEADING)
2494  {
2495  // rad2deg limitedrange -1 to +1
2496  cstate = (m_simbuf.simbuf_rotation * 57.29578f) / 360.0f;
2497  div++;
2498  }
2499 
2500  //torque - WRITES
2501  const bool has_engine = (m_actor->ar_engine!= nullptr);
2502  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_TORQUE)
2503  {
2504  float torque = m_simbuf.simbuf_engine_crankfactor;
2505  if (torque <= 0.0f)
2506  torque = 0.0f;
2507  if (torque >= m_prop_anim_crankfactor_prev)
2508  cstate -= torque / 10.0f;
2509  else
2510  cstate = 0.0f;
2511 
2512  if (cstate <= -1.0f)
2513  cstate = -1.0f;
2514  m_prop_anim_crankfactor_prev = torque;
2515  div++;
2516  }
2517 
2518  //shifterseq, to amimate sequentiell shifting
2519  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == ShifterPropAnim::SHIFTERSEQ)
2520  {
2521  float shifterseq_cstate = 0;
2522  // opt1 &opt2 = 0 this is a shifter
2523  if (!anim.lower_limit && !anim.upper_limit)
2524  {
2525  int shifter = m_simbuf.simbuf_gear;
2526  if (shifter > m_prop_anim_prev_gear)
2527  {
2528  shifterseq_cstate = 1.0f;
2529  m_prop_anim_shift_timer = 0.2f;
2530  }
2531  if (shifter < m_prop_anim_prev_gear)
2532  {
2533  shifterseq_cstate = -1.0f;
2534  m_prop_anim_shift_timer = -0.2f;
2535  }
2536  m_prop_anim_prev_gear = shifter;
2537 
2538  if (m_prop_anim_shift_timer > 0.0f)
2539  {
2540  shifterseq_cstate = 1.0f;
2541  m_prop_anim_shift_timer -= dt;
2542  if (m_prop_anim_shift_timer < 0.0f)
2543  m_prop_anim_shift_timer = 0.0f;
2544  }
2545  if (m_prop_anim_shift_timer < 0.0f)
2546  {
2547  shifterseq_cstate = -1.0f;
2548  m_prop_anim_shift_timer += dt;
2549  if (m_prop_anim_shift_timer > 0.0f)
2550  m_prop_anim_shift_timer = 0.0f;
2551  }
2552  }
2553  else
2554  {
2555  // check if anim.lower_limit is a valid to get commandvalue, then get commandvalue
2556  if (anim.lower_limit >= 1.0f && anim.lower_limit <= 48.0)
2557  if (m_simbuf.simbuf_commandkey[int(anim.lower_limit)].simbuf_cmd_value > 0)
2558  shifterseq_cstate = 1.0f;
2559  // check if anim.upper_limit is a valid to get commandvalue, then get commandvalue
2560  if (anim.upper_limit >= 1.0f && anim.upper_limit <= 48.0)
2561  if (m_simbuf.simbuf_commandkey[int(anim.upper_limit)].simbuf_cmd_value > 0)
2562  shifterseq_cstate = -1.0f;
2563  }
2564 
2565 
2566  cstate += UpdateSmoothShift(anim, dt, shifterseq_cstate);
2567  div++;
2568  }
2569 
2570  //shifterman1, left/right
2571  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == ShifterPropAnim::SHIFTERMAN1)
2572  {
2573  float shifterman1_cstate = 0.f;
2574  int shifter = m_simbuf.simbuf_gear;
2575  if (!shifter)
2576  {
2577  shifterman1_cstate = -0.5f;
2578  }
2579  else if (shifter < 0)
2580  {
2581  shifterman1_cstate = 1.0f;
2582  }
2583  else
2584  {
2585  shifterman1_cstate = -int((shifter - 1.0) / 2.0);
2586  }
2587 
2588  cstate += UpdateSmoothShift(anim, dt, shifterman1_cstate);
2589  div++;
2590  }
2591 
2592  //shifterman2, up/down
2593  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == ShifterPropAnim::SHIFTERMAN2)
2594  {
2595  float shifterman2_cstate = 0.f;
2596  int shifter = m_simbuf.simbuf_gear;
2597  shifterman2_cstate = 0.5f;
2598  if (shifter < 0)
2599  {
2600  shifterman2_cstate = 1.0f;
2601  }
2602  if (shifter > 0)
2603  {
2604  shifterman2_cstate = shifter % 2;
2605  }
2606 
2607  cstate += UpdateSmoothShift(anim, dt, shifterman2_cstate);
2608  div++;
2609  }
2610 
2611  //shifterlinear, to amimate cockpit gearselect gauge and autotransmission stick
2612  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == ShifterPropAnim::SHIFTERLIN)
2613  {
2614  float shifterlin_cstate = 0.f;
2615  int shifter = m_simbuf.simbuf_gear;
2616  int numgears = m_simbuf.simbuf_num_gears;
2617  shifterlin_cstate -= (shifter + 2.0) / (numgears + 2.0);
2618 
2619  cstate += UpdateSmoothShift(anim, dt, shifterlin_cstate);
2620  div++;
2621  }
2622 
2623  //parking brake
2624  if (anim.animFlags & PROP_ANIM_FLAG_PBRAKE)
2625  {
2626  float pbrake = static_cast<float>(m_simbuf.simbuf_parking_brake); // Bool --> float
2627  cstate -= pbrake;
2628  div++;
2629  }
2630 
2631  //speedo ( scales with speedomax )
2632  if (anim.animFlags & PROP_ANIM_FLAG_SPEEDO)
2633  {
2634  float speedo = m_simbuf.simbuf_wheel_speed / m_simbuf.simbuf_speedo_highest_kph;
2635  cstate -= speedo * 3.0f;
2636  div++;
2637  }
2638 
2639  //engine tacho ( scales with maxrpm, default is 3500 )
2640  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_TACHO)
2641  {
2642  float tacho = m_simbuf.simbuf_engine_rpm / m_simbuf.simbuf_engine_max_rpm;
2643  cstate -= tacho;
2644  div++;
2645  }
2646 
2647  //turbo
2648  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_TURBO)
2649  {
2650  float turbo = m_simbuf.simbuf_engine_turbo_psi * 3.34;
2651  cstate -= turbo / 67.0f;
2652  div++;
2653  }
2654 
2655  //brake
2656  if (anim.animFlags & PROP_ANIM_FLAG_BRAKE)
2657  {
2658  float brakes = m_simbuf.simbuf_brake;
2659  cstate -= brakes;
2660  div++;
2661  }
2662 
2663  //accelerator
2664  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_ACCEL)
2665  {
2666  float accel = m_simbuf.simbuf_engine_accel;
2667  cstate -= accel + 0.06f;
2668  //( small correction, get acc is nver smaller then 0.06.
2669  div++;
2670  }
2671 
2672  //clutch
2673  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_CLUTCH)
2674  {
2675  float clutch = m_simbuf.simbuf_clutch;
2676  cstate -= fabs(1.0f - clutch);
2677  div++;
2678  }
2679 
2680  //turn indicator stalk
2682  {
2683  float signal = 0.0f;
2684  if (m_simbuf.simbuf_lightmask & RoRnet::LIGHTMASK_BLINK_LEFT)
2685  signal = -1.0f;
2686  if (m_simbuf.simbuf_lightmask & RoRnet::LIGHTMASK_BLINK_RIGHT)
2687  signal = 1.0f;
2688  cstate -= signal;
2689  div++;
2690  }
2691 
2692  //aeroengines rpm + throttle + torque ( turboprop ) + pitch ( turboprop ) + status + fire
2693  // `anim.animOpt3` is aeroengine number (starting from 1)
2694  if (anim.animOpt3 > 0.f && anim.animOpt3 <= float(m_simbuf.simbuf_aeroengines.size()))
2695  {
2696  const int aenum = int(anim.animOpt3 - 1.f);
2697  if (anim.animFlags & PROP_ANIM_FLAG_RPM)
2698  {
2699  float angle;
2700  float pcent = m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_rpmpc;
2701  if (pcent < 60.0)
2702  angle = -5.0 + pcent * 1.9167;
2703  else if (pcent < 110.0)
2704  angle = 110.0 + (pcent - 60.0) * 4.075;
2705  else
2706  angle = 314.0;
2707  cstate -= angle / 314.0f;
2708  div++;
2709  }
2711  {
2712  float throttle = m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_throttle;
2713  cstate -= throttle;
2714  div++;
2715  }
2716 
2717  if (m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_type == AeroEngineType::AE_XPROP)
2718  {
2720  {
2721  cstate = m_simbuf.simbuf_aeroengines[aenum].simbuf_tp_aetorque / 120.0f;
2722  div++;
2723  }
2724 
2725  if (anim.animFlags & PROP_ANIM_FLAG_AEPITCH)
2726  {
2727  cstate = m_simbuf.simbuf_aeroengines[aenum].simbuf_tp_aepitch / 120.0f;
2728  div++;
2729  }
2730  }
2731 
2733  {
2734  if (!m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_ignition)
2735  cstate = 0.0f;
2736  else
2737  cstate = 0.5f;
2738  if (m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_failed)
2739  cstate = 1.0f;
2740  div++;
2741  }
2742  }
2743 
2744  const Ogre::Vector3 node0_pos = this->GetSimNodeBuffer()[0].AbsPosition;
2745  const Ogre::Vector3 node0_velo = m_simbuf.simbuf_node0_velo;
2746 
2747  //airspeed indicator
2749  {
2750  float ground_speed_kt = node0_velo.length() * 1.9438;
2751  float altitude = node0_pos.y;
2752 
2753  float sea_level_pressure = 101325; //in Pa
2754 
2755  float airpressure = sea_level_pressure * pow(1.0 - 0.0065 * altitude / 288.15, 5.24947); //in Pa
2756  float airdensity = airpressure * 0.0000120896;//1.225 at sea level
2757  float kt = ground_speed_kt * sqrt(airdensity / 1.225);
2758  cstate -= kt / 100.0f;
2759  div++;
2760  }
2761 
2762  //vvi indicator
2763  if (anim.animFlags & PROP_ANIM_FLAG_VVI)
2764  {
2765  float vvi = node0_velo.y * 196.85;
2766  // limit vvi scale to +/- 6m/s
2767  cstate -= vvi / 6000.0f;
2768  if (cstate >= 1.0f)
2769  cstate = 1.0f;
2770  if (cstate <= -1.0f)
2771  cstate = -1.0f;
2772  div++;
2773  }
2774 
2775  //altimeter
2777  {
2778  //altimeter indicator 1k oscillating
2779  if (anim.animOpt3 == 3.0f)
2780  {
2781  float altimeter = (node0_pos.y * 1.1811) / 360.0f;
2782  int alti_int = int(altimeter);
2783  float alti_mod = (altimeter - alti_int);
2784  cstate -= alti_mod;
2785  }
2786 
2787  //altimeter indicator 10k oscillating
2788  if (anim.animOpt3 == 2.0f)
2789  {
2790  float alti = node0_pos.y * 1.1811 / 3600.0f;
2791  int alti_int = int(alti);
2792  float alti_mod = (alti - alti_int);
2793  cstate -= alti_mod;
2794  if (cstate <= -1.0f)
2795  cstate = -1.0f;
2796  }
2797 
2798  //altimeter indicator 100k limited
2799  if (anim.animOpt3 == 1.0f)
2800  {
2801  float alti = node0_pos.y * 1.1811 / 36000.0f;
2802  cstate -= alti;
2803  if (cstate <= -1.0f)
2804  cstate = -1.0f;
2805  }
2806  div++;
2807  }
2808 
2809  //AOA
2810  if (anim.animFlags & PROP_ANIM_FLAG_AOA)
2811  {
2812  float aoa = m_simbuf.simbuf_wing4_aoa / 25.f;
2813  if ((node0_velo.length() * 1.9438) < 10.0f)
2814  aoa = 0;
2815  cstate -= aoa;
2816  if (cstate <= -1.0f)
2817  cstate = -1.0f;
2818  if (cstate >= 1.0f)
2819  cstate = 1.0f;
2820  div++;
2821  }
2822 
2823  Ogre::Vector3 cam_pos = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_pos ].AbsPosition;
2824  Ogre::Vector3 cam_roll = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_roll].AbsPosition;
2825  Ogre::Vector3 cam_dir = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_dir ].AbsPosition;
2826 
2827  // roll
2828  if (anim.animFlags & PROP_ANIM_FLAG_ROLL)
2829  {
2830  Ogre::Vector3 rollv = (cam_pos - cam_roll).normalisedCopy();
2831  Ogre::Vector3 dirv = (cam_pos - cam_dir).normalisedCopy();
2832  Ogre::Vector3 upv = dirv.crossProduct(-rollv);
2833  float rollangle = asin(rollv.dotProduct(Ogre::Vector3::UNIT_Y));
2834  // rad to deg
2835  rollangle = Ogre::Math::RadiansToDegrees(rollangle);
2836  // flip to other side when upside down
2837  if (upv.y < 0)
2838  rollangle = 180.0f - rollangle;
2839  cstate = rollangle / 180.0f;
2840  // data output is -0.5 to 1.5, normalize to -1 to +1 without changing the zero position.
2841  // this is vital for the animator beams and does not effect the animated props
2842  if (cstate >= 1.0f)
2843  cstate = cstate - 2.0f;
2844  div++;
2845  }
2846 
2847  // pitch
2848  if (anim.animFlags & PROP_ANIM_FLAG_PITCH)
2849  {
2850  Ogre::Vector3 dirv = (cam_pos - cam_dir).normalisedCopy();
2851  float pitchangle = asin(dirv.dotProduct(Ogre::Vector3::UNIT_Y));
2852  // radian to degrees with a max cstate of +/- 1.0
2853  cstate = (Ogre::Math::RadiansToDegrees(pitchangle) / 90.0f);
2854  div++;
2855  }
2856 
2857  // airbrake
2859  {
2860  float airbrake = static_cast<float>(m_simbuf.simbuf_airbrake_state);
2861  // cstate limited to -1.0f
2862  cstate -= airbrake / 5.0f;
2863  div++;
2864  }
2865 
2866  //flaps
2867  if (anim.animFlags & PROP_ANIM_FLAG_FLAP)
2868  {
2869  float flaps = FLAP_ANGLES[m_simbuf.simbuf_aero_flap_state];
2870  // cstate limited to -1.0f
2871  cstate = flaps;
2872  div++;
2873  }
2874 }
2875 
2877 {
2878  int prop_anim_key_index = 0;
2879 
2880  for (Prop& prop: m_props)
2881  {
2882  int animnum = 0;
2883  float rx = 0.0f;
2884  float ry = 0.0f;
2885  float rz = 0.0f;
2886 
2887  for (PropAnim& anim: prop.pp_animations)
2888  {
2889  float cstate = 0.0f;
2890  int div = 0.0f;
2891 
2892  this->CalcPropAnimation(anim, cstate, div, dt);
2893 
2894  // key triggered animations - state determined in simulation
2895  if (anim.animFlags & PROP_ANIM_FLAG_EVENT)
2896  {
2897  ROR_ASSERT(prop_anim_key_index < (int)m_simbuf.simbuf_prop_anim_keys.size());
2898  const bool anim_active = m_simbuf.simbuf_prop_anim_keys[prop_anim_key_index++].simbuf_anim_active;
2899  cstate += (float)anim_active;
2900  }
2901 
2902  // dashboard animations - state determined in simulation
2904  {
2905  int link_id = (int)anim.animOpt3;
2906  ROR_ASSERT(link_id < DD_MAX);
2907  cstate += m_actor->ar_dashboard->getNumeric(link_id);
2908  }
2909 
2910  //propanimation placed here to avoid interference with existing hydros(cstate) and permanent prop animation
2911  //land vehicle steering
2913  cstate += m_simbuf.simbuf_hydro_dir_state;
2914  //aileron
2916  cstate += m_simbuf.simbuf_hydro_aileron_state;
2917  //elevator
2919  cstate += m_simbuf.simbuf_hydro_elevator_state;
2920  //rudder
2921  if (anim.animFlags & PROP_ANIM_FLAG_ARUDDER)
2922  cstate += m_simbuf.simbuf_hydro_aero_rudder_state;
2923  //permanent
2925  cstate += 1.0f;
2926 
2927  cstate *= anim.animratio;
2928 
2929  // autoanimate noflip_bouncer
2930  if (anim.animOpt5)
2931  cstate *= (anim.animOpt5);
2932 
2933  //rotate prop
2935  {
2936  float limiter = 0.0f;
2937  // This code was formerly executed within a fixed timestep of 0.5ms and finetuned accordingly.
2938  // This is now taken into account by factoring in the respective fraction of the variable timestep.
2939  float const dt_frac = dt * 2000.f;
2941  {
2942  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
2943  {
2944  prop.pp_rota.x += cstate * dt_frac;
2945  limiter = prop.pp_rota.x;
2946  }
2947  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
2948  {
2949  prop.pp_rota.y += cstate * dt_frac;
2950  limiter = prop.pp_rota.y;
2951  }
2952  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
2953  {
2954  prop.pp_rota.z += cstate * dt_frac;
2955  limiter = prop.pp_rota.z;
2956  }
2957  }
2958  else
2959  {
2960  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
2961  rx += cstate;
2962  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
2963  ry += cstate;
2964  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
2965  rz += cstate;
2966  }
2967 
2968  bool limiterchanged = false;
2969  // check if a positive custom limit is set to evaluate/calc flip back
2970 
2971  if (limiter > anim.upper_limit)
2972  {
2973  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
2974  {
2975  limiter = anim.upper_limit; // stop at limit
2976  anim.animOpt5 *= -1.0f; // change cstate multiplier if bounce is set
2977  }
2978  else
2979  {
2980  limiter = anim.lower_limit; // flip to other side at limit
2981  }
2982  limiterchanged = true;
2983  }
2984 
2985  if (limiter < anim.lower_limit)
2986  {
2987  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
2988  {
2989  limiter = anim.lower_limit; // stop at limit
2990  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
2991  }
2992  else
2993  {
2994  limiter = anim.upper_limit; // flip to other side at limit
2995  }
2996  limiterchanged = true;
2997  }
2998 
2999  if (limiterchanged)
3000  {
3001  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
3002  prop.pp_rota.x = limiter;
3003  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
3004  prop.pp_rota.y = limiter;
3005  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
3006  prop.pp_rota.z = limiter;
3007  }
3008  }
3009 
3010  //offset prop
3011 
3013  {
3014  float offset = 0.0f;
3015  float autooffset = 0.0f;
3016 
3017  if (anim.animMode & PROP_ANIM_MODE_OFFSET_X)
3018  offset = prop.pp_offset_orig.x;
3019  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Y)
3020  offset = prop.pp_offset_orig.y;
3021  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Z)
3022  offset = prop.pp_offset_orig.z;
3023 
3025  {
3026  // This code was formerly executed within a fixed timestep of 0.5ms and finetuned accordingly.
3027  // This is now taken into account by factoring in the respective fraction of the variable timestep.
3028  float const dt_frac = dt * 2000.f;
3029  autooffset = offset + cstate * dt_frac;
3030 
3031  if (autooffset > anim.upper_limit)
3032  {
3033  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3034  {
3035  autooffset = anim.upper_limit; // stop at limit
3036  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
3037  }
3038  else
3039  {
3040  autooffset = anim.lower_limit; // flip to other side at limit
3041  }
3042  }
3043 
3044  if (autooffset < anim.lower_limit)
3045  {
3046  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3047  {
3048  autooffset = anim.lower_limit; // stop at limit
3049  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
3050  }
3051  else
3052  {
3053  autooffset = anim.upper_limit; // flip to other side at limit
3054  }
3055  }
3056  }
3057  offset += cstate;
3058 
3059  if (anim.animMode & PROP_ANIM_MODE_OFFSET_X)
3060  {
3061  prop.pp_offset.x = offset;
3063  prop.pp_offset_orig.x = autooffset;
3064  }
3065  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Y)
3066  {
3067  prop.pp_offset.y = offset;
3069  prop.pp_offset_orig.y = autooffset;
3070  }
3071  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Z)
3072  {
3073  prop.pp_offset.z = offset;
3075  prop.pp_offset_orig.z = autooffset;
3076  }
3077  }
3078  animnum++;
3079  }
3080  //recalc the quaternions with final stacked rotation values ( rx, ry, rz )
3081  rx += prop.pp_rota.x;
3082  ry += prop.pp_rota.y;
3083  rz += prop.pp_rota.z;
3084 
3085  prop.pp_rot = Ogre::Quaternion(Ogre::Degree(rz), Ogre::Vector3::UNIT_Z) *
3086  Ogre::Quaternion(Ogre::Degree(ry), Ogre::Vector3::UNIT_Y) *
3087  Ogre::Quaternion(Ogre::Degree(rx), Ogre::Vector3::UNIT_X);
3088  }
3089 }
3090 
3092 {
3093  std::sort(m_flexbodies.begin(), m_flexbodies.end(), [](FlexBody* a, FlexBody* b) { return a->getVertexCount() > b->getVertexCount(); });
3094 }
3095 
3097 {
3098  m_flexbody_tasks.clear();
3099 
3100  for (FlexBody* fb: m_flexbodies)
3101  {
3102  // Update visibility (same logic as props)
3103  const bool visible = (fb->fb_camera_mode_active == CAMERA_MODE_ALWAYS_VISIBLE || fb->fb_camera_mode_active == m_simbuf.simbuf_cur_cinecam);
3104  fb->setVisible(visible);
3105 
3106  // Update visible on background thread
3107  if (fb->isVisible())
3108  {
3109  auto func = std::function<void()>([fb]()
3110  {
3111  fb->computeFlexbody();
3112  });
3113  auto task_handle = App::GetThreadPool()->RunTask(func);
3114  m_flexbody_tasks.push_back(task_handle);
3115  }
3116  }
3117 }
3118 
3120 {
3121  for (FlexBody* fb: m_flexbodies)
3122  {
3123  fb->reset();
3124  }
3125 }
3126 
3128 {
3129  for (FlexBody* fb: m_flexbodies)
3130  {
3131  fb->setVisible(visible);
3132  }
3133 }
3134 
3136 {
3137  for (auto& task: m_flexbody_tasks)
3138  {
3139  task->join();
3140  }
3141  for (FlexBody* fb: m_flexbodies)
3142  {
3143  if (fb->isVisible())
3144  {
3145  fb->updateFlexbodyVertexBuffers();
3146  }
3147  }
3148 }
3149 
3150 // internal helper
3151 bool ShouldEnableLightSource(FlareType type, bool is_player)
3152 {
3153  switch (App::gfx_flares_mode->getEnum<GfxFlaresMode>())
3154  {
3156  return is_player && (type == FlareType::HEADLIGHT);
3158  return (type == FlareType::HEADLIGHT);
3160  return true;
3161  default:
3162  return false;
3163  }
3164 }
3165 
3166 void RoR::GfxActor::UpdateFlares(float dt_sec, bool is_player)
3167 {
3168  // Flare states are determined in simulation, this function only applies them to OGRE objects
3169  // ------------------------------------------------------------------------------------------
3170 
3171  NodeSB* nodes = this->GetSimNodeBuffer();
3172 
3173  int num_flares = static_cast<int>(m_actor->ar_flares.size());
3174  for (int i=0; i<num_flares; ++i)
3175  {
3176  flare_t& flare = m_actor->ar_flares[i];
3177 
3178  // Skip placeholder flares (removed via Tuning system)
3179  if (!flare.bbs)
3180  {
3181  continue;
3182  }
3183 
3184  this->SetMaterialFlareOn(i, flare.intensity > 0.3);
3185 
3186  // The billboard flare
3187  flare.snode->setVisible(flare.intensity > 0);
3188 
3189  // The light source
3190  if (flare.light)
3191  {
3192  flare.light->setVisible(flare.intensity > 0 && ShouldEnableLightSource(flare.fl_type, is_player));
3193  }
3194 
3195  Ogre::Vector3 normal = (nodes[flare.nodey].AbsPosition - nodes[flare.noderef].AbsPosition).crossProduct(nodes[flare.nodex].AbsPosition - nodes[flare.noderef].AbsPosition);
3196  normal.normalise();
3197  Ogre::Vector3 mposition = nodes[flare.noderef].AbsPosition + flare.offsetx * (nodes[flare.nodex].AbsPosition - nodes[flare.noderef].AbsPosition) + flare.offsety * (nodes[flare.nodey].AbsPosition - nodes[flare.noderef].AbsPosition);
3198  Ogre::Vector3 vdir = mposition - App::GetCameraManager()->GetCameraNode()->getPosition();
3199  float vlen = vdir.length();
3200  // not visible from 500m distance
3201  if (vlen > 500.0)
3202  {
3203  flare.snode->setVisible(false);
3204  continue;
3205  }
3206  //normalize
3207  vdir = vdir / vlen;
3208  float amplitude = normal.dotProduct(vdir);
3209  flare.snode->setPosition(mposition - 0.1 * amplitude * normal * flare.offsetz);
3210  flare.snode->setDirection(normal);
3211  float fsize = flare.size * flare.intensity;
3212  if (fsize < 0)
3213  {
3214  amplitude = 1;
3215  fsize *= -1;
3216  }
3217  if (flare.light)
3218  {
3219  flare.light->setPosition(mposition - 0.2 * amplitude * normal);
3220  // point the real light towards the ground a bit
3221  flare.light->setDirection(-normal - Ogre::Vector3(0, 0.2, 0));
3222  }
3223  if (flare.intensity > 0)
3224  {
3225  if (amplitude > 0)
3226  {
3227  flare.bbs->setDefaultDimensions(amplitude * fsize, amplitude * fsize);
3228  flare.snode->setVisible(true);
3229  }
3230  else
3231  {
3232  flare.snode->setVisible(false);
3233  }
3234  }
3235  }
3236 }
3237 
3239 {
3240  // Cab mesh
3241  if (m_cab_scene_node != nullptr)
3242  {
3243  static_cast<Ogre::Entity*>(m_cab_scene_node->getAttachedObject(0))->setCastShadows(value);
3244  }
3245 
3246  // Props
3247  for (Prop& prop: m_props)
3248  {
3249  if (prop.pp_mesh_obj != nullptr && prop.pp_mesh_obj->getEntity())
3250  prop.pp_mesh_obj->getEntity()->setCastShadows(value);
3251  if (prop.pp_wheel_mesh_obj != nullptr && prop.pp_wheel_mesh_obj->getEntity())
3252  prop.pp_wheel_mesh_obj->getEntity()->setCastShadows(value);
3253  }
3254 
3255  // Wheels
3256  for (WheelGfx& wheel: m_wheels)
3257  {
3258  static_cast<Ogre::Entity*>(wheel.wx_scenenode->getAttachedObject(0))->setCastShadows(value);
3259  }
3260 
3261  // Softbody beams
3262  for (BeamGfx& rod: m_gfx_beams)
3263  {
3264  static_cast<Ogre::Entity*>(rod.rod_scenenode->getAttachedObject(0))->setCastShadows(value);
3265  }
3266 
3267  // Flexbody meshes
3268  for (FlexBody* fb: m_flexbodies)
3269  {
3270  fb->setFlexbodyCastShadow(value);
3271  }
3272 }
3273 
3274 void RoR::GfxActor::RegisterCabMesh(Ogre::Entity* ent, Ogre::SceneNode* snode, FlexObj* flexobj)
3275 {
3276  m_cab_mesh = flexobj;
3277  m_cab_entity = ent;
3278  m_cab_scene_node = snode;
3279 }
3280 
3282 {
3283  if (m_cab_entity != nullptr)
3284  {
3285  m_cab_entity->setVisible(visible);
3286  }
3287  this->SetWheelsVisible(visible);
3288  this->SetPropsVisible(visible);
3289  this->SetFlexbodyVisible(visible);
3290  this->SetWingsVisible(visible);
3291  this->SetRodsVisible(visible);
3292  this->SetAeroEnginesVisible(visible);
3293 }
3294 
3296 {
3297  for (int i = 0; i < m_actor->ar_num_wings; ++i)
3298  {
3299  m_actor->ar_wings[i].cnode->setVisible(visible);
3300  }
3301 
3302  for (size_t i=0; i< m_actor->ar_airbrakes.size(); ++i)
3303  {
3304  m_gfx_airbrakes[i].abx_scenenode->setVisible(visible);
3305  }
3306 }
3307 
3309 {
3310  for (int i = 0; i < m_actor->ar_num_wings; ++i)
3311  {
3312  wing_t& wing = m_actor->ar_wings[i];
3313  wing.cnode->setPosition(wing.fa->updateVerticesGfx(this));
3314  wing.fa->uploadVertices();
3315  }
3316 }
3317 
3318 int RoR::GfxActor::FetchNumBeams () const { return m_actor->ar_num_beams; }
3319 int RoR::GfxActor::FetchNumNodes () const { return m_actor->ar_num_nodes; }
3320 int RoR::GfxActor::FetchNumWheelNodes () const { return m_actor->getWheelNodeCount(); }
3321 
3322 void RoR::GfxActor::SetNodeHot(NodeNum_t nodenum, bool value)
3323 {
3324  for (NodeGfx& nfx : m_gfx_nodes)
3325  {
3326  if (nfx.nx_node_idx == nodenum)
3327  {
3328  nfx.nx_is_hot = value;
3329  }
3330  }
3331 }
3332 
3333 void RoR::GfxActor::RemoveBeam(int beam_index)
3334 {
3335  auto itor = m_gfx_beams.begin();
3336  auto endi = m_gfx_beams.end();
3337  while (itor != endi)
3338  {
3339  if (itor->rod_beam_index == beam_index)
3340  {
3341  // Destroy OGRE objects
3342  if (itor->rod_scenenode)
3343  {
3344  if (itor->rod_scenenode->getAttachedObject(0))
3345  {
3346  Ogre::Entity* ent = static_cast<Ogre::Entity*>(itor->rod_scenenode->getAttachedObject(0));
3347  if (ent)
3348  {
3349  ent->detachFromParent();
3350  App::GetGfxScene()->GetSceneManager()->destroyEntity(ent);
3351  }
3352  }
3353 
3354  App::GetGfxScene()->GetSceneManager()->destroySceneNode(itor->rod_scenenode);
3355  itor->rod_scenenode = nullptr;
3356  }
3357 
3358  // Destroy the beam visuals
3359  m_gfx_beams.erase(itor);
3360  return;
3361  }
3362  itor++;
3363  }
3364 }
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
GameContext.h
Game state manager and message-queue provider.
RoR::Turbojet::getExhaustVelocity
float getExhaustVelocity() const
Definition: TurboJet.h:87
RoR::VCTYPE_MIRROR
@ VCTYPE_MIRROR
Definition: GfxData.h:222
RoR::PROP_ANIM_FLAG_VVI
static const BitMask64_t PROP_ANIM_FLAG_VVI
Definition: GfxData.h:44
RoR::flare_t::offsety
float offsety
Definition: SimData.h:620
MAX_COMMANDS
static const int MAX_COMMANDS
maximum number of commands per actor
Definition: SimConstants.h:28
RoR::VCTYPE_VIDEOCAM
@ VCTYPE_VIDEOCAM
Definition: GfxData.h:220
RoR::GfxActor::SetDebugView
void SetDebugView(DebugViewType dv)
Definition: GfxActor.cpp:1528
RoR::GfxScene::DrawNetLabel
void DrawNetLabel(Ogre::Vector3 pos, float cam_dist, std::string const &nick, int colornum)
Definition: GfxScene.cpp:339
RoR::IWater::GetStaticWaterHeight
virtual float GetStaticWaterHeight()=0
Returns static water level configured in 'terrn2'.
RoR::wing_t::fa
FlexAirfoil * fa
Definition: SimData.h:538
RoR::rotator_t::nodes2
NodeNum_t nodes2[4]
Definition: SimData.h:602
RoR::PROP_ANIM_FLAG_RPM
static const BitMask64_t PROP_ANIM_FLAG_RPM
Definition: GfxData.h:52
RoR::GfxActor::UpdateAirbrakes
void UpdateAirbrakes()
Definition: GfxActor.cpp:1961
RoR::SHOCK3
@ SHOCK3
shock3
Definition: SimData.h:109
RoR::PropAnim::upper_limit
float upper_limit
The upper limit for the animation.
Definition: GfxData.h:151
RoR::PROP_ANIM_FLAG_TURBO
static const BitMask64_t PROP_ANIM_FLAG_TURBO
Definition: GfxData.h:59
RoR::GfxActor::RemoveBeam
void RemoveBeam(int beam_index)
Definition: GfxActor.cpp:3333
RoR::wing_t::cnode
Ogre::SceneNode * cnode
Definition: SimData.h:539
RoR::node_t::Velocity
Ogre::Vector3 Velocity
Definition: SimData.h:294
RoR::PROP_ANIM_FLAG_AOA
static const BitMask64_t PROP_ANIM_FLAG_AOA
Definition: GfxData.h:46
RoR::SHIFTERLIN
@ SHIFTERLIN
Definition: GfxData.h:134
RoR::GfxActor::GfxActor
GfxActor(ActorPtr actor, ActorSpawner *spawner, std::string ogre_resource_group, RoR::Renderdash *renderdash)
Definition: GfxActor.cpp:61
RoR::DebugViewType::DEBUGVIEW_BEAMS
@ DEBUGVIEW_BEAMS
FlexMeshWheel.h
RoR::App::mp_hide_net_labels
CVar * mp_hide_net_labels
Definition: Application.cpp:118
RoR::AeroEngine::isFailed
virtual bool isFailed()=0
RoR::Collisions::FX_CLUMPY
@ FX_CLUMPY
Definition: Collisions.h:91
RoR::flare_t::bbs
Ogre::BillboardSet * bbs
This remains nullptr if removed via addonpart_unwanted_flare or Tuning UI.
Definition: SimData.h:623
RoR::PROP_ANIM_FLAG_PBRAKE
static const BitMask64_t PROP_ANIM_FLAG_PBRAKE
Definition: GfxData.h:58
RoR::flare_t::size
float size
Definition: SimData.h:631
RoR::GfxActor::UpdateSimDataBuffer
void UpdateSimDataBuffer()
Copies sim. data from Actor to GfxActor for later update.
Definition: GfxActor.cpp:1727
RoR::PROP_ANIM_FLAG_ARUDDER
static const BitMask64_t PROP_ANIM_FLAG_ARUDDER
Definition: GfxData.h:70
RoR::Actor::ar_filename
std::string ar_filename
Attribute; filled at spawn.
Definition: Actor.h:419
RoR::MpState::CONNECTED
@ CONNECTED
RoR::node_t::nd_avg_collision_slip
Ogre::Real nd_avg_collision_slip
Physics state; average slip velocity across the last few physics frames.
Definition: SimData.h:322
RoR::BeamGfx
Visuals of softbody beam (beam_t struct); Partially updated along with SimBuffer.
Definition: GfxData.h:274
RoR::FlexAirfoil::updateVerticesGfx
Ogre::Vector3 updateVerticesGfx(RoR::GfxActor *gfx_actor)
Definition: FlexAirfoil.cpp:442
RoR::NodeSB
Definition: SimBuffers.h:67
RoR::node_t::nd_last_collision_gm
ground_model_t * nd_last_collision_gm
Physics state; last collision 'ground model' (surface definition)
Definition: SimData.h:325
RoR::GfxActor::CalculateDriverPos
void CalculateDriverPos(Ogre::Vector3 &out_pos, Ogre::Quaternion &out_rot)
Definition: GfxActor.cpp:2034
RoR::AeroEngineSB::simbuf_ae_type
AeroEngineType simbuf_ae_type
Definition: SimBuffers.h:92
RoR::GfxActor::SetFlexbodyVisible
void SetFlexbodyVisible(bool visible)
Definition: GfxActor.cpp:3127
RoR::App::GetCameraManager
CameraManager * GetCameraManager()
Definition: Application.cpp:275
RoR::node_t::AbsPosition
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition: SimData.h:293
RoR::Prop::pp_beacon_rot_angle
float pp_beacon_rot_angle[4]
Radians.
Definition: GfxData.h:193
RoR::Prop::pp_offset
Ogre::Vector3 pp_offset
Definition: GfxData.h:166
RoR::App::GetGuiManager
GUIManager * GetGuiManager()
Definition: Application.cpp:269
RoR::App::diag_hide_broken_beams
CVar * diag_hide_broken_beams
Definition: Application.cpp:152
RoR::GfxActor::UpdateDebugView
void UpdateDebugView()
Definition: GfxActor.cpp:674
RoR::PropAnim::lower_limit
float lower_limit
The lower limit for the animation.
Definition: GfxData.h:150
RoR::beam_t::p1
node_t * p1
Definition: SimData.h:334
DashBoardManager.h
RoR::VideoCamera::vcam_render_tex
Ogre::TexturePtr vcam_render_tex
Definition: GfxData.h:243
RoR::GfxActor::FetchNumBeams
int FetchNumBeams() const
Definition: GfxActor.cpp:3318
RoR::DebugViewType::DEBUGVIEW_NONE
@ DEBUGVIEW_NONE
RoR::GfxActor::ToggleDebugView
void ToggleDebugView()
Definition: GfxActor.cpp:1520
RoR::Actor::ar_instance_id
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition: Actor.h:370
SkyManager.h
Renderdash.h
RoR::AeroEngineSB::simbuf_tp_aepitch
float simbuf_tp_aepitch
Turboprop pitch, used by animation "aepitch".
Definition: SimBuffers.h:97
RoR::node_t::nd_under_water
bool nd_under_water
State; GFX hint.
Definition: SimData.h:319
RoR::PROP_ANIM_FLAG_HEADING
static const BitMask64_t PROP_ANIM_FLAG_HEADING
Definition: GfxData.h:65
RoR::GfxActor::RegisterCabMaterial
void RegisterCabMaterial(Ogre::MaterialPtr mat, Ogre::MaterialPtr mat_trans)
Definition: GfxActor.cpp:357
RoR::SHIFTERMAN2
@ SHIFTERMAN2
Definition: GfxData.h:132
RoR::GfxActor::UpdateRods
void UpdateRods()
Definition: GfxActor.cpp:1598
RoR::GfxActor::CycleDebugViews
void CycleDebugViews()
Definition: GfxActor.cpp:1546
format
Truck file format(technical spec)
GUIUtils.h
RoR::wheel_t
Definition: SimData.h:420
RoR::GfxActor::GetActor
ActorPtr GetActor()
Definition: GfxActor.cpp:288
RoR::DebugViewType::DEBUGVIEW_ROTATORS
@ DEBUGVIEW_ROTATORS
RoR::Terrain::getSkyManager
SkyManager * getSkyManager()
Definition: Terrain.cpp:513
RoR::DebugViewType
DebugViewType
Definition: GfxData.h:101
RoR::HandleGenericException
void HandleGenericException(const std::string &from, BitMask_t flags)
Definition: Application.cpp:369
RoR::PROP_ANIM_FLAG_DASHBOARD
static const BitMask64_t PROP_ANIM_FLAG_DASHBOARD
Used with dashboard system inputs, see enum DashData in file DashBoardManager.h.
Definition: GfxData.h:75
RoR::AeroEngineSB
Definition: SimBuffers.h:90
BEAM_BROKEN_THICKNESS
const float BEAM_BROKEN_THICKNESS(1.8f)
RoR::GfxActor::m_particles_drip
DustPool * m_particles_drip
Definition: GfxActor.h:192
RoR::AeroEngineSB::simbuf_tj_exhaust_velo
float simbuf_tj_exhaust_velo
Turbojet afterburner.
Definition: SimBuffers.h:99
RoR::Round
Ogre::Real Round(Ogre::Real value, unsigned short ndigits=0)
Definition: Utils.cpp:98
RoR::flare_t::fl_type
FlareType fl_type
Definition: SimData.h:625
RoR::Turboprop::pitch
float pitch
Definition: TurboProp.h:43
RoR::GUIManager::IsGuiHidden
bool IsGuiHidden() const
Definition: GUIManager.h:146
MeshObject.h
RoR::FlareType::HEADLIGHT
@ HEADLIGHT
RoR::beam_t::bm_locked_actor
ActorPtr bm_locked_actor
in case p2 is on another actor
Definition: SimData.h:349
RoR::rotator_t::axis2
NodeNum_t axis2
Definition: SimData.h:604
NODE_MASS_TEXT_COLOR
const ImU32 NODE_MASS_TEXT_COLOR(0xff77bb66)
RoR::Collisions::FX_DUSTY
@ FX_DUSTY
Definition: Collisions.h:90
RoR::PROP_ANIM_FLAG_EVENT
static const BitMask64_t PROP_ANIM_FLAG_EVENT
Definition: GfxData.h:68
RoR::WheelGfx
Definition: GfxData.h:290
RoR::DebugViewType::DEBUGVIEW_SUBMESH
@ DEBUGVIEW_SUBMESH
RoR::VideoCamera::vcam_render_target
Ogre::RenderTexture * vcam_render_target
Definition: GfxData.h:242
MovableText.h
This creates a billboarding object that displays a text.
RoR::AeroEngine::getRPM
virtual float getRPM()=0
RoR::rotator_t::axis1
NodeNum_t axis1
rot axis
Definition: SimData.h:603
RoR::FlexObj
A visual mesh, forming a chassis for softbody actor At most one instance is created per actor.
Definition: FlexObj.h:59
RoR::Prop::pp_rot
Ogre::Quaternion pp_rot
Definition: GfxData.h:169
RoR::FlareType
FlareType
Definition: SimData.h:228
RoR::flare_t::nodey
NodeNum_t nodey
Definition: SimData.h:618
RoR::GfxActor::UpdateCabMesh
void UpdateCabMesh()
Definition: GfxActor.cpp:1901
RoR::GfxActor::FinishFlexbodyTasks
void FinishFlexbodyTasks()
Definition: GfxActor.cpp:3135
RoR::PROP_ANIM_FLAG_SPEEDO
static const BitMask64_t PROP_ANIM_FLAG_SPEEDO
Definition: GfxData.h:57
RoR::AeroEngine
Definition: AeroEngine.h:36
RoR::App::diag_hide_beam_stress
CVar * diag_hide_beam_stress
Definition: Application.cpp:153
RoR::CameraManager::GetCameraNode
Ogre::SceneNode * GetCameraNode()
Definition: CameraManager.h:63
Utils.h
BEAM_STRESS_TEXT_COLOR
const ImU32 BEAM_STRESS_TEXT_COLOR(0xff58bbfc)
RoR::node_t::RelPosition
Ogre::Vector3 RelPosition
relative to the local physics origin (one origin per actor) (shaky)
Definition: SimData.h:292
RoR::Turbojet::getAfterburner
float getAfterburner()
Definition: TurboJet.h:85
RoR::Prop::pp_beacon_type
char pp_beacon_type
Special prop: beacon {0 = none, 'b' = user-specified, 'r' = red, 'p' = police lightbar,...
Definition: GfxData.h:188
RoR::GfxActor::SortFlexbodies
void SortFlexbodies()
Definition: GfxActor.cpp:3091
RoR::PropAnim::animMode
PropAnimMode animMode
Definition: GfxData.h:141
RoR::SHIFTERSEQ
@ SHIFTERSEQ
Definition: GfxData.h:133
OgreImGui.h
RefCountingObjectPtr< Actor >
RoR::World2ScreenConverter::Convert
Ogre::Vector3 Convert(Ogre::Vector3 world_pos)
Definition: Utils.h:89
RoR::PROP_ANIM_MODE_ROTA_Z
@ PROP_ANIM_MODE_ROTA_Z
Definition: GfxData.h:82
RoR::SS_TRIG_SCREETCH
@ SS_TRIG_SCREETCH
Definition: SoundScriptManager.h:83
ActorSpawner.h
Vehicle spawning logic.
RoR::flare_t::offsetz
float offsetz
Definition: SimData.h:621
GUIManager.h
RoR::PropAnim::animratio
float animratio
A coefficient for the animation, prop degree if used with mode: rotation and propoffset if used with ...
Definition: GfxData.h:139
RoR::GfxActor::SetVideoCamState
void SetVideoCamState(VideoCamState state)
Definition: GfxActor.cpp:394
Actor.h
RoR::GfxActor::SetNodeHot
void SetNodeHot(NodeNum_t nodenum, bool value)
Definition: GfxActor.cpp:3322
RoR::App::diag_hide_wheel_info
CVar * diag_hide_wheel_info
Definition: Application.cpp:154
RoRnet::LIGHTMASK_BLINK_LEFT
@ LIGHTMASK_BLINK_LEFT
left blinker on
Definition: RoRnet.h:121
RoR::FlexAirfoil::uploadVertices
void uploadVertices()
Definition: FlexAirfoil.cpp:563
RoR::GfxScene::GetSceneManager
Ogre::SceneManager * GetSceneManager()
Definition: GfxScene.h:64
w
float w
Definition: (ValueTypes) quaternion.h:4
RoR::ActorSpawner
Processes a RigDef::Document (parsed from 'truck' file format) into a simulated gameplay object (Acto...
Definition: ActorSpawner.h:70
RoR::PROP_ANIM_FLAG_TACHO
static const BitMask64_t PROP_ANIM_FLAG_TACHO
Definition: GfxData.h:56
RoR::GfxFlaresMode::ALL_VEHICLES_HEAD_ONLY
@ ALL_VEHICLES_HEAD_ONLY
All vehicles, main lights.
EngineSim.h
RoR::VCTYPE_MIRROR_PROP_LEFT
@ VCTYPE_MIRROR_PROP_LEFT
The classic 'special prop/rear view mirror'.
Definition: GfxData.h:223
BEAM_THICKNESS
const float BEAM_THICKNESS(1.2f)
RoR::PROP_ANIM_FLAG_SHIFTER
static const BitMask64_t PROP_ANIM_FLAG_SHIFTER
Definition: GfxData.h:60
RoR::PropAnim::shifterStep
float shifterStep
Definition: GfxData.h:155
NODE_IMMOVABLE_RADIUS
const float NODE_IMMOVABLE_RADIUS(2.8f)
RoR::Prop::pp_node_ref
NodeNum_t pp_node_ref
Definition: GfxData.h:163
RoR::AeroEngineSB::simbuf_tp_aetorque
float simbuf_tp_aetorque
Turboprop torque, used by animation "aetorque".
Definition: SimBuffers.h:96
RoR::PROP_ANIM_MODE_OFFSET_Z
@ PROP_ANIM_MODE_OFFSET_Z
Definition: GfxData.h:85
RoR::GfxActor::FinishWheelUpdates
void FinishWheelUpdates()
Definition: GfxActor.cpp:1927
RoR::App::mp_state
CVar * mp_state
Definition: Application.cpp:115
RoR::PropAnim
Definition: GfxData.h:137
RoR::GfxActor::UpdateParticles
void UpdateParticles(float dt_sec)
Definition: GfxActor.cpp:542
RoR::GfxActor::UpdatePropAnimations
void UpdatePropAnimations(float dt)
Definition: GfxActor.cpp:2876
RoR::Turboprop::indicated_torque
float indicated_torque
Definition: TurboProp.h:44
RoR::GfxActor::FetchNumNodes
int FetchNumNodes() const
Definition: GfxActor.cpp:3319
RoR::Prop::pp_node_y
NodeNum_t pp_node_y
Definition: GfxData.h:165
RoR::GfxActor::UpdateProps
void UpdateProps(float dt, bool is_player_actor)
Definition: GfxActor.cpp:2226
TurboJet.h
RoR::wheel_t::debug_force
Ogre::Vector3 debug_force
Definition: SimData.h:460
RoR::DebugViewType::DEBUGVIEW_SLIDENODES
@ DEBUGVIEW_SLIDENODES
RoR::beam_t
Simulation: An edge in the softbody structure.
Definition: SimData.h:329
SlideNode.h
RoR::wheel_t::debug_slip
Ogre::Vector3 debug_slip
Definition: SimData.h:459
RoR::PROP_ANIM_MODE_OFFSET_X
@ PROP_ANIM_MODE_OFFSET_X
Definition: GfxData.h:83
RoR::PROP_ANIM_FLAG_DIFFLOCK
static const BitMask64_t PROP_ANIM_FLAG_DIFFLOCK
Definition: GfxData.h:66
RoR::AeroEngineSB::simbuf_ae_rpm
float simbuf_ae_rpm
Definition: SimBuffers.h:93
RoR::PROP_ANIM_FLAG_AESTATUS
static const BitMask64_t PROP_ANIM_FLAG_AESTATUS
Definition: GfxData.h:63
BEAM_STRENGTH_TEXT_COLOR
const ImU32 BEAM_STRENGTH_TEXT_COLOR(0xffcfd0cc)
RoR::NodeNum_t
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
Definition: ForwardDeclarations.h:52
RoR::Str
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition: Str.h:35
RoR::beam_t::bounded
SpecialBeam bounded
Definition: SimData.h:346
RoR::GfxActor::UpdateAeroEngines
void UpdateAeroEngines()
Definition: GfxActor.cpp:2006
RoR::Turboprop
Definition: TurboProp.h:38
RoR::PROP_ANIM_FLAG_BTHROTTLE
static const BitMask64_t PROP_ANIM_FLAG_BTHROTTLE
Definition: GfxData.h:72
RoR::NodeGfx
Gfx attributes/state of a softbody node.
Definition: GfxData.h:250
RoR::GfxFlaresMode::ALL_VEHICLES_ALL_LIGHTS
@ ALL_VEHICLES_ALL_LIGHTS
All vehicles, all lights.
RoR::PROP_ANIM_FLAG_THROTTLE
static const BitMask64_t PROP_ANIM_FLAG_THROTTLE
Definition: GfxData.h:51
RoR::GfxActor::UpdateRenderdashRTT
void UpdateRenderdashRTT()
Definition: GfxActor.cpp:2329
RoR::GfxActor::UpdateSmoothShift
float UpdateSmoothShift(PropAnim &anim, float dt, float new_target_cstate)
Definition: GfxActor.cpp:2406
RoR::Prop::pp_beacon_light
Ogre::Light * pp_beacon_light[4]
Definition: GfxData.h:191
RoR::GfxActor::GetActorState
int GetActorState() const
Definition: GfxActor.cpp:1959
RoR::OPEN_DIFF
@ OPEN_DIFF
Definition: Differentials.h:60
RoR::GetImDummyFullscreenWindow
ImDrawList * GetImDummyFullscreenWindow()
Definition: GUIUtils.cpp:355
BEAM_HYDRO_THICKNESS
const float BEAM_HYDRO_THICKNESS(1.4f)
RoR::PROP_ANIM_FLAG_AIRSPEED
static const BitMask64_t PROP_ANIM_FLAG_AIRSPEED
Definition: GfxData.h:43
BITMASK_IS_1
#define BITMASK_IS_1(VAR, FLAGS)
Definition: BitFlags.h:14
RoR::DD_MAX
@ DD_MAX
Definition: DashBoardManager.h:201
RoR::App::diag_hide_wheels
CVar * diag_hide_wheels
Definition: Application.cpp:155
RoR::wheel_t::wh_axis_node_1
node_t * wh_axis_node_1
Definition: SimData.h:439
RoR::GfxActor::SetCabLightsActive
void SetCabLightsActive(bool state_on)
Definition: GfxActor.cpp:373
RoR::GfxActor::SetWheelsVisible
void SetWheelsVisible(bool value)
Definition: GfxActor.cpp:1942
RoR::World2ScreenConverter
< Keeps data close for faster access.
Definition: Utils.h:81
RoR::SHIFTERMAN1
@ SHIFTERMAN1
Definition: GfxData.h:131
RoR::AeroEngineSB::simbuf_ae_rpmpc
float simbuf_ae_rpmpc
Definition: SimBuffers.h:94
RoR::App::app_state
CVar * app_state
Definition: Application.cpp:79
RoR::flare_t::nodex
NodeNum_t nodex
Definition: SimData.h:617
RoR::GfxActor::SetWingsVisible
void SetWingsVisible(bool visible)
Definition: GfxActor.cpp:3295
ShouldEnableLightSource
bool ShouldEnableLightSource(FlareType type, bool is_player)
Definition: GfxActor.cpp:3151
RoR::Str::ToCStr
const char * ToCStr() const
Definition: Str.h:46
RoR::ActorState::LOCAL_SLEEPING
@ LOCAL_SLEEPING
sleeping (local) actor
RoR::GfxActor::SetRodsVisible
void SetRodsVisible(bool visible)
Definition: GfxActor.cpp:1706
RoR::VideoCamera::vcam_ogre_camera
Ogre::Camera * vcam_ogre_camera
Definition: GfxData.h:241
RoR::GfxActor::UpdateVideoCameras
void UpdateVideoCameras(float dt_sec)
Definition: GfxActor.cpp:422
RoR::Turboprop::max_torque
float max_torque
Definition: TurboProp.h:45
RoR::PROP_ANIM_FLAG_ACCEL
static const BitMask64_t PROP_ANIM_FLAG_ACCEL
Definition: GfxData.h:53
RoR::PROP_ANIM_FLAG_FLAP
static const BitMask64_t PROP_ANIM_FLAG_FLAP
Definition: GfxData.h:47
RoR::node_t::pos
NodeNum_t pos
This node's index in Actor::ar_nodes array.
Definition: SimData.h:303
RoR::PROP_ANIM_FLAG_ALTIMETER
static const BitMask64_t PROP_ANIM_FLAG_ALTIMETER
Definition: GfxData.h:45
GfxScene.h
RoR::node_t::nd_tyre_node
bool nd_tyre_node
Attr; This node is part of a tyre.
Definition: SimData.h:310
fast_normalise
Ogre::Vector3 fast_normalise(Ogre::Vector3 v)
Definition: ApproxMath.h:151
RoR::flare_t::intensity
float intensity
Definition: SimData.h:632
RoR::wheel_t::wh_near_attach_node
node_t * wh_near_attach_node
Definition: SimData.h:437
RoR::Prop
A mesh attached to vehicle frame via 3 nodes.
Definition: GfxData.h:160
RoR::SHOCK2
@ SHOCK2
shock2
Definition: SimData.h:108
RoR::ground_model_t::fx_type
int fx_type
Definition: SimData.h:760
RoR::AirbrakeGfx
Definition: GfxData.h:299
RoR::App::mp_hide_own_net_label
CVar * mp_hide_own_net_label
Definition: Application.cpp:119
RoR::PROP_ANIM_FLAG_AETORQUE
static const BitMask64_t PROP_ANIM_FLAG_AETORQUE
Definition: GfxData.h:61
RoR::PROP_ANIM_FLAG_BRUDDER
static const BitMask64_t PROP_ANIM_FLAG_BRUDDER
Definition: GfxData.h:71
RoR::GfxActor::m_particles_dust
DustPool * m_particles_dust
Definition: GfxActor.h:193
RoR::PROP_ANIM_FLAG_PERMANENT
static const BitMask64_t PROP_ANIM_FLAG_PERMANENT
Definition: GfxData.h:73
SoundScriptManager.h
RoR::GfxFlaresMode::CURR_VEHICLE_HEAD_ONLY
@ CURR_VEHICLE_HEAD_ONLY
Only current vehicle, main lights.
RoR::node_t
Physics: A vertex in the softbody structure.
Definition: SimData.h:285
FlexBody.h
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:280
RoR::CVar::getEnum
T getEnum() const
Definition: CVar.h:99
RoR::GfxActor::GetActorId
int GetActorId() const
Definition: GfxActor.cpp:1958
RoR::AeroEngineSB::simbuf_ae_throttle
float simbuf_ae_throttle
Definition: SimBuffers.h:95
RoR::AppState::SIMULATION
@ SIMULATION
RoR::Renderdash
'renderdash' is a name of a classic Render-To-Texture animated material with gauges and other dashboa...
Definition: Renderdash.h:34
RoR::PropAnim::shifterTarget
float shifterTarget
Definition: GfxData.h:156
RoR::PropAnim::shifterSmooth
float shifterSmooth
Definition: GfxData.h:154
RoR::GfxActor::ScaleActor
void ScaleActor(Ogre::Vector3 relpos, float ratio)
Definition: GfxActor.cpp:1665
RoR::SS_MOD_SCREETCH
@ SS_MOD_SCREETCH
Definition: SoundScriptManager.h:137
RoR::GfxActor::m_particles_clump
DustPool * m_particles_clump
Definition: GfxActor.h:197
RoR::VideoCamera
An Ogre::Camera mounted on the actor and rendering into either in-scene texture or external window.
Definition: GfxData.h:229
RoR::AirbrakeGfx::abx_scenenode
Ogre::SceneNode * abx_scenenode
Definition: GfxData.h:302
RoR::VideoCamState::VCSTATE_DISABLED
@ VCSTATE_DISABLED
RoR::App::gfx_flares_mode
CVar * gfx_flares_mode
Definition: Application.cpp:215
RoR::node_t::nd_last_collision_slip
Ogre::Vector3 nd_last_collision_slip
Physics state; last collision slip vector.
Definition: SimData.h:323
RoR::GfxScene::GetDustPool
DustPool * GetDustPool(const char *name)
Definition: GfxScene.cpp:263
NODE_COLOR
const ImU32 NODE_COLOR(0xff44ddff)
BEAM_COLOR
const ImU32 BEAM_COLOR(0xff556633)
RoR::DebugViewType::DEBUGVIEW_SHOCKS
@ DEBUGVIEW_SHOCKS
RoR::VCTYPE_MIRROR_PROP_RIGHT
@ VCTYPE_MIRROR_PROP_RIGHT
The classic 'special prop/rear view mirror'.
Definition: GfxData.h:224
ApproxMath.h
RoR::PROP_ANIM_FLAG_STEERING
static const BitMask64_t PROP_ANIM_FLAG_STEERING
Definition: GfxData.h:67
RoR::wheel_t::debug_scaled_cforce
Ogre::Vector3 debug_scaled_cforce
Definition: SimData.h:461
DustPool.h
RoR::GfxActor::SpecialGetRotationTo
static Ogre::Quaternion SpecialGetRotationTo(const Ogre::Vector3 &src, const Ogre::Vector3 &dest)
Definition: GfxActor.cpp:1621
RoR::VCTYPE_TRACKING_VIDEOCAM
@ VCTYPE_TRACKING_VIDEOCAM
Definition: GfxData.h:221
RoR::PropAnim::animOpt5
float animOpt5
Definition: GfxData.h:149
BEAM_HYDRO_COLOR
const ImU32 BEAM_HYDRO_COLOR(0xff55a3e0)
FlexAirfoil.h
RoR::PROP_ANIM_MODE_ROTA_X
@ PROP_ANIM_MODE_ROTA_X
Definition: GfxData.h:80
RoR::NodeSB::AbsPosition
Ogre::Vector3 AbsPosition
Definition: SimBuffers.h:69
RoR::PROP_ANIM_FLAG_ELEVATORS
static const BitMask64_t PROP_ANIM_FLAG_ELEVATORS
Definition: GfxData.h:74
RoR::AeroEngineSB::simbuf_tj_ab_thrust
float simbuf_tj_ab_thrust
Definition: SimBuffers.h:98
RoR::flare_t::snode
Ogre::SceneNode * snode
Definition: SimData.h:622
SOUND_MODULATE
#define SOUND_MODULATE(_ACTOR_, _MOD_, _VALUE_)
Definition: SoundScriptManager.h:40
RoR::Collisions::FX_HARD
@ FX_HARD
Definition: Collisions.h:89
RoR::PROP_ANIM_MODE_ROTA_Y
@ PROP_ANIM_MODE_ROTA_Y
Definition: GfxData.h:81
RoR::wheel_t::wh_arm_node
node_t * wh_arm_node
Definition: SimData.h:436
RoR::beam_t::bm_inter_actor
bool bm_inter_actor
in case p2 is on another actor
Definition: SimData.h:348
RoR::PROP_ANIM_MODE_AUTOANIMATE
@ PROP_ANIM_MODE_AUTOANIMATE
Definition: GfxData.h:86
RoR::wheel_t::wh_radius
Ogre::Real wh_radius
Definition: SimData.h:441
RoR::DebugViewType::DEBUGVIEW_NODES
@ DEBUGVIEW_NODES
RoR::PropAnim::animOpt3
float animOpt3
MULTIPURPOSE.
Definition: GfxData.h:148
TurboProp.h
RoR::GfxActor::RegisterCabMesh
void RegisterCabMesh(Ogre::Entity *ent, Ogre::SceneNode *snode, FlexObj *flexobj)
Definition: GfxActor.cpp:3274
RoR::GfxActor::SetAeroEnginesVisible
void SetAeroEnginesVisible(bool visible)
Definition: GfxActor.cpp:2310
RoR::node_t::nd_rim_node
bool nd_rim_node
Attr; This node is part of a rim.
Definition: SimData.h:309
RoR::VideoCamState::VCSTATE_ENABLED_ONLINE
@ VCSTATE_ENABLED_ONLINE
RoR::GfxActor::SetCastShadows
void SetCastShadows(bool value)
Definition: GfxActor.cpp:3238
RoR::GfxActor::SetBeaconsEnabled
void SetBeaconsEnabled(bool beacon_light_is_active)
Definition: GfxActor.cpp:2337
RoR::GfxFlaresMode
GfxFlaresMode
Definition: Application.h:238
RoR::PropAnim::animFlags
BitMask64_t animFlags
Definition: GfxData.h:140
RoR::AeroEngine::getIgnition
virtual bool getIgnition()=0
RoR::BEAM_HYDRO
@ BEAM_HYDRO
Definition: SimData.h:71
RoR::flare_t
Definition: SimData.h:614
RoR::DebugViewType::DEBUGVIEW_SKELETON
@ DEBUGVIEW_SKELETON
RoR::ThreadPool::RunTask
std::shared_ptr< Task > RunTask(const std::function< void()> &task_func)
Submit new asynchronous task to thread pool and return Task handle to allow for synchronization.
Definition: ThreadPool.h:178
RoR::AeroEngine::getType
virtual AeroEngineType getType()=0
RoR::node_t::nd_has_ground_contact
bool nd_has_ground_contact
Physics state.
Definition: SimData.h:313
RoR::DebugViewType::DEBUGVIEW_WHEELS
@ DEBUGVIEW_WHEELS
RoR::PROP_ANIM_FLAG_CLUTCH
static const BitMask64_t PROP_ANIM_FLAG_CLUTCH
Definition: GfxData.h:55
SOUND_PLAY_ONCE
#define SOUND_PLAY_ONCE(_ACTOR_, _TRIG_)
Definition: SoundScriptManager.h:34
RoR::AeroEngine::getRPMpc
virtual float getRPMpc()=0
RoR::GfxActor::SetRenderdashActive
void SetRenderdashActive(bool active)
Definition: GfxActor.cpp:2321
RoR::GfxActor::UpdateCParticles
void UpdateCParticles()
Definition: GfxActor.cpp:1990
nodes
or anywhere else will not be considered a but parsed as regular data ! Each line is treated as values separated by separators Possible i e animators Multiline description Single does not affect it Directive usualy set global attributes or change behavior of the parsing Directive may appear in any block section Modularity The elements can be grouped into modules Each module must belong to one or more configurations Directives sectionconfig specify truck configurations the user can choose from Exactly one must be selected If the first defined is used lettercase matches original docs(parsing is insensitive). NAME TYPE NOTES advdrag BLOCK add_animation DIRECTIVE Special syntax airbrakes BLOCK animators BLOCK Special syntax IF(values[0]=="") bad trailing chars are silently ignored no space at the end Items delimited On each side of there is max item Empty invalid string parses as node num items Acceptable item the node is the others When a node range has more than nodes
Definition: ReadMe.txt:302
RoR::GfxActor::UpdateWingMeshes
void UpdateWingMeshes()
Definition: GfxActor.cpp:3308
RoR::wheel_t::wh_axis_node_0
node_t * wh_axis_node_0
Definition: SimData.h:438
RoR::GfxFlaresMode::NONE
@ NONE
None (fastest)
RoR::GfxActor::IsActorLive
bool IsActorLive() const
Should the visuals be updated for this actor?
Definition: GfxActor.cpp:1896
BEAM_BROKEN_COLOR
const ImU32 BEAM_BROKEN_COLOR(0xff4466dd)
RoR::CAMERA_MODE_ALWAYS_VISIBLE
static CameraMode_t CAMERA_MODE_ALWAYS_VISIBLE
Definition: GfxData.h:125
RoR::FlareMaterial
Definition: GfxData.h:310
RoR::wheel_t::debug_vel
Ogre::Vector3 debug_vel
Definition: SimData.h:458
NODE_RADIUS
const float NODE_RADIUS(2.f)
RoR::flare_t::light
Ogre::Light * light
Definition: SimData.h:624
RoR::GfxActor::CalcPropAnimation
void CalcPropAnimation(PropAnim &anim, float &cstate, int &div, float dt)
Definition: GfxActor.cpp:2430
RoR::beam_t::p2
node_t * p2
Definition: SimData.h:335
RoR::AeroEngineType::AE_XPROP
@ AE_XPROP
RoR::App::diag_hide_nodes
CVar * diag_hide_nodes
Definition: Application.cpp:156
RoR::VideoCamState
VideoCamState
Definition: GfxData.h:93
Terrain.h
RoR::Actor::ar_net_stream_id
int ar_net_stream_id
Definition: Actor.h:423
RoR::GfxActor::m_particles_sparks
DustPool * m_particles_sparks
Definition: GfxActor.h:196
RoR::GfxActor
Definition: GfxActor.h:52
RoR::ActorState::NETWORKED_HIDDEN
@ NETWORKED_HIDDEN
not simulated, not updated (remote)
RoR::rotator_t::nodes1
NodeNum_t nodes1[4]
Definition: SimData.h:601
InputEngine.h
Handles controller inputs from player. Defines input events and binding mechanism,...
FLAP_ANGLES
static const float FLAP_ANGLES[6]
Definition: SimConstants.h:82
RoR::GfxActor::SetMaterialFlareOn
void SetMaterialFlareOn(int flare_index, bool state_on)
Definition: GfxActor.cpp:293
Ogre
Definition: ExtinguishableFireAffector.cpp:35
GfxActor.h
Manager for all visuals belonging to a single actor.
RoR::AeroEngineSB::simbuf_ae_failed
bool simbuf_ae_failed
Definition: SimBuffers.h:101
RoR::PROP_ANIM_FLAG_AEPITCH
static const BitMask64_t PROP_ANIM_FLAG_AEPITCH
Definition: GfxData.h:62
RoR::SPLIT_DIFF
@ SPLIT_DIFF
Definition: Differentials.h:59
RoR::GfxActor::UpdateBeaconFlare
void UpdateBeaconFlare(Prop &prop, float dt, bool is_player_actor)
Definition: GfxActor.cpp:2065
RoR::PROP_ANIM_FLAG_TORQUE
static const BitMask64_t PROP_ANIM_FLAG_TORQUE
Definition: GfxData.h:64
RoR::Prop::pp_beacon_rot_rate
float pp_beacon_rot_rate[4]
Radians per second.
Definition: GfxData.h:192
RoR::SHOCK1
@ SHOCK1
shock1
Definition: SimData.h:107
RoR::AirbrakeGfx::abx_offset
Ogre::Vector3 abx_offset
Definition: GfxData.h:304
FlexObj.h
RoRnet::LIGHTMASK_BEACONS
@ LIGHTMASK_BEACONS
beacons on
Definition: RoRnet.h:120
RoR::PROP_ANIM_FLAG_SIGNALSTALK
static const BitMask64_t PROP_ANIM_FLAG_SIGNALSTALK
Turn indicator stalk position (-1=left, 0=off, 1=right)
Definition: GfxData.h:76
RoR::PROP_ANIM_FLAG_ROLL
static const BitMask64_t PROP_ANIM_FLAG_ROLL
Definition: GfxData.h:49
WhereFrom
std::string WhereFrom(GfxActor *gfx_actor, const std::string &doing_what)
Definition: GfxActor.cpp:76
RoR::PROP_ANIM_FLAG_PITCH
static const BitMask64_t PROP_ANIM_FLAG_PITCH
Definition: GfxData.h:50
RoR::GfxActor::FetchNumWheelNodes
int FetchNumWheelNodes() const
Definition: GfxActor.cpp:3320
AirBrake.h
fast_sqrt
float fast_sqrt(const float x)
Definition: ApproxMath.h:135
RoR::Turbojet
Definition: TurboJet.h:62
RoR::FlexBody
Flexbody = A deformable mesh; updated on CPU every frame, then uploaded to video memory.
Definition: FlexBody.h:43
Collisions.h
RoR::GfxActor::UpdateFlares
void UpdateFlares(float dt_sec, bool is_player)
Definition: GfxActor.cpp:3166
RoR::GfxActor::SetAllMeshesVisible
void SetAllMeshesVisible(bool value)
Definition: GfxActor.cpp:3281
RoR::GfxActor::m_particles_ripple
DustPool * m_particles_ripple
Definition: GfxActor.h:195
RoR::ActorState::NETWORKED_OK
@ NETWORKED_OK
not simulated (remote) actor
RoR::Prop::pp_beacon_bbs
Ogre::BillboardSet * pp_beacon_bbs[4]
Definition: GfxData.h:189
RoR::flare_t::offsetx
float offsetx
Definition: SimData.h:619
RoR::App::GetThreadPool
ThreadPool * GetThreadPool()
Definition: Application.cpp:274
RoR::Prop::pp_scene_node
Ogre::SceneNode * pp_scene_node
The pivot scene node (parented to root-node).
Definition: GfxData.h:170
RoR::PROP_ANIM_MODE_OFFSET_Y
@ PROP_ANIM_MODE_OFFSET_Y
Definition: GfxData.h:84
RoR::PROP_ANIM_MODE_NOFLIP
@ PROP_ANIM_MODE_NOFLIP
Definition: GfxData.h:87
RoR::GfxActor::UpdateFlexbodies
void UpdateFlexbodies()
Definition: GfxActor.cpp:3096
RoR::GfxActor::ResetFlexbodies
void ResetFlexbodies()
Definition: GfxActor.cpp:3119
NODE_IMMOVABLE_COLOR
const ImU32 NODE_IMMOVABLE_COLOR(0xff0033ff)
RoR::GfxActor::UpdateWheelVisuals
void UpdateWheelVisuals()
Definition: GfxActor.cpp:1909
RoR::GfxActor::UpdateNetLabels
void UpdateNetLabels(float dt)
Definition: GfxActor.cpp:2014
RoR::AeroEngineSB::simbuf_ae_ignition
bool simbuf_ae_ignition
Turbojet.
Definition: SimBuffers.h:100
RoR::Prop::pp_node_x
NodeNum_t pp_node_x
Definition: GfxData.h:164
RoR::GfxActor::getOwningActor
const ActorPtr & getOwningActor()
Definition: GfxActor.h:153
RoR::AeroEngineSB::simbuf_tj_afterburn
bool simbuf_tj_afterburn
Definition: SimBuffers.h:102
RoR::AeroEngine::getThrottle
virtual float getThrottle()=0
RoR::GfxActor::m_particles_splash
DustPool * m_particles_splash
Definition: GfxActor.h:194
RoR
Definition: AppContext.h:36
RoR::HANDLEGENERICEXCEPTION_LOGFILE
@ HANDLEGENERICEXCEPTION_LOGFILE
Definition: Application.h:549
RoR::LOCKED_DIFF
@ LOCKED_DIFF
Definition: Differentials.h:62
RoRnet::LIGHTMASK_BLINK_RIGHT
@ LIGHTMASK_BLINK_RIGHT
right blinker on
Definition: RoRnet.h:122
RoR::GfxFlaresMode::NO_LIGHTSOURCES
@ NO_LIGHTSOURCES
No light sources.
RoR::Turbojet::getAfterburnThrust
float getAfterburnThrust() const
Definition: TurboJet.h:86
RoR::Prop::pp_beacon_scene_node
Ogre::SceneNode * pp_beacon_scene_node[4]
Definition: GfxData.h:190
RoR::App::GetGfxScene
GfxScene * GetGfxScene()
Definition: Application.cpp:276
RoR::PROP_ANIM_FLAG_AILERONS
static const BitMask64_t PROP_ANIM_FLAG_AILERONS
Definition: GfxData.h:69
RoR::PROP_ANIM_FLAG_BRAKE
static const BitMask64_t PROP_ANIM_FLAG_BRAKE
Definition: GfxData.h:54
RoR::beam_t::bm_disabled
bool bm_disabled
Definition: SimData.h:350
RoR::Terrain::getWater
IWater * getWater()
Definition: Terrain.h:84
HydraxWater.h
RoR::wing_t
Definition: SimData.h:536
RoR::beam_t::L
float L
length
Definition: SimData.h:338
RoR::PROP_ANIM_FLAG_AIRBRAKE
static const BitMask64_t PROP_ANIM_FLAG_AIRBRAKE
Definition: GfxData.h:48
RoR::flare_t::noderef
NodeNum_t noderef
Definition: SimData.h:616
RoR::beam_t::bm_broken
bool bm_broken
Definition: SimData.h:351
RoR::rotator_t
Definition: SimData.h:598
RoR::GameContext::GetTerrain
const TerrainPtr & GetTerrain()
Definition: GameContext.h:117
RoR::ground_model_t::fx_colour
Ogre::ColourValue fx_colour
Definition: SimData.h:761
RoR::GfxActor::SetPropsVisible
void SetPropsVisible(bool visible)
Definition: GfxActor.cpp:2302
RoR::GfxActor::~GfxActor
~GfxActor()
Definition: GfxActor.cpp:85
NODE_TEXT_COLOR
const ImU32 NODE_TEXT_COLOR(0xffcccccf)