RigsofRods
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->getParentSceneNode()->setPosition(center);
514  vidcam.vcam_ogre_camera->getParentSceneNode()->lookAt(App::GetCameraManager()->GetCameraNode()->getPosition() - 2.0f * project, Ogre::Node::TS_WORLD);
515  // vidcam.vcam_ogre_camera->getParentSceneNode()->roll(roll); // makes the camera spin
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->getParentSceneNode()->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->getParentSceneNode()->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->getParentSceneNode()->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->getParentSceneNode()->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->getParentSceneNode()->getOrientation());
584  }
585 
586  // set the new position
587  vidcam.vcam_ogre_camera->getParentSceneNode()->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 = static_cast<float>(rod.rod_diameter_mm) * 0.001;
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(GfxActor::SpecialGetRotationTo(Ogre::Vector3::UNIT_Y, (pos1 - pos2)));
1667  }
1668 }
1669 
1670 Ogre::Quaternion RoR::GfxActor::SpecialGetRotationTo(const Ogre::Vector3& src, const Ogre::Vector3& dest)
1671 {
1672  // Based on Stan Melax's article in Game Programming Gems
1673  Ogre::Quaternion q;
1674  // Copy, since cannot modify local
1675  Ogre::Vector3 v0 = src;
1676  Ogre::Vector3 v1 = dest;
1677  v0.normalise();
1678  v1.normalise();
1679 
1680  // NB if the crossProduct approaches zero, we get unstable because ANY axis will do
1681  // when v0 == -v1
1682  Ogre::Real d = v0.dotProduct(v1);
1683  // If dot == 1, vectors are the same
1684  if (d >= 1.0f)
1685  {
1686  return Ogre::Quaternion::IDENTITY;
1687  }
1688  if (d < (1e-6f - 1.0f))
1689  {
1690  // Generate an axis
1691  Ogre::Vector3 axis = Ogre::Vector3::UNIT_X.crossProduct(src);
1692  if (axis.isZeroLength()) // pick another if colinear
1693  axis = Ogre::Vector3::UNIT_Y.crossProduct(src);
1694  axis.normalise();
1695  q.FromAngleAxis(Ogre::Radian(Ogre::Math::PI), axis);
1696  }
1697  else
1698  {
1699  Ogre::Real s = fast_sqrt((1 + d) * 2);
1700  if (s == 0)
1701  return Ogre::Quaternion::IDENTITY;
1702 
1703  Ogre::Vector3 c = v0.crossProduct(v1);
1704  Ogre::Real invs = 1 / s;
1705 
1706  q.x = c.x * invs;
1707  q.y = c.y * invs;
1708  q.z = c.z * invs;
1709  q.w = s * 0.5;
1710  }
1711  return q;
1712 }
1713 
1714 void RoR::GfxActor::ScaleActor(Ogre::Vector3 relpos, float ratio)
1715 {
1716  for (BeamGfx& rod: m_gfx_beams)
1717  {
1718  float diameter2 = static_cast<float>(rod.rod_diameter_mm) * (ratio*1000.f);
1719  rod.rod_diameter_mm = static_cast<uint16_t>(diameter2);
1720  }
1721 
1722  // props and stuff
1723  // TOFIX: care about prop positions as well!
1724  for (Prop& prop: m_props)
1725  {
1726  if (prop.pp_scene_node)
1727  prop.pp_scene_node->scale(ratio, ratio, ratio);
1728 
1729  if (prop.pp_wheel_scene_node)
1730  {
1731  prop.pp_wheel_scene_node->scale(ratio, ratio, ratio);
1732  prop.pp_wheel_pos = relpos + (prop.pp_wheel_pos - relpos) * ratio;
1733  }
1734 
1735  if (prop.pp_beacon_scene_node[0])
1736  prop.pp_beacon_scene_node[0]->scale(ratio, ratio, ratio);
1737 
1738  if (prop.pp_beacon_scene_node[1])
1739  prop.pp_beacon_scene_node[1]->scale(ratio, ratio, ratio);
1740 
1741  if (prop.pp_beacon_scene_node[2])
1742  prop.pp_beacon_scene_node[2]->scale(ratio, ratio, ratio);
1743 
1744  if (prop.pp_beacon_scene_node[3])
1745  prop.pp_beacon_scene_node[3]->scale(ratio, ratio, ratio);
1746  }
1747 
1748  // Old cab mesh
1749  if (m_cab_mesh)
1750  {
1751  m_cab_mesh->ScaleFlexObj(ratio);
1752  }
1753 }
1754 
1756 {
1757  if (m_gfx_beams_parent_scenenode == nullptr)
1758  {
1759  return; // Vehicle has no visual softbody beams -> nothing to do.
1760  }
1761 
1762  // NOTE: We don't use Ogre::SceneNode::setVisible() for performance reasons:
1763  // 1. That function traverses all attached Entities and updates their visibility - too much overhead
1764  // 2. For OGRE up to 1.9 (I don't know about 1.10+) OGRE developers recommended to detach rather than hide.
1765  // ~ only_a_ptr, 12/2017
1766  if (visible && !m_gfx_beams_parent_scenenode->isInSceneGraph())
1767  {
1768  App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->addChild(m_gfx_beams_parent_scenenode);
1769  }
1770  else if (!visible && m_gfx_beams_parent_scenenode->isInSceneGraph())
1771  {
1772  App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->removeChild(m_gfx_beams_parent_scenenode);
1773  }
1774 }
1775 
1777 {
1778  // PLEASE maintain the same order as in `struct ActorSB`
1779 
1780  // Gameplay state
1781  m_simbuf.simbuf_actor_state = m_actor->ar_state;
1782  m_simbuf.simbuf_physics_paused = m_actor->ar_physics_paused;
1783  m_simbuf.simbuf_cur_cinecam = m_actor->ar_current_cinecam;
1784  m_simbuf.simbuf_net_username = m_actor->m_net_username;
1785  m_simbuf.simbuf_net_colornum = m_actor->m_net_color_num;
1786  m_simbuf.simbuf_driveable = m_actor->ar_driveable;
1787 
1788  // Movement
1789  m_simbuf.simbuf_pos = m_actor->getRotationCenter();
1790  m_simbuf.simbuf_node0_velo = m_actor->ar_nodes[0].Velocity;
1791  m_simbuf.simbuf_rotation = m_actor->getRotation();
1792  m_simbuf.simbuf_direction = m_actor->getDirection();
1793  m_simbuf.simbuf_wheel_speed = m_actor->ar_wheel_speed;
1794  m_simbuf.simbuf_top_speed = m_actor->ar_top_speed;
1795  m_simbuf.simbuf_aabb = m_actor->ar_bounding_box;
1796  if (m_actor->ar_num_cameras > 0)
1797  {
1798  m_simbuf.simbuf_camera0_pos_node = m_actor->ar_camera_node_pos[0];
1799  m_simbuf.simbuf_camera0_roll_node = m_actor->ar_camera_node_roll[0];
1800  }
1801 
1802  // Elements: nodes
1803  m_simbuf.simbuf_nodes.resize(m_actor->ar_num_nodes);
1804  for (int i = 0; i < m_actor->ar_num_nodes; ++i)
1805  {
1806  auto node = m_actor->ar_nodes[i];
1807  m_simbuf.simbuf_nodes[i].AbsPosition = node.AbsPosition;
1808  m_simbuf.simbuf_nodes[i].nd_has_contact = node.nd_has_ground_contact || node.nd_has_mesh_contact;
1809  }
1810 
1811  for (NodeGfx& nx: m_gfx_nodes)
1812  {
1813  m_simbuf.simbuf_nodes[nx.nx_node_idx].nd_is_wet = (nx.nx_wet_time_sec != -1.f);
1814  }
1815 
1816  // Elements: beams
1817  for (BeamGfx& rod: m_gfx_beams)
1818  {
1819  const beam_t& beam = m_actor->ar_beams[rod.rod_beam_index];
1820  rod.rod_node1 = static_cast<uint16_t>(beam.p1->pos);
1821  rod.rod_node2 = static_cast<uint16_t>(beam.p2->pos);
1822  if (beam.bm_inter_actor)
1823  {
1824  rod.rod_target_actor = beam.bm_locked_actor;
1825  }
1826  rod.rod_is_visible = !beam.bm_disabled && !beam.bm_broken;
1827  }
1828 
1829  // Elements: airbrakes
1830  m_simbuf.simbuf_airbrakes.resize(m_actor->ar_airbrakes.size());
1831  for (size_t i=0; i< m_actor->ar_airbrakes.size(); ++i)
1832  {
1833  m_simbuf.simbuf_airbrakes[i].simbuf_ab_ratio = m_actor->ar_airbrakes[i]->getRatio();
1834  }
1835 
1836  // Elements: Command keys
1837  for (int i = 1; i <= MAX_COMMANDS; ++i) // BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
1838  {
1839  m_simbuf.simbuf_commandkey[i].simbuf_cmd_value = m_actor->ar_command_key[i].commandValue;
1840  }
1841 
1842  // Elements: Prop animation keys
1843  m_simbuf.simbuf_prop_anim_keys.resize(m_actor->m_prop_anim_key_states.size());
1844  for (size_t i = 0; i < m_actor->m_prop_anim_key_states.size(); ++i)
1845  {
1846  m_simbuf.simbuf_prop_anim_keys[i].simbuf_anim_active = m_actor->m_prop_anim_key_states[i].anim_active;
1847  }
1848 
1849  // Elements: Aeroengines
1850  m_simbuf.simbuf_aeroengines.resize(m_actor->ar_num_aeroengines);
1851  for (int i = 0; i < m_actor->ar_num_aeroengines; ++i)
1852  {
1853  AeroEngine* src = m_actor->ar_aeroengines[i];
1854  AeroEngineSB& dst = m_simbuf.simbuf_aeroengines[i];
1855 
1856  dst.simbuf_ae_type = src->getType();
1857  dst.simbuf_ae_throttle = src->getThrottle();
1858  dst.simbuf_ae_rpm = src->getRPM();
1859  dst.simbuf_ae_rpmpc = src->getRPMpc();
1860  dst.simbuf_ae_rpm = src->getRPM();
1861  dst.simbuf_ae_ignition = src->getIgnition();
1862  dst.simbuf_ae_failed = src->isFailed();
1863 
1865  {
1866  Turboprop* tp = static_cast<Turboprop*>(src);
1867  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
1868  dst.simbuf_tp_aepitch = tp->pitch;
1869  }
1870  else // turbojet
1871  {
1872  Turbojet* tj = static_cast<Turbojet*>(src);
1873  dst.simbuf_tj_afterburn = tj->getAfterburner() != 0.f;
1876  }
1877  }
1878 
1879  // Engine (+drivetrain)
1880  m_simbuf.simbuf_hydro_dir_state = m_actor->ar_hydro_dir_state;
1881  m_simbuf.simbuf_brake = m_actor->ar_brake;
1882  if (m_actor->ar_engine != nullptr)
1883  {
1884  m_simbuf.simbuf_has_engine = true;
1885  m_simbuf.simbuf_gear = m_actor->ar_engine->getGear();
1886  m_simbuf.simbuf_autoshift = m_actor->ar_engine->getAutoShift();
1887  m_simbuf.simbuf_engine_rpm = m_actor->ar_engine->getRPM();
1888  m_simbuf.simbuf_engine_turbo_psi= m_actor->ar_engine->getTurboPSI();
1889  m_simbuf.simbuf_engine_accel = m_actor->ar_engine->getAcc();
1890  m_simbuf.simbuf_engine_torque = m_actor->ar_engine->getCurEngineTorque();
1891  m_simbuf.simbuf_inputshaft_rpm = m_actor->ar_engine->getInputShaftRPM();
1892  m_simbuf.simbuf_drive_ratio = m_actor->ar_engine->getDriveRatio();
1893  m_simbuf.simbuf_clutch = m_actor->ar_engine->getClutch();
1894  m_simbuf.simbuf_num_gears = m_actor->ar_engine->getNumGears();
1895  m_simbuf.simbuf_engine_max_rpm = m_actor->ar_engine->getShiftUpRPM();
1896  m_simbuf.simbuf_engine_smoke = m_actor->ar_engine->getSmoke();
1897  }
1898  if (m_actor->m_num_wheel_diffs > 0)
1899  {
1900  m_simbuf.simbuf_diff_type = m_actor->m_wheel_diffs[0]->GetActiveDiffType();
1901  }
1902 
1903  // Tyre pressure
1904  m_simbuf.simbuf_tyre_pressure = m_actor->getTyrePressure().GetCurPressure();
1905  m_simbuf.simbuf_tyre_pressurizing = m_actor->getTyrePressure().IsPressurizing();
1906 
1907  // Effects
1908  m_simbuf.simbuf_lightmask = m_actor->m_lightmask;
1909  m_simbuf.simbuf_smoke_enabled = m_actor->getSmokeEnabled();
1910  m_simbuf.simbuf_parking_brake = m_actor->ar_parking_brake;
1911  m_simbuf.simbuf_cparticles_active = m_actor->ar_cparticles_active;
1912 
1913  // Aerial
1914  m_simbuf.simbuf_hydro_aileron_state = m_actor->ar_hydro_aileron_state;
1915  m_simbuf.simbuf_hydro_elevator_state = m_actor->ar_hydro_elevator_state;
1916  m_simbuf.simbuf_hydro_aero_rudder_state = m_actor->ar_hydro_rudder_state;
1917  m_simbuf.simbuf_aero_flap_state = m_actor->ar_aerial_flap;
1918  m_simbuf.simbuf_airbrake_state = m_actor->ar_airbrake_intensity;
1919  if (m_actor->ar_num_wings > 4)
1920  {
1921  m_simbuf.simbuf_wing4_aoa = m_actor->ar_wings[4].fa->aoa;
1922  }
1923 
1924  // Autopilot
1925  if (m_actor->ar_autopilot != nullptr)
1926  {
1927  m_simbuf.simbuf_has_autopilot = true;
1928  m_simbuf.simbuf_ap_heading_mode = m_actor->ar_autopilot->GetHeadingMode();
1929  m_simbuf.simbuf_ap_heading_value = m_actor->ar_autopilot->heading;
1930  m_simbuf.simbuf_ap_alt_mode = m_actor->ar_autopilot->GetAltMode();
1931  m_simbuf.simbuf_ap_alt_value = m_actor->ar_autopilot->GetAltValue();
1932  m_simbuf.simbuf_ap_ias_mode = m_actor->ar_autopilot->GetIasMode();
1933  m_simbuf.simbuf_ap_ias_value = m_actor->ar_autopilot->GetIasValue();
1934  m_simbuf.simbuf_ap_gpws_mode = m_actor->ar_autopilot->GetGpwsMode();
1935  m_simbuf.simbuf_ap_ils_available = m_actor->ar_autopilot->IsIlsAvailable();
1936  m_simbuf.simbuf_ap_ils_vdev = m_actor->ar_autopilot->GetVerticalApproachDeviation();
1937  m_simbuf.simbuf_ap_ils_hdev = m_actor->ar_autopilot->GetHorizontalApproachDeviation();
1938  m_simbuf.simbuf_ap_vs_value = m_actor->ar_autopilot->GetVsValue();
1939  }
1940 
1941  m_simbuf.simbuf_speedo_highest_kph = m_actor->ar_guisettings_speedo_max_kph;
1942  m_simbuf.simbuf_speedo_use_engine_max_rpm = m_actor->ar_guisettings_use_engine_max_rpm;
1943  m_simbuf.simbuf_shifter_anim_time = m_actor->ar_guisettings_shifter_anim_time;
1944 
1945 }
1946 
1948 {
1949  return (m_actor->ar_state < ActorState::LOCAL_SLEEPING);
1950 }
1951 
1953 {
1954  if ((m_cab_entity != nullptr) && (m_cab_mesh != nullptr))
1955  {
1956  m_cab_scene_node->setPosition(m_cab_mesh->UpdateFlexObj());
1957  }
1958 }
1959 
1961 {
1962  m_flexwheel_tasks.clear();
1963 
1964  for (WheelGfx& w: m_wheels)
1965  {
1966  if (w.wx_flex_mesh != nullptr && w.wx_flex_mesh->flexitPrepare())
1967  {
1968  auto func = std::function<void()>([this, w]()
1969  {
1970  w.wx_flex_mesh->flexitCompute();
1971  });
1972  auto task_handle = App::GetThreadPool()->RunTask(func);
1973  m_flexwheel_tasks.push_back(task_handle);
1974  }
1975  }
1976 }
1977 
1979 {
1980  for (auto& task: m_flexwheel_tasks)
1981  {
1982  task->join();
1983  }
1984  for (WheelGfx& w: m_wheels)
1985  {
1986  if (w.wx_scenenode != nullptr && w.wx_flex_mesh != nullptr)
1987  {
1988  w.wx_scenenode->setPosition(w.wx_flex_mesh->flexitFinal());
1989  }
1990  }
1991 }
1992 
1994 {
1995  for (WheelGfx& w: m_wheels)
1996  {
1997  if (w.wx_scenenode != nullptr)
1998  {
1999  w.wx_scenenode->setVisible(value);
2000  }
2001  if (w.wx_flex_mesh != nullptr)
2002  {
2003  w.wx_flex_mesh->setVisible(value);
2004  }
2005  }
2006 }
2007 
2008 
2009 int RoR::GfxActor::GetActorId () const { return m_actor->ar_instance_id; }
2010 int RoR::GfxActor::GetActorState () const { return static_cast<int>(m_actor->ar_state); }
2011 
2013 {
2014  const size_t num_airbrakes = m_gfx_airbrakes.size();
2015  for (size_t i=0; i<num_airbrakes; ++i)
2016  {
2017  AirbrakeGfx abx = m_gfx_airbrakes[i];
2018  const float ratio = m_simbuf.simbuf_airbrakes[i].simbuf_ab_ratio;
2019  const float maxangle = m_actor->ar_airbrakes[i]->getMaxAngle();
2020  Ogre::Vector3 ref_node_pos = m_simbuf.simbuf_nodes[m_gfx_airbrakes[i].abx_ref_node].AbsPosition;
2021  Ogre::Vector3 x_node_pos = m_simbuf.simbuf_nodes[m_gfx_airbrakes[i].abx_x_node].AbsPosition;
2022  Ogre::Vector3 y_node_pos = m_simbuf.simbuf_nodes[m_gfx_airbrakes[i].abx_y_node].AbsPosition;
2023 
2024  // -- Ported from `AirBrake::updatePosition()` --
2025  Ogre::Vector3 normal = (y_node_pos - ref_node_pos).crossProduct(x_node_pos - ref_node_pos);
2026  normal.normalise();
2027  //position
2028  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);
2029  abx.abx_scenenode->setPosition(mposition + normal * abx.abx_offset.z);
2030  //orientation
2031  Ogre::Vector3 refx = x_node_pos - ref_node_pos;
2032  refx.normalise();
2033  Ogre::Vector3 refy = refx.crossProduct(normal);
2034  Ogre::Quaternion orientation = Ogre::Quaternion(Ogre::Degree(-ratio * maxangle), (x_node_pos - ref_node_pos).normalisedCopy()) * Ogre::Quaternion(refx, normal, refy);
2035  abx.abx_scenenode->setOrientation(orientation);
2036 
2037  }
2038 }
2039 
2041 {
2042  for (CParticle& cparticle: m_cparticles)
2043  {
2045  const Ogre::Vector3 pos = m_simbuf.simbuf_nodes[cparticle.emitterNode].AbsPosition;
2046  const Ogre::Vector3 dir = fast_normalise(pos - m_simbuf.simbuf_nodes[cparticle.directionNode].AbsPosition);
2047  cparticle.snode->setPosition(pos);
2048 
2049  for (unsigned short j = 0; j < cparticle.psys->getNumEmitters(); j++)
2050  {
2051  cparticle.psys->getEmitter(j)->setEnabled(m_simbuf.simbuf_cparticles_active);
2052  cparticle.psys->getEmitter(j)->setDirection(dir);
2053  }
2054  }
2055 }
2056 
2058 {
2059  if (!m_simbuf.simbuf_has_engine)
2060  return;
2061 
2062  for (Exhaust& exhaust: m_exhausts)
2063  {
2064  if (!exhaust.smoker) // This remains `nullptr` if removed via `addonpart_unwanted_exhaust` or Tuning UI.
2065  continue;
2066 
2068  const Ogre::Vector3 pos = m_simbuf.simbuf_nodes[exhaust.emitterNode].AbsPosition;
2069  const Ogre::Vector3 dir = pos - m_simbuf.simbuf_nodes[exhaust.directionNode].AbsPosition;
2070  exhaust.smokeNode->setPosition(pos);
2071 
2072  const bool active = m_simbuf.simbuf_smoke_enabled && m_simbuf.simbuf_engine_smoke != -1.f;
2073  exhaust.smoker->getEmitter(0)->setEnabled(active);
2074  if (active) // `setTimeToLive()` assert()s that argument is not negative.
2075  {
2076  exhaust.smoker->getEmitter(0)->setColour(Ogre::ColourValue(0.0, 0.0, 0.0, 0.02 + m_simbuf.simbuf_engine_smoke * 0.06));
2077  exhaust.smoker->getEmitter(0)->setTimeToLive((0.02 + m_simbuf.simbuf_engine_smoke * 0.06) / 0.04);
2078  exhaust.smoker->getEmitter(0)->setParticleVelocity(1.0 + m_simbuf.simbuf_engine_smoke * 2.0, 2.0 + m_simbuf.simbuf_engine_smoke * 3.0);
2079  }
2080  }
2081 }
2082 
2084 {
2085  for (int i = 0; i < m_actor->ar_num_aeroengines; i++)
2086  {
2087  m_actor->ar_aeroengines[i]->updateVisuals(this);
2088  }
2089 }
2090 
2092 {
2093  const bool is_remote =
2094  m_simbuf.simbuf_actor_state == ActorState::NETWORKED_OK ||
2095  m_simbuf.simbuf_actor_state == ActorState::NETWORKED_HIDDEN;
2096 
2097  if (App::mp_hide_net_labels->getBool() || (!is_remote && App::mp_hide_own_net_label->getBool()) || App::mp_state->getEnum<MpState>() != MpState::CONNECTED)
2098  {
2099  return;
2100  }
2101 
2102  float vlen = m_simbuf.simbuf_pos.distance(App::GetCameraManager()->GetCameraNode()->getPosition());
2103 
2104  float y_offset = (m_simbuf.simbuf_aabb.getMaximum().y - m_simbuf.simbuf_pos.y) + (vlen / 100.0);
2105  Ogre::Vector3 scene_pos = m_simbuf.simbuf_pos + Ogre::Vector3::UNIT_Y * y_offset;
2106 
2107  App::GetGfxScene()->DrawNetLabel(scene_pos, vlen, m_simbuf.simbuf_net_username, m_simbuf.simbuf_net_colornum);
2108 
2109 }
2110 
2111 void RoR::GfxActor::CalculateDriverPos(Ogre::Vector3& out_pos, Ogre::Quaternion& out_rot)
2112 {
2113  ROR_ASSERT(m_driverseat_prop_index != -1);
2114  Prop* driverseat_prop = &m_props[m_driverseat_prop_index];
2115 
2116  NodeSB* nodes = this->GetSimNodeBuffer();
2117 
2118  const Ogre::Vector3 x_pos = nodes[driverseat_prop->pp_node_x].AbsPosition;
2119  const Ogre::Vector3 y_pos = nodes[driverseat_prop->pp_node_y].AbsPosition;
2120  const Ogre::Vector3 center_pos = nodes[driverseat_prop->pp_node_ref].AbsPosition;
2121 
2122  const Ogre::Vector3 x_vec = x_pos - center_pos;
2123  const Ogre::Vector3 y_vec = y_pos - center_pos;
2124  const Ogre::Vector3 normal = (y_vec.crossProduct(x_vec)).normalisedCopy();
2125 
2126  // Output position
2127  Ogre::Vector3 pos = center_pos;
2128  pos += (driverseat_prop->pp_offset.x * x_vec);
2129  pos += (driverseat_prop->pp_offset.y * y_vec);
2130  pos += (driverseat_prop->pp_offset.z * normal);
2131  out_pos = pos;
2132 
2133  // Output orientation
2134  const Ogre::Vector3 x_vec_norm = x_vec.normalisedCopy();
2135  const Ogre::Vector3 y_vec_norm = x_vec_norm.crossProduct(normal);
2136  Ogre::Quaternion rot(x_vec_norm, normal, y_vec_norm);
2137  rot = rot * driverseat_prop->pp_rot;
2138  rot = rot * Ogre::Quaternion(Ogre::Degree(180), Ogre::Vector3::UNIT_Y); // rotate towards the driving direction
2139  out_rot = rot;
2140 }
2141 
2142 void RoR::GfxActor::UpdateBeaconFlare(Prop & prop, float dt, bool is_player_actor)
2143 {
2144  // TODO: Quick and dirty port from Beam::updateFlares(), clean it up ~ only_a_ptr, 06/2018
2145  using namespace Ogre;
2146 
2147  bool enableAll = !((App::gfx_flares_mode->getEnum<GfxFlaresMode>() == GfxFlaresMode::CURR_VEHICLE_HEAD_ONLY) && !is_player_actor);
2148  NodeSB* nodes = this->GetSimNodeBuffer();
2149 
2150  if (prop.pp_beacon_type == 'b')
2151  {
2152  // Get data
2153  Ogre::SceneNode* beacon_scene_node = prop.pp_scene_node;
2154  Ogre::Quaternion beacon_orientation = beacon_scene_node->getOrientation();
2155  Ogre::Light* pp_beacon_light = prop.pp_beacon_light[0];
2156  float beacon_rotation_rate = prop.pp_beacon_rot_rate[0];
2157  float beacon_rotation_angle = prop.pp_beacon_rot_angle[0]; // Updated at end of block
2158 
2159  // Transform
2160  pp_beacon_light->getParentSceneNode()->setPosition(beacon_scene_node->getPosition() + beacon_orientation * Ogre::Vector3(0, 0, 0.12));
2161  beacon_rotation_angle += dt * beacon_rotation_rate;//rotate baby!
2162  pp_beacon_light->getParentSceneNode()->setDirection(beacon_orientation * Ogre::Vector3(cos(beacon_rotation_angle), sin(beacon_rotation_angle), 0));
2163  //billboard
2164  Ogre::Vector3 vdir = pp_beacon_light->getParentSceneNode()->getPosition() - App::GetCameraManager()->GetCameraNode()->getPosition(); // TODO: verify the position is already updated here ~ only_a_ptr, 06/2018
2165  float vlen = vdir.length();
2166  if (vlen > 100.0)
2167  {
2168  prop.pp_beacon_scene_node[0]->setVisible(false);
2169  return;
2170  }
2171  //normalize
2172  vdir = vdir / vlen;
2173  prop.pp_beacon_scene_node[0]->setPosition(pp_beacon_light->getParentSceneNode()->getPosition() - vdir * 0.1);
2174  float amplitude = pp_beacon_light->getDerivedDirection().dotProduct(vdir);
2175  if (amplitude > 0)
2176  {
2177  prop.pp_beacon_scene_node[0]->setVisible(true);
2178  prop.pp_beacon_bbs[0]->setDefaultDimensions(amplitude * amplitude * amplitude, amplitude * amplitude * amplitude);
2179  }
2180  else
2181  {
2182  prop.pp_beacon_scene_node[0]->setVisible(false);
2183  }
2184  pp_beacon_light->setVisible(enableAll);
2185 
2186  // Update
2187  prop.pp_beacon_rot_angle[0] = beacon_rotation_angle;
2188  // NOTE: Light position is not updated here!
2189  }
2190  else if (prop.pp_beacon_type == 'p')
2191  {
2192  for (int k = 0; k < 4; k++)
2193  {
2194  //update light
2195  Quaternion orientation = prop.pp_scene_node->getOrientation();
2196  switch (k)
2197  {
2198  case 0: prop.pp_beacon_light[k]->getParentSceneNode()->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(-0.64, 0, 0.14));
2199  break;
2200  case 1: prop.pp_beacon_light[k]->getParentSceneNode()->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(-0.32, 0, 0.14));
2201  break;
2202  case 2: prop.pp_beacon_light[k]->getParentSceneNode()->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(+0.32, 0, 0.14));
2203  break;
2204  case 3: prop.pp_beacon_light[k]->getParentSceneNode()->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(+0.64, 0, 0.14));
2205  break;
2206  }
2207  prop.pp_beacon_rot_angle[k] += dt * prop.pp_beacon_rot_rate[k];//rotate baby!
2208  prop.pp_beacon_light[k]->getParentSceneNode()->setDirection(orientation * Vector3(cos(prop.pp_beacon_rot_angle[k]), sin(prop.pp_beacon_rot_angle[k]), 0));
2209  //billboard
2210  Vector3 vdir = prop.pp_beacon_light[k]->getParentSceneNode()->getPosition() - App::GetCameraManager()->GetCameraNode()->getPosition();
2211  float vlen = vdir.length();
2212  if (vlen > 100.0)
2213  {
2214  prop.pp_beacon_scene_node[k]->setVisible(false);
2215  continue;
2216  }
2217  //normalize
2218  vdir = vdir / vlen;
2219  prop.pp_beacon_scene_node[k]->setPosition(prop.pp_beacon_light[k]->getParentSceneNode()->getPosition() - vdir * 0.2);
2220  float amplitude = prop.pp_beacon_light[k]->getDerivedDirection().dotProduct(vdir);
2221  if (amplitude > 0)
2222  {
2223  prop.pp_beacon_scene_node[k]->setVisible(true);
2224  prop.pp_beacon_bbs[k]->setDefaultDimensions(amplitude * amplitude * amplitude, amplitude * amplitude * amplitude);
2225  }
2226  else
2227  {
2228  prop.pp_beacon_scene_node[k]->setVisible(false);
2229  }
2230  prop.pp_beacon_light[k]->setVisible(enableAll);
2231  }
2232  }
2233  else if (prop.pp_beacon_type == 'r')
2234  {
2235  //update light
2236  Quaternion orientation = prop.pp_scene_node->getOrientation();
2237  prop.pp_beacon_light[0]->getParentSceneNode()->setPosition(prop.pp_scene_node->getPosition() + orientation * Vector3(0, 0, 0.06));
2238  prop.pp_beacon_rot_angle[0] += dt * prop.pp_beacon_rot_rate[0];//rotate baby!
2239  //billboard
2240  Vector3 vdir = prop.pp_beacon_light[0]->getParentSceneNode()->getPosition() - App::GetCameraManager()->GetCameraNode()->getPosition();
2241  float vlen = vdir.length();
2242  if (vlen > 100.0)
2243  {
2244  prop.pp_beacon_scene_node[0]->setVisible(false);
2245  return;
2246  }
2247  //normalize
2248  vdir = vdir / vlen;
2249  prop.pp_beacon_scene_node[0]->setPosition(prop.pp_beacon_light[0]->getParentSceneNode()->getPosition() - vdir * 0.1);
2250  bool visible = false;
2251  if (prop.pp_beacon_rot_angle[0] > 1.0)
2252  {
2253  prop.pp_beacon_rot_angle[0] = 0.0;
2254  visible = true;
2255  }
2256  visible = visible && enableAll;
2257  prop.pp_beacon_light[0]->setVisible(visible);
2258  prop.pp_beacon_scene_node[0]->setVisible(visible);
2259  }
2260  else if (prop.pp_beacon_type == 'R' || prop.pp_beacon_type == 'L') // Avionic navigation lights (red/green)
2261  {
2262  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);
2263  //billboard
2264  Vector3 vdir = mposition - App::GetCameraManager()->GetCameraNode()->getPosition();
2265  float vlen = vdir.length();
2266  if (vlen > 100.0)
2267  {
2268  prop.pp_beacon_scene_node[0]->setVisible(false);
2269  return;
2270  }
2271  //normalize
2272  vdir = vdir / vlen;
2273  prop.pp_beacon_scene_node[0]->setPosition(mposition - vdir * 0.1);
2274  }
2275  else if (prop.pp_beacon_type == 'w') // Avionic navigation lights (white rotating beacon)
2276  {
2277  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);
2278  prop.pp_beacon_light[0]->getParentSceneNode()->setPosition(mposition);
2279  prop.pp_beacon_rot_angle[0] += dt * prop.pp_beacon_rot_rate[0];//rotate baby!
2280  //billboard
2281  Vector3 vdir = mposition - App::GetCameraManager()->GetCameraNode()->getPosition();
2282  float vlen = vdir.length();
2283  if (vlen > 100.0)
2284  {
2285  prop.pp_beacon_scene_node[0]->setVisible(false);
2286  return;
2287  }
2288  //normalize
2289  vdir = vdir / vlen;
2290  prop.pp_beacon_scene_node[0]->setPosition(mposition - vdir * 0.1);
2291  bool visible = false;
2292  if (prop.pp_beacon_rot_angle[0] > 1.0)
2293  {
2294  prop.pp_beacon_rot_angle[0] = 0.0;
2295  visible = true;
2296  }
2297  visible = visible && enableAll;
2298  prop.pp_beacon_light[0]->setVisible(visible);
2299  prop.pp_beacon_scene_node[0]->setVisible(visible);
2300  }
2301 }
2302 
2303 void RoR::GfxActor::UpdateProps(float dt, bool is_player_actor)
2304 {
2305  using namespace Ogre;
2306 
2307  NodeSB* nodes = this->GetSimNodeBuffer();
2308 
2309  // Update prop meshes
2310  for (Prop& prop: m_props)
2311  {
2312  if (prop.pp_scene_node == nullptr) // Wing beacons don't have scenenodes
2313  continue;
2314 
2315  // Update visibility
2316  if (prop.pp_aero_propeller_blade || prop.pp_aero_propeller_spin)
2317  {
2318  const float SPINNER_THRESHOLD = 200.f; // TODO: magic! ~ only_a_ptr, 09/2018
2319  const bool show_spinner = m_simbuf.simbuf_aeroengines[prop.pp_aero_engine_idx].simbuf_ae_rpm > SPINNER_THRESHOLD;
2320  if (prop.pp_aero_propeller_blade)
2321  prop.pp_scene_node->setVisible(!show_spinner);
2322  else if (prop.pp_aero_propeller_spin)
2323  prop.pp_scene_node->setVisible(show_spinner);
2324  }
2325  else
2326  {
2327  const bool mo_visible = (prop.pp_camera_mode_active == CAMERA_MODE_ALWAYS_VISIBLE || prop.pp_camera_mode_active == m_simbuf.simbuf_cur_cinecam);
2328  prop.pp_mesh_obj->setVisible(mo_visible);
2329  if (!mo_visible)
2330  {
2331  continue; // No need to update hidden meshes
2332  }
2333  }
2334 
2335  // Update position and orientation
2336  // -- quick ugly port from `Actor::updateProps()` --- ~ 06/2018
2337  Vector3 diffX = nodes[prop.pp_node_x].AbsPosition - nodes[prop.pp_node_ref].AbsPosition;
2338  Vector3 diffY = nodes[prop.pp_node_y].AbsPosition - nodes[prop.pp_node_ref].AbsPosition;
2339 
2340  Vector3 normal = (diffY.crossProduct(diffX)).normalisedCopy();
2341 
2342  Vector3 mposition = nodes[prop.pp_node_ref].AbsPosition + prop.pp_offset.x * diffX + prop.pp_offset.y * diffY;
2343  prop.pp_scene_node->setPosition(mposition + normal * prop.pp_offset.z);
2344 
2345  Vector3 refx = diffX.normalisedCopy();
2346  Vector3 refy = refx.crossProduct(normal);
2347  Quaternion orientation = Quaternion(refx, normal, refy) * prop.pp_rot;
2348  prop.pp_scene_node->setOrientation(orientation);
2349 
2350  if (prop.pp_wheel_scene_node) // special prop - steering wheel
2351  {
2352  Quaternion brot = Quaternion(Degree(-59.0), Vector3::UNIT_X);
2353  brot = brot * Quaternion(Degree(m_simbuf.simbuf_hydro_dir_state * prop.pp_wheel_rot_degree), Vector3::UNIT_Y);
2354  prop.pp_wheel_scene_node->setPosition(mposition + normal * prop.pp_offset.z + orientation * prop.pp_wheel_pos);
2355  prop.pp_wheel_scene_node->setOrientation(orientation * brot);
2356  }
2357  }
2358 
2359  // Update beacon flares
2360  if (BITMASK_IS_1(m_simbuf.simbuf_lightmask, RoRnet::LIGHTMASK_BEACONS) != m_beaconlight_active)
2361  {
2362  m_beaconlight_active = (m_simbuf.simbuf_lightmask & RoRnet::LIGHTMASK_BEACONS);
2363  this->SetBeaconsEnabled(m_beaconlight_active);
2364  }
2365 
2366  if ((App::gfx_flares_mode->getEnum<GfxFlaresMode>() != GfxFlaresMode::NONE)
2367  && m_beaconlight_active)
2368  {
2369  for (Prop& prop: m_props)
2370  {
2371  if (prop.pp_beacon_type != 0)
2372  {
2373  this->UpdateBeaconFlare(prop, dt, is_player_actor);
2374  }
2375  }
2376  }
2377 }
2378 
2380 {
2381  for (Prop& prop: m_props)
2382  {
2383  prop.setPropMeshesVisible(visible);
2384  }
2385 }
2386 
2388 {
2389  // For turbojets, this hides meshes (nozzle, abflame) and particles
2390  // For turbo/piston-props, this only hides particles, meshes are in props.
2391 
2392  for (int i = 0; i < m_actor->ar_num_aeroengines; i++)
2393  {
2394  m_actor->ar_aeroengines[i]->setVisible(visible);
2395  }
2396 }
2397 
2399 {
2400  if (m_renderdash != nullptr)
2401  {
2402  m_renderdash->setEnable(active);
2403  }
2404 }
2405 
2407 {
2408  if (m_renderdash != nullptr)
2409  {
2410  m_renderdash->getRenderTarget()->update();
2411  }
2412 }
2413 
2414 void RoR::GfxActor::SetBeaconsEnabled(bool beacon_light_is_active)
2415 {
2417 
2418  for (Prop& prop: m_props)
2419  {
2420  char beacon_type = prop.pp_beacon_type;
2421  if (beacon_type == 'b')
2422  {
2423  prop.pp_beacon_light[0]->setVisible(beacon_light_is_active && enableLight);
2424  prop.pp_beacon_scene_node[0]->setVisible(beacon_light_is_active);
2425  if (prop.pp_beacon_bbs[0] && beacon_light_is_active && !prop.pp_beacon_scene_node[0]->numAttachedObjects())
2426  {
2427  prop.pp_beacon_bbs[0]->setVisible(true);
2428  prop.pp_beacon_scene_node[0]->attachObject(prop.pp_beacon_bbs[0]);
2429  }
2430  else if (prop.pp_beacon_bbs[0] && !beacon_light_is_active)
2431  {
2432  prop.pp_beacon_scene_node[0]->detachAllObjects();
2433  prop.pp_beacon_bbs[0]->setVisible(false);
2434  }
2435  }
2436  else if (beacon_type == 'R' || beacon_type == 'L')
2437  {
2438  prop.pp_beacon_scene_node[0]->setVisible(beacon_light_is_active);
2439  if (prop.pp_beacon_bbs[0] && beacon_light_is_active && !prop.pp_beacon_scene_node[0]->numAttachedObjects())
2440  prop.pp_beacon_scene_node[0]->attachObject(prop.pp_beacon_bbs[0]);
2441  else if (prop.pp_beacon_bbs[0] && !beacon_light_is_active)
2442  prop.pp_beacon_scene_node[0]->detachAllObjects();
2443  }
2444  else if (beacon_type == 'p')
2445  {
2446  for (int k = 0; k < 4; k++)
2447  {
2448  prop.pp_beacon_light[k]->setVisible(beacon_light_is_active && enableLight);
2449  prop.pp_beacon_scene_node[k]->setVisible(beacon_light_is_active);
2450  if (prop.pp_beacon_bbs[k] && beacon_light_is_active && !prop.pp_beacon_scene_node[k]->numAttachedObjects())
2451  prop.pp_beacon_scene_node[k]->attachObject(prop.pp_beacon_bbs[k]);
2452  else if (prop.pp_beacon_bbs[k] && !beacon_light_is_active)
2453  prop.pp_beacon_scene_node[k]->detachAllObjects();
2454  }
2455  }
2456  else
2457  {
2458  for (int k = 0; k < 4; k++)
2459  {
2460  if (prop.pp_beacon_light[k])
2461  {
2462  prop.pp_beacon_light[k]->setVisible(beacon_light_is_active && enableLight);
2463  }
2464  if (prop.pp_beacon_scene_node[k])
2465  {
2466  prop.pp_beacon_scene_node[k]->setVisible(beacon_light_is_active);
2467 
2468  if (prop.pp_beacon_bbs[k] && beacon_light_is_active && !prop.pp_beacon_scene_node[k]->numAttachedObjects())
2469  {
2470  prop.pp_beacon_scene_node[k]->attachObject(prop.pp_beacon_bbs[k]);
2471  }
2472  else if (prop.pp_beacon_bbs[k] && !beacon_light_is_active)
2473  {
2474  prop.pp_beacon_scene_node[k]->detachAllObjects();
2475  }
2476  }
2477  }
2478  }
2479  }
2480 }
2481 
2482 // Returns a smoothened `cstate`
2483 float RoR::GfxActor::UpdateSmoothShift(PropAnim& anim, float dt, float new_target_cstate)
2484 {
2485  const float delta_cstate = new_target_cstate - anim.shifterTarget;
2486  if (delta_cstate != 0)
2487  {
2488  anim.shifterStep = delta_cstate;
2489  anim.shifterTarget = new_target_cstate;
2490  }
2491 
2492  if (anim.shifterSmooth != anim.shifterTarget)
2493  {
2494  const float cstate_step = (dt / m_simbuf.simbuf_shifter_anim_time) * anim.shifterStep;
2495  anim.shifterSmooth += cstate_step;
2496  // boundary check
2497  if ((anim.shifterStep < 0.f && anim.shifterSmooth < anim.shifterTarget) // undershot
2498  || (anim.shifterStep > 0.f) && anim.shifterSmooth > anim.shifterTarget) // overshot
2499  {
2500  anim.shifterSmooth = anim.shifterTarget;
2501  }
2502  }
2503 
2504  return anim.shifterSmooth;
2505 }
2506 
2507 void RoR::GfxActor::CalcPropAnimation(PropAnim& anim, float& cstate, int& div, float dt)
2508 {
2509  // Note: This is not the same as 'animators' - those run on physics thread!
2510  // ------------------------------------------------------------------------
2511 
2512  //boat rudder
2513  if (anim.animFlags & PROP_ANIM_FLAG_BRUDDER)
2514  {
2515  size_t spi;
2516  float ctmp = 0.0f;
2517  for (spi = 0; spi < m_simbuf.simbuf_screwprops.size(); spi++)
2518  {
2519  ctmp += m_simbuf.simbuf_screwprops[spi].simbuf_sp_rudder;
2520  }
2521 
2522  if (spi > 0)
2523  ctmp = ctmp / spi;
2524  cstate = ctmp;
2525  div++;
2526  }
2527 
2528  //boat throttle
2530  {
2531  size_t spi;
2532  float ctmp = 0.0f;
2533  for (spi = 0; spi < m_simbuf.simbuf_screwprops.size(); spi++)
2534  {
2535  ctmp += m_simbuf.simbuf_screwprops[spi].simbuf_sp_throttle;
2536  }
2537 
2538  if (spi > 0)
2539  ctmp = ctmp / spi;
2540  cstate = ctmp;
2541  div++;
2542  }
2543 
2544  //differential lock status
2546  {
2547  if (m_actor->m_num_wheel_diffs > 0) // read-only attribute - safe to read from here
2548  {
2549  switch (m_simbuf.simbuf_diff_type)
2550  {
2551  case DiffType::OPEN_DIFF:
2552  cstate = 0.0f;
2553  break;
2554  case DiffType::SPLIT_DIFF:
2555  cstate = 0.5f;
2556  break;
2557  case DiffType::LOCKED_DIFF:
2558  cstate = 1.0f;
2559  break;
2560  default:;
2561  }
2562  }
2563  else // no axles/diffs avail, mode is split by default
2564  cstate = 0.5f;
2565 
2566  div++;
2567  }
2568 
2569  //heading
2570  if (anim.animFlags & PROP_ANIM_FLAG_HEADING)
2571  {
2572  // rad2deg limitedrange -1 to +1
2573  cstate = (m_simbuf.simbuf_rotation * 57.29578f) / 360.0f;
2574  div++;
2575  }
2576 
2577  //torque
2578  const bool has_engine = (m_actor->ar_engine!= nullptr);
2579  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_TORQUE)
2580  {
2581  float torque = m_simbuf.simbuf_engine_crankfactor;
2582  if (torque <= 0.0f)
2583  torque = 0.0f;
2584  if (torque >= m_prop_anim_crankfactor_prev)
2585  cstate -= torque / 10.0f;
2586  else
2587  cstate = 0.0f;
2588 
2589  if (cstate <= -1.0f)
2590  cstate = -1.0f;
2591  m_prop_anim_crankfactor_prev = torque;
2592  div++;
2593  }
2594 
2595  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_GEAR)
2596  {
2597  bool match = static_cast<int>(anim.animOpt3) == m_actor->ar_engine->getGear();
2598  cstate += static_cast<int>(match);
2599  div++;
2600  }
2601 
2602  //shifterseq, to amimate sequentiell shifting
2603  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == SHIFTERSEQ)
2604  {
2605  float shifterseq_cstate = 0;
2606  // opt1 &opt2 = 0 this is a shifter
2607  if (!anim.lower_limit && !anim.upper_limit)
2608  {
2609  int shifter = m_simbuf.simbuf_gear;
2610  if (shifter > m_prop_anim_prev_gear)
2611  {
2612  shifterseq_cstate = 1.0f;
2613  m_prop_anim_shift_timer = 0.2f;
2614  }
2615  if (shifter < m_prop_anim_prev_gear)
2616  {
2617  shifterseq_cstate = -1.0f;
2618  m_prop_anim_shift_timer = -0.2f;
2619  }
2620  m_prop_anim_prev_gear = shifter;
2621 
2622  if (m_prop_anim_shift_timer > 0.0f)
2623  {
2624  shifterseq_cstate = 1.0f;
2625  m_prop_anim_shift_timer -= dt;
2626  if (m_prop_anim_shift_timer < 0.0f)
2627  m_prop_anim_shift_timer = 0.0f;
2628  }
2629  if (m_prop_anim_shift_timer < 0.0f)
2630  {
2631  shifterseq_cstate = -1.0f;
2632  m_prop_anim_shift_timer += dt;
2633  if (m_prop_anim_shift_timer > 0.0f)
2634  m_prop_anim_shift_timer = 0.0f;
2635  }
2636  }
2637  else
2638  {
2639  // check if anim.lower_limit is a valid to get commandvalue, then get commandvalue
2640  if (anim.lower_limit >= 1.0f && anim.lower_limit <= 48.0)
2641  if (m_simbuf.simbuf_commandkey[int(anim.lower_limit)].simbuf_cmd_value > 0)
2642  shifterseq_cstate = 1.0f;
2643  // check if anim.upper_limit is a valid to get commandvalue, then get commandvalue
2644  if (anim.upper_limit >= 1.0f && anim.upper_limit <= 48.0)
2645  if (m_simbuf.simbuf_commandkey[int(anim.upper_limit)].simbuf_cmd_value > 0)
2646  shifterseq_cstate = -1.0f;
2647  }
2648 
2649 
2650  cstate += UpdateSmoothShift(anim, dt, shifterseq_cstate);
2651  div++;
2652  }
2653 
2654  //shifterman1, left/right
2655  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == SHIFTERMAN1)
2656  {
2657  float shifterman1_cstate = 0.f;
2658  int shifter = m_simbuf.simbuf_gear;
2659  if (!shifter)
2660  {
2661  shifterman1_cstate = -0.5f;
2662  }
2663  else if (shifter < 0)
2664  {
2665  shifterman1_cstate = 1.0f;
2666  }
2667  else
2668  {
2669  shifterman1_cstate = -int((shifter - 1.0) / 2.0);
2670  }
2671 
2672  cstate += UpdateSmoothShift(anim, dt, shifterman1_cstate);
2673  div++;
2674  }
2675 
2676  //shifterman2, up/down
2677  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == SHIFTERMAN2)
2678  {
2679  float shifterman2_cstate = 0.f;
2680  int shifter = m_simbuf.simbuf_gear;
2681  shifterman2_cstate = 0.5f;
2682  if (shifter < 0)
2683  {
2684  shifterman2_cstate = 1.0f;
2685  }
2686  if (shifter > 0)
2687  {
2688  shifterman2_cstate = shifter % 2;
2689  }
2690 
2691  cstate += UpdateSmoothShift(anim, dt, shifterman2_cstate);
2692  div++;
2693  }
2694 
2695  //shifterlinear, to amimate cockpit gearselect gauge and autotransmission stick
2696  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == SHIFTERLIN)
2697  {
2698  float shifterlin_cstate = 0.f;
2699  int shifter = m_simbuf.simbuf_gear;
2700  int numgears = m_simbuf.simbuf_num_gears;
2701  shifterlin_cstate -= (shifter + 2.0) / (numgears + 2.0);
2702 
2703  cstate += UpdateSmoothShift(anim, dt, shifterlin_cstate);
2704  div++;
2705  }
2706 
2707  //autoshifterlin, autotransmission stick with only R/N/D positions
2708  if (has_engine && (anim.animFlags & PROP_ANIM_FLAG_SHIFTER) && anim.animOpt3 == AUTOSHIFTERLIN)
2709  {
2710  float shifterlin_cstate = 0.f;
2711  int shifter = std::min(m_simbuf.simbuf_gear, 1); // Clamp forward gears to 1
2712  int numgears = 1; // Number of forward gears
2713  shifterlin_cstate -= (shifter + 2.0) / (numgears + 2.0);
2714 
2715  cstate += UpdateSmoothShift(anim, dt, shifterlin_cstate);
2716  div++;
2717  }
2718 
2719  //parking brake
2720  if (anim.animFlags & PROP_ANIM_FLAG_PBRAKE)
2721  {
2722  float pbrake = static_cast<float>(m_simbuf.simbuf_parking_brake); // Bool --> float
2723  cstate -= pbrake;
2724  div++;
2725  }
2726 
2727  //speedo ( scales with speedomax )
2728  if (anim.animFlags & PROP_ANIM_FLAG_SPEEDO)
2729  {
2730  float speedo = m_simbuf.simbuf_wheel_speed / m_simbuf.simbuf_speedo_highest_kph;
2731  cstate -= speedo * 3.0f;
2732  div++;
2733  }
2734 
2735  //engine tacho ( scales with maxrpm, default is 3500 )
2736  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_TACHO)
2737  {
2738  float tacho = m_simbuf.simbuf_engine_rpm / m_simbuf.simbuf_engine_max_rpm;
2739  cstate -= tacho;
2740  div++;
2741  }
2742 
2743  //turbo
2744  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_TURBO)
2745  {
2746  float turbo = m_simbuf.simbuf_engine_turbo_psi * 3.34;
2747  cstate -= turbo / 67.0f;
2748  div++;
2749  }
2750 
2751  //brake
2752  if (anim.animFlags & PROP_ANIM_FLAG_BRAKE)
2753  {
2754  float brakes = m_simbuf.simbuf_brake;
2755  cstate -= brakes;
2756  div++;
2757  }
2758 
2759  //accelerator
2760  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_ACCEL)
2761  {
2762  float accel = m_simbuf.simbuf_engine_accel;
2763  cstate -= accel + 0.06f;
2764  //( small correction, get acc is nver smaller then 0.06.
2765  div++;
2766  }
2767 
2768  //clutch
2769  if (has_engine && anim.animFlags & PROP_ANIM_FLAG_CLUTCH)
2770  {
2771  float clutch = m_simbuf.simbuf_clutch;
2772  cstate -= fabs(1.0f - clutch);
2773  div++;
2774  }
2775 
2776  //turn indicator stalk
2778  {
2779  float signal = 0.0f;
2780  if (m_simbuf.simbuf_lightmask & RoRnet::LIGHTMASK_BLINK_LEFT)
2781  signal = -1.0f;
2782  if (m_simbuf.simbuf_lightmask & RoRnet::LIGHTMASK_BLINK_RIGHT)
2783  signal = 1.0f;
2784  cstate -= signal;
2785  div++;
2786  }
2787 
2788  //aeroengines rpm + throttle + torque ( turboprop ) + pitch ( turboprop ) + status + fire
2789  // `anim.animOpt3` is aeroengine number (starting from 1)
2790  if (anim.animOpt3 > 0.f && anim.animOpt3 <= float(m_simbuf.simbuf_aeroengines.size()))
2791  {
2792  const int aenum = int(anim.animOpt3 - 1.f);
2793  if (anim.animFlags & PROP_ANIM_FLAG_RPM)
2794  {
2795  float angle;
2796  float pcent = m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_rpmpc;
2797  if (pcent < 60.0)
2798  angle = -5.0 + pcent * 1.9167;
2799  else if (pcent < 110.0)
2800  angle = 110.0 + (pcent - 60.0) * 4.075;
2801  else
2802  angle = 314.0;
2803  cstate -= angle / 314.0f;
2804  div++;
2805  }
2807  {
2808  float throttle = m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_throttle;
2809  cstate -= throttle;
2810  div++;
2811  }
2812 
2813  if (m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_type == AeroEngineType::AE_XPROP)
2814  {
2816  {
2817  cstate = m_simbuf.simbuf_aeroengines[aenum].simbuf_tp_aetorque / 120.0f;
2818  div++;
2819  }
2820 
2821  if (anim.animFlags & PROP_ANIM_FLAG_AEPITCH)
2822  {
2823  cstate = m_simbuf.simbuf_aeroengines[aenum].simbuf_tp_aepitch / 120.0f;
2824  div++;
2825  }
2826  }
2827 
2829  {
2830  if (!m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_ignition)
2831  cstate = 0.0f;
2832  else
2833  cstate = 0.5f;
2834  if (m_simbuf.simbuf_aeroengines[aenum].simbuf_ae_failed)
2835  cstate = 1.0f;
2836  div++;
2837  }
2838  }
2839 
2840  const Ogre::Vector3 node0_pos = this->GetSimNodeBuffer()[0].AbsPosition;
2841  const Ogre::Vector3 node0_velo = m_simbuf.simbuf_node0_velo;
2842 
2843  //airspeed indicator
2845  {
2846  float ground_speed_kt = node0_velo.length() * 1.9438;
2847  float altitude = node0_pos.y;
2848 
2849  float sea_level_pressure = 101325; //in Pa
2850 
2851  float airpressure = sea_level_pressure * pow(1.0 - 0.0065 * altitude / 288.15, 5.24947); //in Pa
2852  float airdensity = airpressure * 0.0000120896;//1.225 at sea level
2853  float kt = ground_speed_kt * sqrt(airdensity / 1.225);
2854  cstate -= kt / 100.0f;
2855  div++;
2856  }
2857 
2858  //vvi indicator
2859  if (anim.animFlags & PROP_ANIM_FLAG_VVI)
2860  {
2861  float vvi = node0_velo.y * 196.85;
2862  // limit vvi scale to +/- 6m/s
2863  cstate -= vvi / 6000.0f;
2864  if (cstate >= 1.0f)
2865  cstate = 1.0f;
2866  if (cstate <= -1.0f)
2867  cstate = -1.0f;
2868  div++;
2869  }
2870 
2871  //altimeter
2873  {
2874  //altimeter indicator 1k oscillating
2875  if (anim.animOpt3 == 3.0f)
2876  {
2877  float altimeter = (node0_pos.y * 1.1811) / 360.0f;
2878  int alti_int = int(altimeter);
2879  float alti_mod = (altimeter - alti_int);
2880  cstate -= alti_mod;
2881  }
2882 
2883  //altimeter indicator 10k oscillating
2884  if (anim.animOpt3 == 2.0f)
2885  {
2886  float alti = node0_pos.y * 1.1811 / 3600.0f;
2887  int alti_int = int(alti);
2888  float alti_mod = (alti - alti_int);
2889  cstate -= alti_mod;
2890  if (cstate <= -1.0f)
2891  cstate = -1.0f;
2892  }
2893 
2894  //altimeter indicator 100k limited
2895  if (anim.animOpt3 == 1.0f)
2896  {
2897  float alti = node0_pos.y * 1.1811 / 36000.0f;
2898  cstate -= alti;
2899  if (cstate <= -1.0f)
2900  cstate = -1.0f;
2901  }
2902  div++;
2903  }
2904 
2905  //AOA
2906  if (anim.animFlags & PROP_ANIM_FLAG_AOA)
2907  {
2908  float aoa = m_simbuf.simbuf_wing4_aoa / 25.f;
2909  if ((node0_velo.length() * 1.9438) < 10.0f)
2910  aoa = 0;
2911  cstate -= aoa;
2912  if (cstate <= -1.0f)
2913  cstate = -1.0f;
2914  if (cstate >= 1.0f)
2915  cstate = 1.0f;
2916  div++;
2917  }
2918 
2919  Ogre::Vector3 cam_pos = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_pos ].AbsPosition;
2920  Ogre::Vector3 cam_roll = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_roll].AbsPosition;
2921  Ogre::Vector3 cam_dir = this->GetSimNodeBuffer()[m_actor->ar_main_camera_node_dir ].AbsPosition;
2922 
2923  // roll
2924  if (anim.animFlags & PROP_ANIM_FLAG_ROLL)
2925  {
2926  Ogre::Vector3 rollv = (cam_pos - cam_roll).normalisedCopy();
2927  Ogre::Vector3 dirv = (cam_pos - cam_dir).normalisedCopy();
2928  Ogre::Vector3 upv = dirv.crossProduct(-rollv);
2929  float rollangle = asin(rollv.dotProduct(Ogre::Vector3::UNIT_Y));
2930  // rad to deg
2931  rollangle = Ogre::Math::RadiansToDegrees(rollangle);
2932  // flip to other side when upside down
2933  if (upv.y < 0)
2934  rollangle = 180.0f - rollangle;
2935  cstate = rollangle / 180.0f;
2936  // data output is -0.5 to 1.5, normalize to -1 to +1 without changing the zero position.
2937  // this is vital for the animator beams and does not effect the animated props
2938  if (cstate >= 1.0f)
2939  cstate = cstate - 2.0f;
2940  div++;
2941  }
2942 
2943  // pitch
2944  if (anim.animFlags & PROP_ANIM_FLAG_PITCH)
2945  {
2946  Ogre::Vector3 dirv = (cam_pos - cam_dir).normalisedCopy();
2947  float pitchangle = asin(dirv.dotProduct(Ogre::Vector3::UNIT_Y));
2948  // radian to degrees with a max cstate of +/- 1.0
2949  cstate = (Ogre::Math::RadiansToDegrees(pitchangle) / 90.0f);
2950  div++;
2951  }
2952 
2953  // airbrake
2955  {
2956  float airbrake = static_cast<float>(m_simbuf.simbuf_airbrake_state);
2957  // cstate limited to -1.0f
2958  cstate -= airbrake / 5.0f;
2959  div++;
2960  }
2961 
2962  //flaps
2963  if (anim.animFlags & PROP_ANIM_FLAG_FLAP)
2964  {
2965  float flaps = FLAP_ANGLES[m_simbuf.simbuf_aero_flap_state];
2966  // cstate limited to -1.0f
2967  cstate = flaps;
2968  div++;
2969  }
2970 }
2971 
2973 {
2974  int prop_anim_key_index = 0;
2975 
2976  for (Prop& prop: m_props)
2977  {
2978  int animnum = 0;
2979  float rx = 0.0f;
2980  float ry = 0.0f;
2981  float rz = 0.0f;
2982 
2983  for (PropAnim& anim: prop.pp_animations)
2984  {
2985  float cstate = 0.0f;
2986  int div = 0.0f;
2987 
2988  this->CalcPropAnimation(anim, cstate, div, dt);
2989 
2990  // key triggered animations - state determined in simulation
2991  if (anim.animFlags & PROP_ANIM_FLAG_EVENT)
2992  {
2993  ROR_ASSERT(prop_anim_key_index < (int)m_simbuf.simbuf_prop_anim_keys.size());
2994  const bool anim_active = m_simbuf.simbuf_prop_anim_keys[prop_anim_key_index++].simbuf_anim_active;
2995  cstate += (float)anim_active;
2996  }
2997 
2998  // dashboard animations - state determined in simulation
3000  {
3001  int link_id = (int)anim.animOpt3;
3002  ROR_ASSERT(link_id < DD_MAX);
3003  cstate += m_actor->ar_dashboard->getNumeric(link_id);
3004  }
3005 
3006  //propanimation placed here to avoid interference with existing hydros(cstate) and permanent prop animation
3007  //land vehicle steering
3009  cstate += m_simbuf.simbuf_hydro_dir_state;
3010  //aileron
3012  cstate += m_simbuf.simbuf_hydro_aileron_state;
3013  //elevator
3015  cstate += m_simbuf.simbuf_hydro_elevator_state;
3016  //rudder
3017  if (anim.animFlags & PROP_ANIM_FLAG_ARUDDER)
3018  cstate += m_simbuf.simbuf_hydro_aero_rudder_state;
3019  //permanent
3021  cstate += 1.0f;
3022 
3023  cstate *= anim.animratio;
3024 
3025  // autoanimate noflip_bouncer
3026  if (anim.animOpt5)
3027  cstate *= (anim.animOpt5);
3028 
3029  //rotate prop
3031  {
3032  float limiter = 0.0f;
3033  // This code was formerly executed within a fixed timestep of 0.5ms and finetuned accordingly.
3034  // This is now taken into account by factoring in the respective fraction of the variable timestep.
3035  float const dt_frac = dt * 2000.f;
3037  {
3038  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
3039  {
3040  prop.pp_rota.x += cstate * dt_frac;
3041  limiter = prop.pp_rota.x;
3042  }
3043  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
3044  {
3045  prop.pp_rota.y += cstate * dt_frac;
3046  limiter = prop.pp_rota.y;
3047  }
3048  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
3049  {
3050  prop.pp_rota.z += cstate * dt_frac;
3051  limiter = prop.pp_rota.z;
3052  }
3053  }
3054  else
3055  {
3056  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
3057  rx += cstate;
3058  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
3059  ry += cstate;
3060  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
3061  rz += cstate;
3062  }
3063 
3064  bool limiterchanged = false;
3065  // check if a positive custom limit is set to evaluate/calc flip back
3066 
3067  if (limiter > anim.upper_limit)
3068  {
3069  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3070  {
3071  limiter = anim.upper_limit; // stop at limit
3072  anim.animOpt5 *= -1.0f; // change cstate multiplier if bounce is set
3073  }
3074  else
3075  {
3076  limiter = anim.lower_limit; // flip to other side at limit
3077  }
3078  limiterchanged = true;
3079  }
3080 
3081  if (limiter < anim.lower_limit)
3082  {
3083  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3084  {
3085  limiter = anim.lower_limit; // stop at limit
3086  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
3087  }
3088  else
3089  {
3090  limiter = anim.upper_limit; // flip to other side at limit
3091  }
3092  limiterchanged = true;
3093  }
3094 
3095  if (limiterchanged)
3096  {
3097  if (anim.animMode & PROP_ANIM_MODE_ROTA_X)
3098  prop.pp_rota.x = limiter;
3099  if (anim.animMode & PROP_ANIM_MODE_ROTA_Y)
3100  prop.pp_rota.y = limiter;
3101  if (anim.animMode & PROP_ANIM_MODE_ROTA_Z)
3102  prop.pp_rota.z = limiter;
3103  }
3104  }
3105 
3106  //offset prop
3107 
3109  {
3110  float offset = 0.0f;
3111  float autooffset = 0.0f;
3112 
3113  if (anim.animMode & PROP_ANIM_MODE_OFFSET_X)
3114  offset = prop.pp_offset_orig.x;
3115  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Y)
3116  offset = prop.pp_offset_orig.y;
3117  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Z)
3118  offset = prop.pp_offset_orig.z;
3119 
3121  {
3122  // This code was formerly executed within a fixed timestep of 0.5ms and finetuned accordingly.
3123  // This is now taken into account by factoring in the respective fraction of the variable timestep.
3124  float const dt_frac = dt * 2000.f;
3125  autooffset = offset + cstate * dt_frac;
3126 
3127  if (autooffset > anim.upper_limit)
3128  {
3129  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3130  {
3131  autooffset = anim.upper_limit; // stop at limit
3132  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
3133  }
3134  else
3135  {
3136  autooffset = anim.lower_limit; // flip to other side at limit
3137  }
3138  }
3139 
3140  if (autooffset < anim.lower_limit)
3141  {
3142  if (anim.animMode & PROP_ANIM_MODE_NOFLIP)
3143  {
3144  autooffset = anim.lower_limit; // stop at limit
3145  anim.animOpt5 *= -1.0f; // change cstate multiplier if active
3146  }
3147  else
3148  {
3149  autooffset = anim.upper_limit; // flip to other side at limit
3150  }
3151  }
3152  }
3153  offset += cstate;
3154 
3155  if (anim.animMode & PROP_ANIM_MODE_OFFSET_X)
3156  {
3157  prop.pp_offset.x = offset;
3159  prop.pp_offset_orig.x = autooffset;
3160  }
3161  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Y)
3162  {
3163  prop.pp_offset.y = offset;
3165  prop.pp_offset_orig.y = autooffset;
3166  }
3167  if (anim.animMode & PROP_ANIM_MODE_OFFSET_Z)
3168  {
3169  prop.pp_offset.z = offset;
3171  prop.pp_offset_orig.z = autooffset;
3172  }
3173  }
3174  animnum++;
3175  }
3176  //recalc the quaternions with final stacked rotation values ( rx, ry, rz )
3177  rx += prop.pp_rota.x;
3178  ry += prop.pp_rota.y;
3179  rz += prop.pp_rota.z;
3180 
3181  prop.pp_rot = Ogre::Quaternion(Ogre::Degree(rz), Ogre::Vector3::UNIT_Z) *
3182  Ogre::Quaternion(Ogre::Degree(ry), Ogre::Vector3::UNIT_Y) *
3183  Ogre::Quaternion(Ogre::Degree(rx), Ogre::Vector3::UNIT_X);
3184  }
3185 }
3186 
3188 {
3189  std::sort(m_flexbodies.begin(), m_flexbodies.end(), [](FlexBody* a, FlexBody* b) { return a->getVertexCount() > b->getVertexCount(); });
3190 }
3191 
3193 {
3194  m_flexbody_tasks.clear();
3195 
3196  for (FlexBody* fb: m_flexbodies)
3197  {
3198  // Update visibility (same logic as props)
3199  const bool visible = (fb->fb_camera_mode_active == CAMERA_MODE_ALWAYS_VISIBLE || fb->fb_camera_mode_active == m_simbuf.simbuf_cur_cinecam);
3200  fb->setVisible(visible);
3201 
3202  // Update visible on background thread
3203  if (fb->isVisible())
3204  {
3205  auto func = std::function<void()>([fb]()
3206  {
3207  fb->computeFlexbody();
3208  });
3209  auto task_handle = App::GetThreadPool()->RunTask(func);
3210  m_flexbody_tasks.push_back(task_handle);
3211  }
3212  }
3213 }
3214 
3216 {
3217  for (FlexBody* fb: m_flexbodies)
3218  {
3219  fb->reset();
3220  }
3221 }
3222 
3224 {
3225  for (FlexBody* fb: m_flexbodies)
3226  {
3227  fb->setVisible(visible);
3228  }
3229 }
3230 
3232 {
3233  for (auto& task: m_flexbody_tasks)
3234  {
3235  task->join();
3236  }
3237  for (FlexBody* fb: m_flexbodies)
3238  {
3239  if (fb->isVisible())
3240  {
3241  fb->updateFlexbodyVertexBuffers();
3242  }
3243  }
3244 }
3245 
3246 // internal helper
3247 bool ShouldEnableLightSource(FlareType type, bool is_player)
3248 {
3249  switch (App::gfx_flares_mode->getEnum<GfxFlaresMode>())
3250  {
3252  return is_player && (type == FlareType::HEADLIGHT);
3254  return (type == FlareType::HEADLIGHT);
3256  return true;
3257  default:
3258  return false;
3259  }
3260 }
3261 
3262 void RoR::GfxActor::UpdateFlares(float dt, bool is_player)
3263 {
3264  // Flare states are determined in simulation, this function only applies them to OGRE objects
3265  // ------------------------------------------------------------------------------------------
3266 
3267  NodeSB* nodes = this->GetSimNodeBuffer();
3268 
3269  int num_flares = static_cast<int>(m_actor->ar_flares.size());
3270  for (int i=0; i<num_flares; ++i)
3271  {
3272  flare_t& flare = m_actor->ar_flares[i];
3273 
3274  // Skip placeholder flares (removed via Tuning system)
3275  if (!flare.bbs)
3276  {
3277  continue;
3278  }
3279 
3280  this->SetMaterialFlareOn(i, flare.intensity > 0.3);
3281 
3282  // The billboard flare
3283  flare.snode->setVisible(flare.intensity > 0);
3284 
3285  // The light source
3286  if (flare.light)
3287  {
3288  flare.light->setVisible(flare.intensity > 0 && ShouldEnableLightSource(flare.fl_type, is_player));
3289  }
3290 
3291  Ogre::Vector3 normal = (nodes[flare.nodey].AbsPosition - nodes[flare.noderef].AbsPosition).crossProduct(nodes[flare.nodex].AbsPosition - nodes[flare.noderef].AbsPosition);
3292  normal.normalise();
3293  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);
3294  Ogre::Vector3 vdir = mposition - App::GetCameraManager()->GetCameraNode()->getPosition();
3295  float vlen = vdir.length();
3296  // not visible from 500m distance
3297  if (vlen > 500.0)
3298  {
3299  flare.snode->setVisible(false);
3300  continue;
3301  }
3302  //normalize
3303  vdir = vdir / vlen;
3304  float amplitude = normal.dotProduct(vdir);
3305  flare.snode->setPosition(mposition - 0.1 * amplitude * normal * flare.offsetz);
3306  flare.snode->setDirection(normal);
3307  float fsize = flare.size * flare.intensity;
3308  if (fsize < 0)
3309  {
3310  amplitude = 1;
3311  fsize *= -1;
3312  }
3313  if (flare.light)
3314  {
3315  flare.light->getParentSceneNode()->setPosition(mposition - 0.2 * amplitude * normal);
3316  // point the real light towards the ground a bit
3317  flare.light->getParentSceneNode()->setDirection(-normal - Ogre::Vector3(0, 0.2, 0));
3318  }
3319  if (flare.intensity > 0)
3320  {
3321  if (amplitude > 0)
3322  {
3323  flare.bbs->setDefaultDimensions(amplitude * fsize, amplitude * fsize);
3324  flare.snode->setVisible(true);
3325  }
3326  else
3327  {
3328  flare.snode->setVisible(false);
3329  }
3330  }
3331  }
3332 }
3333 
3335 {
3336  // Cab mesh
3337  if (m_cab_scene_node != nullptr)
3338  {
3339  static_cast<Ogre::Entity*>(m_cab_scene_node->getAttachedObject(0))->setCastShadows(value);
3340  }
3341 
3342  // Props
3343  for (Prop& prop: m_props)
3344  {
3345  if (prop.pp_mesh_obj != nullptr && prop.pp_mesh_obj->getEntity())
3346  prop.pp_mesh_obj->getEntity()->setCastShadows(value);
3347  if (prop.pp_wheel_mesh_obj != nullptr && prop.pp_wheel_mesh_obj->getEntity())
3348  prop.pp_wheel_mesh_obj->getEntity()->setCastShadows(value);
3349  }
3350 
3351  // Wheels
3352  for (WheelGfx& wheel: m_wheels)
3353  {
3354  static_cast<Ogre::Entity*>(wheel.wx_scenenode->getAttachedObject(0))->setCastShadows(value);
3355  }
3356 
3357  // Softbody beams
3358  for (BeamGfx& rod: m_gfx_beams)
3359  {
3360  static_cast<Ogre::Entity*>(rod.rod_scenenode->getAttachedObject(0))->setCastShadows(value);
3361  }
3362 
3363  // Flexbody meshes
3364  for (FlexBody* fb: m_flexbodies)
3365  {
3366  fb->setFlexbodyCastShadow(value);
3367  }
3368 }
3369 
3370 void RoR::GfxActor::RegisterCabMesh(Ogre::Entity* ent, Ogre::SceneNode* snode, FlexObj* flexobj)
3371 {
3372  m_cab_mesh = flexobj;
3373  m_cab_entity = ent;
3374  m_cab_scene_node = snode;
3375 }
3376 
3378 {
3379  if (m_cab_entity != nullptr)
3380  {
3381  m_cab_entity->setVisible(visible);
3382  }
3383  this->SetWheelsVisible(visible);
3384  this->SetPropsVisible(visible);
3385  this->SetFlexbodyVisible(visible);
3386  this->SetWingsVisible(visible);
3387  this->SetRodsVisible(visible);
3388  this->SetAeroEnginesVisible(visible);
3389 }
3390 
3392 {
3393  for (int i = 0; i < m_actor->ar_num_wings; ++i)
3394  {
3395  m_actor->ar_wings[i].cnode->setVisible(visible);
3396  }
3397 
3398  for (size_t i=0; i< m_actor->ar_airbrakes.size(); ++i)
3399  {
3400  m_gfx_airbrakes[i].abx_scenenode->setVisible(visible);
3401  }
3402 }
3403 
3405 {
3406  for (int i = 0; i < m_actor->ar_num_wings; ++i)
3407  {
3408  wing_t& wing = m_actor->ar_wings[i];
3409  wing.cnode->setPosition(wing.fa->updateVerticesGfx(this));
3410  wing.fa->uploadVertices();
3411  }
3412 }
3413 
3415 {
3416  int count = 0;
3417  for (const Prop& prop : m_props)
3418  {
3419  if (prop.pp_beacon_type != 0)
3420  {
3421  count++;
3422  }
3423  }
3424  return count;
3425 }
3426 
3427 void RoR::GfxActor::SetNodeHot(NodeNum_t nodenum, bool value)
3428 {
3429  for (NodeGfx& nfx : m_gfx_nodes)
3430  {
3431  if (nfx.nx_node_idx == nodenum)
3432  {
3433  nfx.nx_is_hot = value;
3434  }
3435  }
3436 }
3437 
3438 void RoR::GfxActor::RemoveBeam(int beam_index)
3439 {
3440  auto itor = m_gfx_beams.begin();
3441  auto endi = m_gfx_beams.end();
3442  while (itor != endi)
3443  {
3444  if (itor->rod_beam_index == beam_index)
3445  {
3446  // Destroy OGRE objects
3447  if (itor->rod_scenenode)
3448  {
3449  if (itor->rod_scenenode->getAttachedObject(0))
3450  {
3451  Ogre::Entity* ent = static_cast<Ogre::Entity*>(itor->rod_scenenode->getAttachedObject(0));
3452  if (ent)
3453  {
3454  ent->detachFromParent();
3455  App::GetGfxScene()->GetSceneManager()->destroyEntity(ent);
3456  }
3457  }
3458 
3459  App::GetGfxScene()->GetSceneManager()->destroySceneNode(itor->rod_scenenode);
3460  itor->rod_scenenode = nullptr;
3461  }
3462 
3463  // Destroy the beam visuals
3464  m_gfx_beams.erase(itor);
3465  return;
3466  }
3467  itor++;
3468  }
3469 }
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:620
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:367
RoR::IWater::GetStaticWaterHeight
virtual float GetStaticWaterHeight()=0
Returns static water level configured in 'terrn2'.
RoR::wing_t::fa
FlexAirfoil * fa
Definition: SimData.h:538
RoR::rotator_t::nodes2
NodeNum_t nodes2[4]
Definition: SimData.h:602
RoR::PROP_ANIM_FLAG_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:2012
RoR::SHOCK3
@ SHOCK3
shock3
Definition: SimData.h:109
RoR::PropAnim::upper_limit
float upper_limit
The upper limit for the animation.
Definition: GfxData.h:153
RoR::PROP_ANIM_MODE_OFFSET_X
const PropAnimMode_t PROP_ANIM_MODE_OFFSET_X
Definition: GfxData.h:85
RoR::GfxActor::RemoveBeam
void RemoveBeam(int beam_index)
Definition: GfxActor.cpp:3438
RoR::wing_t::cnode
Ogre::SceneNode * cnode
Definition: SimData.h:539
RoR::node_t::Velocity
Ogre::Vector3 Velocity
Definition: SimData.h:294
RoR::SHIFTERLIN
@ SHIFTERLIN
Definition: GfxData.h:134
RoR::GfxActor::GfxActor
GfxActor(ActorPtr actor, ActorSpawner *spawner, std::string ogre_resource_group, RoR::Renderdash *renderdash)
Definition: GfxActor.cpp:61
RoR::PROP_ANIM_FLAG_AIRBRAKE
const PropAnimFlag_t PROP_ANIM_FLAG_AIRBRAKE
Definition: GfxData.h:49
RoR::VCAM_ROLE_TRACKING_VIDEOCAM
@ VCAM_ROLE_TRACKING_VIDEOCAM
Definition: Application.h:378
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:623
RoR::flare_t::size
float size
Definition: SimData.h:631
RoR::GfxActor::UpdateSimDataBuffer
void UpdateSimDataBuffer()
Copies sim. data from Actor to GfxActor for later update.
Definition: GfxActor.cpp:1776
RoR::Actor::ar_filename
std::string ar_filename
Attribute; filled at spawn.
Definition: Actor.h:423
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:322
RoR::BeamGfx
Visuals of softbody beam (beam_t struct); Partially updated along with SimBuffer.
Definition: GfxData.h:267
RoR::FlexAirfoil::updateVerticesGfx
Ogre::Vector3 updateVerticesGfx(RoR::GfxActor *gfx_actor)
Definition: FlexAirfoil.cpp:442
RoR::NodeSB
Definition: SimBuffers.h:67
RoR::node_t::nd_last_collision_gm
ground_model_t * nd_last_collision_gm
Physics state; last collision 'ground model' (surface definition)
Definition: SimData.h:325
RoR::GfxActor::CalculateDriverPos
void CalculateDriverPos(Ogre::Vector3 &out_pos, Ogre::Quaternion &out_rot)
Definition: GfxActor.cpp:2111
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:3223
RoR::App::GetCameraManager
CameraManager * GetCameraManager()
Definition: Application.cpp:279
RoR::node_t::AbsPosition
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition: SimData.h:293
RoR::Prop::pp_beacon_rot_angle
float pp_beacon_rot_angle[4]
Radians.
Definition: GfxData.h:195
RoR::Prop::pp_offset
Ogre::Vector3 pp_offset
Definition: GfxData.h:168
RoR::App::GetGuiManager
GUIManager * GetGuiManager()
Definition: Application.cpp:273
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:152
RoR::PROP_ANIM_FLAG_DASHBOARD
const PropAnimFlag_t PROP_ANIM_FLAG_DASHBOARD
Used with dashboard system inputs, see enum DashData in file DashBoardManager.h.
Definition: GfxData.h:76
RoR::beam_t::p1
node_t * p1
Definition: SimData.h:334
DashBoardManager.h
RoR::VideoCamera::vcam_render_tex
Ogre::TexturePtr vcam_render_tex
Definition: GfxData.h:236
RoR::DebugViewType::DEBUGVIEW_NONE
@ DEBUGVIEW_NONE
RoR::GfxActor::ToggleDebugView
void ToggleDebugView()
Definition: GfxActor.cpp:1569
RoR::PropAnim::animMode
PropAnimMode_t animMode
Definition: GfxData.h:142
RoR::Actor::ar_instance_id
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition: Actor.h:376
SkyManager.h
Renderdash.h
RoR::AeroEngineSB::simbuf_tp_aepitch
float simbuf_tp_aepitch
Turboprop pitch, used by animation "aepitch".
Definition: SimBuffers.h:97
RoR::PropAnim::animFlags
PropAnimFlag_t animFlags
Definition: GfxData.h:141
RoR::node_t::nd_under_water
bool nd_under_water
State; GFX hint.
Definition: SimData.h:319
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:132
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:420
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:3414
RoR::DebugViewType
DebugViewType
Definition: GfxData.h:101
RoR::HandleGenericException
void HandleGenericException(const std::string &from, BitMask_t flags)
Definition: Application.cpp:373
RoR::Exhaust
Definition: GfxData.h:310
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:198
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:625
RoR::Turboprop::pitch
float pitch
Definition: TurboProp.h:43
RoR::GUIManager::IsGuiHidden
bool IsGuiHidden() const
Definition: GUIManager.h:154
RoR::PROP_ANIM_MODE_NOFLIP
const PropAnimMode_t PROP_ANIM_MODE_NOFLIP
Definition: GfxData.h:89
MeshObject.h
RoR::FlareType::HEADLIGHT
@ HEADLIGHT
RoR::beam_t::bm_locked_actor
ActorPtr bm_locked_actor
in case p2 is on another actor
Definition: SimData.h:349
RoR::rotator_t::axis2
NodeNum_t axis2
Definition: SimData.h:604
RoR::VCAM_ROLE_VIDEOCAM
@ VCAM_ROLE_VIDEOCAM
Definition: Application.h:377
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:319
RoR::WheelGfx
Definition: GfxData.h:283
RoR::DebugViewType::DEBUGVIEW_SUBMESH
@ DEBUGVIEW_SUBMESH
RoR::VideoCamera::vcam_render_target
Ogre::RenderTexture * vcam_render_target
Definition: GfxData.h:235
MovableText.h
This creates a billboarding object that displays a text.
RoR::AeroEngine::getRPM
virtual float getRPM()=0
RoR::rotator_t::axis1
NodeNum_t axis1
rot axis
Definition: SimData.h:603
RoR::FlexObj
A visual mesh, forming a chassis for softbody actor At most one instance is created per actor.
Definition: FlexObj.h:59
RoR::Prop::pp_rot
Ogre::Quaternion pp_rot
Definition: GfxData.h:171
RoR::FlareType
FlareType
Definition: SimData.h:228
RoR::flare_t::nodey
NodeNum_t nodey
Definition: SimData.h:618
RoR::GfxActor::UpdateCabMesh
void UpdateCabMesh()
Definition: GfxActor.cpp:1952
RoR::GfxActor::FinishFlexbodyTasks
void FinishFlexbodyTasks()
Definition: GfxActor.cpp:3231
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:292
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:384
RoR::Turbojet::getAfterburner
float getAfterburner()
Definition: TurboJet.h:85
RoR::Prop::pp_beacon_type
char pp_beacon_type
Special prop: beacon {0 = none, 'b' = user-specified, 'r' = red, 'p' = police lightbar,...
Definition: GfxData.h:190
RoR::GfxActor::SortFlexbodies
void SortFlexbodies()
Definition: GfxActor.cpp:3187
RoR::PROP_ANIM_FLAG_ALTIMETER
const PropAnimFlag_t PROP_ANIM_FLAG_ALTIMETER
Definition: GfxData.h:46
RoR::SHIFTERSEQ
@ SHIFTERSEQ
Definition: GfxData.h:133
RoR::PROP_ANIM_MODE_ROTA_Y
const PropAnimMode_t PROP_ANIM_MODE_ROTA_Y
Definition: GfxData.h:83
OgreImGui.h
RefCountingObjectPtr< Actor >
RoR::World2ScreenConverter::Convert
Ogre::Vector3 Convert(Ogre::Vector3 world_pos)
Definition: Utils.h: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:621
GUIManager.h
RoR::PropAnim::animratio
float animratio
A coefficient for the animation, prop degree if used with mode: rotation and propoffset if used with ...
Definition: GfxData.h:140
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:3427
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:69
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:157
NODE_IMMOVABLE_RADIUS
const float NODE_IMMOVABLE_RADIUS(2.8f)
RoR::Prop::pp_node_ref
NodeNum_t pp_node_ref
Definition: GfxData.h:165
RoR::AeroEngineSB::simbuf_tp_aetorque
float simbuf_tp_aetorque
Turboprop torque, used by animation "aetorque".
Definition: SimBuffers.h:96
RoR::GfxActor::FinishWheelUpdates
void FinishWheelUpdates()
Definition: GfxActor.cpp:1978
RoR::App::mp_state
CVar * mp_state
Definition: Application.cpp:115
RoR::PropAnim
Definition: GfxData.h:138
RoR::GfxActor::UpdatePropAnimations
void UpdatePropAnimations(float dt)
Definition: GfxActor.cpp:2972
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:167
RoR::GfxActor::UpdateProps
void UpdateProps(float dt, bool is_player_actor)
Definition: GfxActor.cpp:2303
TurboJet.h
RoR::wheel_t::debug_force
Ogre::Vector3 debug_force
Definition: SimData.h:460
RoR::DebugViewType::DEBUGVIEW_SLIDENODES
@ DEBUGVIEW_SLIDENODES
RoR::beam_t
Simulation: An edge in the softbody structure.
Definition: SimData.h:329
SlideNode.h
RoR::wheel_t::debug_slip
Ogre::Vector3 debug_slip
Definition: SimData.h:459
RoR::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:346
RoR::GfxActor::UpdateAeroEngines
void UpdateAeroEngines()
Definition: GfxActor.cpp:2083
RoR::Turboprop
Definition: TurboProp.h:38
RoR::NodeGfx
Gfx attributes/state of a softbody node.
Definition: GfxData.h:243
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:2406
RoR::GfxActor::UpdateSmoothShift
float UpdateSmoothShift(PropAnim &anim, float dt, float new_target_cstate)
Definition: GfxActor.cpp:2483
RoR::Prop::pp_beacon_light
Ogre::Light * pp_beacon_light[4]
Definition: GfxData.h:193
RoR::GfxActor::GetActorState
int GetActorState() const
Definition: GfxActor.cpp:2010
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:439
RoR::GfxActor::SetCabLightsActive
void SetCabLightsActive(bool state_on)
Definition: GfxActor.cpp:421
RoR::GfxActor::SetWheelsVisible
void SetWheelsVisible(bool value)
Definition: GfxActor.cpp:1993
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:131
RoR::AeroEngineSB::simbuf_ae_rpmpc
float simbuf_ae_rpmpc
Definition: SimBuffers.h:94
RoR::App::app_state
CVar * app_state
Definition: Application.cpp:79
RoR::flare_t::nodex
NodeNum_t nodex
Definition: SimData.h:617
RoR::GfxActor::SetWingsVisible
void SetWingsVisible(bool visible)
Definition: GfxActor.cpp:3391
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:3247
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:1755
RoR::VideoCamera::vcam_ogre_camera
Ogre::Camera * vcam_ogre_camera
Definition: GfxData.h:234
RoR::Turboprop::max_torque
float max_torque
Definition: TurboProp.h:45
RoR::AUTOSHIFTERLIN
@ AUTOSHIFTERLIN
Definition: GfxData.h:135
RoR::node_t::pos
NodeNum_t pos
This node's index in Actor::ar_nodes array.
Definition: SimData.h:303
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:310
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:632
RoR::wheel_t::wh_near_attach_node
node_t * wh_near_attach_node
Definition: SimData.h:437
RoR::Prop
A mesh attached to vehicle frame via 3 nodes.
Definition: GfxData.h:162
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:380
RoR::PROP_ANIM_FLAG_VVI
const PropAnimFlag_t PROP_ANIM_FLAG_VVI
Definition: GfxData.h:45
RoR::SHOCK2
@ SHOCK2
shock2
Definition: SimData.h:108
RoR::ground_model_t::fx_type
int fx_type
Definition: SimData.h:741
RoR::AirbrakeGfx
Definition: GfxData.h:292
RoR::PROP_ANIM_MODE_OFFSET_Z
const PropAnimMode_t PROP_ANIM_MODE_OFFSET_Z
Definition: GfxData.h:87
RoR::App::mp_hide_own_net_label
CVar * mp_hide_own_net_label
Definition: Application.cpp:119
RoR::GfxActor::m_particles_dust
DustPool * m_particles_dust
Definition: GfxActor.h:199
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:379
SoundScriptManager.h
RoR::GfxFlaresMode::CURR_VEHICLE_HEAD_ONLY
@ CURR_VEHICLE_HEAD_ONLY
Only current vehicle, main lights.
RoR::node_t
Physics: A vertex in the softbody structure.
Definition: SimData.h:285
FlexBody.h
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:284
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:2009
RoR::VCAM_ROLE_MIRROR_PROP_RIGHT
@ VCAM_ROLE_MIRROR_PROP_RIGHT
The classic 'special prop/rear view mirror'.
Definition: Application.h:387
RoR::AeroEngineSB::simbuf_ae_throttle
float simbuf_ae_throttle
Definition: SimBuffers.h:95
RoR::AppState::SIMULATION
@ SIMULATION
RoR::Renderdash
'renderdash' is a name of a classic Render-To-Texture animated material with gauges and other dashboa...
Definition: Renderdash.h:34
RoR::PropAnim::shifterTarget
float shifterTarget
Definition: GfxData.h:158
RoR::PropAnim::shifterSmooth
float shifterSmooth
Definition: GfxData.h:156
RoR::GfxActor::ScaleActor
void ScaleActor(Ogre::Vector3 relpos, float ratio)
Definition: GfxActor.cpp:1714
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:203
RoR::VideoCamera
An Ogre::Camera mounted on the actor and rendering into either in-scene texture or external window.
Definition: GfxData.h:221
RoR::VCAM_ROLE_TRACKING_MIRROR
@ VCAM_ROLE_TRACKING_MIRROR
A MIRROR(1) with tracking node set.
Definition: Application.h:383
RoR::AirbrakeGfx::abx_scenenode
Ogre::SceneNode * abx_scenenode
Definition: GfxData.h:295
RoR::VideoCamState::VCSTATE_DISABLED
@ VCSTATE_DISABLED
RoR::App::gfx_flares_mode
CVar * gfx_flares_mode
Definition: Application.cpp:215
RoR::node_t::nd_last_collision_slip
Ogre::Vector3 nd_last_collision_slip
Physics state; last collision slip vector.
Definition: SimData.h:323
RoR::GfxScene::GetDustPool
DustPool * GetDustPool(const char *name)
Definition: GfxScene.cpp:270
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:461
RoR::PROP_ANIM_FLAG_PITCH
const PropAnimFlag_t PROP_ANIM_FLAG_PITCH
Definition: GfxData.h:51
DustPool.h
RoR::GfxActor::SpecialGetRotationTo
static Ogre::Quaternion SpecialGetRotationTo(const Ogre::Vector3 &src, const Ogre::Vector3 &dest)
Definition: GfxActor.cpp:1670
RoR::PROP_ANIM_FLAG_TURBO
const PropAnimFlag_t PROP_ANIM_FLAG_TURBO
Definition: GfxData.h:60
RoR::PropAnim::animOpt5
float animOpt5
Definition: GfxData.h:151
BEAM_HYDRO_COLOR
const ImU32 BEAM_HYDRO_COLOR(0xff55a3e0)
FlexAirfoil.h
RoR::NodeSB::AbsPosition
Ogre::Vector3 AbsPosition
Definition: SimBuffers.h:69
RoR::PROP_ANIM_FLAG_HEADING
const PropAnimFlag_t PROP_ANIM_FLAG_HEADING
Definition: GfxData.h:66
RoR::AeroEngineSB::simbuf_tj_ab_thrust
float simbuf_tj_ab_thrust
Definition: SimBuffers.h:98
RoR::flare_t::snode
Ogre::SceneNode * snode
Definition: SimData.h:622
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:436
RoR::beam_t::bm_inter_actor
bool bm_inter_actor
in case p2 is on another actor
Definition: SimData.h:348
RoR::wheel_t::wh_radius
Ogre::Real wh_radius
Definition: SimData.h:441
RoR::DebugViewType::DEBUGVIEW_NODES
@ DEBUGVIEW_NODES
RoR::PropAnim::animOpt3
float animOpt3
MULTIPURPOSE.
Definition: GfxData.h:150
TurboProp.h
RoR::GfxActor::RegisterCabMesh
void RegisterCabMesh(Ogre::Entity *ent, Ogre::SceneNode *snode, FlexObj *flexobj)
Definition: GfxActor.cpp:3370
RoR::GfxActor::SetAeroEnginesVisible
void SetAeroEnginesVisible(bool visible)
Definition: GfxActor.cpp:2387
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:309
RoR::VideoCamState::VCSTATE_ENABLED_ONLINE
@ VCSTATE_ENABLED_ONLINE
RoR::GfxActor::SetCastShadows
void SetCastShadows(bool value)
Definition: GfxActor.cpp:3334
RoR::GfxActor::SetBeaconsEnabled
void SetBeaconsEnabled(bool beacon_light_is_active)
Definition: GfxActor.cpp:2414
RoR::GfxFlaresMode
GfxFlaresMode
Definition: Application.h:243
RoR::AeroEngine::getIgnition
virtual bool getIgnition()=0
RoR::BEAM_HYDRO
@ BEAM_HYDRO
Definition: SimData.h:71
RoR::flare_t
Definition: SimData.h:614
RoR::DebugViewType::DEBUGVIEW_SKELETON
@ DEBUGVIEW_SKELETON
RoR::ThreadPool::RunTask
std::shared_ptr< Task > RunTask(const std::function< void()> &task_func)
Submit new asynchronous task to thread pool and return Task handle to allow for synchronization.
Definition: ThreadPool.h:178
RoR::AeroEngine::getType
virtual AeroEngineType getType()=0
RoR::node_t::nd_has_ground_contact
bool nd_has_ground_contact
Physics state.
Definition: SimData.h:313
RoR::DebugViewType::DEBUGVIEW_WHEELS
@ DEBUGVIEW_WHEELS
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:2398
RoR::GfxActor::UpdateCParticles
void UpdateCParticles()
Definition: GfxActor.cpp:2040
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:3404
RoR::wheel_t::wh_axis_node_0
node_t * wh_axis_node_0
Definition: SimData.h:438
RoR::GfxFlaresMode::NONE
@ NONE
None (fastest)
RoR::GfxActor::IsActorLive
bool IsActorLive() const
Should the visuals be updated for this actor?
Definition: GfxActor.cpp:1947
RoR::PROP_ANIM_FLAG_BRUDDER
const PropAnimFlag_t PROP_ANIM_FLAG_BRUDDER
Definition: GfxData.h:72
BEAM_BROKEN_COLOR
const ImU32 BEAM_BROKEN_COLOR(0xff4466dd)
RoR::CAMERA_MODE_ALWAYS_VISIBLE
static CameraMode_t CAMERA_MODE_ALWAYS_VISIBLE
Definition: GfxData.h:125
RoR::FlareMaterial
Definition: GfxData.h:303
RoR::wheel_t::debug_vel
Ogre::Vector3 debug_vel
Definition: SimData.h:458
NODE_RADIUS
const float NODE_RADIUS(2.f)
RoR::flare_t::light
Ogre::Light * light
Definition: SimData.h:624
RoR::GfxActor::CalcPropAnimation
void CalcPropAnimation(PropAnim &anim, float &cstate, int &div, float dt)
Definition: GfxActor.cpp:2507
RoR::beam_t::p2
node_t * p2
Definition: SimData.h:335
RoR::AeroEngineType::AE_XPROP
@ AE_XPROP
RoR::App::diag_hide_nodes
CVar * diag_hide_nodes
Definition: Application.cpp:156
RoR::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:427
RoR::GfxActor::m_particles_sparks
DustPool * m_particles_sparks
Definition: GfxActor.h:202
RoR::GfxActor
Definition: GfxActor.h:52
RoR::ActorState::NETWORKED_HIDDEN
@ NETWORKED_HIDDEN
not simulated, not updated (remote)
RoR::rotator_t::nodes1
NodeNum_t nodes1[4]
Definition: SimData.h:601
InputEngine.h
Handles controller inputs from player. Defines input events and binding mechanism,...
FLAP_ANGLES
static const float FLAP_ANGLES[6]
Definition: SimConstants.h:82
RoR::GfxActor::SetMaterialFlareOn
void SetMaterialFlareOn(int flare_index, bool state_on)
Definition: GfxActor.cpp: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:2142
RoR::Prop::pp_beacon_rot_rate
float pp_beacon_rot_rate[4]
Radians per second.
Definition: GfxData.h:194
RoR::GfxActor::UpdateFlares
void UpdateFlares(float dt, bool is_player)
Definition: GfxActor.cpp:3262
RoR::SHOCK1
@ SHOCK1
shock1
Definition: SimData.h:107
RoR::AirbrakeGfx::abx_offset
Ogre::Vector3 abx_offset
Definition: GfxData.h:297
FlexObj.h
RoRnet::LIGHTMASK_BEACONS
@ LIGHTMASK_BEACONS
beacons on
Definition: RoRnet.h:120
WhereFrom
std::string WhereFrom(GfxActor *gfx_actor, const std::string &doing_what)
Definition: GfxActor.cpp:76
AirBrake.h
fast_sqrt
float fast_sqrt(const float x)
Definition: ApproxMath.h:135
RoR::Turbojet
Definition: TurboJet.h:62
RoR::FlexBody
Flexbody = A deformable mesh; updated on CPU every frame, then uploaded to video memory.
Definition: FlexBody.h:43
Collisions.h
RoR::GfxActor::SetAllMeshesVisible
void SetAllMeshesVisible(bool value)
Definition: GfxActor.cpp:3377
RoR::GfxActor::m_particles_ripple
DustPool * m_particles_ripple
Definition: GfxActor.h:201
RoR::ActorState::NETWORKED_OK
@ NETWORKED_OK
not simulated (remote) actor
RoR::Prop::pp_beacon_bbs
Ogre::BillboardSet * pp_beacon_bbs[4]
Definition: GfxData.h:191
RoR::flare_t::offsetx
float offsetx
Definition: SimData.h:619
RoR::App::GetThreadPool
ThreadPool * GetThreadPool()
Definition: Application.cpp:278
RoR::Prop::pp_scene_node
Ogre::SceneNode * pp_scene_node
The pivot scene node (parented to root-node).
Definition: GfxData.h:172
RoR::PROP_ANIM_FLAG_CLUTCH
const PropAnimFlag_t PROP_ANIM_FLAG_CLUTCH
Definition: GfxData.h:56
RoR::GfxScene::AdjustParticleSystemTimeFactor
void AdjustParticleSystemTimeFactor(Ogre::ParticleSystem *psys)
Definition: GfxScene.cpp:428
RoR::GfxActor::UpdateFlexbodies
void UpdateFlexbodies()
Definition: GfxActor.cpp:3192
RoR::GfxActor::ResetFlexbodies
void ResetFlexbodies()
Definition: GfxActor.cpp:3215
NODE_IMMOVABLE_COLOR
const ImU32 NODE_IMMOVABLE_COLOR(0xff0033ff)
RoR::GfxActor::UpdateWheelVisuals
void UpdateWheelVisuals()
Definition: GfxActor.cpp:1960
RoR::GfxActor::UpdateNetLabels
void UpdateNetLabels(float dt)
Definition: GfxActor.cpp:2091
RoR::AeroEngineSB::simbuf_ae_ignition
bool simbuf_ae_ignition
Turbojet.
Definition: SimBuffers.h:100
RoR::PROP_ANIM_FLAG_ROLL
const PropAnimFlag_t PROP_ANIM_FLAG_ROLL
Definition: GfxData.h:50
RoR::Prop::pp_node_x
NodeNum_t pp_node_x
Definition: GfxData.h:166
RoR::GfxActor::getOwningActor
const ActorPtr & getOwningActor()
Definition: GfxActor.h: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:200
RoR
Definition: AppContext.h:36
RoR::HANDLEGENERICEXCEPTION_LOGFILE
@ HANDLEGENERICEXCEPTION_LOGFILE
Definition: Application.h:636
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::Prop::pp_beacon_scene_node
Ogre::SceneNode * pp_beacon_scene_node[4]
Definition: GfxData.h:192
RoR::App::GetGfxScene
GfxScene * GetGfxScene()
Definition: Application.cpp:280
RoR::GfxActor::UpdateExhausts
void UpdateExhausts()
Definition: GfxActor.cpp:2057
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:350
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:386
RoR::Terrain::getWater
IWater * getWater()
Definition: Terrain.h:86
HydraxWater.h
RoR::wing_t
Definition: SimData.h:536
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:338
RoR::flare_t::noderef
NodeNum_t noderef
Definition: SimData.h:616
RoR::beam_t::bm_broken
bool bm_broken
Definition: SimData.h:351
RoR::rotator_t
Definition: SimData.h:598
RoR::GameContext::GetTerrain
const TerrainPtr & GetTerrain()
Definition: GameContext.h:117
RoR::ground_model_t::fx_colour
Ogre::ColourValue fx_colour
Definition: SimData.h:742
RoR::GfxActor::SetPropsVisible
void SetPropsVisible(bool visible)
Definition: GfxActor.cpp:2379
RoR::GfxActor::~GfxActor
~GfxActor()
Definition: GfxActor.cpp:85
NODE_TEXT_COLOR
const ImU32 NODE_TEXT_COLOR(0xffcccccf)