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