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