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
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  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_GEAR)
2519  {
2520  bool match = static_cast<int>(anim.animOpt3) == m_actor->ar_engine->GetGear();
2521  cstate += static_cast<int>(match);
2522  div++;
2523  }
2524 
2525  //shifterseq, to amimate sequentiell shifting
2526  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == SHIFTERSEQ)
2527  {
2528  float shifterseq_cstate = 0;
2529  // opt1 &opt2 = 0 this is a shifter
2530  if (!anim.lower_limit && !anim.upper_limit)
2531  {
2532  int shifter = m_simbuf.simbuf_gear;
2533  if (shifter > m_prop_anim_prev_gear)
2534  {
2535  shifterseq_cstate = 1.0f;
2536  m_prop_anim_shift_timer = 0.2f;
2537  }
2538  if (shifter < m_prop_anim_prev_gear)
2539  {
2540  shifterseq_cstate = -1.0f;
2541  m_prop_anim_shift_timer = -0.2f;
2542  }
2543  m_prop_anim_prev_gear = shifter;
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  if (m_prop_anim_shift_timer < 0.0f)
2553  {
2554  shifterseq_cstate = -1.0f;
2555  m_prop_anim_shift_timer += dt;
2556  if (m_prop_anim_shift_timer > 0.0f)
2557  m_prop_anim_shift_timer = 0.0f;
2558  }
2559  }
2560  else
2561  {
2562  // check if anim.lower_limit is a valid to get commandvalue, then get commandvalue
2563  if (anim.lower_limit >= 1.0f && anim.lower_limit <= 48.0)
2564  if (m_simbuf.simbuf_commandkey[int(anim.lower_limit)].simbuf_cmd_value > 0)
2565  shifterseq_cstate = 1.0f;
2566  // check if anim.upper_limit is a valid to get commandvalue, then get commandvalue
2567  if (anim.upper_limit >= 1.0f && anim.upper_limit <= 48.0)
2568  if (m_simbuf.simbuf_commandkey[int(anim.upper_limit)].simbuf_cmd_value > 0)
2569  shifterseq_cstate = -1.0f;
2570  }
2571 
2572 
2573  cstate += UpdateSmoothShift(anim, dt, shifterseq_cstate);
2574  div++;
2575  }
2576 
2577  //shifterman1, left/right
2578  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == SHIFTERMAN1)
2579  {
2580  float shifterman1_cstate = 0.f;
2581  int shifter = m_simbuf.simbuf_gear;
2582  if (!shifter)
2583  {
2584  shifterman1_cstate = -0.5f;
2585  }
2586  else if (shifter < 0)
2587  {
2588  shifterman1_cstate = 1.0f;
2589  }
2590  else
2591  {
2592  shifterman1_cstate = -int((shifter - 1.0) / 2.0);
2593  }
2594 
2595  cstate += UpdateSmoothShift(anim, dt, shifterman1_cstate);
2596  div++;
2597  }
2598 
2599  //shifterman2, up/down
2600  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == SHIFTERMAN2)
2601  {
2602  float shifterman2_cstate = 0.f;
2603  int shifter = m_simbuf.simbuf_gear;
2604  shifterman2_cstate = 0.5f;
2605  if (shifter < 0)
2606  {
2607  shifterman2_cstate = 1.0f;
2608  }
2609  if (shifter > 0)
2610  {
2611  shifterman2_cstate = shifter % 2;
2612  }
2613 
2614  cstate += UpdateSmoothShift(anim, dt, shifterman2_cstate);
2615  div++;
2616  }
2617 
2618  //shifterlinear, to amimate cockpit gearselect gauge and autotransmission stick
2619  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == SHIFTERLIN)
2620  {
2621  float shifterlin_cstate = 0.f;
2622  int shifter = m_simbuf.simbuf_gear;
2623  int numgears = m_simbuf.simbuf_num_gears;
2624  shifterlin_cstate -= (shifter + 2.0) / (numgears + 2.0);
2625 
2626  cstate += UpdateSmoothShift(anim, dt, shifterlin_cstate);
2627  div++;
2628  }
2629 
2630  //autoshifterlin, autotransmission stick with only R/N/D positions
2631  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == AUTOSHIFTERLIN)
2632  {
2633  float shifterlin_cstate = 0.f;
2634  int shifter = std::min(m_simbuf.simbuf_gear, 1); // Clamp forward gears to 1
2635  int numgears = 1; // Number of forward gears
2636  shifterlin_cstate -= (shifter + 2.0) / (numgears + 2.0);
2637 
2638  cstate += UpdateSmoothShift(anim, dt, shifterlin_cstate);
2639  div++;
2640  }
2641 
2642  //parking brake
2643  if (anim.animFlags & PROP_ANIM_FLAG_PBRAKE)
2644  {
2645  float pbrake = static_cast<float>(m_simbuf.simbuf_parking_brake); // Bool --> float
2646  cstate -= pbrake;
2647  div++;
2648  }
2649 
2650  //speedo ( scales with speedomax )
2651  if (anim.animFlags & PROP_ANIM_FLAG_SPEEDO)
2652  {
2653  float speedo = m_simbuf.simbuf_wheel_speed / m_simbuf.simbuf_speedo_highest_kph;
2654  cstate -= speedo * 3.0f;
2655  div++;
2656  }
2657 
2658  //engine tacho ( scales with maxrpm, default is 3500 )
2659  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_TACHO)
2660  {
2661  float tacho = m_simbuf.simbuf_engine_rpm / m_simbuf.simbuf_engine_max_rpm;
2662  cstate -= tacho;
2663  div++;
2664  }
2665 
2666  //turbo
2667  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_TURBO)
2668  {
2669  float turbo = m_simbuf.simbuf_engine_turbo_psi * 3.34;
2670  cstate -= turbo / 67.0f;
2671  div++;
2672  }
2673 
2674  //brake
2675  if (anim.animFlags & PROP_ANIM_FLAG_BRAKE)
2676  {
2677  float brakes = m_simbuf.simbuf_brake;
2678  cstate -= brakes;
2679  div++;
2680  }
2681 
2682  //accelerator
2683  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_ACCEL)
2684  {
2685  float accel = m_simbuf.simbuf_engine_accel;
2686  cstate -= accel + 0.06f;
2687  //( small correction, get acc is nver smaller then 0.06.
2688  div++;
2689  }
2690 
2691  //clutch
2692  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_CLUTCH)
2693  {
2694  float clutch = m_simbuf.simbuf_clutch;
2695  cstate -= fabs(1.0f - clutch);
2696  div++;
2697  }
2698 
2699  //turn indicator stalk
2701  {
2702  float signal = 0.0f;
2703  if (m_simbuf.simbuf_lightmask & RoRnet::LIGHTMASK_BLINK_LEFT)
2704  signal = -1.0f;
2705  if (m_simbuf.simbuf_lightmask & RoRnet::LIGHTMASK_BLINK_RIGHT)
2706  signal = 1.0f;
2707  cstate -= signal;
2708  div++;
2709  }
2710 
2711  //aeroengines rpm + throttle + torque ( turboprop ) + pitch ( turboprop ) + status + fire
2712  // `anim.animOpt3` is aeroengine number (starting from 1)
2713  if (anim.animOpt3 > 0.f && anim.animOpt3 <= float(m_simbuf.simbuf_aeroengines.size()))
2714  {
2715  const int aenum = int(anim.animOpt3 - 1.f);
2716  if (anim.animFlags & PROP_ANIM_FLAG_RPM)
2717  {
2718  float angle;
2719  float pcent = m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_rpmpc;
2720  if (pcent < 60.0)
2721  angle = -5.0 + pcent * 1.9167;
2722  else if (pcent < 110.0)
2723  angle = 110.0 + (pcent - 60.0) * 4.075;
2724  else
2725  angle = 314.0;
2726  cstate -= angle / 314.0f;
2727  div++;
2728  }
2730  {
2731  float throttle = m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_throttle;
2732  cstate -= throttle;
2733  div++;
2734  }
2735 
2736  if (m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_type == AeroEngineType::AE_XPROP)
2737  {
2739  {
2740  cstate = m_simbuf.simbuf_aeroengines[aenum].simbuf_tp_aetorque / 120.0f;
2741  div++;
2742  }
2743 
2744  if (anim.animFlags & PROP_ANIM_FLAG_AEPITCH)
2745  {
2746  cstate = m_simbuf.simbuf_aeroengines[aenum].simbuf_tp_aepitch / 120.0f;
2747  div++;
2748  }
2749  }
2750 
2752  {
2753  if (!m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_ignition)
2754  cstate = 0.0f;
2755  else
2756  cstate = 0.5f;
2757  if (m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_failed)
2758  cstate = 1.0f;
2759  div++;
2760  }
2761  }
2762 
2763  const Ogre::Vector3 node0_pos = this->GetSimNodeBuffer()[0].AbsPosition;
2764  const Ogre::Vector3 node0_velo = m_simbuf.simbuf_node0_velo;
2765 
2766  //airspeed indicator
2768  {
2769  float ground_speed_kt = node0_velo.length() * 1.9438;
2770  float altitude = node0_pos.y;
2771 
2772  float sea_level_pressure = 101325; //in Pa
2773 
2774  float airpressure = sea_level_pressure * pow(1.0 - 0.0065 * altitude / 288.15, 5.24947); //in Pa
2775  float airdensity = airpressure * 0.0000120896;//1.225 at sea level
2776  float kt = ground_speed_kt * sqrt(airdensity / 1.225);
2777  cstate -= kt / 100.0f;
2778  div++;
2779  }
2780 
2781  //vvi indicator
2782  if (anim.animFlags & PROP_ANIM_FLAG_VVI)
2783  {
2784  float vvi = node0_velo.y * 196.85;
2785  // limit vvi scale to +/- 6m/s
2786  cstate -= vvi / 6000.0f;
2787  if (cstate >= 1.0f)
2788  cstate = 1.0f;
2789  if (cstate <= -1.0f)
2790  cstate = -1.0f;
2791  div++;
2792  }
2793 
2794  //altimeter
2796  {
2797  //altimeter indicator 1k oscillating
2798  if (anim.animOpt3 == 3.0f)
2799  {
2800  float altimeter = (node0_pos.y * 1.1811) / 360.0f;
2801  int alti_int = int(altimeter);
2802  float alti_mod = (altimeter - alti_int);
2803  cstate -= alti_mod;
2804  }
2805 
2806  //altimeter indicator 10k oscillating
2807  if (anim.animOpt3 == 2.0f)
2808  {
2809  float alti = node0_pos.y * 1.1811 / 3600.0f;
2810  int alti_int = int(alti);
2811  float alti_mod = (alti - alti_int);
2812  cstate -= alti_mod;
2813  if (cstate <= -1.0f)
2814  cstate = -1.0f;
2815  }
2816 
2817  //altimeter indicator 100k limited
2818  if (anim.animOpt3 == 1.0f)
2819  {
2820  float alti = node0_pos.y * 1.1811 / 36000.0f;
2821  cstate -= alti;
2822  if (cstate <= -1.0f)
2823  cstate = -1.0f;
2824  }
2825  div++;
2826  }
2827 
2828  //AOA
2829  if (anim.animFlags & PROP_ANIM_FLAG_AOA)
2830  {
2831  float aoa = m_simbuf.simbuf_wing4_aoa / 25.f;
2832  if ((node0_velo.length() * 1.9438) < 10.0f)
2833  aoa = 0;
2834  cstate -= aoa;
2835  if (cstate <= -1.0f)
2836  cstate = -1.0f;
2837  if (cstate >= 1.0f)
2838  cstate = 1.0f;
2839  div++;
2840  }
2841 
2842  Ogre::Vector3 cam_pos = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_pos ].AbsPosition;
2843  Ogre::Vector3 cam_roll = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_roll].AbsPosition;
2844  Ogre::Vector3 cam_dir = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_dir ].AbsPosition;
2845 
2846  // roll
2847  if (anim.animFlags & PROP_ANIM_FLAG_ROLL)
2848  {
2849  Ogre::Vector3 rollv = (cam_pos - cam_roll).normalisedCopy();
2850  Ogre::Vector3 dirv = (cam_pos - cam_dir).normalisedCopy();
2851  Ogre::Vector3 upv = dirv.crossProduct(-rollv);
2852  float rollangle = asin(rollv.dotProduct(Ogre::Vector3::UNIT_Y));
2853  // rad to deg
2854  rollangle = Ogre::Math::RadiansToDegrees(rollangle);
2855  // flip to other side when upside down
2856  if (upv.y < 0)
2857  rollangle = 180.0f - rollangle;
2858  cstate = rollangle / 180.0f;
2859  // data output is -0.5 to 1.5, normalize to -1 to +1 without changing the zero position.
2860  // this is vital for the animator beams and does not effect the animated props
2861  if (cstate >= 1.0f)
2862  cstate = cstate - 2.0f;
2863  div++;
2864  }
2865 
2866  // pitch
2867  if (anim.animFlags & PROP_ANIM_FLAG_PITCH)
2868  {
2869  Ogre::Vector3 dirv = (cam_pos - cam_dir).normalisedCopy();
2870  float pitchangle = asin(dirv.dotProduct(Ogre::Vector3::UNIT_Y));
2871  // radian to degrees with a max cstate of +/- 1.0
2872  cstate = (Ogre::Math::RadiansToDegrees(pitchangle) / 90.0f);
2873  div++;
2874  }
2875 
2876  // airbrake
2878  {
2879  float airbrake = static_cast<float>(m_simbuf.simbuf_airbrake_state);
2880  // cstate limited to -1.0f
2881  cstate -= airbrake / 5.0f;
2882  div++;
2883  }
2884 
2885  //flaps
2886  if (anim.animFlags & PROP_ANIM_FLAG_FLAP)
2887  {
2888  float flaps = FLAP_ANGLES[m_simbuf.simbuf_aero_flap_state];
2889  // cstate limited to -1.0f
2890  cstate = flaps;
2891  div++;
2892  }
2893 }
2894 
2896 {
2897  int prop_anim_key_index = 0;
2898 
2899  for (Prop& prop: m_props)
2900  {
2901  int animnum = 0;
2902  float rx = 0.0f;
2903  float ry = 0.0f;
2904  float rz = 0.0f;
2905 
2906  for (PropAnim& anim: prop.pp_animations)
2907  {
2908  float cstate = 0.0f;
2909  int div = 0.0f;
2910 
2911  this->CalcPropAnimation(anim, cstate, div, dt);
2912 
2913  // key triggered animations - state determined in simulation
2914  if (anim.animFlags & PROP_ANIM_FLAG_EVENT)
2915  {
2916  ROR_ASSERT(prop_anim_key_index < (int)m_simbuf.simbuf_prop_anim_keys.size());
2917  const bool anim_active = m_simbuf.simbuf_prop_anim_keys[prop_anim_key_index++].simbuf_anim_active;
2918  cstate += (float)anim_active;
2919  }
2920 
2921  // dashboard animations - state determined in simulation
2923  {
2924  int link_id = (int)anim.animOpt3;
2925  ROR_ASSERT(link_id < DD_MAX);
2926  cstate += m_actor->ar_dashboard->getNumeric(link_id);
2927  }
2928 
2929  //propanimation placed here to avoid interference with existing hydros(cstate) and permanent prop animation
2930  //land vehicle steering
2932  cstate += m_simbuf.simbuf_hydro_dir_state;
2933  //aileron
2935  cstate += m_simbuf.simbuf_hydro_aileron_state;
2936  //elevator
2938  cstate += m_simbuf.simbuf_hydro_elevator_state;
2939  //rudder
2940  if (anim.animFlags & PROP_ANIM_FLAG_ARUDDER)
2941  cstate += m_simbuf.simbuf_hydro_aero_rudder_state;
2942  //permanent
2944  cstate += 1.0f;
2945 
2946  cstate *= anim.animratio;
2947 
2948  // autoanimate noflip_bouncer
2949  if (anim.animOpt5)
2950  cstate *= (anim.animOpt5);
2951 
2952  //rotate prop
2954  {
2955  float limiter = 0.0f;
2956  // This code was formerly executed within a fixed timestep of 0.5ms and finetuned accordingly.
2957  // This is now taken into account by factoring in the respective fraction of the variable timestep.
2958  float const dt_frac = dt * 2000.f;
2960  {
2961  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
2962  {
2963  prop.pp_rota.x += cstate * dt_frac;
2964  limiter = prop.pp_rota.x;
2965  }
2966  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
2967  {
2968  prop.pp_rota.y += cstate * dt_frac;
2969  limiter = prop.pp_rota.y;
2970  }
2971  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
2972  {
2973  prop.pp_rota.z += cstate * dt_frac;
2974  limiter = prop.pp_rota.z;
2975  }
2976  }
2977  else
2978  {
2979  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
2980  rx += cstate;
2981  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
2982  ry += cstate;
2983  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
2984  rz += cstate;
2985  }
2986 
2987  bool limiterchanged = false;
2988  // check if a positive custom limit is set to evaluate/calc flip back
2989 
2990  if (limiter > anim.upper_limit)
2991  {
2992  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
2993  {
2994  limiter = anim.upper_limit; // stop at limit
2995  anim.animOpt5 *= -1.0f; // change cstate multiplier if bounce is set
2996  }
2997  else
2998  {
2999  limiter = anim.lower_limit; // flip to other side at limit
3000  }
3001  limiterchanged = true;
3002  }
3003 
3004  if (limiter < anim.lower_limit)
3005  {
3006  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3007  {
3008  limiter = anim.lower_limit; // stop at limit
3009  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
3010  }
3011  else
3012  {
3013  limiter = anim.upper_limit; // flip to other side at limit
3014  }
3015  limiterchanged = true;
3016  }
3017 
3018  if (limiterchanged)
3019  {
3020  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
3021  prop.pp_rota.x = limiter;
3022  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
3023  prop.pp_rota.y = limiter;
3024  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
3025  prop.pp_rota.z = limiter;
3026  }
3027  }
3028 
3029  //offset prop
3030 
3032  {
3033  float offset = 0.0f;
3034  float autooffset = 0.0f;
3035 
3036  if (anim.animMode & PROP_ANIM_MODE_OFFSET_X)
3037  offset = prop.pp_offset_orig.x;
3038  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Y)
3039  offset = prop.pp_offset_orig.y;
3040  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Z)
3041  offset = prop.pp_offset_orig.z;
3042 
3044  {
3045  // This code was formerly executed within a fixed timestep of 0.5ms and finetuned accordingly.
3046  // This is now taken into account by factoring in the respective fraction of the variable timestep.
3047  float const dt_frac = dt * 2000.f;
3048  autooffset = offset + cstate * dt_frac;
3049 
3050  if (autooffset > anim.upper_limit)
3051  {
3052  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3053  {
3054  autooffset = anim.upper_limit; // stop at limit
3055  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
3056  }
3057  else
3058  {
3059  autooffset = anim.lower_limit; // flip to other side at limit
3060  }
3061  }
3062 
3063  if (autooffset < anim.lower_limit)
3064  {
3065  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3066  {
3067  autooffset = anim.lower_limit; // stop at limit
3068  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
3069  }
3070  else
3071  {
3072  autooffset = anim.upper_limit; // flip to other side at limit
3073  }
3074  }
3075  }
3076  offset += cstate;
3077 
3078  if (anim.animMode & PROP_ANIM_MODE_OFFSET_X)
3079  {
3080  prop.pp_offset.x = offset;
3082  prop.pp_offset_orig.x = autooffset;
3083  }
3084  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Y)
3085  {
3086  prop.pp_offset.y = offset;
3088  prop.pp_offset_orig.y = autooffset;
3089  }
3090  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Z)
3091  {
3092  prop.pp_offset.z = offset;
3094  prop.pp_offset_orig.z = autooffset;
3095  }
3096  }
3097  animnum++;
3098  }
3099  //recalc the quaternions with final stacked rotation values ( rx, ry, rz )
3100  rx += prop.pp_rota.x;
3101  ry += prop.pp_rota.y;
3102  rz += prop.pp_rota.z;
3103 
3104  prop.pp_rot = Ogre::Quaternion(Ogre::Degree(rz), Ogre::Vector3::UNIT_Z) *
3105  Ogre::Quaternion(Ogre::Degree(ry), Ogre::Vector3::UNIT_Y) *
3106  Ogre::Quaternion(Ogre::Degree(rx), Ogre::Vector3::UNIT_X);
3107  }
3108 }
3109 
3111 {
3112  std::sort(m_flexbodies.begin(), m_flexbodies.end(), [](FlexBody* a, FlexBody* b) { return a->getVertexCount() > b->getVertexCount(); });
3113 }
3114 
3116 {
3117  m_flexbody_tasks.clear();
3118 
3119  for (FlexBody* fb: m_flexbodies)
3120  {
3121  // Update visibility (same logic as props)
3122  const bool visible = (fb->fb_camera_mode_active == CAMERA_MODE_ALWAYS_VISIBLE || fb->fb_camera_mode_active == m_simbuf.simbuf_cur_cinecam);
3123  fb->setVisible(visible);
3124 
3125  // Update visible on background thread
3126  if (fb->isVisible())
3127  {
3128  auto func = std::function<void()>([fb]()
3129  {
3130  fb->computeFlexbody();
3131  });
3132  auto task_handle = App::GetThreadPool()->RunTask(func);
3133  m_flexbody_tasks.push_back(task_handle);
3134  }
3135  }
3136 }
3137 
3139 {
3140  for (FlexBody* fb: m_flexbodies)
3141  {
3142  fb->reset();
3143  }
3144 }
3145 
3147 {
3148  for (FlexBody* fb: m_flexbodies)
3149  {
3150  fb->setVisible(visible);
3151  }
3152 }
3153 
3155 {
3156  for (auto& task: m_flexbody_tasks)
3157  {
3158  task->join();
3159  }
3160  for (FlexBody* fb: m_flexbodies)
3161  {
3162  if (fb->isVisible())
3163  {
3164  fb->updateFlexbodyVertexBuffers();
3165  }
3166  }
3167 }
3168 
3169 // internal helper
3170 bool ShouldEnableLightSource(FlareType type, bool is_player)
3171 {
3172  switch (App::gfx_flares_mode->getEnum<GfxFlaresMode>())
3173  {
3175  return is_player && (type == FlareType::HEADLIGHT);
3177  return (type == FlareType::HEADLIGHT);
3179  return true;
3180  default:
3181  return false;
3182  }
3183 }
3184 
3185 void RoR::GfxActor::UpdateFlares(float dt_sec, bool is_player)
3186 {
3187  // Flare states are determined in simulation, this function only applies them to OGRE objects
3188  // ------------------------------------------------------------------------------------------
3189 
3190  NodeSB* nodes = this->GetSimNodeBuffer();
3191 
3192  int num_flares = static_cast<int>(m_actor->ar_flares.size());
3193  for (int i=0; i<num_flares; ++i)
3194  {
3195  flare_t& flare = m_actor->ar_flares[i];
3196 
3197  // Skip placeholder flares (removed via Tuning system)
3198  if (!flare.bbs)
3199  {
3200  continue;
3201  }
3202 
3203  this->SetMaterialFlareOn(i, flare.intensity > 0.3);
3204 
3205  // The billboard flare
3206  flare.snode->setVisible(flare.intensity > 0);
3207 
3208  // The light source
3209  if (flare.light)
3210  {
3211  flare.light->setVisible(flare.intensity > 0 && ShouldEnableLightSource(flare.fl_type, is_player));
3212  }
3213 
3214  Ogre::Vector3 normal = (nodes[flare.nodey].AbsPosition - nodes[flare.noderef].AbsPosition).crossProduct(nodes[flare.nodex].AbsPosition - nodes[flare.noderef].AbsPosition);
3215  normal.normalise();
3216  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);
3217  Ogre::Vector3 vdir = mposition - App::GetCameraManager()->GetCameraNode()->getPosition();
3218  float vlen = vdir.length();
3219  // not visible from 500m distance
3220  if (vlen > 500.0)
3221  {
3222  flare.snode->setVisible(false);
3223  continue;
3224  }
3225  //normalize
3226  vdir = vdir / vlen;
3227  float amplitude = normal.dotProduct(vdir);
3228  flare.snode->setPosition(mposition - 0.1 * amplitude * normal * flare.offsetz);
3229  flare.snode->setDirection(normal);
3230  float fsize = flare.size * flare.intensity;
3231  if (fsize < 0)
3232  {
3233  amplitude = 1;
3234  fsize *= -1;
3235  }
3236  if (flare.light)
3237  {
3238  flare.light->setPosition(mposition - 0.2 * amplitude * normal);
3239  // point the real light towards the ground a bit
3240  flare.light->setDirection(-normal - Ogre::Vector3(0, 0.2, 0));
3241  }
3242  if (flare.intensity > 0)
3243  {
3244  if (amplitude > 0)
3245  {
3246  flare.bbs->setDefaultDimensions(amplitude * fsize, amplitude * fsize);
3247  flare.snode->setVisible(true);
3248  }
3249  else
3250  {
3251  flare.snode->setVisible(false);
3252  }
3253  }
3254  }
3255 }
3256 
3258 {
3259  // Cab mesh
3260  if (m_cab_scene_node != nullptr)
3261  {
3262  static_cast<Ogre::Entity*>(m_cab_scene_node->getAttachedObject(0))->setCastShadows(value);
3263  }
3264 
3265  // Props
3266  for (Prop& prop: m_props)
3267  {
3268  if (prop.pp_mesh_obj != nullptr && prop.pp_mesh_obj->getEntity())
3269  prop.pp_mesh_obj->getEntity()->setCastShadows(value);
3270  if (prop.pp_wheel_mesh_obj != nullptr && prop.pp_wheel_mesh_obj->getEntity())
3271  prop.pp_wheel_mesh_obj->getEntity()->setCastShadows(value);
3272  }
3273 
3274  // Wheels
3275  for (WheelGfx& wheel: m_wheels)
3276  {
3277  static_cast<Ogre::Entity*>(wheel.wx_scenenode->getAttachedObject(0))->setCastShadows(value);
3278  }
3279 
3280  // Softbody beams
3281  for (BeamGfx& rod: m_gfx_beams)
3282  {
3283  static_cast<Ogre::Entity*>(rod.rod_scenenode->getAttachedObject(0))->setCastShadows(value);
3284  }
3285 
3286  // Flexbody meshes
3287  for (FlexBody* fb: m_flexbodies)
3288  {
3289  fb->setFlexbodyCastShadow(value);
3290  }
3291 }
3292 
3293 void RoR::GfxActor::RegisterCabMesh(Ogre::Entity* ent, Ogre::SceneNode* snode, FlexObj* flexobj)
3294 {
3295  m_cab_mesh = flexobj;
3296  m_cab_entity = ent;
3297  m_cab_scene_node = snode;
3298 }
3299 
3301 {
3302  if (m_cab_entity != nullptr)
3303  {
3304  m_cab_entity->setVisible(visible);
3305  }
3306  this->SetWheelsVisible(visible);
3307  this->SetPropsVisible(visible);
3308  this->SetFlexbodyVisible(visible);
3309  this->SetWingsVisible(visible);
3310  this->SetRodsVisible(visible);
3311  this->SetAeroEnginesVisible(visible);
3312 }
3313 
3315 {
3316  for (int i = 0; i < m_actor->ar_num_wings; ++i)
3317  {
3318  m_actor->ar_wings[i].cnode->setVisible(visible);
3319  }
3320 
3321  for (size_t i=0; i< m_actor->ar_airbrakes.size(); ++i)
3322  {
3323  m_gfx_airbrakes[i].abx_scenenode->setVisible(visible);
3324  }
3325 }
3326 
3328 {
3329  for (int i = 0; i < m_actor->ar_num_wings; ++i)
3330  {
3331  wing_t& wing = m_actor->ar_wings[i];
3332  wing.cnode->setPosition(wing.fa->updateVerticesGfx(this));
3333  wing.fa->uploadVertices();
3334  }
3335 }
3336 
3338 {
3339  int count = 0;
3340  for (const Prop& prop : m_props)
3341  {
3342  if (prop.pp_beacon_type != 0)
3343  {
3344  count++;
3345  }
3346  }
3347  return count;
3348 }
3349 
3350 void RoR::GfxActor::SetNodeHot(NodeNum_t nodenum, bool value)
3351 {
3352  for (NodeGfx& nfx : m_gfx_nodes)
3353  {
3354  if (nfx.nx_node_idx == nodenum)
3355  {
3356  nfx.nx_is_hot = value;
3357  }
3358  }
3359 }
3360 
3361 void RoR::GfxActor::RemoveBeam(int beam_index)
3362 {
3363  auto itor = m_gfx_beams.begin();
3364  auto endi = m_gfx_beams.end();
3365  while (itor != endi)
3366  {
3367  if (itor->rod_beam_index == beam_index)
3368  {
3369  // Destroy OGRE objects
3370  if (itor->rod_scenenode)
3371  {
3372  if (itor->rod_scenenode->getAttachedObject(0))
3373  {
3374  Ogre::Entity* ent = static_cast<Ogre::Entity*>(itor->rod_scenenode->getAttachedObject(0));
3375  if (ent)
3376  {
3377  ent->detachFromParent();
3378  App::GetGfxScene()->GetSceneManager()->destroyEntity(ent);
3379  }
3380  }
3381 
3382  App::GetGfxScene()->GetSceneManager()->destroySceneNode(itor->rod_scenenode);
3383  itor->rod_scenenode = nullptr;
3384  }
3385 
3386  // Destroy the beam visuals
3387  m_gfx_beams.erase(itor);
3388  return;
3389  }
3390  itor++;
3391  }
3392 }
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:224
RoR::flare_t::offsety
float offsety
Definition: SimData.h:621
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:222
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:539
RoR::rotator_t::nodes2
NodeNum_t nodes2[4]
Definition: SimData.h:603
RoR::PROP_ANIM_FLAG_AIRSPEED
const PropAnimFlag_t PROP_ANIM_FLAG_AIRSPEED
Definition: GfxData.h:44
RoR::PROP_ANIM_FLAG_TORQUE
const PropAnimFlag_t PROP_ANIM_FLAG_TORQUE
Definition: GfxData.h:65
RoR::PROP_ANIM_FLAG_SHIFTER
const PropAnimFlag_t PROP_ANIM_FLAG_SHIFTER
'shifterman1, shifterman2, sequential, shifterlin, autoshifterlin'; animOpt3: see RoR::ShifterPropAni...
Definition: GfxData.h:61
RoR::GfxActor::UpdateAirbrakes
void UpdateAirbrakes()
Definition: GfxActor.cpp:1961
RoR::SHOCK3
@ SHOCK3
shock3
Definition: SimData.h:110
RoR::PropAnim::upper_limit
float upper_limit
The upper limit for the animation.
Definition: GfxData.h:153
RoR::PROP_ANIM_MODE_OFFSET_X
const PropAnimMode_t PROP_ANIM_MODE_OFFSET_X
Definition: GfxData.h:85
RoR::GfxActor::RemoveBeam
void RemoveBeam(int beam_index)
Definition: GfxActor.cpp:3361
RoR::wing_t::cnode
Ogre::SceneNode * cnode
Definition: SimData.h:540
RoR::node_t::Velocity
Ogre::Vector3 Velocity
Definition: SimData.h:295
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::PROP_ANIM_FLAG_AIRBRAKE
const PropAnimFlag_t PROP_ANIM_FLAG_AIRBRAKE
Definition: GfxData.h:49
RoR::PROP_ANIM_FLAG_AOA
const PropAnimFlag_t PROP_ANIM_FLAG_AOA
Definition: GfxData.h:47
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:624
RoR::flare_t::size
float size
Definition: SimData.h:632
RoR::GfxActor::UpdateSimDataBuffer
void UpdateSimDataBuffer()
Copies sim. data from Actor to GfxActor for later update.
Definition: GfxActor.cpp:1727
RoR::Actor::ar_filename
std::string ar_filename
Attribute; filled at spawn.
Definition: Actor.h:425
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:323
RoR::BeamGfx
Visuals of softbody beam (beam_t struct); Partially updated along with SimBuffer.
Definition: GfxData.h:276
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:326
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::PROP_ANIM_FLAG_BRAKE
const PropAnimFlag_t PROP_ANIM_FLAG_BRAKE
Definition: GfxData.h:55
RoR::GfxActor::SetFlexbodyVisible
void SetFlexbodyVisible(bool visible)
Definition: GfxActor.cpp:3146
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:294
RoR::Prop::pp_beacon_rot_angle
float pp_beacon_rot_angle[4]
Radians.
Definition: GfxData.h:195
RoR::Prop::pp_offset
Ogre::Vector3 pp_offset
Definition: GfxData.h:168
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::PROP_ANIM_MODE_ROTA_Z
const PropAnimMode_t PROP_ANIM_MODE_ROTA_Z
Definition: GfxData.h:84
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:152
RoR::PROP_ANIM_FLAG_DASHBOARD
const PropAnimFlag_t PROP_ANIM_FLAG_DASHBOARD
Used with dashboard system inputs, see enum DashData in file DashBoardManager.h.
Definition: GfxData.h:76
RoR::beam_t::p1
node_t * p1
Definition: SimData.h:335
DashBoardManager.h
RoR::VideoCamera::vcam_render_tex
Ogre::TexturePtr vcam_render_tex
Definition: GfxData.h:245
RoR::DebugViewType::DEBUGVIEW_NONE
@ DEBUGVIEW_NONE
RoR::GfxActor::ToggleDebugView
void ToggleDebugView()
Definition: GfxActor.cpp:1520
RoR::PropAnim::animMode
PropAnimMode_t animMode
Definition: GfxData.h:142
RoR::Actor::ar_instance_id
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition: Actor.h:376
SkyManager.h
Renderdash.h
RoR::AeroEngineSB::simbuf_tp_aepitch
float simbuf_tp_aepitch
Turboprop pitch, used by animation "aepitch".
Definition: SimBuffers.h:97
RoR::PropAnim::animFlags
PropAnimFlag_t animFlags
Definition: GfxData.h:141
RoR::node_t::nd_under_water
bool nd_under_water
State; GFX hint.
Definition: SimData.h:320
RoR::GfxActor::RegisterCabMaterial
void RegisterCabMaterial(Ogre::MaterialPtr mat, Ogre::MaterialPtr mat_trans)
Definition: GfxActor.cpp:357
RoR::PROP_ANIM_MODE_ROTA_X
const PropAnimMode_t PROP_ANIM_MODE_ROTA_X
Definition: GfxData.h:82
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:421
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::GfxActor::countBeaconProps
int countBeaconProps() const
Definition: GfxActor.cpp:3337
RoR::DebugViewType
DebugViewType
Definition: GfxData.h:101
RoR::HandleGenericException
void HandleGenericException(const std::string &from, BitMask_t flags)
Definition: Application.cpp:369
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:190
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:626
RoR::Turboprop::pitch
float pitch
Definition: TurboProp.h:43
RoR::GUIManager::IsGuiHidden
bool IsGuiHidden() const
Definition: GUIManager.h:142
RoR::PROP_ANIM_MODE_NOFLIP
const PropAnimMode_t PROP_ANIM_MODE_NOFLIP
Definition: GfxData.h:89
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:350
RoR::rotator_t::axis2
NodeNum_t axis2
Definition: SimData.h:605
NODE_MASS_TEXT_COLOR
const ImU32 NODE_MASS_TEXT_COLOR(0xff77bb66)
RoR::Collisions::FX_DUSTY
@ FX_DUSTY
Definition: Collisions.h:90
RoR::WheelGfx
Definition: GfxData.h:292
RoR::DebugViewType::DEBUGVIEW_SUBMESH
@ DEBUGVIEW_SUBMESH
RoR::VideoCamera::vcam_render_target
Ogre::RenderTexture * vcam_render_target
Definition: GfxData.h:244
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:604
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:171
RoR::FlareType
FlareType
Definition: SimData.h:229
RoR::flare_t::nodey
NodeNum_t nodey
Definition: SimData.h:619
RoR::GfxActor::UpdateCabMesh
void UpdateCabMesh()
Definition: GfxActor.cpp:1901
RoR::GfxActor::FinishFlexbodyTasks
void FinishFlexbodyTasks()
Definition: GfxActor.cpp:3154
RoR::AeroEngine
Definition: AeroEngine.h:36
RoR::PROP_ANIM_FLAG_PBRAKE
const PropAnimFlag_t PROP_ANIM_FLAG_PBRAKE
Definition: GfxData.h:59
RoR::App::diag_hide_beam_stress
CVar * diag_hide_beam_stress
Definition: Application.cpp:153
RoR::PROP_ANIM_FLAG_ARUDDER
const PropAnimFlag_t PROP_ANIM_FLAG_ARUDDER
Definition: GfxData.h:71
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:293
RoR::PROP_ANIM_FLAG_RPM
const PropAnimFlag_t PROP_ANIM_FLAG_RPM
Definition: GfxData.h:53
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:190
RoR::GfxActor::SortFlexbodies
void SortFlexbodies()
Definition: GfxActor.cpp:3110
RoR::PROP_ANIM_FLAG_ALTIMETER
const PropAnimFlag_t PROP_ANIM_FLAG_ALTIMETER
Definition: GfxData.h:46
RoR::SHIFTERSEQ
@ SHIFTERSEQ
Definition: GfxData.h:133
RoR::PROP_ANIM_MODE_ROTA_Y
const PropAnimMode_t PROP_ANIM_MODE_ROTA_Y
Definition: GfxData.h:83
OgreImGui.h
RefCountingObjectPtr< Actor >
RoR::World2ScreenConverter::Convert
Ogre::Vector3 Convert(Ogre::Vector3 world_pos)
Definition: Utils.h:89
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:622
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:140
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:3350
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_STEERING
const PropAnimFlag_t PROP_ANIM_FLAG_STEERING
Definition: GfxData.h:68
RoR::GfxFlaresMode::ALL_VEHICLES_HEAD_ONLY
@ ALL_VEHICLES_HEAD_ONLY
All vehicles, main lights.
RoR::PROP_ANIM_FLAG_GEAR
const PropAnimFlag_t PROP_ANIM_FLAG_GEAR
'gearreverse' (animOpt3=-1), 'gearneutral' (animOpt3=0), 'gear#' (animOpt3=#)
Definition: GfxData.h:78
EngineSim.h
RoR::VCTYPE_MIRROR_PROP_LEFT
@ VCTYPE_MIRROR_PROP_LEFT
The classic 'special prop/rear view mirror'.
Definition: GfxData.h:225
BEAM_THICKNESS
const float BEAM_THICKNESS(1.2f)
RoR::PropAnim::shifterStep
float shifterStep
Definition: GfxData.h:157
NODE_IMMOVABLE_RADIUS
const float NODE_IMMOVABLE_RADIUS(2.8f)
RoR::Prop::pp_node_ref
NodeNum_t pp_node_ref
Definition: GfxData.h:165
RoR::AeroEngineSB::simbuf_tp_aetorque
float simbuf_tp_aetorque
Turboprop torque, used by animation "aetorque".
Definition: SimBuffers.h:96
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:138
RoR::GfxActor::UpdateParticles
void UpdateParticles(float dt_sec)
Definition: GfxActor.cpp:542
RoR::GfxActor::UpdatePropAnimations
void UpdatePropAnimations(float dt)
Definition: GfxActor.cpp:2895
RoR::GetImDummyFullscreenWindow
ImDrawList * GetImDummyFullscreenWindow(const std::string &name="RoR_TransparentFullscreenWindow")
Definition: GUIUtils.cpp:356
RoR::Turboprop::indicated_torque
float indicated_torque
Definition: TurboProp.h:44
RoR::Prop::pp_node_y
NodeNum_t pp_node_y
Definition: GfxData.h:167
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:461
RoR::DebugViewType::DEBUGVIEW_SLIDENODES
@ DEBUGVIEW_SLIDENODES
RoR::beam_t
Simulation: An edge in the softbody structure.
Definition: SimData.h:330
SlideNode.h
RoR::wheel_t::debug_slip
Ogre::Vector3 debug_slip
Definition: SimData.h:460
RoR::AeroEngineSB::simbuf_ae_rpm
float simbuf_ae_rpm
Definition: SimBuffers.h:93
RoR::PROP_ANIM_FLAG_AETORQUE
const PropAnimFlag_t PROP_ANIM_FLAG_AETORQUE
Definition: GfxData.h:62
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::PROP_ANIM_FLAG_PERMANENT
const PropAnimFlag_t PROP_ANIM_FLAG_PERMANENT
Definition: GfxData.h:74
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:347
RoR::GfxActor::UpdateAeroEngines
void UpdateAeroEngines()
Definition: GfxActor.cpp:2006
RoR::Turboprop
Definition: TurboProp.h:38
RoR::NodeGfx
Gfx attributes/state of a softbody node.
Definition: GfxData.h:252
RoR::GfxFlaresMode::ALL_VEHICLES_ALL_LIGHTS
@ ALL_VEHICLES_ALL_LIGHTS
All vehicles, all lights.
RoR::PROP_ANIM_MODE_AUTOANIMATE
const PropAnimMode_t PROP_ANIM_MODE_AUTOANIMATE
Definition: GfxData.h:88
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:193
RoR::GfxActor::GetActorState
int GetActorState() const
Definition: GfxActor.cpp:1959
RoR::OPEN_DIFF
@ OPEN_DIFF
Definition: Differentials.h:60
BEAM_HYDRO_THICKNESS
const float BEAM_HYDRO_THICKNESS(1.4f)
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:440
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::PROP_ANIM_FLAG_FLAP
const PropAnimFlag_t PROP_ANIM_FLAG_FLAP
Definition: GfxData.h:48
RoR::PROP_ANIM_FLAG_DIFFLOCK
const PropAnimFlag_t PROP_ANIM_FLAG_DIFFLOCK
Definition: GfxData.h:67
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:618
RoR::GfxActor::SetWingsVisible
void SetWingsVisible(bool visible)
Definition: GfxActor.cpp:3314
RoR::PROP_ANIM_FLAG_AESTATUS
const PropAnimFlag_t PROP_ANIM_FLAG_AESTATUS
Definition: GfxData.h:64
ShouldEnableLightSource
bool ShouldEnableLightSource(FlareType type, bool is_player)
Definition: GfxActor.cpp:3170
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:243
RoR::GfxActor::UpdateVideoCameras
void UpdateVideoCameras(float dt_sec)
Definition: GfxActor.cpp:422
RoR::Turboprop::max_torque
float max_torque
Definition: TurboProp.h:45
RoR::AUTOSHIFTERLIN
@ AUTOSHIFTERLIN
Definition: GfxData.h:135
RoR::node_t::pos
NodeNum_t pos
This node's index in Actor::ar_nodes array.
Definition: SimData.h:304
GfxScene.h
RoR::node_t::nd_tyre_node
bool nd_tyre_node
Attr; This node is part of a tyre (note some wheel types don't use rim nodes at all)
Definition: SimData.h:311
RoR::PROP_ANIM_FLAG_SIGNALSTALK
const PropAnimFlag_t PROP_ANIM_FLAG_SIGNALSTALK
Turn indicator stalk position (-1=left, 0=off, 1=right)
Definition: GfxData.h:77
fast_normalise
Ogre::Vector3 fast_normalise(Ogre::Vector3 v)
Definition: ApproxMath.h:151
RoR::flare_t::intensity
float intensity
Definition: SimData.h:633
RoR::wheel_t::wh_near_attach_node
node_t * wh_near_attach_node
Definition: SimData.h:438
RoR::Prop
A mesh attached to vehicle frame via 3 nodes.
Definition: GfxData.h:162
RoR::PROP_ANIM_FLAG_VVI
const PropAnimFlag_t PROP_ANIM_FLAG_VVI
Definition: GfxData.h:45
RoR::SHOCK2
@ SHOCK2
shock2
Definition: SimData.h:109
RoR::ground_model_t::fx_type
int fx_type
Definition: SimData.h:761
RoR::AirbrakeGfx
Definition: GfxData.h:301
RoR::PROP_ANIM_MODE_OFFSET_Z
const PropAnimMode_t PROP_ANIM_MODE_OFFSET_Z
Definition: GfxData.h:87
RoR::App::mp_hide_own_net_label
CVar * mp_hide_own_net_label
Definition: Application.cpp:119
RoR::GfxActor::m_particles_dust
DustPool * m_particles_dust
Definition: GfxActor.h:191
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:286
FlexBody.h
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:280
RoR::CVar::getEnum
T getEnum() const
Definition: CVar.h:99
RoR::PROP_ANIM_FLAG_ACCEL
const PropAnimFlag_t PROP_ANIM_FLAG_ACCEL
Definition: GfxData.h:54
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:158
RoR::PropAnim::shifterSmooth
float shifterSmooth
Definition: GfxData.h:156
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::PROP_ANIM_FLAG_THROTTLE
const PropAnimFlag_t PROP_ANIM_FLAG_THROTTLE
Definition: GfxData.h:52
RoR::GfxActor::m_particles_clump
DustPool * m_particles_clump
Definition: GfxActor.h:195
RoR::VideoCamera
An Ogre::Camera mounted on the actor and rendering into either in-scene texture or external window.
Definition: GfxData.h:231
RoR::AirbrakeGfx::abx_scenenode
Ogre::SceneNode * abx_scenenode
Definition: GfxData.h:304
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:324
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:226
ApproxMath.h
RoR::wheel_t::debug_scaled_cforce
Ogre::Vector3 debug_scaled_cforce
Definition: SimData.h:462
RoR::PROP_ANIM_FLAG_PITCH
const PropAnimFlag_t PROP_ANIM_FLAG_PITCH
Definition: GfxData.h:51
DustPool.h
RoR::GfxActor::SpecialGetRotationTo
static Ogre::Quaternion SpecialGetRotationTo(const Ogre::Vector3 &src, const Ogre::Vector3 &dest)
Definition: GfxActor.cpp:1621
RoR::PROP_ANIM_FLAG_TURBO
const PropAnimFlag_t PROP_ANIM_FLAG_TURBO
Definition: GfxData.h:60
RoR::VCTYPE_TRACKING_VIDEOCAM
@ VCTYPE_TRACKING_VIDEOCAM
Definition: GfxData.h:223
RoR::PropAnim::animOpt5
float animOpt5
Definition: GfxData.h:151
BEAM_HYDRO_COLOR
const ImU32 BEAM_HYDRO_COLOR(0xff55a3e0)
FlexAirfoil.h
RoR::NodeSB::AbsPosition
Ogre::Vector3 AbsPosition
Definition: SimBuffers.h:69
RoR::PROP_ANIM_FLAG_HEADING
const PropAnimFlag_t PROP_ANIM_FLAG_HEADING
Definition: GfxData.h:66
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:623
SOUND_MODULATE
#define SOUND_MODULATE(_ACTOR_, _MOD_, _VALUE_)
Definition: SoundScriptManager.h:40
RoR::Collisions::FX_HARD
@ FX_HARD
Definition: Collisions.h:89
RoR::wheel_t::wh_arm_node
node_t * wh_arm_node
Definition: SimData.h:437
RoR::beam_t::bm_inter_actor
bool bm_inter_actor
in case p2 is on another actor
Definition: SimData.h:349
RoR::wheel_t::wh_radius
Ogre::Real wh_radius
Definition: SimData.h:442
RoR::DebugViewType::DEBUGVIEW_NODES
@ DEBUGVIEW_NODES
RoR::PropAnim::animOpt3
float animOpt3
MULTIPURPOSE.
Definition: GfxData.h:150
TurboProp.h
RoR::GfxActor::RegisterCabMesh
void RegisterCabMesh(Ogre::Entity *ent, Ogre::SceneNode *snode, FlexObj *flexobj)
Definition: GfxActor.cpp:3293
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 (only wheel types with separate rim nodes)
Definition: SimData.h:310
RoR::VideoCamState::VCSTATE_ENABLED_ONLINE
@ VCSTATE_ENABLED_ONLINE
RoR::GfxActor::SetCastShadows
void SetCastShadows(bool value)
Definition: GfxActor.cpp:3257
RoR::GfxActor::SetBeaconsEnabled
void SetBeaconsEnabled(bool beacon_light_is_active)
Definition: GfxActor.cpp:2337
RoR::GfxFlaresMode
GfxFlaresMode
Definition: Application.h:238
RoR::AeroEngine::getIgnition
virtual bool getIgnition()=0
RoR::BEAM_HYDRO
@ BEAM_HYDRO
Definition: SimData.h:72
RoR::flare_t
Definition: SimData.h:615
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:314
RoR::DebugViewType::DEBUGVIEW_WHEELS
@ DEBUGVIEW_WHEELS
SOUND_PLAY_ONCE
#define SOUND_PLAY_ONCE(_ACTOR_, _TRIG_)
Definition: SoundScriptManager.h:34
RoR::AeroEngine::getRPMpc
virtual float getRPMpc()=0
RoR::PROP_ANIM_FLAG_AEPITCH
const PropAnimFlag_t PROP_ANIM_FLAG_AEPITCH
Definition: GfxData.h:63
RoR::PROP_ANIM_FLAG_ELEVATORS
const PropAnimFlag_t PROP_ANIM_FLAG_ELEVATORS
Definition: GfxData.h:75
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:3327
RoR::wheel_t::wh_axis_node_0
node_t * wh_axis_node_0
Definition: SimData.h:439
RoR::GfxFlaresMode::NONE
@ NONE
None (fastest)
RoR::GfxActor::IsActorLive
bool IsActorLive() const
Should the visuals be updated for this actor?
Definition: GfxActor.cpp:1896
RoR::PROP_ANIM_FLAG_BRUDDER
const PropAnimFlag_t PROP_ANIM_FLAG_BRUDDER
Definition: GfxData.h:72
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:312
RoR::wheel_t::debug_vel
Ogre::Vector3 debug_vel
Definition: SimData.h:459
NODE_RADIUS
const float NODE_RADIUS(2.f)
RoR::flare_t::light
Ogre::Light * light
Definition: SimData.h:625
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:336
RoR::AeroEngineType::AE_XPROP
@ AE_XPROP
RoR::App::diag_hide_nodes
CVar * diag_hide_nodes
Definition: Application.cpp:156
RoR::PROP_ANIM_FLAG_SPEEDO
const PropAnimFlag_t PROP_ANIM_FLAG_SPEEDO
Definition: GfxData.h:58
RoR::VideoCamState
VideoCamState
Definition: GfxData.h:93
Terrain.h
RoR::Actor::ar_net_stream_id
int ar_net_stream_id
Definition: Actor.h:429
RoR::GfxActor::m_particles_sparks
DustPool * m_particles_sparks
Definition: GfxActor.h:194
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:602
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::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::pp_beacon_rot_rate
float pp_beacon_rot_rate[4]
Radians per second.
Definition: GfxData.h:194
RoR::SHOCK1
@ SHOCK1
shock1
Definition: SimData.h:108
RoR::AirbrakeGfx::abx_offset
Ogre::Vector3 abx_offset
Definition: GfxData.h:306
FlexObj.h
RoRnet::LIGHTMASK_BEACONS
@ LIGHTMASK_BEACONS
beacons on
Definition: RoRnet.h:120
WhereFrom
std::string WhereFrom(GfxActor *gfx_actor, const std::string &doing_what)
Definition: GfxActor.cpp:76
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:3185
RoR::GfxActor::SetAllMeshesVisible
void SetAllMeshesVisible(bool value)
Definition: GfxActor.cpp:3300
RoR::GfxActor::m_particles_ripple
DustPool * m_particles_ripple
Definition: GfxActor.h:193
RoR::ActorState::NETWORKED_OK
@ NETWORKED_OK
not simulated (remote) actor
RoR::Prop::pp_beacon_bbs
Ogre::BillboardSet * pp_beacon_bbs[4]
Definition: GfxData.h:191
RoR::flare_t::offsetx
float offsetx
Definition: SimData.h:620
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:172
RoR::PROP_ANIM_FLAG_CLUTCH
const PropAnimFlag_t PROP_ANIM_FLAG_CLUTCH
Definition: GfxData.h:56
RoR::GfxActor::UpdateFlexbodies
void UpdateFlexbodies()
Definition: GfxActor.cpp:3115
RoR::GfxActor::ResetFlexbodies
void ResetFlexbodies()
Definition: GfxActor.cpp:3138
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_ANIM_FLAG_ROLL
const PropAnimFlag_t PROP_ANIM_FLAG_ROLL
Definition: GfxData.h:50
RoR::Prop::pp_node_x
NodeNum_t pp_node_x
Definition: GfxData.h:166
RoR::GfxActor::getOwningActor
const ActorPtr & getOwningActor()
Definition: GfxActor.h:150
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:192
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_ANIM_FLAG_AILERONS
const PropAnimFlag_t PROP_ANIM_FLAG_AILERONS
Definition: GfxData.h:70
RoR::Prop::pp_beacon_scene_node
Ogre::SceneNode * pp_beacon_scene_node[4]
Definition: GfxData.h:192
RoR::App::GetGfxScene
GfxScene * GetGfxScene()
Definition: Application.cpp:276
RoR::PROP_ANIM_FLAG_BTHROTTLE
const PropAnimFlag_t PROP_ANIM_FLAG_BTHROTTLE
Definition: GfxData.h:73
RoR::beam_t::bm_disabled
bool bm_disabled
Definition: SimData.h:351
RoR::PROP_ANIM_FLAG_EVENT
const PropAnimFlag_t PROP_ANIM_FLAG_EVENT
Definition: GfxData.h:69
RoR::Terrain::getWater
IWater * getWater()
Definition: Terrain.h:84
HydraxWater.h
RoR::wing_t
Definition: SimData.h:537
RoR::PROP_ANIM_MODE_OFFSET_Y
const PropAnimMode_t PROP_ANIM_MODE_OFFSET_Y
Definition: GfxData.h:86
RoR::PROP_ANIM_FLAG_TACHO
const PropAnimFlag_t PROP_ANIM_FLAG_TACHO
Definition: GfxData.h:57
RoR::beam_t::L
float L
length
Definition: SimData.h:339
RoR::flare_t::noderef
NodeNum_t noderef
Definition: SimData.h:617
RoR::beam_t::bm_broken
bool bm_broken
Definition: SimData.h:352
RoR::rotator_t
Definition: SimData.h:599
RoR::GameContext::GetTerrain
const TerrainPtr & GetTerrain()
Definition: GameContext.h:117
RoR::ground_model_t::fx_colour
Ogre::ColourValue fx_colour
Definition: SimData.h:762
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)