RigsofRods
Soft-body Physics Simulation
GUI_FlexbodyDebug.cpp
Go to the documentation of this file.
1 /*
2  This source file is part of Rigs of Rods
3  Copyright 2022 Petr Ohlidal
4 
5  For more information, see http://www.rigsofrods.org/
6 
7  Rigs of Rods is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License version 3, as
9  published by the Free Software Foundation.
10 
11  Rigs of Rods is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
21 
22 
23 #include "GUI_FlexbodyDebug.h"
24 
25 #include "Actor.h"
26 #include "Application.h"
27 #include "SimData.h"
28 #include "Collisions.h"
29 #include "FlexBody.h"
30 #include "GameContext.h"
31 #include "GUIManager.h"
32 #include "GUIUtils.h"
33 #include "Language.h"
34 #include "Terrain.h"
35 #include "Utils.h"
36 
37 using namespace RoR;
38 using namespace GUI;
39 
41 {
42  ImGui::SetNextWindowPosCenter(ImGuiCond_FirstUseEver);
43  ImGuiWindowFlags win_flags = ImGuiWindowFlags_NoCollapse;
44  bool keep_open = true;
45  ImGui::Begin(_LC("FlexbodyDebug", "Flexbody/Prop debug"), &keep_open, win_flags);
46 
48  if (!actor)
49  {
50  ImGui::Text("%s", _LC("FlexbodyDebug", "You are on foot."));
51  ImGui::End();
52  return;
53  }
54 
55  if (actor->GetGfxActor()->GetFlexbodies().size() == 0
56  && actor->GetGfxActor()->getProps().size() == 0)
57  {
58  ImGui::Text("%s", _LC("FlexbodyDebug", "This vehicle has no flexbodies or props."));
59  ImGui::End();
60  return;
61  }
62 
63  if (ImGui::Combo(_LC("FlexbodyDebug", "Select element"), &m_combo_selection, m_combo_items.c_str()))
64  {
65  this->UpdateVisibility();
66  show_locator.resize(0);
68  {
69  show_locator.resize(actor->GetGfxActor()->GetFlexbodies()[m_combo_selection]->getVertexCount(), false);
70  }
71  }
72  if (ImGui::Checkbox("Hide other (note: pauses reflections)", &this->hide_other_elements))
73  {
74  this->UpdateVisibility();
75  }
76  ImGui::Separator();
77 
78  // Fetch the element (prop or flexbody)
79  FlexBody* flexbody = nullptr;
80  Prop* prop = nullptr;
81  Ogre::MaterialPtr mat; // Assume one submesh (=> subentity); can be NULL if the flexbody is a placeholder, see `FlexBodyPlaceholder_t`
82  NodeNum_t node_ref = NODENUM_INVALID, node_x = NODENUM_INVALID, node_y = NODENUM_INVALID;
83  std::string mesh_name;
84  if (actor->GetGfxActor()->getProps().size() > 0
86  {
88  node_ref = prop->pp_node_ref;
89  node_x = prop->pp_node_x;
90  node_y = prop->pp_node_y;
91  if (prop->pp_mesh_obj // Aerial beacons 'L/R/w' have no mesh
92  && prop->pp_mesh_obj->getLoadedMesh()) // Props are spawned even if meshes fail to load.
93  {
94  mat = prop->pp_mesh_obj->getEntity()->getSubEntity(0)->getMaterial();
95  mesh_name = prop->pp_mesh_obj->getEntity()->getMesh()->getName();
96  }
97  }
98  else
99  {
100  flexbody = actor->GetGfxActor()->GetFlexbodies()[m_combo_selection];
102  {
103  mat = flexbody->getEntity()->getSubEntity(0)->getMaterial();
104  }
105  node_ref = flexbody->getRefNode();
106  node_x = flexbody->getXNode();
107  node_y = flexbody->getYNode();
108  mesh_name = flexbody->getOrigMeshName();
109  }
110 
111  ImGui::Text("Mesh: '%s'", mesh_name.c_str());
112  if (mat)
113  {
114  ImGui::SameLine();
115  if (ImGui::Checkbox("Wireframe (per material)", &this->draw_mesh_wireframe))
116  {
117  // Assume one technique and one pass
118  if (mat->getTechniques().size() > 0 && mat->getTechniques()[0]->getPasses().size() > 0)
119  {
120  Ogre::PolygonMode mode = (this->draw_mesh_wireframe) ? Ogre::PM_WIREFRAME : Ogre::PM_SOLID;
121  mat->getTechniques()[0]->getPasses()[0]->setPolygonMode(mode);
122  }
123  }
124  }
125 
126  ImGui::Text("Base nodes: Ref=%d, X=%d, Y=%d", (int)node_ref, (int)node_x, (int)node_y);
127  ImGui::SameLine();
128  ImGui::Checkbox("Show##base", &this->show_base_nodes);
129 
130  bool flexbody_locators_visible = false;
131  if (flexbody)
132  {
133  ImGui::Text("Forset nodes: (total %d)", (int)flexbody->getForsetNodes().size());
134  ImGui::SameLine();
135  ImGui::Checkbox("Show all##forset", &this->show_forset_nodes);
136 
137  ImGui::Text("Vertices: (total %d)", (int)flexbody->getVertexCount());
138  ImGui::SameLine();
139  ImGui::Checkbox("Show all (pick with mouse)##verts", &this->show_vertices);
140 
141  if (ImGui::CollapsingHeader("Vertex locators table"))
142  {
143  this->DrawLocatorsTable(flexbody, /*out:*/flexbody_locators_visible);
144  }
145 
146  if (ImGui::CollapsingHeader("Vertex locators memory (experimental!)"))
147  {
148  this->DrawMemoryOrderGraph(flexbody);
149  }
150  }
151 
152  if (ImGui::CollapsingHeader("Mesh info"))
153  {
154  if (flexbody)
155  this->DrawMeshInfo(flexbody);
156  else
157  this->DrawMeshInfo(prop);
158  }
159 
160  m_is_hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
162  ImGui::End();
163 
164  if (!keep_open)
165  {
166  this->SetVisible(false);
167  }
168 
169  if (this->show_base_nodes || this->show_forset_nodes || this->show_vertices || flexbody_locators_visible)
170  {
171  this->DrawDebugView(flexbody, prop, node_ref, node_x, node_y);
172  }
173 }
174 
176 {
177  // Reset
178  m_combo_items = "";
179  m_combo_props_start = -1;
180  m_combo_selection = -1;
181 
182  // Var
183  int num_combo_items = 0;
184 
185  // Analyze flexbodies
187  if (actor && actor->GetGfxActor()->GetFlexbodies().size() > 0)
188  {
189  for (FlexBody* fb : actor->GetGfxActor()->GetFlexbodies())
190  {
191  if (fb->getPlaceholderType() == FlexBody::PlaceholderType::NOT_A_PLACEHOLDER)
192  {
193  ImAddItemToComboboxString(m_combo_items, fmt::format("{} ({} verts -> {} nodes)",
194  fb->getOrigMeshName(), fb->getVertexCount(), fb->getForsetNodes().size()));
195  }
196  else
197  {
199  fb->getOrigMeshName(), FlexBody::PlaceholderTypeToString(fb->getPlaceholderType())));
200 
201  }
202  num_combo_items++;
203  }
204 
205  m_combo_selection = 0;
206 
207  show_locator.resize(0);
208  show_locator.resize(actor->GetGfxActor()->GetFlexbodies()[m_combo_selection]->getVertexCount(), false);
209  }
210 
211  // Analyze props as well
212  if (actor && actor->GetGfxActor()->getProps().size() > 0)
213  {
214  m_combo_props_start = num_combo_items;
215  for (Prop const& p: actor->GetGfxActor()->getProps())
216  {
217  std::string caption;
218  if (p.pp_beacon_type == 'L' || p.pp_beacon_type == 'R' || p.pp_beacon_type == 'w')
219  {
220  caption = fmt::format("(special prop - aerial nav light '{}')", p.pp_beacon_type);
221  }
222  else if (p.pp_wheel_mesh_obj)
223  {
224  if (p.pp_mesh_obj->getLoadedMesh() && p.pp_wheel_mesh_obj->getLoadedMesh())
225  {
226  caption = fmt::format("(special prop: dashboard '{}' + dirwheel '{}')", p.pp_mesh_obj->getLoadedMesh()->getName(), p.pp_wheel_mesh_obj->getLoadedMesh()->getName());
227  }
228  else if (!p.pp_mesh_obj->getLoadedMesh() && p.pp_wheel_mesh_obj->getLoadedMesh())
229  {
230  caption = fmt::format("(special prop: no dashboard + dirwheel '{}')", p.pp_wheel_mesh_obj->getLoadedMesh()->getName());
231  }
232  else
233  {
234  caption = "(corrupted dashboard prop - no meshes loaded)";
235  }
236  }
237  else if (p.pp_mesh_obj && p.pp_mesh_obj->getLoadedMesh())
238  {
239  caption = fmt::format("{} (prop)", p.pp_mesh_obj->getLoadedMesh()->getName());
240  }
241  else
242  {
243  caption = "(corrupted prop - mesh not loaded)";
244  }
246  num_combo_items++;
247  }
248 
249  if (m_combo_selection == -1)
250  m_combo_selection = 0;
251  }
252 
254 }
255 
256 const ImVec4 FORSETNODE_COLOR_V4(1.f, 0.87f, 0.3f, 1.f);
257 const ImU32 FORSETNODE_COLOR = ImColor(FORSETNODE_COLOR_V4);
258 const float FORSETNODE_RADIUS(2.f);
259 const ImU32 BASENODE_COLOR(0xff44a5ff); // ABGR format (alpha, blue, green, red)
260 const float BASENODE_RADIUS(3.f);
261 const float BEAM_THICKNESS(1.2f);
262 const float BLUE_BEAM_THICKNESS = BEAM_THICKNESS + 0.8f; // Blue beam looks a lot thinner for some reason
263 const ImU32 NODE_TEXT_COLOR(0xffcccccf); // node ID text color - ABGR format (alpha, blue, green, red)
264 const ImVec4 VERTEX_COLOR_V4(0.1f, 1.f, 1.f, 1.f);
265 const ImU32 VERTEX_COLOR = ImColor(VERTEX_COLOR_V4);
266 const ImU32 VERTEX_TEXT_COLOR = ImColor(171, 194, 186);
267 const float VERTEX_RADIUS(1.f);
268 const float LOCATOR_BEAM_THICKNESS(1.0f);
269 const ImVec4 LOCATOR_BEAM_COLOR_V4(0.05f, 1.f, 0.65f, 1.f);
270 const ImVec4 AXIS_X_BEAM_COLOR_V4(1.f, 0.f, 0.f, 1.f);
271 const ImVec4 AXIS_Y_BEAM_COLOR_V4(0.15f, 0.15f, 1.f, 1.f);
275 const float VERT_HOVER_MAX_DISTANCE = 25.f;
276 const float MEMGRAPH_NODE_RADIUS(1.f);
277 const ImVec4 MEMGRAPH_NODEREF_COLOR_V4(1.f, 0.89f, 0.22f, 1.f);
278 const ImVec4 MEMGRAPH_NODEX_COLOR_V4(1.f, 0.21f, 0.21f, 1.f);
279 const ImVec4 MEMGRAPH_NODEY_COLOR_V4(0.27f, 0.76f, 1.f, 1.f);
280 
281 void FlexbodyDebug::DrawDebugView(FlexBody* flexbody, Prop* prop, NodeNum_t node_ref, NodeNum_t node_x, NodeNum_t node_y)
282 {
283  ROR_ASSERT(App::GetGameContext()->GetPlayerActor() != nullptr);
285 
286  // Var
287  ImVec2 screen_size = ImGui::GetIO().DisplaySize;
288  World2ScreenConverter world2screen(
289  App::GetCameraManager()->GetCamera()->getViewMatrix(true), App::GetCameraManager()->GetCamera()->getProjectionMatrix(), Ogre::Vector2(screen_size.x, screen_size.y));
290 
291  ImDrawList* drawlist = GetImDummyFullscreenWindow();
292 
293  const int LAYER_BEAMS = 0;
294  const int LAYER_NODES = 1;
295  const int LAYER_TEXT = 2;
296  drawlist->ChannelsSplit(3);
297 
298  if (this->show_base_nodes)
299  {
300  drawlist->ChannelsSetCurrent(LAYER_NODES);
301  Ogre::Vector3 refnode_pos = world2screen.Convert(nodes[node_ref].AbsPosition);
302  Ogre::Vector3 xnode_pos = world2screen.Convert(nodes[node_x].AbsPosition);
303  Ogre::Vector3 ynode_pos = world2screen.Convert(nodes[node_y].AbsPosition);
304  // (z < 0) means "in front of the camera"
305  if (refnode_pos.z < 0.f) {drawlist->AddCircleFilled(ImVec2(refnode_pos.x, refnode_pos.y), BASENODE_RADIUS, BASENODE_COLOR); }
306  if (xnode_pos.z < 0.f) { drawlist->AddCircleFilled(ImVec2(xnode_pos.x, xnode_pos.y), BASENODE_RADIUS, BASENODE_COLOR); }
307  if (ynode_pos.z < 0.f) { drawlist->AddCircleFilled(ImVec2(ynode_pos.x, ynode_pos.y), BASENODE_RADIUS, BASENODE_COLOR); }
308 
309  drawlist->ChannelsSetCurrent(LAYER_BEAMS);
310  if (refnode_pos.z < 0)
311  {
312  if (xnode_pos.z < 0) { drawlist->AddLine(ImVec2(refnode_pos.x, refnode_pos.y), ImVec2(xnode_pos.x, xnode_pos.y), AXIS_X_BEAM_COLOR, BEAM_THICKNESS); }
313  if (ynode_pos.z < 0) { drawlist->AddLine(ImVec2(refnode_pos.x, refnode_pos.y), ImVec2(ynode_pos.x, ynode_pos.y), AXIS_Y_BEAM_COLOR, BLUE_BEAM_THICKNESS); }
314  }
315 
316  drawlist->ChannelsSetCurrent(LAYER_TEXT);
317  drawlist->AddText(ImVec2(refnode_pos.x, refnode_pos.y), NODE_TEXT_COLOR, fmt::format("{}", node_ref).c_str());
318  drawlist->AddText(ImVec2(xnode_pos.x, xnode_pos.y), NODE_TEXT_COLOR, fmt::format("{}", node_x).c_str());
319  drawlist->AddText(ImVec2(ynode_pos.x, ynode_pos.y), NODE_TEXT_COLOR, fmt::format("{}", node_y).c_str());
320  }
321 
322  if (flexbody && this->show_forset_nodes)
323  {
324  for (NodeNum_t node : flexbody->getForsetNodes())
325  {
326  drawlist->ChannelsSetCurrent(LAYER_NODES);
327  Ogre::Vector3 pos = world2screen.Convert(nodes[node].AbsPosition);
328  if (pos.z < 0.f) { drawlist->AddCircleFilled(ImVec2(pos.x, pos.y), FORSETNODE_RADIUS, FORSETNODE_COLOR); }
329 
330  drawlist->ChannelsSetCurrent(LAYER_TEXT);
331  drawlist->AddText(ImVec2(pos.x, pos.y), NODE_TEXT_COLOR, fmt::format("{}", node).c_str());
332  }
333  }
334 
335  int hovered_vert = -1;
336  float hovered_vert_dist_squared = FLT_MAX;
337  ImVec2 mouse_pos = ImGui::GetMousePos();
338  ImVec2 dbg_cursor_dist(0, 0);
339  if (flexbody && this->show_vertices)
340  {
341  for (int i = 0; i < flexbody->getVertexCount(); i++)
342  {
343  Ogre::Vector3 vert_pos = world2screen.Convert(flexbody->getVertexPos(i));
344  if (vert_pos.z < 0.f)
345  {
346  // Draw the visual dot
347  drawlist->ChannelsSetCurrent(LAYER_NODES);
348  drawlist->AddCircleFilled(ImVec2(vert_pos.x, vert_pos.y), VERTEX_RADIUS, VERTEX_COLOR);
349 
350  // Check mouse hover
351  ImVec2 cursor_dist((vert_pos.x - mouse_pos.x), (vert_pos.y - mouse_pos.y));
352  float dist_squared = (cursor_dist.x * cursor_dist.x) + (cursor_dist.y * cursor_dist.y);
353  if (dist_squared < hovered_vert_dist_squared)
354  {
355  hovered_vert = i;
356  hovered_vert_dist_squared = dist_squared;
357  dbg_cursor_dist = cursor_dist;
358  }
359  }
360  }
361  }
362 
363  // Validate mouse hover
364  if (hovered_vert != -1
365  && hovered_vert_dist_squared > VERT_HOVER_MAX_DISTANCE * VERT_HOVER_MAX_DISTANCE)
366  {
367  hovered_vert = -1;
368  }
369 
370  if (flexbody)
371  {
372  for (int i = 0; i < flexbody->getVertexCount(); i++)
373  {
374  if (this->show_locator[i] || i == hovered_vert)
375  {
376  // The vertex
377  Ogre::Vector3 vert_pos = world2screen.Convert(flexbody->getVertexPos(i));
378 
379  if (vert_pos.z < 0.f)
380  {
381  if (!this->show_vertices) // don't draw twice
382  {
383  drawlist->ChannelsSetCurrent(LAYER_NODES);
384  drawlist->AddCircleFilled(ImVec2(vert_pos.x, vert_pos.y), VERTEX_RADIUS, VERTEX_COLOR);
385 
386  // Check mouse hover
387  ImVec2 cursor_dist((vert_pos.x - mouse_pos.x), (vert_pos.y - mouse_pos.y));
388  float dist_squared = (cursor_dist.x * cursor_dist.x) + (cursor_dist.y * cursor_dist.y);
389  if (dist_squared < hovered_vert_dist_squared)
390  {
391  hovered_vert = i;
392  hovered_vert_dist_squared = dist_squared;
393  dbg_cursor_dist = cursor_dist;
394  }
395  }
396 
397  drawlist->ChannelsSetCurrent(LAYER_TEXT);
398  drawlist->AddText(ImVec2(vert_pos.x, vert_pos.y), VERTEX_TEXT_COLOR, fmt::format("v{}", i).c_str());
399  }
400 
401  // The locator nodes
402  Locator_t& loc = flexbody->getVertexLocator(i);
403  Ogre::Vector3 refnode_pos = world2screen.Convert(nodes[loc.ref].AbsPosition);
404  Ogre::Vector3 xnode_pos = world2screen.Convert(nodes[loc.nx].AbsPosition);
405  Ogre::Vector3 ynode_pos = world2screen.Convert(nodes[loc.ny].AbsPosition);
406  if (!this->show_forset_nodes) // don't draw twice
407  {
408  // (z < 0) means "in front of the camera"
409  if (refnode_pos.z < 0.f) { drawlist->AddCircleFilled(ImVec2(refnode_pos.x, refnode_pos.y), FORSETNODE_RADIUS, FORSETNODE_COLOR); }
410  if (xnode_pos.z < 0.f) { drawlist->AddCircleFilled(ImVec2(xnode_pos.x, xnode_pos.y), FORSETNODE_RADIUS, FORSETNODE_COLOR); }
411  if (ynode_pos.z < 0.f) { drawlist->AddCircleFilled(ImVec2(ynode_pos.x, ynode_pos.y), FORSETNODE_RADIUS, FORSETNODE_COLOR); }
412  }
413 
414  drawlist->ChannelsSetCurrent(LAYER_BEAMS);
415  if (refnode_pos.z < 0)
416  {
417  if (xnode_pos.z < 0) { drawlist->AddLine(ImVec2(refnode_pos.x, refnode_pos.y), ImVec2(xnode_pos.x, xnode_pos.y), AXIS_X_BEAM_COLOR, BEAM_THICKNESS); }
418  if (ynode_pos.z < 0) { drawlist->AddLine(ImVec2(refnode_pos.x, refnode_pos.y), ImVec2(ynode_pos.x, ynode_pos.y), AXIS_Y_BEAM_COLOR, BLUE_BEAM_THICKNESS); }
419  if (vert_pos.z < 0) { drawlist->AddLine(ImVec2(refnode_pos.x, refnode_pos.y), ImVec2(vert_pos.x, vert_pos.y), LOCATOR_BEAM_COLOR, LOCATOR_BEAM_THICKNESS); }
420  }
421 
422  if (!this->show_forset_nodes) // don't draw twice
423  {
424  drawlist->AddText(ImVec2(refnode_pos.x, refnode_pos.y), NODE_TEXT_COLOR, fmt::format("{}", loc.ref).c_str());
425  drawlist->AddText(ImVec2(xnode_pos.x, xnode_pos.y), NODE_TEXT_COLOR, fmt::format("{}", loc.nx).c_str());
426  drawlist->AddText(ImVec2(ynode_pos.x, ynode_pos.y), NODE_TEXT_COLOR, fmt::format("{}", loc.ny).c_str());
427  }
428 
429  if (i == hovered_vert && ImGui::IsMouseClicked(0))
430  {
431  this->show_locator[i] = !this->show_locator[i];
432  }
433  }
434  }
435  }
436 
437  drawlist->ChannelsMerge();
438 }
439 
441 {
442  // Both flexbodies and props use the same dynamic visibility mode, see `CameraMode_t` typedef and constants in file GfxData.h
443  // ---------------------------------------------------------------------------------------------------------------------------
444 
446  if (!actor)
447  {
448  return;
449  }
450 
451  if (this->hide_other_elements)
452  {
453  // Hide everything, even meshes scattered across gameplay components (wheels, wings...).
454  // Note: Environment map (dynamic reflections) is halted while the "Hide other" mode is active - see `RoR::GfxEnvmap::UpdateEnvMap()`
455  actor->GetGfxActor()->SetAllMeshesVisible(false);
456  // Override prop dynamic visibility mode
457  for (Prop& prop : actor->GetGfxActor()->getProps())
458  {
459  prop.pp_camera_mode_active = CAMERA_MODE_ALWAYS_HIDDEN;
460  }
461  // Override flexbody dynamic visibility mode
462  for (FlexBody* flexbody: actor->GetGfxActor()->GetFlexbodies())
463  {
464  flexbody->fb_camera_mode_active = CAMERA_MODE_ALWAYS_HIDDEN;
465  }
466 
467  // Then re-display what we need manually.
468  auto& flexbody_vec = actor->GetGfxActor()->GetFlexbodies();
469  const int combo_flexbody_selection = m_combo_selection;
470  if (combo_flexbody_selection >= 0 && combo_flexbody_selection < (int)flexbody_vec.size())
471  {
472  flexbody_vec[combo_flexbody_selection]->fb_camera_mode_active = CAMERA_MODE_ALWAYS_VISIBLE;
473  }
474 
475  auto& prop_vec = actor->GetGfxActor()->getProps();
476  const int combo_prop_selection = m_combo_selection - (int)flexbody_vec.size();
477  if (combo_prop_selection >= 0 && combo_prop_selection < (int)prop_vec.size())
478  {
479  prop_vec[combo_prop_selection].pp_camera_mode_active = CAMERA_MODE_ALWAYS_VISIBLE;
480  if (prop_vec[combo_prop_selection].pp_wheel_mesh_obj)
481  {
482  // Special case: the steering wheel mesh visibility is not controlled by 'camera mode'
483  prop_vec[combo_prop_selection].pp_wheel_mesh_obj->setVisible(true);
484  }
485  }
486  }
487  else
488  {
489  // Show everything, `GfxActor::UpdateProps()` will update visibility as needed.
490  actor->GetGfxActor()->SetAllMeshesVisible(true);
491  // Restore prop dynamic visibility mode
492  for (Prop& prop : actor->GetGfxActor()->getProps())
493  {
494  prop.pp_camera_mode_active = prop.pp_camera_mode_orig;
495  }
496  // Restore flexbody dynamic visibility mode
497  for (FlexBody* flexbody: actor->GetGfxActor()->GetFlexbodies())
498  {
499  flexbody->fb_camera_mode_active = flexbody->fb_camera_mode_orig;
500  }
501  }
502 }
503 
504 void FlexbodyDebug::DrawLocatorsTable(FlexBody* flexbody, bool& locators_visible)
505 {
506  const float content_height =
507  (2.f * ImGui::GetStyle().WindowPadding.y)
508  + (5.f * ImGui::GetItemsLineHeightWithSpacing())
509  + ImGui::GetStyle().ItemSpacing.y * 5;
510  const float child_height = ImGui::GetWindowHeight() - (content_height + 100);
511 
512 
513  ImGui::BeginChild("FlexbodyDebug-scroll", ImVec2(0.f, child_height), false);
514 
515  // Begin table
516  ImGui::Columns(5);
517  ImGui::TextDisabled("Vert#");
518  ImGui::NextColumn();
519  ImGui::TextDisabled("REF node");
520  ImGui::NextColumn();
521  ImGui::TextDisabled("VX node");
522  ImGui::NextColumn();
523  ImGui::TextDisabled("VY node");
524  ImGui::NextColumn();
525  // show checkbox
526  ImGui::NextColumn();
527  ImGui::Separator();
528 
529  for (int i = 0; i < flexbody->getVertexCount(); i++)
530  {
531  ImGui::PushID(i);
532  Locator_t& loc = flexbody->getVertexLocator(i);
533  ImGui::TextDisabled("%d", i);
534  ImGui::NextColumn();
535  ImGui::Text("%d", (int)loc.ref);
536  ImGui::NextColumn();
537  ImGui::Text("%d", (int)loc.nx);
538  ImGui::NextColumn();
539  ImGui::Text("%d", (int)loc.ny);
540  ImGui::NextColumn();
541  bool show = this->show_locator[i];
542  if (ImGui::Checkbox("Show", &show))
543  {
544  this->show_locator[i] = show;
545  }
546  ImGui::NextColumn();
547  ImGui::PopID();
548 
549  locators_visible = locators_visible || this->show_locator[i];
550  }
551 
552  // End table
553  ImGui::Columns(1);
554  ImGui::EndChild();
555 }
556 
558 {
559  // Analysis
560  NodeNum_t forset_max = std::numeric_limits<NodeNum_t>::min();
561  NodeNum_t forset_min = std::numeric_limits<NodeNum_t>::max();
562  for (NodeNum_t n : flexbody->getForsetNodes())
563  {
564  if (n > forset_max) { forset_max = n; }
565  if (n < forset_min) { forset_min = n; }
566  }
567 
568  // Tools!
569  const float SLIDER_WIDTH = 150;
571  ImGui::SameLine();
572  if (ImGui::Button("Reload vehicle"))
573  {
578  }
579 
580  if (App::flexbody_defrag_enabled->getBool())
581  {
582  if (ImGui::CollapsingHeader("Artistic effects (keep all enabled for correct visual)."))
583  {
585  ImGui::SameLine();
587  ImGui::SameLine();
588  DrawGCheckbox(App::flexbody_defrag_invert_lookup, "Invert index lookup");
589  }
590  }
591 
592  if (App::flexbody_defrag_enabled->getBool())
593  {
594  ImGui::TextDisabled("Sorting: insert-sort by lowest penalty, start: REF=VX=VY=%d", (int)forset_min);
595  ImGui::TextDisabled("Penalty calc: nodes (each x each), smalest nodes, node means");
596  ImGui::SetNextItemWidth(SLIDER_WIDTH);
597  DrawGIntSlider(App::flexbody_defrag_const_penalty, "Const penalty for inequality", 0, 15);
598  ImGui::SetNextItemWidth(SLIDER_WIDTH);
599  DrawGIntSlider(App::flexbody_defrag_prog_up_penalty, "Progressive penalty for upward direction", 0, 15);
600  ImGui::SetNextItemWidth(SLIDER_WIDTH);
601  DrawGIntSlider(App::flexbody_defrag_prog_down_penalty, "Progressive penalty for downward direction", 0, 15);
602  }
603 
604  // Legend
605  ImGui::TextDisabled("For optimal CPU cache usage, all dots should be roughly in ascending order (left->right), gaps are OK");
606  ImGui::TextDisabled("X axis (left->right) = verts (total %d)", flexbody->getVertexCount());
607  ImGui::TextDisabled("Y axis (bottom->top) = nodes (lowest %d, higest %d) ", (int)forset_min, (int)forset_max);
608  ImGui::SameLine();
609  ImGui::TextColored(MEMGRAPH_NODEREF_COLOR_V4, "REF");
610  ImGui::SameLine();
611  ImGui::TextColored(MEMGRAPH_NODEX_COLOR_V4, " VX");
612  ImGui::SameLine();
613  ImGui::TextColored(MEMGRAPH_NODEY_COLOR_V4, " VY");
614  ImGui::Separator();
615 
616  // The graph
617  ImVec2 size(ImGui::GetWindowWidth() - 2 * ImGui::GetStyle().WindowPadding.x, 200);
618  ImVec2 top_left_pos = ImGui::GetCursorScreenPos();
619  ImGui::Dummy(size);
620 
621  ImDrawList* drawlist = ImGui::GetWindowDrawList();
622  int num_verts = flexbody->getVertexCount();
623  const float x_step = (size.x / (float)num_verts);
624  const float y_step = (size.y / (float)(forset_max - forset_min));
625  for (int i = 0; i < num_verts; i++)
626  {
627  const int NUM_SEGMENTS = 5;
628  Locator_t& loc = flexbody->getVertexLocator(i);
629  ImVec2 bottom_x_pos = top_left_pos + ImVec2(i * x_step, size.y);
630 
631  drawlist->AddCircleFilled(bottom_x_pos - ImVec2(0, (loc.ref - forset_min) * y_step), MEMGRAPH_NODE_RADIUS, ImColor(MEMGRAPH_NODEREF_COLOR_V4), NUM_SEGMENTS);
632  drawlist->AddCircleFilled(bottom_x_pos - ImVec2(0, (loc.nx - forset_min) * y_step), MEMGRAPH_NODE_RADIUS, ImColor(MEMGRAPH_NODEX_COLOR_V4), NUM_SEGMENTS);
633  drawlist->AddCircleFilled(bottom_x_pos - ImVec2(0, (loc.ny - forset_min) * y_step), MEMGRAPH_NODE_RADIUS, ImColor(MEMGRAPH_NODEY_COLOR_V4), NUM_SEGMENTS);
634  }
635  ImGui::Separator();
636 }
637 
639 {
640  ImGui::Text("For developers only; modders cannot affect this.");
641  ImGui::Separator();
642  ImGui::Text("%s", flexbody->getOrigMeshInfo().c_str());
643  ImGui::Separator();
644  ImGui::Text("%s", flexbody->getLiveMeshInfo().c_str());
645 }
646 
648 {
649  ImGui::Text("The prop mesh files as provided by modder.");
650  if (prop->pp_mesh_obj && prop->pp_mesh_obj->getLoadedMesh())
651  {
652  ImGui::Separator();
653  ImGui::Text("%s", RoR::PrintMeshInfo("Prop", prop->pp_mesh_obj->getLoadedMesh()).c_str());
654  }
655  if (prop->pp_wheel_mesh_obj && prop->pp_wheel_mesh_obj->getLoadedMesh())
656  {
657  ImGui::Separator();
658  ImGui::Text("%s", RoR::PrintMeshInfo("Special: steering wheel", prop->pp_wheel_mesh_obj->getLoadedMesh()).c_str());
659  }
660  // NOTE: `prop->pp_beacon_scene_node` has only billboards attached, not meshes.
661 }
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
GameContext.h
Game state manager and message-queue provider.
RoR::Locator_t::nx
NodeNum_t nx
Definition: Locator_t.h:13
VERT_HOVER_MAX_DISTANCE
const float VERT_HOVER_MAX_DISTANCE
Definition: GUI_FlexbodyDebug.cpp:275
MEMGRAPH_NODEY_COLOR_V4
const ImVec4 MEMGRAPH_NODEY_COLOR_V4(0.27f, 0.76f, 1.f, 1.f)
RoR::GUI::FlexbodyDebug::UpdateVisibility
void UpdateVisibility()
Definition: GUI_FlexbodyDebug.cpp:440
RoR::GUI::FlexbodyDebug::show_vertices
bool show_vertices
Definition: GUI_FlexbodyDebug.h:56
RoR::Locator_t::ny
NodeNum_t ny
Definition: Locator_t.h:14
RoR::ImAddItemToComboboxString
void ImAddItemToComboboxString(std::string &target, std::string const &item)
Definition: GUIUtils.cpp:374
RoR::MSG_SIM_MODIFY_ACTOR_REQUESTED
@ MSG_SIM_MODIFY_ACTOR_REQUESTED
Payload = RoR::ActorModifyRequest* (owner)
Definition: Application.h:120
VERTEX_RADIUS
const float VERTEX_RADIUS(1.f)
RoR::FlexBody::PlaceholderType::NOT_A_PLACEHOLDER
@ NOT_A_PLACEHOLDER
MeshObject::getLoadedMesh
Ogre::MeshPtr getLoadedMesh()
Definition: MeshObject.h:45
RoR::NodeSB
Definition: SimBuffers.h:67
MEMGRAPH_NODEX_COLOR_V4
const ImVec4 MEMGRAPH_NODEX_COLOR_V4(1.f, 0.21f, 0.21f, 1.f)
RoR::Locator_t::ref
NodeNum_t ref
Definition: Locator_t.h:12
RoR::Locator_t
Definition: Locator_t.h:10
RoR::App::GetCameraManager
CameraManager * GetCameraManager()
Definition: Application.cpp:275
RoR::App::GetGuiManager
GUIManager * GetGuiManager()
Definition: Application.cpp:269
BLUE_BEAM_THICKNESS
const float BLUE_BEAM_THICKNESS
Definition: GUI_FlexbodyDebug.cpp:262
VERTEX_COLOR
const ImU32 VERTEX_COLOR
Definition: GUI_FlexbodyDebug.cpp:265
MEMGRAPH_NODE_RADIUS
const float MEMGRAPH_NODE_RADIUS(1.f)
RoR::GfxActor::GetSimNodeBuffer
NodeSB * GetSimNodeBuffer()
Definition: GfxActor.h:120
RoR::Actor::ar_instance_id
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition: Actor.h:376
MeshObject::getEntity
Ogre::Entity * getEntity()
Definition: MeshObject.h:43
RoR::NODENUM_INVALID
static const NodeNum_t NODENUM_INVALID
Definition: ForwardDeclarations.h:53
format
Truck file format(technical spec)
GUIUtils.h
RoR::PrintMeshInfo
std::string PrintMeshInfo(std::string const &title, Ogre::MeshPtr mesh)
RoR::FlexBody::getForsetNodes
std::vector< NodeNum_t > & getForsetNodes()
Definition: FlexBody.h:97
RoR::FlexBody::getPlaceholderType
PlaceholderType getPlaceholderType() const
Definition: FlexBody.h:106
RoR::FlexBody::getYNode
NodeNum_t getYNode()
Definition: FlexBody.h:103
LOCATOR_BEAM_THICKNESS
const float LOCATOR_BEAM_THICKNESS(1.0f)
VERTEX_COLOR_V4
const ImVec4 VERTEX_COLOR_V4(0.1f, 1.f, 1.f, 1.f)
LOCATOR_BEAM_COLOR
const ImU32 LOCATOR_BEAM_COLOR
Definition: GUI_FlexbodyDebug.cpp:272
RoR::DrawGIntSlider
void DrawGIntSlider(CVar *cvar, const char *label, int v_min, int v_max)
Definition: GUIUtils.cpp:290
RoR::CAMERA_MODE_ALWAYS_HIDDEN
static CameraMode_t CAMERA_MODE_ALWAYS_HIDDEN
Definition: GfxData.h:124
RoR::GUI::FlexbodyDebug::show_locator
std::vector< bool > show_locator
Definition: GUI_FlexbodyDebug.h:58
RoR::FlexBody::getVertexLocator
Locator_t & getVertexLocator(int vert)
Definition: FlexBody.h:93
RoR::Actor::GetGfxActor
GfxActor * GetGfxActor()
Definition: Actor.h:269
RoR::GUI::FlexbodyDebug::m_combo_selection
int m_combo_selection
Definition: GUI_FlexbodyDebug.h:63
Utils.h
Language.h
RoR::FlexBody::getVertexCount
int getVertexCount()
Definition: FlexBody.h:92
RefCountingObjectPtr< Actor >
RoR::World2ScreenConverter::Convert
Ogre::Vector3 Convert(Ogre::Vector3 world_pos)
Definition: Utils.h:89
GUIManager.h
Actor.h
BEAM_THICKNESS
const float BEAM_THICKNESS(1.2f)
RoR::GUI::FlexbodyDebug::hide_other_elements
bool hide_other_elements
Definition: GUI_FlexbodyDebug.h:57
VERTEX_TEXT_COLOR
const ImU32 VERTEX_TEXT_COLOR
Definition: GUI_FlexbodyDebug.cpp:266
RoR::FlexBody::getLiveMeshInfo
std::string getLiveMeshInfo()
Definition: FlexBody.h:99
RoR::Prop::pp_node_ref
NodeNum_t pp_node_ref
Definition: GfxData.h:165
NODE_TEXT_COLOR
const ImU32 NODE_TEXT_COLOR(0xffcccccf)
RoR::GetImDummyFullscreenWindow
ImDrawList * GetImDummyFullscreenWindow(const std::string &name="RoR_TransparentFullscreenWindow")
Definition: GUIUtils.cpp:356
RoR::ActorModifyRequest::amr_actor
ActorInstanceID_t amr_actor
Definition: SimData.h:886
LOCATOR_BEAM_COLOR_V4
const ImVec4 LOCATOR_BEAM_COLOR_V4(0.05f, 1.f, 0.65f, 1.f)
RoR::App::flexbody_defrag_enabled
CVar * flexbody_defrag_enabled
Definition: Application.cpp:253
RoR::Prop::pp_node_y
NodeNum_t pp_node_y
Definition: GfxData.h:167
RoR::Prop::pp_wheel_mesh_obj
MeshObject * pp_wheel_mesh_obj
Definition: GfxData.h:184
RoR::FlexBody::getOrigMeshName
const std::string & getOrigMeshName() const
Definition: FlexBody.h:96
RoR::NodeNum_t
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
Definition: ForwardDeclarations.h:52
RoR::App::flexbody_defrag_prog_up_penalty
CVar * flexbody_defrag_prog_up_penalty
Definition: Application.cpp:255
SimData.h
Core data structures for simulation; Everything affected by by either physics, network or user intera...
RoR::ActorModifyRequest
Definition: SimData.h:870
BASENODE_COLOR
const ImU32 BASENODE_COLOR(0xff44a5ff)
RoR::World2ScreenConverter
< Keeps data close for faster access.
Definition: Utils.h:81
RoR::GameContext::PushMessage
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
Definition: GameContext.cpp:65
MEMGRAPH_NODEREF_COLOR_V4
const ImVec4 MEMGRAPH_NODEREF_COLOR_V4(1.f, 0.89f, 0.22f, 1.f)
RoR::App::flexbody_defrag_reorder_indices
CVar * flexbody_defrag_reorder_indices
Definition: Application.cpp:257
RoR::FlexBody::getOrigMeshInfo
std::string getOrigMeshInfo()
Definition: FlexBody.h:98
RoR::GUI::FlexbodyDebug::m_is_hovered
bool m_is_hovered
Definition: GUI_FlexbodyDebug.h:67
RoR::App::flexbody_defrag_reorder_texcoords
CVar * flexbody_defrag_reorder_texcoords
Definition: Application.cpp:258
RoR::Prop
A mesh attached to vehicle frame via 3 nodes.
Definition: GfxData.h:162
RoR::GUI::FlexbodyDebug::Draw
void Draw()
Definition: GUI_FlexbodyDebug.cpp:40
Application.h
Central state/object manager and communications hub.
FlexBody.h
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:280
RoR::FlexBody::getEntity
Ogre::Entity * getEntity()
Definition: FlexBody.h:95
RoR::GUI::FlexbodyDebug::m_combo_items
std::string m_combo_items
Flexbodies come first, props second.
Definition: GUI_FlexbodyDebug.h:61
RoR::GUI::FlexbodyDebug::m_combo_props_start
int m_combo_props_start
Index of first prop in the combobox. -1 means no props.
Definition: GUI_FlexbodyDebug.h:62
AXIS_X_BEAM_COLOR
const ImU32 AXIS_X_BEAM_COLOR
Definition: GUI_FlexbodyDebug.cpp:273
RoR::GUI::FlexbodyDebug::SetVisible
void SetVisible(bool value)
Definition: GUI_FlexbodyDebug.h:37
RoR::GUI::FlexbodyDebug::AnalyzeFlexbodies
void AnalyzeFlexbodies()
populates the combobox
Definition: GUI_FlexbodyDebug.cpp:175
RoR::GUI::FlexbodyDebug::DrawLocatorsTable
void DrawLocatorsTable(FlexBody *flexbody, bool &locators_visible)
Definition: GUI_FlexbodyDebug.cpp:504
RoR::GUI::FlexbodyDebug::show_forset_nodes
bool show_forset_nodes
Definition: GUI_FlexbodyDebug.h:55
_LC
#define _LC(ctx, str)
Definition: Language.h:42
RoR::App::flexbody_defrag_invert_lookup
CVar * flexbody_defrag_invert_lookup
Definition: Application.cpp:259
FORSETNODE_COLOR
const ImU32 FORSETNODE_COLOR
Definition: GUI_FlexbodyDebug.cpp:257
RoR::FlexBody::PlaceholderTypeToString
static const char * PlaceholderTypeToString(PlaceholderType type)
Definition: FlexBody.cpp:543
AXIS_X_BEAM_COLOR_V4
const ImVec4 AXIS_X_BEAM_COLOR_V4(1.f, 0.f, 0.f, 1.f)
RoR::GUI::FlexbodyDebug::DrawMeshInfo
void DrawMeshInfo(FlexBody *flexbody)
Definition: GUI_FlexbodyDebug.cpp:638
RoR::GfxActor::GetFlexbodies
std::vector< FlexBody * > & GetFlexbodies()
Definition: GfxActor.h:135
RoR::Message
Unified game event system - all requests and state changes are reported using a message.
Definition: GameContext.h:51
RoR::GUI::FlexbodyDebug::show_base_nodes
bool show_base_nodes
Definition: GUI_FlexbodyDebug.h:54
FORSETNODE_COLOR_V4
const ImVec4 FORSETNODE_COLOR_V4(1.f, 0.87f, 0.3f, 1.f)
RoR::GfxActor::getProps
std::vector< Prop > & getProps()
Definition: GfxActor.h:145
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::FlexBody::getVertexPos
Ogre::Vector3 getVertexPos(int vert)
Definition: FlexBody.h:94
AXIS_Y_BEAM_COLOR_V4
const ImVec4 AXIS_Y_BEAM_COLOR_V4(0.15f, 0.15f, 1.f, 1.f)
GUI_FlexbodyDebug.h
RoR::CAMERA_MODE_ALWAYS_VISIBLE
static CameraMode_t CAMERA_MODE_ALWAYS_VISIBLE
Definition: GfxData.h:125
RoR::ImTerminateComboboxString
void ImTerminateComboboxString(std::string &target)
Definition: GUIUtils.cpp:396
Terrain.h
AXIS_Y_BEAM_COLOR
const ImU32 AXIS_Y_BEAM_COLOR
Definition: GUI_FlexbodyDebug.cpp:274
RoR::Prop::pp_mesh_obj
MeshObject * pp_mesh_obj
Optional; NULL if removed via tuneup/addonpart.
Definition: GfxData.h:173
RoR::DrawGCheckbox
bool DrawGCheckbox(CVar *cvar, const char *label)
Definition: GUIUtils.cpp:261
RoR::App::flexbody_defrag_prog_down_penalty
CVar * flexbody_defrag_prog_down_penalty
Definition: Application.cpp:256
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:3300
RoR::FlexBody::getRefNode
NodeNum_t getRefNode()
Definition: FlexBody.h:101
BASENODE_RADIUS
const float BASENODE_RADIUS(3.f)
FORSETNODE_RADIUS
const float FORSETNODE_RADIUS(2.f)
RoR::FlexBody::getXNode
NodeNum_t getXNode()
Definition: FlexBody.h:102
RoR::GameContext::GetPlayerActor
const ActorPtr & GetPlayerActor()
Definition: GameContext.h:134
RoR::Prop::pp_node_x
NodeNum_t pp_node_x
Definition: GfxData.h:166
RoR::ActorModifyRequest::amr_type
Type amr_type
Definition: SimData.h:887
RoR::GUIManager::RequestGuiCaptureKeyboard
void RequestGuiCaptureKeyboard(bool val)
Pass true during frame to prevent input passing to application.
Definition: GUIManager.cpp:439
RoR
Definition: AppContext.h:36
RoR::GUI::FlexbodyDebug::DrawMemoryOrderGraph
void DrawMemoryOrderGraph(FlexBody *flexbody)
Definition: GUI_FlexbodyDebug.cpp:557
RoR::App::flexbody_defrag_const_penalty
CVar * flexbody_defrag_const_penalty
Definition: Application.cpp:254
RoR::GUI::FlexbodyDebug::DrawDebugView
void DrawDebugView(FlexBody *flexbody, Prop *prop, NodeNum_t node_ref, NodeNum_t node_x, NodeNum_t node_y)
Definition: GUI_FlexbodyDebug.cpp:281
RoR::GUI::FlexbodyDebug::draw_mesh_wireframe
bool draw_mesh_wireframe
Definition: GUI_FlexbodyDebug.h:53
RoR::ActorModifyRequest::Type::RELOAD
@ RELOAD
Full reload from filesystem, requested by user.