Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
GUI_NodeBeamUtils.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2016-2020 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
20
21#include "GUI_NodeBeamUtils.h"
22
23#include "Application.h"
24#include "Actor.h"
25#include "GameContext.h"
26#include "GUIManager.h"
27#include "Language.h"
28
29using namespace RoR;
30using namespace GUI;
31
33{
35 if (!actor)
36 {
37 this->SetVisible(false);
38 return;
39 }
40 const bool is_project = actor->getUsedActorEntry()->resource_bundle_type != "Zip";
41
42 ImGui::SetNextWindowPosCenter(ImGuiCond_FirstUseEver);
43 ImGui::SetNextWindowSize(ImVec2(600.f, 675.f), ImGuiCond_FirstUseEver);
44 int flags = ImGuiWindowFlags_NoCollapse;
45 if (is_project)
46 {
47 flags |= ImGuiWindowFlags_MenuBar;
48 }
49 bool keep_open = true;
50 ImGui::Begin(_LC("NodeBeamUtils", "Node/Beam Utils"), &keep_open, flags);
51
52 if (!is_project)
53 {
54 this->DrawCreateProjectBanner(actor, keep_open);
55 }
56 else
57 {
58 this->DrawMenubar(actor);
59 }
60
61 if (ImGui::BeginTabBar("NodeBeamUtilsTabBar", ImGuiTabBarFlags_None))
62 {
63 if (ImGui::BeginTabItem(_LC("NodeBeamUtils", "Mass")))
64 {
65 this->DrawMassTab(actor);
66 ImGui::EndTabItem();
67 }
68 if (ImGui::BeginTabItem(_LC("NodeBeamUtils", "Spring/Damp")))
69 {
70 this->DrawSpringDampTab(actor);
71 ImGui::EndTabItem();
72 }
73 ImGui::EndTabBar();
74 }
75
77 {
78 actor->searchBeamDefaults();
79 }
80
81 m_is_hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
83
84 ImGui::End();
85 if (!keep_open)
86 {
87 this->SetVisible(false);
88 }
89}
90
92{
93 ImGui::PushItemWidth(500.f); // Width includes [+/-] buttons
94
95 ImGui::TextColored(GRAY_HINT_TEXT, _LC("NodeBeamUtils", "Beams:"));
96 if (ImGui::SliderFloat("Spring##Beams", &actor->ar_nb_beams_scale.first, 0.1f, 10.0f, "%.5f"))
97 {
98 actor->applyNodeBeamScales();
99 }
100 if (ImGui::SliderFloat("Damping##Beams", &actor->ar_nb_beams_scale.second, 0.1f, 10.0f, "%.5f"))
101 {
102 actor->applyNodeBeamScales();
103 }
104 ImGui::Separator();
105 ImGui::TextColored(GRAY_HINT_TEXT, _LC("NodeBeamUtils", "Shocks:"));
106 if (ImGui::SliderFloat("Spring##Shocks", &actor->ar_nb_shocks_scale.first, 0.1f, 10.0f, "%.5f"))
107 {
108 actor->applyNodeBeamScales();
109 }
110 if (ImGui::SliderFloat("Damping##Shocks", &actor->ar_nb_shocks_scale.second, 0.1f, 10.0f, "%.5f"))
111 {
112 actor->applyNodeBeamScales();
113 }
114 ImGui::Separator();
115
116 ImGui::TextColored(GRAY_HINT_TEXT, _LC("NodeBeamUtils", "Wheels:"));
117 const float WBASE_WIDTH = 125.f;
118 const float WSCALE_WIDTH = 225.f;
119 const float WLABEL_GAP = 20.f;
120 // WHEEL-SPECIFIC: assume all wheels have same spring/damp and use wheel [0] as the master record
121
122 // wheel spring
123 ImGui::SetNextItemWidth(WBASE_WIDTH);
124 if (ImGui::InputFloat("Base##wheels-spring", &actor->ar_wheels[0].wh_arg_simple_spring))
125 {
126 actor->applyNodeBeamScales();
127 }
128 ImGui::SameLine();
129 ImGui::SetNextItemWidth(WSCALE_WIDTH);
130 if (ImGui::SliderFloat("Scale##Wheels-spring", &actor->ar_nb_wheels_scale.first, 0.1f, 10.0f, "%.5f"))
131 {
132 actor->applyNodeBeamScales();
133 }
134 ImGui::SameLine();
135 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + WLABEL_GAP);
136 ImGui::Text("Spring (%.2f)", actor->ar_nb_wheels_scale.first * actor->ar_wheels[0].wh_arg_simple_spring);
137
138 // wheel damping
139 ImGui::SetNextItemWidth(WBASE_WIDTH);
140 if (ImGui::InputFloat("Base##wheels-damping", &actor->ar_wheels[0].wh_arg_simple_damping))
141 {
142 actor->applyNodeBeamScales();
143 }
144 ImGui::SameLine();
145 ImGui::SetNextItemWidth(WSCALE_WIDTH);
146 if (ImGui::SliderFloat("Scale##Wheels-damping", &actor->ar_nb_wheels_scale.second, 0.1f, 10.0f, "%.5f"))
147 {
148 actor->applyNodeBeamScales();
149 }
150 ImGui::SameLine();
151 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + WLABEL_GAP);
152 ImGui::Text("Damping (%.2f)", actor->ar_nb_wheels_scale.second * actor->ar_wheels[0].wh_arg_simple_damping);
153
154 ImGui::Separator();
155 ImGui::Spacing();
156 if (ImGui::Button(_LC("NodeBeamUtils", "Reset to default settings"), ImVec2(280.f, 25.f)))
157 {
158 actor->ar_nb_beams_scale = { 1.0f, 1.0f };
159 actor->ar_nb_shocks_scale = { 1.0f, 1.0f };
160 actor->ar_nb_wheels_scale = { 1.0f, 1.0f };
161 actor->SyncReset(true);
162 }
163 ImGui::SameLine();
164 if (ImGui::Button(_LC("NodeBeamUtils", "Update initial node positions"), ImVec2(280.f, 25.f)))
165 {
166 actor->updateInitPosition();
167 }
168 ImGui::PopItemWidth();
169
170 ImGui::PushItemWidth(235.f); // Width includes [+/-] buttons
171 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("NodeBeamUtils", "Physics steps:"));
172 ImGui::SliderInt("Skip##BeamsInt", &actor->ar_nb_skip_steps, 0, 2000);
173 ImGui::SameLine();
174 ImGui::SliderInt("Measure##BeamsInt", &actor->ar_nb_measure_steps, 2, 6000);
175 ImGui::PopItemWidth();
176 ImGui::PushItemWidth(138.f); // Width includes [+/-] buttons
177 ImGui::Separator();
178 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("NodeBeamUtils", "Beams (spring & damping search interval):"));
179 ImGui::SliderFloat("##BSL", &actor->ar_nb_beams_k_interval.first, 0.1f, actor->ar_nb_beams_k_interval.second);
180 ImGui::SameLine();
181 ImGui::SliderFloat("##BSU", &actor->ar_nb_beams_k_interval.second, actor->ar_nb_beams_k_interval.first, 10.0f);
182 ImGui::SameLine();
183 ImGui::SliderFloat("##BDL", &actor->ar_nb_beams_d_interval.first, 0.1f, actor->ar_nb_beams_d_interval.second);
184 ImGui::SameLine();
185 ImGui::SliderFloat("##BDU", &actor->ar_nb_beams_d_interval.second, actor->ar_nb_beams_d_interval.first, 10.0f);
186 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("NodeBeamUtils", "Shocks (spring & damping search interval):"));
187 ImGui::SliderFloat("##SSL", &actor->ar_nb_shocks_k_interval.first, 0.1f, actor->ar_nb_shocks_k_interval.second);
188 ImGui::SameLine();
189 ImGui::SliderFloat("##SSU", &actor->ar_nb_shocks_k_interval.second, actor->ar_nb_shocks_k_interval.first, 10.0f);
190 ImGui::SameLine();
191 ImGui::SliderFloat("##SDL", &actor->ar_nb_shocks_d_interval.first, 0.1f, actor->ar_nb_shocks_d_interval.second);
192 ImGui::SameLine();
193 ImGui::SliderFloat("##SDU", &actor->ar_nb_shocks_d_interval.second, actor->ar_nb_shocks_d_interval.first, 10.0f);
194 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("NodeBeamUtils", "Wheels (spring & damping search interval):"));
195 ImGui::SliderFloat("##WSL", &actor->ar_nb_wheels_k_interval.first, 0.1f, actor->ar_nb_wheels_k_interval.second);
196 ImGui::SameLine();
197 ImGui::SliderFloat("##WSU", &actor->ar_nb_wheels_k_interval.second, actor->ar_nb_wheels_k_interval.first, 10.0f);
198 ImGui::SameLine();
199 ImGui::SliderFloat("##WDL", &actor->ar_nb_wheels_d_interval.first, 0.1f, actor->ar_nb_wheels_d_interval.second);
200 ImGui::SameLine();
201 ImGui::SliderFloat("##WDU", &actor->ar_nb_wheels_d_interval.second, actor->ar_nb_wheels_d_interval.first, 10.0f);
202 ImGui::PopItemWidth();
203 ImGui::Separator();
204 ImGui::Spacing();
205 if (ImGui::Button(m_is_searching ? _LC("NodeBeamUtils", "Stop searching") : actor->ar_nb_initialized ? _LC("NodeBeamUtils", "Continue searching") : _LC("NodeBeamUtils", "Start searching"),
206 ImVec2(280.f, 25.f)))
207 {
209 if (!m_is_searching)
210 {
211 actor->SyncReset(true);
212 }
213 }
214 ImGui::SameLine();
215 if (ImGui::Button(_LC("NodeBeamUtils", "Reset search"), ImVec2(280.f, 25.f)))
216 {
217 actor->ar_nb_initialized = false;
218 m_is_searching = false;
219 }
220 ImGui::Separator();
221 ImGui::Spacing();
222 if (actor->ar_nb_initialized)
223 {
224 ImGui::Columns(2, _LC("NodeBeamUtils", "Search results"));
225 ImGui::SetColumnOffset(1, 290.f);
226 ImGui::Text("%s", _LC("NodeBeamUtils", "Reference"));
227 ImGui::NextColumn();
228 ImGui::Text("%s", _LC("NodeBeamUtils", "Optimum"));
229 ImGui::NextColumn();
230 ImGui::Separator();
231 ImGui::Text("%s: %f (%f)", _LC("NodeBeamUtils", "Movement"), actor->ar_nb_reference[5] / actor->ar_num_nodes, actor->ar_nb_reference[4]);
232 ImGui::Text("%s: %.2f (%.2f)", _LC("NodeBeamUtils", "Stress"), actor->ar_nb_reference[1] / actor->ar_num_beams, actor->ar_nb_reference[0]);
233 ImGui::Text("%s: %f (%f)", _LC("NodeBeamUtils", "Yitter"), actor->ar_nb_reference[3] / actor->ar_num_beams, actor->ar_nb_reference[2]);
234 ImGui::NextColumn();
235 ImGui::Text("%s: %f (%f)", _LC("NodeBeamUtils", "Movement"), actor->ar_nb_optimum[5] / actor->ar_num_nodes, actor->ar_nb_optimum[4]);
236 ImGui::Text("%s: %.2f (%.2f)", _LC("NodeBeamUtils", "Stress"), actor->ar_nb_optimum[1] / actor->ar_num_beams, actor->ar_nb_optimum[0]);
237 ImGui::Text("%s: %f (%f)", _LC("NodeBeamUtils", "Yitter"), actor->ar_nb_optimum[3] / actor->ar_num_beams, actor->ar_nb_optimum[2]);
238 ImGui::Columns(1);
239 }
240}
241
243{
244 m_is_visible = v;
245 m_is_hovered = false;
246 if (!v)
247 {
248 m_is_searching = false;
249 }
250}
251
253{
254 // Show [[ "read only files - create writeable project?" ]] banner.
255 // If [yes], unpack the project files, unload current actor and show hint box.
256 // ---------------------------------------------------------------------------
257
259
260 // Draw a banner
261 const ImVec2 PAD(3, 3);
262 ImVec2 cursor = ImGui::GetCursorScreenPos();
263 ImVec2 rect_min = cursor - PAD;
264 ImVec2 rect_max = cursor + PAD + ImVec2(ImGui::GetWindowContentRegionMax().x, ImGui::GetTextLineHeightWithSpacing());
265 ImGui::GetWindowDrawList()->AddRectFilled(rect_min, rect_max, ImColor(theme.tip_panel_bg_color));
266 ImGui::AlignTextToFramePadding();
267 ImGui::Text(_LC("NodeBeamUtils", "This mod is read only (ZIP archive)"));
268 ImGui::SameLine();
269 if (ImGui::Button(_LC("NodeBeamUtils", "Create writable project (if not existing)")))
270 {
271 // Unzip the mod
273 req->cpr_name = "nbutil_" + actor->getUsedActorEntry()->fname_without_uid;
274 req->cpr_description = "Node/Beam Utils project for " + actor->getUsedActorEntry()->dname;
275 req->cpr_source_entry = actor->getUsedActorEntry();
278
279 // Show a message box "please load the project"
280 // - it cannot be loaded automatically because it's not in modcache yet so there's no way to locate it.
282 box->mbc_title = _LC("NodeBeamUtils", "Project created");
283 box->mbc_text = fmt::format(_LC("NodeBeamUtils", "Project created successfully as \n\"{}\"\n\nPlease load it and open the N/B utility again"), req->cpr_name);
284 box->mbc_allow_close = true;
286
287 // Unload current actor
289
290 window_open = false;
291 }
292 ImGui::Dummy(ImVec2(1.f, 6.f));
293}
294
296{
297 if (ImGui::BeginMenuBar())
298 {
299 ImGui::TextDisabled(_LC("NodeBeamUtils", "Project:"));
300 ImGui::SameLine();
301 ImGui::Text("%s", actor->getUsedActorEntry()->dname.c_str());
302 ImGui::SameLine();
303 if (ImGui::SmallButton(_LC("NodeBeamUtils", "Save and reload")))
304 {
307 req->mpr_target_actor = actor;
309 }
310
311 ImGui::EndMenuBar();
312 }
313}
314
316{
317 ImGui::PushID("drymass"); // To disambiguate the 'reset' buttons.
318 ImGui::TextDisabled(_LC("NodeBeamUtils", "User-defined values:"));
319 if (ImGui::SliderFloat(_LC("NodeBeamUtils", "Dry mass"), &actor->ar_dry_mass,
320 actor->ar_original_dry_mass * 0.4f, actor->ar_original_dry_mass * 1.6f, "%.2f Kg"))
321 {
322 actor->recalculateNodeMasses();
323 }
324 ImGui::SameLine();
325 if (ImGui::SmallButton(_LC("NodeBeamUtils", "Reset")))
326 {
327 actor->ar_dry_mass = actor->ar_original_dry_mass;
328 actor->recalculateNodeMasses();
329 }
330 ImGui::PopID();
331
332 ImGui::PushID("loadmass"); // To disambiguate the 'reset' buttons.
333 if (ImGui::SliderFloat(_LC("NodeBeamUtils", "Load mass"), &actor->ar_load_mass,
334 actor->ar_original_load_mass * 0.4f, actor->ar_original_load_mass * 1.6f, "%.2f Kg"))
335 {
336 actor->recalculateNodeMasses();
337 }
338 ImGui::SameLine();
339 if (ImGui::SmallButton(_LC("NodeBeamUtils", "Reset")))
340 {
341 actor->ar_load_mass = actor->ar_original_load_mass;
342 actor->recalculateNodeMasses();
343 }
344 ImGui::PopID();
345
346 ImGui::PushID("minimass"); // To disambiguate the 'reset' buttons.
347 if (ImGui::SliderFloat(_LC("NodeBeamUtils", "Minimum node mass scale"), &actor->ar_nb_minimass_scale, 0.4, 1.6))
348 {
349 for (int i = 0; i < actor->ar_num_nodes; i++)
350 {
351 actor->ar_minimass[i] = actor->ar_nb_minimass_scale * actor->ar_orig_minimass[i];
352 }
353 actor->recalculateNodeMasses();
354 }
355 ImGui::SameLine();
356 if (ImGui::SmallButton(_LC("NodeBeamUtils", "Reset")))
357 {
358 for (int i = 0; i < actor->ar_num_nodes; i++)
359 {
360 actor->ar_minimass[i] = actor->ar_orig_minimass[i];
361 }
362 actor->ar_nb_minimass_scale = 1.0f;
363 actor->recalculateNodeMasses();
364 }
365 ImGui::PopID();
366
367 ImGui::Separator();
368 ImGui::TextDisabled(_LC("NodeBeamUtils", "Calculated values:"));
369 ImGui::Text("%s: %f", _LC("NodeBeamUtils", "Total mass"), actor->ar_total_mass);
370 ImGui::Text("%s: %d", _LC("NodeBeamUtils", "Total nodes"), actor->ar_num_nodes);
371 ImGui::Text("%s: %d", _LC("NodeBeamUtils", "Loaded nodes"), actor->ar_masscount);
372}
Central state/object manager and communications hub.
Game state manager and message-queue provider.
#define _LC(ctx, str)
Definition Language.h:38
std::pair< float, float > ar_nb_wheels_k_interval
Search interval for springiness & damping of wheel / rim beams.
Definition Actor.h:545
std::pair< float, float > ar_nb_shocks_k_interval
Search interval for springiness & damping of shock beams.
Definition Actor.h:543
std::vector< float > ar_orig_minimass
minimum node mass in Kg - original unscaled values
Definition Actor.h:338
int ar_nb_measure_steps
Amount of physics steps to be measured.
Definition Actor.h:535
std::vector< float > ar_minimass
minimum node mass in Kg - can be scaled in-game via NBUtil
Definition Actor.h:337
float ar_dry_mass
User-defined (editable via NBUtil); from 'globals' arg#1 - default for all nodes.
Definition Actor.h:320
std::pair< float, float > ar_nb_beams_scale
Scales for springiness & damping of regular beams.
Definition Actor.h:537
float ar_nb_minimass_scale
scale of 'set_default_minimass' (affects all nodes the same way)
Definition Actor.h:536
wheel_t ar_wheels[MAX_WHEELS]
Definition Actor.h:384
std::vector< float > ar_nb_reference
Temporary storage of the reference search result.
Definition Actor.h:533
float ar_original_load_mass
Un-edited value from 'globals' arg#2.
Definition Actor.h:323
std::pair< float, float > ar_nb_wheels_scale
Scales for springiness & damping of wheel / rim beams.
Definition Actor.h:539
float ar_original_dry_mass
Un-edited value from 'globals' arg#1.
Definition Actor.h:321
void updateInitPosition()
Definition Actor.cpp:1285
bool ar_nb_initialized
Definition Actor.h:531
std::vector< float > ar_nb_optimum
Temporary storage of the optimum search result.
Definition Actor.h:532
std::pair< float, float > ar_nb_wheels_d_interval
Search interval for springiness & damping of wheel / rim beams.
Definition Actor.h:544
void recalculateNodeMasses()
Definition Actor.cpp:701
std::pair< float, float > ar_nb_beams_d_interval
Search interval for springiness & damping of regular beams.
Definition Actor.h:540
float ar_load_mass
User-defined (editable via NBUtil); from 'globals' arg#2 - only applies to nodes with 'l' flag.
Definition Actor.h:322
int ar_masscount
Calculated; Number of nodes loaded with l option.
Definition Actor.h:324
float ar_total_mass
Calculated; total mass in Kg.
Definition Actor.h:325
void applyNodeBeamScales()
For GUI::NodeBeamUtils.
Definition Actor.cpp:1814
void searchBeamDefaults()
Searches for more stable beam defaults.
Definition Actor.cpp:1858
int ar_num_beams
Definition Actor.h:349
void SyncReset(bool reset_position)
this one should be called only synchronously (without physics running in background)
Definition Actor.cpp:1657
int ar_num_nodes
Definition Actor.h:345
std::pair< float, float > ar_nb_shocks_scale
Scales for springiness & damping of shock beams.
Definition Actor.h:538
std::pair< float, float > ar_nb_shocks_d_interval
Search interval for springiness & damping of shock beams.
Definition Actor.h:542
std::pair< float, float > ar_nb_beams_k_interval
Search interval for springiness & damping of regular beams.
Definition Actor.h:541
CacheEntryPtr & getUsedActorEntry()
The actor entry itself.
Definition Actor.cpp:4873
int ar_nb_skip_steps
Amount of physics steps to be skipped before measuring.
Definition Actor.h:534
Ogre::String dname
name parsed from the file
Definition CacheSystem.h:70
Ogre::String fname_without_uid
filename
Definition CacheSystem.h:68
std::string resource_bundle_type
Archive type recognized by OGRE resource system: 'FileSystem' or 'Zip'.
Definition CacheSystem.h:80
void DrawMassTab(ActorPtr actor)
void DrawCreateProjectBanner(ActorPtr actor, bool &window_open)
void DrawMenubar(ActorPtr actor)
void DrawSpringDampTab(ActorPtr actor)
void SetVisible(bool visible)
void RequestGuiCaptureKeyboard(bool val)
Pass true during frame to prevent input passing to application.
GuiTheme & GetTheme()
Definition GUIManager.h:168
const ActorPtr & GetPlayerActor()
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
void ChainMessage(Message m)
Add to last pushed message's chain.
@ MSG_GUI_SHOW_MESSAGE_BOX_REQUESTED
Payload = MessageBoxConfig* (owner)
@ MSG_EDI_CREATE_PROJECT_REQUESTED
Payload = RoR::CreateProjectRequest* (owner)
@ MSG_EDI_MODIFY_PROJECT_REQUESTED
Payload = RoR::UpdateProjectRequest* (owner)
@ MSG_SIM_DELETE_ACTOR_REQUESTED
Payload = RoR::ActorPtr* (owner)
GUIManager * GetGuiManager()
GameContext * GetGameContext()
@ ACTOR_PROJECT
Like DEFAULT but fixes up name + category in the truckfile.
@ ACTOR_UPDATE_DEF_DOCUMENT
'subject' is empty; 'target_actor' is the actual subject. Propagates modifications from the live acto...
RefCountingObjectPtr< Actor > ActorPtr
Creates subdirectory in 'My Games\Rigs of Rods\projects', pre-populates it with files and adds modcac...
std::string cpr_description
Optional, implemented for tuneups.
CreateProjectRequestType cpr_type
CacheEntryPtr cpr_source_entry
The original mod to copy files from.
std::string cpr_name
Directory and also the mod file (without extension).
bool mbc_allow_close
Show close handle even if dbc_close_handle isn't set.
Unified game event system - all requests and state changes are reported using a message.
Definition GameContext.h:52
ModifyProjectRequestType mpr_type
float wh_arg_simple_spring
Whole wheel or just tire, depending on type.
Definition SimData.h:431
float wh_arg_simple_damping
Whole wheel or just tire, depending on type.
Definition SimData.h:432