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