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
ActorForcesEuler.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5
6 For more information, see http://www.rigsofrods.org/
7
8 Rigs of Rods is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 3, as
10 published by the Free Software Foundation.
11
12 Rigs of Rods is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "Application.h"
22#include "AeroEngine.h"
23#include "AirBrake.h"
24#include "Airfoil.h"
25#include "ApproxMath.h"
26#include "Actor.h"
27#include "ActorManager.h"
28#include "Buoyance.h"
29#include "CmdKeyInertia.h"
30#include "Collisions.h"
31#include "Console.h"
32#include "Differentials.h"
33#include "Engine.h"
34#include "FlexAirfoil.h"
35#include "GameContext.h"
36#include "Replay.h"
37#include "ScrewProp.h"
38#include "ScriptEngine.h"
39#include "SoundScriptManager.h"
40#include "Terrain.h"
41#include "GfxWater.h"
42
43using namespace Ogre;
44using namespace RoR;
45
46void Actor::CalcForcesEulerCompute(bool doUpdate, int num_steps)
47{
48 this->CalcNodes(); // must be done directly after the inter truck collisions are handled
49 this->UpdateBoundingBoxes();
50 this->CalcEventBoxes();
51 this->CalcReplay();
52 this->CalcAircraftForces(doUpdate);
53 this->CalcFuseDrag();
54 this->CalcBuoyance(doUpdate);
55 this->CalcDifferentials();
56 this->CalcWheels(doUpdate, num_steps);
57 this->CalcShocks(doUpdate, num_steps);
58 this->CalcHydros();
59 this->CalcCommands(doUpdate);
60 this->CalcTies();
61 this->CalcTruckEngine(doUpdate); // must be done after the commands / engine triggers are updated
62 this->CalcMouse();
63 this->CalcBeams(doUpdate);
64 this->CalcCabCollisions();
65 this->updateSlideNodeForces(PHYSICS_DT); // must be done after the contacters are updated
66 this->CalcForceFeedback(doUpdate);
67}
68
69void Actor::CalcForceFeedback(bool doUpdate)
70{
71 if (this == App::GetGameContext()->GetPlayerActor().GetRef())
72 {
73 if (doUpdate)
74 {
76 }
77
79 {
81 }
82
83 for (hydrobeam_t& hydrobeam: ar_hydros)
84 {
85 beam_t* beam = &ar_beams[hydrobeam.hb_beam_index];
86 if ((hydrobeam.hb_flags & (HYDRO_FLAG_DIR | HYDRO_FLAG_SPEED)) && !beam->bm_broken)
87 {
88 m_force_sensors.accu_hydros_forces += hydrobeam.hb_speed * beam->refL * beam->stress;
89 }
90 }
91 }
92}
93
102
103void Actor::CalcAircraftForces(bool doUpdate)
104{
105 //airbrake forces
106 for (Airbrake* ab: ar_airbrakes)
107 ab->applyForce();
108
109 //turboprop forces
110 for (int i = 0; i < ar_num_aeroengines; i++)
111 if (ar_aeroengines[i])
113
114 //screwprop forces
115 for (int i = 0; i < ar_num_screwprops; i++)
116 if (ar_screwprops[i])
117 ar_screwprops[i]->updateForces(doUpdate);
118
119 //wing forces
120 for (int i = 0; i < ar_num_wings; i++)
121 if (ar_wings[i].fa)
123}
124
126{
128 {
129 Vector3 wind = -m_fusealge_front->Velocity;
130 float wspeed = wind.length();
132 float s = axis.length() * m_fusealge_width;
133 float cz, cx, cm;
134 float v = axis.getRotationTo(wind).w;
135 float aoa = 0;
136 if (v < 1.0 && v > -1.0)
137 aoa = 2.0 * acos(v); //quaternion fun
138 m_fusealge_airfoil->getparams(aoa, 1.0, 0.0, &cz, &cx, &cm);
139
140 //tropospheric model valid up to 11.000m (33.000ft)
141 float altitude = m_fusealge_front->AbsPosition.y;
142 float sea_level_pressure = 101325; //in Pa
143 float airpressure = sea_level_pressure * approx_pow(1.0 - 0.0065 * altitude / 288.1, 5.24947); //in Pa
144 float airdensity = airpressure * 0.0000120896f;//1.225 at sea level
145
146 //fuselage as an airfoil + parasitic drag (half fuselage front surface almost as a flat plane!)
147 ar_fusedrag = ((cx * s + m_fusealge_width * m_fusealge_width * 0.5) * 0.5 * airdensity * wspeed / ar_num_nodes) * wind;
148 }
149}
150
151const int BUOYANCY_SAMPLING_INTERVAL = 10; // steps (200x per sec)
152const int BUOYANCY_VISUAL_UPDATE_INTERVAL = 40; // steps (50x per sec)
153
154void Actor::CalcBuoyance(bool doUpdate)
155{
156 auto iwater = App::GetGameContext()->GetTerrain()->getWater();
157 if (!iwater || !ar_num_buoycabs)
158 {
159 return;
160 }
161
162 if (m_buoyance->buoy_total_steps % BUOYANCY_SAMPLING_INTERVAL == 0)
163 {
164 // ~~~ Create 2 snapshots of node forces: current and projected ~~~ //
165 const float timeshift_sec = PHYSICS_DT * BUOYANCY_SAMPLING_INTERVAL;
166 doUpdate = m_buoyance->buoy_total_steps % BUOYANCY_VISUAL_UPDATE_INTERVAL;
167 if (doUpdate)
168 {
169 m_buoyance->buoy_debug_subcabs.clear();
170 }
171
172 for (size_t i = 0; i < m_buoyance->buoy_cached_nodes.size(); ++i)
173 {
174 // Refresh cached node from current simulation state
175 NodeNum_t nodenum = m_buoyance->buoy_cached_nodes[i].nodenum;
176 m_buoyance->buoy_cached_nodes[i].AbsPosition = ar_nodes[nodenum].AbsPosition;
177 m_buoyance->buoy_cached_nodes[i].Velocity = ar_nodes[nodenum].Velocity;
178 m_buoyance->buoy_cached_nodes[i].Forces = Ogre::Vector3::ZERO;
179
180 // Projected node: advance position by N steps
181 m_buoyance->buoy_projected_nodes[i].AbsPosition = ar_nodes[nodenum].AbsPosition + ar_nodes[nodenum].Velocity * timeshift_sec;
182 m_buoyance->buoy_projected_nodes[i].Velocity = ar_nodes[nodenum].Velocity;
183 m_buoyance->buoy_projected_nodes[i].Forces = Ogre::Vector3::ZERO;
184 }
185
186 // Update node forces.
187 for (int i = 0; i < ar_num_buoycabs; i++)
188 {
189 int tmpv = ar_buoycabs[i] * 3;
190
191 BuoyCachedNode& bcn_a = m_buoyance->buoy_cached_nodes[ar_cabs_buoy_cache_ids[tmpv]];
192 BuoyCachedNode& bcn_b = m_buoyance->buoy_cached_nodes[ar_cabs_buoy_cache_ids[tmpv+1]];
193 BuoyCachedNode& bcn_c = m_buoyance->buoy_cached_nodes[ar_cabs_buoy_cache_ids[tmpv+2]];
194 m_buoyance->update = doUpdate;
195 m_buoyance->computeNodeForce(&bcn_a, &bcn_b, &bcn_c, ar_buoycab_types[i], /* timeshift: */ 0.f);
196
197 BuoyCachedNode& bpn_a = m_buoyance->buoy_projected_nodes[ar_cabs_buoy_cache_ids[tmpv]];
198 BuoyCachedNode& bpn_b = m_buoyance->buoy_projected_nodes[ar_cabs_buoy_cache_ids[tmpv+1]];
199 BuoyCachedNode& bpn_c = m_buoyance->buoy_projected_nodes[ar_cabs_buoy_cache_ids[tmpv+2]];
200 m_buoyance->update = false;
201 m_buoyance->computeNodeForce(&bpn_a, &bpn_b, &bpn_c, ar_buoycab_types[i], timeshift_sec);
202 }
203
204 // Apply forces to nodes.
205 for (const BuoyCachedNode& bcn: m_buoyance->buoy_cached_nodes)
206 {
207 ar_nodes[bcn.nodenum].Forces += bcn.Forces;
208 }
209
210 m_buoyance->buoy_last_sample_steps = m_buoyance->buoy_total_steps;
211 }
212 else
213 {
214 // ~~~ Interpolate between cached_nodes and projected_nodes
215 const float interp_ratio = static_cast<double>(m_buoyance->buoy_total_steps - m_buoyance->buoy_last_sample_steps) / BUOYANCY_SAMPLING_INTERVAL;
216
217 // Apply forces to nodes.
218 for (size_t i = 0; i < m_buoyance->buoy_cached_nodes.size(); ++i)
219 {
220 const NodeNum_t nodenum = m_buoyance->buoy_cached_nodes[i].nodenum;
221 const Ogre::Vector3& force_a = m_buoyance->buoy_cached_nodes[i].Forces;
222 const Ogre::Vector3& force_b = m_buoyance->buoy_projected_nodes[i].Forces;
223 Ogre::Vector3 interp_force = force_a * (1.0f - interp_ratio) + force_b * interp_ratio;
224 ar_nodes[nodenum].Forces += interp_force;
225 }
226 }
227
228 m_buoyance->buoy_total_steps++;
229}
230
232{
234 {
235 float torque = ar_engine->getTorque() / m_num_proped_wheels;
237 {
238 torque *= 2.0f; // Required to stay backwards compatible
239 }
240 for (int i = 0; i < ar_num_wheels; i++)
241 {
242 if (ar_wheels[i].wh_propulsed != WheelPropulsion::NONE && !ar_wheels[i].wh_is_detached)
243 {
244 ar_wheels[i].wh_torque += torque;
245 }
246 }
247 }
248
250
251 // Handle detached wheels
252 for (int i = 0; i < num_axle_diffs; i++)
253 {
254 int a_1 = m_axle_diffs[i]->di_idx_1;
255 int a_2 = m_axle_diffs[i]->di_idx_2;
256 wheel_t* axle_1_wheels[2] = {&ar_wheels[m_wheel_diffs[a_1]->di_idx_1], &ar_wheels[m_wheel_diffs[a_1]->di_idx_2]};
257 wheel_t* axle_2_wheels[2] = {&ar_wheels[m_wheel_diffs[a_2]->di_idx_1], &ar_wheels[m_wheel_diffs[a_2]->di_idx_2]};
258 if (axle_1_wheels[0]->wh_is_detached && axle_1_wheels[1]->wh_is_detached)
259 {
260 axle_1_wheels[0]->wh_speed = axle_2_wheels[0]->wh_speed;
261 axle_1_wheels[1]->wh_speed = axle_2_wheels[1]->wh_speed;
262 }
263 if (axle_2_wheels[0]->wh_is_detached && axle_2_wheels[1]->wh_is_detached)
264 {
265 axle_2_wheels[0]->wh_speed = axle_1_wheels[0]->wh_speed;
266 axle_2_wheels[1]->wh_speed = axle_1_wheels[1]->wh_speed;
267 }
268 }
269 for (int i = 0; i < m_num_wheel_diffs; i++)
270 {
271 wheel_t* axle_wheels[2] = {&ar_wheels[m_wheel_diffs[i]->di_idx_1], &ar_wheels[m_wheel_diffs[i]->di_idx_2]};
272 if (axle_wheels[0]->wh_is_detached) axle_wheels[0]->wh_speed = axle_wheels[1]->wh_speed;
273 if (axle_wheels[1]->wh_is_detached) axle_wheels[1]->wh_speed = axle_wheels[0]->wh_speed;
274 }
275
276 // loop through all interaxle differentials, this is the torsion to keep
277 // the axles aligned with each other as if they connected by a shaft
278 for (int i = 0; i < num_axle_diffs; i++)
279 {
280 int a_1 = m_axle_diffs[i]->di_idx_1;
281 int a_2 = m_axle_diffs[i]->di_idx_2;
282
283 Ogre::Real axle_torques[2] = {0.0f, 0.0f};
284 DifferentialData diff_data =
285 {
286 {
288 (ar_wheels[m_wheel_diffs[a_2]->di_idx_1].wh_speed + ar_wheels[m_wheel_diffs[a_2]->di_idx_2].wh_speed) * 0.5f
289 },
291 {axle_torques[0], axle_torques[1]},
295 };
296
297 m_axle_diffs[i]->CalcAxleTorque(diff_data);
298
300
301 ar_wheels[m_wheel_diffs[a_1]->di_idx_1].wh_torque = diff_data.out_torque[0] * 0.5f;
302 ar_wheels[m_wheel_diffs[a_1]->di_idx_2].wh_torque = diff_data.out_torque[0] * 0.5f;
303 ar_wheels[m_wheel_diffs[a_2]->di_idx_1].wh_torque = diff_data.out_torque[1] * 0.5f;
304 ar_wheels[m_wheel_diffs[a_2]->di_idx_2].wh_torque = diff_data.out_torque[1] * 0.5f;
305 }
306
307 // loop through all interwheel differentials, this is the torsion to keep
308 // the wheels aligned with each other as if they connected by a shaft
309 for (int i = 0; i < m_num_wheel_diffs; i++)
310 {
311 Ogre::Real axle_torques[2] = {0.0f, 0.0f};
312 wheel_t* axle_wheels[2] = {&ar_wheels[m_wheel_diffs[i]->di_idx_1], &ar_wheels[m_wheel_diffs[i]->di_idx_2]};
313
314 DifferentialData diff_data =
315 {
316 {axle_wheels[0]->wh_speed, axle_wheels[1]->wh_speed},
318 {axle_torques[0], axle_torques[1]},
319 axle_wheels[0]->wh_torque + axle_wheels[1]->wh_torque,
321 };
322
323 m_wheel_diffs[i]->CalcAxleTorque(diff_data);
324
326
329 }
330}
331
332void Actor::CalcWheels(bool doUpdate, int num_steps)
333{
334 // driving aids traction control & anti-lock brake pulse
337
339 {
340 alb_timer = 0.0f;
342 }
343 if (tc_timer >= tc_pulse_time)
344 {
345 tc_timer = 0.0f;
347 }
348
349 m_antilockbrake = false;
350 m_tractioncontrol = false;
351
352 ar_wheel_spin = 0.0f;
353 ar_wheel_speed = 0.0f;
354
355 for (int i = 0; i < ar_num_wheels; i++)
356 {
357 if (doUpdate)
358 {
359 ar_wheels[i].debug_rpm = 0.0f;
360 ar_wheels[i].debug_torque = 0.0f;
361 ar_wheels[i].debug_vel = Vector3::ZERO;
362 ar_wheels[i].debug_slip = Vector3::ZERO;
363 ar_wheels[i].debug_force = Vector3::ZERO;
364 ar_wheels[i].debug_scaled_cforce = Vector3::ZERO;
365 }
366
367 if (ar_wheels[i].wh_is_detached)
368 continue;
369
370 float relspeed = ar_nodes[0].Velocity.dotProduct(getDirection());
371 float curspeed = fabs(relspeed);
372 float wheel_slip = fabs(ar_wheels[i].wh_speed - relspeed) / std::max(1.0f, curspeed);
373
374 // traction control
375 if (tc_mode && fabs(ar_wheels[i].wh_torque) > 0.0f && fabs(ar_wheels[i].wh_speed) > curspeed && wheel_slip > tc_wheelslip_constant)
376 {
377 if (tc_pulse_state)
378 {
379 ar_wheels[i].wh_tc_coef = curspeed / fabs(ar_wheels[i].wh_speed);
380 ar_wheels[i].wh_tc_coef = pow(ar_wheels[i].wh_tc_coef, tc_ratio);
381 }
382 float tc_coef = pow(ar_wheels[i].wh_tc_coef, std::min(std::abs(ar_wheels[i].wh_speed) / 5.0f, 1.0f));
383 ar_wheels[i].wh_torque *= tc_coef;
384 m_tractioncontrol = true;
385 }
386 else
387 {
388 ar_wheels[i].wh_tc_coef = 1.0f;
389 }
390
391 if (ar_wheels[i].wh_braking != WheelBraking::NONE)
392 {
393 // footbrake
394 float abrake = ar_brake_force * ar_brake;
395
396 // handbrake
397 float hbrake = 0.0f;
398 if (ar_parking_brake && (ar_wheels[i].wh_braking != WheelBraking::FOOT_ONLY))
399 {
400 hbrake = m_handbrake_force;
401 }
402
403 // directional braking
404 float dbrake = 0.0f;
405 if ((ar_wheels[i].wh_speed < 20.0f)
406 && (((ar_wheels[i].wh_braking == WheelBraking::FOOT_HAND_SKID_LEFT) && (ar_hydro_dir_state > 0.0f))
407 || ((ar_wheels[i].wh_braking == WheelBraking::FOOT_HAND_SKID_RIGHT) && (ar_hydro_dir_state < 0.0f))))
408 {
409 dbrake = ar_brake_force * abs(ar_hydro_dir_state);
410 }
411
412 if (abrake != 0.0 || dbrake != 0.0 || hbrake != 0.0)
413 {
414 float adbrake = abrake + dbrake;
415
416 // anti-lock braking
417 if (alb_mode && curspeed > alb_minspeed && curspeed > fabs(ar_wheels[i].wh_speed) && (adbrake > 0.0f) && wheel_slip > 0.25f)
418 {
419 if (alb_pulse_state)
420 {
421 ar_wheels[i].wh_alb_coef = fabs(ar_wheels[i].wh_speed) / curspeed;
422 ar_wheels[i].wh_alb_coef = pow(ar_wheels[i].wh_alb_coef, alb_ratio);
423 }
424 adbrake *= ar_wheels[i].wh_alb_coef;
425 m_antilockbrake = true;
426 }
427
429 force -= ar_wheels[i].wh_last_retorque;
430
431 if (ar_wheels[i].wh_speed > 0)
432 ar_wheels[i].wh_torque += Math::Clamp(force, -(adbrake + hbrake), 0.0f);
433 else
434 ar_wheels[i].wh_torque += Math::Clamp(force, 0.0f, +(adbrake + hbrake));
435 }
436 else
437 {
438 ar_wheels[i].wh_alb_coef = 1.0f;
439 }
440 }
441
442 ar_wheels[i].debug_torque += ar_wheels[i].wh_torque / (float)num_steps;
443
444 // application to wheel
445 Vector3 axis = (ar_wheels[i].wh_axis_node_1->RelPosition - ar_wheels[i].wh_axis_node_0->RelPosition).normalisedCopy();
446 float axis_precalc = ar_wheels[i].wh_torque / (Real)(ar_wheels[i].wh_num_nodes);
447
448 float expected_wheel_speed = ar_wheels[i].wh_speed;
449 ar_wheels[i].wh_speed = 0.0f;
450
451 Real contact_counter = 0.0f;
452 Vector3 slip = Vector3::ZERO;
453 Vector3 force = Vector3::ZERO;
454 for (int j = 0; j < ar_wheels[i].wh_num_nodes; j++)
455 {
456 node_t* outer_node = ar_wheels[i].wh_nodes[j];
457 node_t* inner_node = (j % 2) ? ar_wheels[i].wh_axis_node_1 : ar_wheels[i].wh_axis_node_0;
458
459 Vector3 radius = outer_node->RelPosition - inner_node->RelPosition;
460 float inverted_rlen = 1.0f / radius.length();
461
462 if (ar_wheels[i].wh_propulsed == WheelPropulsion::BACKWARD)
463 {
464 radius = -radius;
465 }
466
467 Vector3 dir = axis.crossProduct(radius) * inverted_rlen;
468 ar_wheels[i].wh_nodes[j]->Forces += dir * axis_precalc * inverted_rlen;
469 ar_wheels[i].wh_speed += (outer_node->Velocity - inner_node->Velocity).dotProduct(dir);
470
471 if (ar_wheels[i].wh_nodes[j]->nd_has_ground_contact || ar_wheels[i].wh_nodes[j]->nd_has_mesh_contact)
472 {
473 contact_counter += 1.0f;
474 float force_ratio = ar_wheels[i].wh_nodes[j]->nd_last_collision_force.length();
475 slip += ar_wheels[i].wh_nodes[j]->nd_last_collision_slip * force_ratio;
477 }
478 }
479 if (contact_counter > 0.0f && !force.isZeroLength())
480 {
481 slip /= force.length(); // slip vector weighted by down force
482 slip /= contact_counter; // average slip vector
483 force /= contact_counter; // average force vector
484 Vector3 normal = force.normalisedCopy(); // contact plane normal
485 Vector3 v = ar_wheels[i].wh_axis_node_0->Velocity.midPoint(ar_wheels[i].wh_axis_node_1->Velocity);
486 Vector3 vel = v - v.dotProduct(normal) * normal;
487 ar_wheels[i].debug_vel += vel / (float)num_steps;
488 ar_wheels[i].debug_slip += slip / (float)num_steps;
489 ar_wheels[i].debug_force += force / (float)num_steps;
490 }
491
492 ar_wheels[i].wh_speed /= (Real)ar_wheels[i].wh_num_nodes;
494 // We overestimate the average speed on purpose in order to improve the quality of the braking force estimate
497 if (ar_wheels[i].wh_propulsed == WheelPropulsion::FORWARD)
498 {
499 float speedacc = ar_wheels[i].wh_speed / (float)m_num_proped_wheels;
500 ar_wheel_speed += speedacc; // Accumulate the average wheel speed (m/s)
501 ar_wheel_spin += speedacc / ar_wheels[i].wh_radius; // Accumulate the average wheel spin (radians)
502 }
503
504 expected_wheel_speed += ((ar_wheels[i].wh_last_torque / ar_wheels[i].wh_radius) / ar_wheels[i].wh_mass) * PHYSICS_DT;
505 ar_wheels[i].wh_last_retorque = ar_wheels[i].wh_mass * (ar_wheels[i].wh_speed - expected_wheel_speed) / PHYSICS_DT;
506
507 // reaction torque
509 Vector3 radius = Plane(axis, ar_wheels[i].wh_near_attach_node->RelPosition).projectVector(rradius);
510 float offset = (rradius - radius).length(); // length of the error arm
511 Real rlen = radius.normalise(); // length of the projected arm
512 // TODO: Investigate the offset length abort condition ~ ulteq 10/2018
513 if (rlen > 0.01 && offset * 2.0f < rlen && fabs(ar_wheels[i].wh_torque) > 0.01f)
514 {
515 Vector3 cforce = axis.crossProduct(radius);
516 // modulate the force according to induced torque error
517 cforce *= (0.5f * ar_wheels[i].wh_torque / rlen) * (1.0f - ((offset * 2.0f) / rlen)); // linear modulation
518 ar_wheels[i].wh_arm_node->Forces -= cforce;
520 ar_wheels[i].debug_scaled_cforce += cforce / ar_total_mass / (float)num_steps;
521 }
522
524 ar_wheels[i].wh_torque = 0.0f;
525 }
526
528
529 if (ar_engine)
530 {
531 ar_engine->setWheelSpin(ar_wheel_spin * RAD_PER_SEC_TO_RPM); // Update the driveshaft speed
532 }
533
534 if (doUpdate)
535 {
536 if (!m_antilockbrake)
537 {
539 }
540 else
541 {
543 }
544
546 {
548 }
549 else
550 {
552 }
553 }
554
555 // calculate driven distance
556 float distance_driven = fabs(ar_wheel_speed * PHYSICS_DT);
557 m_odometer_total += distance_driven;
558 m_odometer_user += distance_driven;
559}
560
561void Actor::CalcShocks(bool doUpdate, int num_steps)
562{
563 //variable shocks for stabilization
565 {
568 for (int i = 0; i < ar_num_shocks; i++)
569 {
570 // active shocks now
571 if (ar_shocks[i].flags & SHOCK_FLAG_RACTIVE)
573 else if (ar_shocks[i].flags & SHOCK_FLAG_LACTIVE)
575 }
576 }
577 //auto shock adjust
578 if (this->ar_has_active_shocks && doUpdate)
579 {
581
582 float roll = asin(GetCameraRoll().dotProduct(Vector3::UNIT_Y));
583 //mWindow->setDebugText("Roll:"+ TOSTRING(roll));
584 if (fabs(roll) > 0.2)
585 {
586 m_stabilizer_shock_sleep = -1.0; //emergency timeout stop
587 }
588 if (fabs(roll) > 0.01 && m_stabilizer_shock_sleep < 0.0)
589 {
590 if (roll > 0.0 && m_stabilizer_shock_request != -1)
591 {
593 }
594 else if (roll < 0.0 && m_stabilizer_shock_request != 1)
595 {
597 }
598 else
599 {
602 }
603 }
604 else
605 {
607 }
608
611 else
613 }
614}
615
617{
618 //direction
620 {
622 {
623 // need a maximum rate for analog devices, otherwise hydro beams break
624 float smoothing = Math::Clamp(App::io_analog_smoothing->getFloat(), 0.5f, 2.0f);
625 float sensitivity = Math::Clamp(App::io_analog_sensitivity->getFloat(), 0.5f, 2.0f);
627 float rate = std::exp(-std::min(std::abs(diff), 1.0f) / sensitivity) * diff;
628 ar_hydro_dir_state += (10.0f / smoothing) * PHYSICS_DT * rate;
629 }
630 else
631 {
632 if (ar_hydro_dir_command != 0)
633 {
634 if (!App::io_hydro_coupling->getBool())
635 {
636 float rate = std::max(1.2f, 30.0f / (10.0f));
639 else
641 }
642 else
643 {
644 // minimum rate: 20% --> enables to steer high velocity vehicles
645 float rate = std::max(1.2f, 30.0f / (10.0f + std::abs(ar_wheel_speed / 2.0f)));
648 else
650 }
651 }
652 float dirdelta = PHYSICS_DT;
653 if (ar_hydro_dir_state > dirdelta)
654 ar_hydro_dir_state -= dirdelta;
655 else if (ar_hydro_dir_state < -dirdelta)
656 ar_hydro_dir_state += dirdelta;
657 else
659 }
660 }
661 //aileron
663 {
665 {
668 else
670 }
671 float delta = PHYSICS_DT;
672 if (ar_hydro_aileron_state > delta)
673 ar_hydro_aileron_state -= delta;
674 else if (ar_hydro_aileron_state < -delta)
675 ar_hydro_aileron_state += delta;
676 else
678 }
679 //rudder
681 {
683 {
686 else
688 }
689
690 float delta = PHYSICS_DT;
691 if (ar_hydro_rudder_state > delta)
692 ar_hydro_rudder_state -= delta;
693 else if (ar_hydro_rudder_state < -delta)
694 ar_hydro_rudder_state += delta;
695 else
697 }
698 //elevator
700 {
702 {
705 else
707 }
708 float delta = PHYSICS_DT;
709 if (ar_hydro_elevator_state > delta)
711 else if (ar_hydro_elevator_state < -delta)
713 else
715 }
716 //update length, dirstate between -1.0 and 1.0
717 const int num_hydros = static_cast<int>(ar_hydros.size());
718 for (int i = 0; i < num_hydros; ++i)
719 {
720 hydrobeam_t& hydrobeam = ar_hydros[i];
721
722 //compound hydro
723 float cstate = 0.0f;
724 int div = 0;
725 if (hydrobeam.hb_flags & HYDRO_FLAG_SPEED)
726 {
727 //special treatment for SPEED
728 if (ar_wheel_speed < 12.0f)
729 cstate += ar_hydro_dir_state * (12.0f - ar_wheel_speed) / 12.0f;
730 div++;
731 }
732 if (hydrobeam.hb_flags & HYDRO_FLAG_DIR)
733 {
734 cstate += ar_hydro_dir_state;
735 div++;
736 }
737 if (hydrobeam.hb_flags & HYDRO_FLAG_AILERON)
738 {
739 cstate += ar_hydro_aileron_state;
740 div++;
741 }
742 if (hydrobeam.hb_flags & HYDRO_FLAG_RUDDER)
743 {
744 cstate += ar_hydro_rudder_state;
745 div++;
746 }
747 if (hydrobeam.hb_flags & HYDRO_FLAG_ELEVATOR)
748 {
749 cstate += ar_hydro_elevator_state;
750 div++;
751 }
752 if (hydrobeam.hb_flags & HYDRO_FLAG_REV_AILERON)
753 {
754 cstate -= ar_hydro_aileron_state;
755 div++;
756 }
757 if (hydrobeam.hb_flags & HYDRO_FLAG_REV_RUDDER)
758 {
759 cstate -= ar_hydro_rudder_state;
760 div++;
761 }
762 if (hydrobeam.hb_flags & HYDRO_FLAG_REV_ELEVATOR)
763 {
764 cstate -= ar_hydro_elevator_state;
765 div++;
766 }
767
768 const uint16_t beam_idx = hydrobeam.hb_beam_index;
769
770 if (cstate > 1.0)
771 cstate = 1.0;
772 if (cstate < -1.0)
773 cstate = -1.0;
774
775 // Animators
776 if (hydrobeam.hb_anim_flags)
777 {
778 this->CalcAnimators(hydrobeam, cstate, div);
779 }
780
781 // Final composition
782 if (div)
783 {
784 cstate /= (float)div;
785
786 cstate = hydrobeam.hb_inertia.CalcCmdKeyDelay(cstate, PHYSICS_DT);
787
788 if (!(hydrobeam.hb_flags & HYDRO_FLAG_SPEED) && !hydrobeam.hb_anim_flags)
790
791 float factor = 1.0 - cstate * hydrobeam.hb_speed;
792
793 // check and apply animators limits if set
794 if (hydrobeam.hb_anim_flags)
795 {
796 if (factor < 1.0f - ar_beams[beam_idx].shortbound)
797 factor = 1.0f - ar_beams[beam_idx].shortbound;
798 if (factor > 1.0f + ar_beams[beam_idx].longbound)
799 factor = 1.0f + ar_beams[beam_idx].longbound;
800 }
801
802 ar_beams[beam_idx].L = hydrobeam.hb_ref_length * factor;
803 }
804 }
805}
806
807void Actor::CalcCommands(bool doUpdate)
808{
810 {
811 int active = 0;
812 bool requested = false;
813 float work = 0.0;
814
815 // hydraulics ready?
816 if (ar_engine)
818 else
820
821 // crankfactor
822 float crankfactor = 1.0f;
823 if (ar_engine)
824 crankfactor = ar_engine->getCrankFactor();
825
826 // speed up machines
827 if (ar_driveable == MACHINE)
828 crankfactor = 2;
829
830 for (int i = 1; i <= MAX_COMMANDS; i++) // BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
831 {
832 for (int j = 0; j < (int)ar_command_key[i].beams.size(); j++)
833 {
834 ar_command_key[i].beams[j].cmb_state->auto_move_lock = false;
835 }
836 }
837
838 for (int i = 1; i <= MAX_COMMANDS; i++) // BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
839 {
840 float oldValue = ar_command_key[i].commandValue;
841
842 ar_command_key[i].commandValue = std::max(ar_command_key[i].playerInputValue, ar_command_key[i].triggerInputValue);
843
844 ar_command_key[i].triggerInputValue = 0.0f;
845
846 if (ar_command_key[i].commandValue > 0.01f && oldValue < 0.01f)
847 {
848 // just started
849 ar_command_key[i].commandValueState = 1;
850 }
851 else if (ar_command_key[i].commandValue < 0.01f && oldValue > 0.01f)
852 {
853 // just stopped
854 ar_command_key[i].commandValueState = -1;
855 }
856
857 for (int j = 0; j < (int)ar_command_key[i].beams.size(); j++)
858 {
859 if (ar_command_key[i].commandValue >= 0.5)
860 {
861 ar_command_key[i].beams[j].cmb_state->auto_move_lock = true;
862 if (ar_command_key[i].beams[j].cmb_is_autocentering)
863 {
864 ar_command_key[i].beams[j].cmb_state->auto_moving_mode = 0;
865 }
866 }
867 }
868 }
869
870 // now process normal commands
871 for (int i = 1; i <= MAX_COMMANDS; i++) // BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
872 {
873 bool requestpower = false;
874 for (int j = 0; j < (int)ar_command_key[i].beams.size(); j++)
875 {
876 commandbeam_t& cmd_beam = ar_command_key[i].beams[j];
877 int bbeam_dir = (cmd_beam.cmb_is_contraction) ? -1 : 1;
878 int bbeam = cmd_beam.cmb_beam_index;
879
880 if (bbeam > ar_num_beams)
881 continue;
882
883 // restrict forces
884 if (cmd_beam.cmb_is_force_restricted)
885 crankfactor = std::min(crankfactor, 1.0f);
886
887 float v = ar_command_key[i].commandValue;
888 int& vst = ar_command_key[i].commandValueState;
889
890 // self centering
891 if (cmd_beam.cmb_is_autocentering && !cmd_beam.cmb_state->auto_move_lock)
892 {
893 // check for some error
894 if (ar_beams[bbeam].refL == 0 || ar_beams[bbeam].L == 0)
895 continue;
896
897 float current = (ar_beams[bbeam].L / ar_beams[bbeam].refL);
898
899 if (fabs(current - cmd_beam.cmb_center_length) < 0.0001)
900 {
901 // hold condition
902 cmd_beam.cmb_state->auto_moving_mode = 0;
903 }
904 else
905 {
906 int mode = cmd_beam.cmb_state->auto_moving_mode;
907
908 // determine direction
909 if (current > cmd_beam.cmb_center_length)
910 cmd_beam.cmb_state->auto_moving_mode = -1;
911 else
912 cmd_beam.cmb_state->auto_moving_mode = 1;
913
914 // avoid overshooting
915 if (mode != 0 && mode != cmd_beam.cmb_state->auto_moving_mode)
916 {
917 ar_beams[bbeam].L = cmd_beam.cmb_center_length * ar_beams[bbeam].refL;
918 cmd_beam.cmb_state->auto_moving_mode = 0;
919 }
920 }
921 }
922
923 if (ar_beams[bbeam].refL != 0 && ar_beams[bbeam].L != 0)
924 {
925 float clen = ar_beams[bbeam].L / ar_beams[bbeam].refL;
926 if ((bbeam_dir > 0 && clen < cmd_beam.cmb_boundary_length) || (bbeam_dir < 0 && clen > cmd_beam.cmb_boundary_length))
927 {
928 float dl = ar_beams[bbeam].L;
929
930 if (cmd_beam.cmb_is_1press_center)
931 {
932 // one press + centering
933 if (bbeam_dir * cmd_beam.cmb_state->auto_moving_mode > 0 && bbeam_dir * clen > bbeam_dir * cmd_beam.cmb_center_length && !cmd_beam.cmb_state->pressed_center_mode)
934 {
935 cmd_beam.cmb_state->pressed_center_mode = true;
936 cmd_beam.cmb_state->auto_moving_mode = 0;
937 }
938 else if (bbeam_dir * cmd_beam.cmb_state->auto_moving_mode < 0 && bbeam_dir * clen > bbeam_dir * cmd_beam.cmb_center_length && cmd_beam.cmb_state->pressed_center_mode)
939 {
940 cmd_beam.cmb_state->pressed_center_mode = false;
941 }
942 }
943 if (cmd_beam.cmb_is_1press || cmd_beam.cmb_is_1press_center)
944 {
945 bool key = (v > 0.5);
946 if (bbeam_dir * cmd_beam.cmb_state->auto_moving_mode <= 0 && key)
947 {
948 cmd_beam.cmb_state->auto_moving_mode = bbeam_dir * 1;
949 }
950 else if (cmd_beam.cmb_state->auto_moving_mode == bbeam_dir * 1 && !key)
951 {
952 cmd_beam.cmb_state->auto_moving_mode = bbeam_dir * 2;
953 }
954 else if (cmd_beam.cmb_state->auto_moving_mode == bbeam_dir * 2 && key)
955 {
956 cmd_beam.cmb_state->auto_moving_mode = bbeam_dir * 3;
957 }
958 else if (cmd_beam.cmb_state->auto_moving_mode == bbeam_dir * 3 && !key)
959 {
960 cmd_beam.cmb_state->auto_moving_mode = 0;
961 }
962 }
963
964 v = ar_command_key[i].command_inertia.CalcCmdKeyDelay(v, PHYSICS_DT);
965
966 if (bbeam_dir * cmd_beam.cmb_state->auto_moving_mode > 0)
967 v = 1;
968
970 continue;
971
972 if (v > 0.0f && cmd_beam.cmb_engine_coupling > 0.0f)
973 requestpower = true;
974
975#ifdef USE_OPENAL
976 if (cmd_beam.cmb_plays_sound)
977 {
978 // command sounds
979 if (vst == 1)
980 {
981 // just started
984 vst = 0;
985 }
986 else if (vst == -1)
987 {
988 // just stopped
990 vst = 0;
991 }
992 else if (vst == 0)
993 {
994 // already running, modulate
996 }
997 }
998#endif //USE_OPENAL
999 float cf = 1.0f;
1000
1001 if (cmd_beam.cmb_engine_coupling > 0)
1002 cf = crankfactor;
1003
1004 if (bbeam_dir > 0)
1005 ar_beams[bbeam].L *= (1.0 + cmd_beam.cmb_speed * v * cf * PHYSICS_DT / ar_beams[bbeam].L);
1006 else
1007 ar_beams[bbeam].L *= (1.0 - cmd_beam.cmb_speed * v * cf * PHYSICS_DT / ar_beams[bbeam].L);
1008
1009 dl = fabs(dl - ar_beams[bbeam].L);
1010 if (requestpower)
1011 {
1012 active++;
1013 work += fabs(ar_beams[bbeam].stress) * dl * cmd_beam.cmb_engine_coupling;
1014 }
1015 }
1016 else if ((cmd_beam.cmb_is_1press || cmd_beam.cmb_is_1press_center) && bbeam_dir * cmd_beam.cmb_state->auto_moving_mode > 0)
1017 {
1018 // beyond length
1019 cmd_beam.cmb_state->auto_moving_mode = 0;
1020 }
1021 }
1022 }
1023 // also for rotators
1024 for (int j = 0; j < (int)ar_command_key[i].rotators.size(); j++)
1025 {
1026 float v = 0.0f;
1027 int rota = std::abs(ar_command_key[i].rotators[j]) - 1;
1028
1029 if (ar_rotators[rota].needs_engine && ((ar_engine && !ar_engine->isRunning()) || !ar_engine_hydraulics_ready))
1030 continue;
1031
1032 v = ar_command_key[i].rotator_inertia.CalcCmdKeyDelay(ar_command_key[i].commandValue, PHYSICS_DT);
1033
1034 if (v > 0.0f && ar_rotators[rota].engine_coupling > 0.0f)
1035 requestpower = true;
1036
1037 float cf = 1.0f;
1038
1039 if (ar_rotators[rota].engine_coupling > 0.0f)
1040 cf = crankfactor;
1041
1042 if (ar_command_key[i].rotators[j] > 0)
1043 ar_rotators[rota].angle += ar_rotators[rota].rate * v * cf * PHYSICS_DT;
1044 else
1045 ar_rotators[rota].angle -= ar_rotators[rota].rate * v * cf * PHYSICS_DT;
1046
1047 if (doUpdate || v != 0.0f)
1048 {
1049 ar_rotators[rota].debug_rate = ar_rotators[rota].rate * v * cf;
1050 }
1051 }
1052 if (requestpower)
1053 requested=true;
1054 }
1055
1056 if (ar_engine)
1057 {
1058 ar_engine->setHydroPump(work);
1059 ar_engine->setPrime(requested);
1060 }
1061
1062 if (doUpdate && this == App::GetGameContext()->GetPlayerActor().GetRef())
1063 {
1064#ifdef USE_OPENAL
1065 if (active > 0)
1066 {
1068 float pump_rpm = 660.0f * (1.0f - (work / (float)active) / 100.0f);
1070 }
1071 else
1072 {
1074 }
1075#endif //USE_OPENAL
1076 }
1077 // rotators
1078 for (int i = 0; i < ar_num_rotators; i++)
1079 {
1080 // compute rotation axis
1081 Vector3 ax1 = ar_nodes[ar_rotators[i].axis1].RelPosition;
1082 Vector3 ax2 = ar_nodes[ar_rotators[i].axis2].RelPosition;
1083 Vector3 axis = ax1 - ax2;
1084 axis.normalise();
1085 // find the reference plane
1086 Plane pl = Plane(axis, 0);
1087 // for each pairar
1089 for (int k = 0; k < 2; k++)
1090 {
1091 // find the reference vectors
1092 Vector3 ref1 = pl.projectVector(ax1 - ar_nodes[ar_rotators[i].nodes1[k]].RelPosition);
1093 Vector3 ref2 = pl.projectVector(ax2 - ar_nodes[ar_rotators[i].nodes2[k]].RelPosition);
1094 float ref1len = ref1.normalise();
1095 float ref2len = ref2.normalise();
1096 // theory vector
1097 Vector3 th1 = Quaternion(Radian(ar_rotators[i].angle + Math::HALF_PI), axis) * ref1;
1098 // find the angle error
1099 float aerror = asin(th1.dotProduct(ref2));
1100 ar_rotators[i].debug_aerror += 0.5f * aerror;
1101 // exert forces
1102 float rigidity = ar_rotators[i].force;
1103 Vector3 dir1 = ref1.crossProduct(axis);
1104 Vector3 dir2 = ref2.crossProduct(axis);
1105
1106 // simple jitter fix
1107 if (ref1len <= ar_rotators[i].tolerance)
1108 ref1len = 0.0f;
1109 if (ref2len <= ar_rotators[i].tolerance)
1110 ref2len = 0.0f;
1111
1112 ar_nodes[ar_rotators[i].nodes1[k ]].Forces += (aerror * ref1len * rigidity) * dir1;
1113 ar_nodes[ar_rotators[i].nodes2[k ]].Forces -= (aerror * ref2len * rigidity) * dir2;
1114 // symmetric
1115 ar_nodes[ar_rotators[i].nodes1[k + 2]].Forces -= (aerror * ref1len * rigidity) * dir1;
1116 ar_nodes[ar_rotators[i].nodes2[k + 2]].Forces += (aerror * ref2len * rigidity) * dir2;
1117 }
1118 }
1119 }
1120}
1121
1123{
1124 // go through all ties and process them
1125 for (std::vector<tie_t>::iterator it = ar_ties.begin(); it != ar_ties.end(); it++)
1126 {
1127 // only process tying ties
1128 if (!it->ti_tying)
1129 continue;
1130
1131 // division through zero guard
1132 if (it->ti_beam->refL == 0 || it->ti_beam->L == 0)
1133 continue;
1134
1135 float clen = it->ti_beam->L / it->ti_beam->refL;
1136 if (clen > it->ti_min_length)
1137 {
1138 it->ti_beam->L *= (1.0 - it->ti_contract_speed * PHYSICS_DT / it->ti_beam->L);
1139 }
1140 else
1141 {
1142 // tying finished, end reached
1143 it->ti_tying = false;
1144 }
1145
1146 // check if we hit a certain force limit, then abort the tying process
1147 if (fabs(it->ti_beam->stress) > it->ti_max_stress)
1148 {
1149 it->ti_tying = false;
1150 }
1151 }
1152}
1153void Actor::CalcTruckEngine(bool doUpdate)
1154{
1155 if (ar_engine)
1156 {
1157 ar_engine->UpdateEngine(PHYSICS_DT, doUpdate);
1158 }
1159}
1160
1162{
1164 {
1166 }
1167}
1168
1170{
1171 if (m_ongoing_reset)
1172 return false;
1174 return false;
1176 return false;
1177
1178 if (doUpdate)
1179 {
1180 //this->hookToggle(-2, HOOK_LOCK, -1);
1184 rq->alr_hook_group = -2;
1186 }
1187
1188 this->CalcHooks();
1189 this->CalcRopes();
1190
1191 return true;
1192}
1193
1194template <size_t L>
1195void LogNodeId(RoR::Str<L>& msg, node_t* node) // Internal helper
1196{
1197 msg << " (index: " << node->pos << ")";
1198}
1199
1200template <size_t L>
1201void LogBeamNodes(RoR::Str<L>& msg, beam_t& beam) // Internal helper
1202{
1203 msg << "It was between nodes ";
1204 LogNodeId(msg, beam.p1);
1205 msg << " and ";
1206 LogNodeId(msg, beam.p2);
1207 msg << ".";
1208}
1209
1210void Actor::CalcBeams(bool trigger_hooks)
1211{
1212 for (int i = 0; i < ar_num_beams; i++)
1213 {
1214 if (!ar_beams[i].bm_disabled && !ar_beams[i].bm_inter_actor)
1215 {
1216 // Calculate beam length
1217 Vector3 dis = ar_beams[i].p1->RelPosition - ar_beams[i].p2->RelPosition;
1218
1219 Real dislen = dis.squaredLength();
1220 Real inverted_dislen = fast_invSqrt(dislen);
1221
1222 dislen *= inverted_dislen;
1223
1224 // Calculate beam's deviation from normal
1225 Real difftoBeamL = dislen - ar_beams[i].L;
1226
1227 Real k = ar_beams[i].k;
1228 Real d = ar_beams[i].d;
1229
1230 // Calculate beam's rate of change
1231 float v = (ar_beams[i].p1->Velocity - ar_beams[i].p2->Velocity).dotProduct(dis) * inverted_dislen;
1232
1233 if (ar_beams[i].bounded == SHOCK1)
1234 {
1235 float interp_ratio = 0.0f;
1236
1237 // Following code interpolates between defined beam parameters and default beam parameters
1238 if (difftoBeamL > ar_beams[i].longbound * ar_beams[i].L)
1239 interp_ratio = difftoBeamL - ar_beams[i].longbound * ar_beams[i].L;
1240 else if (difftoBeamL < -ar_beams[i].shortbound * ar_beams[i].L)
1241 interp_ratio = -difftoBeamL - ar_beams[i].shortbound * ar_beams[i].L;
1242
1243 if (interp_ratio != 0.0f)
1244 {
1245 // Hard (normal) shock bump
1246 float tspring = DEFAULT_SPRING;
1247 float tdamp = DEFAULT_DAMP;
1248
1249 // Skip camera, wheels or any other shocks which are not generated in a shocks or shocks2 section
1250 if (ar_beams[i].bm_type == BEAM_HYDRO)
1251 {
1252 tspring = ar_beams[i].shock->sbd_spring;
1253 tdamp = ar_beams[i].shock->sbd_damp;
1254 }
1255
1256 k += (tspring - k) * interp_ratio;
1257 d += (tdamp - d) * interp_ratio;
1258 }
1259 }
1260 else if (ar_beams[i].bounded == TRIGGER)
1261 {
1262 this->CalcTriggers(i, difftoBeamL, trigger_hooks);
1263 }
1264 else if (ar_beams[i].bounded == SHOCK2)
1265 {
1266 this->CalcShocks2(i, difftoBeamL, k, d, v);
1267 }
1268 else if (ar_beams[i].bounded == SHOCK3)
1269 {
1270 this->CalcShocks3(i, difftoBeamL, k, d, v);
1271 }
1272 else if (ar_beams[i].bounded == SUPPORTBEAM)
1273 {
1274 if (difftoBeamL > 0.0f)
1275 {
1276 k = 0.0f;
1277 d *= 0.1f;
1278 float break_limit = SUPPORT_BEAM_LIMIT_DEFAULT;
1279 if (ar_beams[i].longbound > 0.0f)
1280 {
1281 // This is a supportbeam with a user set break limit, get the user set limit
1282 break_limit = ar_beams[i].longbound;
1283 }
1284
1285 // If support beam is extended the originallength * break_limit, break and disable it
1286 if (difftoBeamL > ar_beams[i].L * break_limit)
1287 {
1288 ar_beams[i].bm_broken = true;
1289 ar_beams[i].bm_disabled = true;
1291 {
1292 RoR::Str<300> msg;
1293 msg << "[RoR|Diag] XXX Support-Beam " << i << " limit extended and broke. "
1294 << "Length: " << difftoBeamL << " / max. Length: " << (ar_beams[i].L*break_limit) << ". ";
1295 LogBeamNodes(msg, ar_beams[i]);
1297 }
1298 }
1299 }
1300 }
1301 else if (ar_beams[i].bounded == ROPE)
1302 {
1303 if (difftoBeamL < 0.0f)
1304 {
1305 k = 0.0f;
1306 d *= 0.1f;
1307 }
1308 }
1309
1310 if (trigger_hooks && ar_beams[i].bounded && ar_beams[i].bm_type == BEAM_HYDRO)
1311 {
1312 ar_beams[i].debug_k = k * std::abs(difftoBeamL);
1313 ar_beams[i].debug_d = d * std::abs(v);
1314 ar_beams[i].debug_v = std::abs(v);
1315 }
1316
1317 float slen = -k * difftoBeamL - d * v;
1318 ar_beams[i].stress = slen;
1319
1320 // Fast test for deformation
1321 float len = std::abs(slen);
1322 if (len > ar_beams[i].minmaxposnegstress)
1323 {
1324 if (ar_beams[i].bm_type == BEAM_NORMAL && ar_beams[i].bounded != SHOCK1 && k != 0.0f)
1325 {
1326 // Actual deformation tests
1327 if (slen > ar_beams[i].maxposstress && difftoBeamL < 0.0f) // compression
1328 {
1329 Real yield_length = ar_beams[i].maxposstress / k;
1330 Real deform = difftoBeamL + yield_length * (1.0f - ar_beams[i].plastic_coef);
1331 Real Lold = ar_beams[i].L;
1332 ar_beams[i].L += deform;
1333 ar_beams[i].L = std::max(MIN_BEAM_LENGTH, ar_beams[i].L);
1334 slen = slen - (slen - ar_beams[i].maxposstress) * 0.5f;
1335 len = slen;
1336 if (ar_beams[i].L > 0.0f && Lold > ar_beams[i].L)
1337 {
1338 ar_beams[i].maxposstress *= Lold / ar_beams[i].L;
1339 ar_beams[i].minmaxposnegstress = std::min(ar_beams[i].maxposstress, -ar_beams[i].maxnegstress);
1340 ar_beams[i].minmaxposnegstress = std::min(ar_beams[i].minmaxposnegstress, ar_beams[i].strength);
1341 }
1342 // For the compression case we do not remove any of the beam's
1343 // strength for structure stability reasons
1344 //ar_beams[i].strength += deform * k * 0.5f;
1346 {
1347 RoR::Str<300> msg;
1348 msg << "[RoR|Diag] YYY Beam " << i << " just deformed with extension force "
1349 << len << " / " << ar_beams[i].strength << ". ";
1350 LogBeamNodes(msg, ar_beams[i]);
1351 RoR::Log(msg.ToCStr());
1352 }
1353 }
1354 else if (slen < ar_beams[i].maxnegstress && difftoBeamL > 0.0f) // expansion
1355 {
1356 Real yield_length = ar_beams[i].maxnegstress / k;
1357 Real deform = difftoBeamL + yield_length * (1.0f - ar_beams[i].plastic_coef);
1358 Real Lold = ar_beams[i].L;
1359 ar_beams[i].L += deform;
1360 slen = slen - (slen - ar_beams[i].maxnegstress) * 0.5f;
1361 len = -slen;
1362 if (Lold > 0.0f && ar_beams[i].L > Lold)
1363 {
1364 ar_beams[i].maxnegstress *= ar_beams[i].L / Lold;
1365 ar_beams[i].minmaxposnegstress = std::min(ar_beams[i].maxposstress, -ar_beams[i].maxnegstress);
1366 ar_beams[i].minmaxposnegstress = std::min(ar_beams[i].minmaxposnegstress, ar_beams[i].strength);
1367 }
1368 ar_beams[i].strength -= deform * k;
1370 {
1371 RoR::Str<300> msg;
1372 msg << "[RoR|Diag] YYY Beam " << i << " just deformed with extension force "
1373 << len << " / " << ar_beams[i].strength << ". ";
1374 LogBeamNodes(msg, ar_beams[i]);
1375 RoR::Log(msg.ToCStr());
1376 }
1377 }
1378 }
1379
1380 // Test if the beam should break
1381 if (len > ar_beams[i].strength)
1382 {
1383 // Sound effect.
1384 // Sound volume depends on springs stored energy
1385 SOUND_MODULATE(ar_instance_id, SS_MOD_BREAK, 0.5 * k * difftoBeamL * difftoBeamL);
1387
1388 //Break the beam only when it is not connected to a node
1389 //which is a part of a collision triangle and has 2 "live" beams or less
1390 //connected to it.
1391 if (!((ar_beams[i].p1->nd_cab_node && GetNumActiveConnectedBeams(ar_beams[i].p1->pos) < 3) || (ar_beams[i].p2->nd_cab_node && GetNumActiveConnectedBeams(ar_beams[i].p2->pos) < 3)))
1392 {
1393 slen = 0.0f;
1394 ar_beams[i].bm_broken = true;
1395 ar_beams[i].bm_disabled = true;
1396
1398 {
1399 RoR::Str<200> msg;
1400 msg << "[RoR|Diag] XXX Beam " << i << " just broke with force " << len << " / " << ar_beams[i].strength << ". ";
1401 LogBeamNodes(msg, ar_beams[i]);
1403 }
1404
1405 // detachergroup check: beam[i] is already broken, check detacher group# == 0/default skip the check ( performance bypass for beams with default setting )
1406 // only perform this check if this is a master detacher beams (positive detacher group id > 0)
1407 if (ar_beams[i].detacher_group > 0)
1408 {
1409 // cycle once through the other beams
1410 for (int j = 0; j < ar_num_beams; j++)
1411 {
1412 // beam[i] detacher group# == checked beams detacher group# -> delete & disable checked beam
1413 // do this with all master(positive id) and minor(negative id) beams of this detacher group
1414 if (abs(ar_beams[j].detacher_group) == ar_beams[i].detacher_group)
1415 {
1416 ar_beams[j].bm_broken = true;
1417 ar_beams[j].bm_disabled = true;
1419 {
1421 "Deleting Detacher BeamID: " + TOSTRING(j) + ", Detacher Group: " + TOSTRING(ar_beams[i].detacher_group)+ ", actor ID: " + TOSTRING(ar_instance_id));
1422 }
1423 }
1424 }
1425 // cycle once through all wheeldetachers
1426 for (wheeldetacher_t const& wheeldetacher: ar_wheeldetachers)
1427 {
1428 if (wheeldetacher.wd_detacher_group == ar_beams[i].detacher_group)
1429 {
1430 ar_wheels[wheeldetacher.wd_wheel_id].wh_is_detached = true;
1432 {
1434 "Detaching wheel ID: " + TOSTRING(wheeldetacher.wd_wheel_id) + ", Detacher Group: " + TOSTRING(ar_beams[i].detacher_group)+ ", actor ID: " + TOSTRING(ar_instance_id));
1435 }
1436 }
1437 }
1438 }
1439 }
1440 else
1441 {
1443 }
1444
1445 // something broke, check buoyant hull
1446 for (int mk = 0; mk < ar_num_buoycabs; mk++)
1447 {
1448 int tmpv = ar_buoycabs[mk] * 3;
1450 continue;
1451 if ((ar_beams[i].p1 == &ar_nodes[ar_cabs[tmpv]] || ar_beams[i].p1 == &ar_nodes[ar_cabs[tmpv + 1]] || ar_beams[i].p1 == &ar_nodes[ar_cabs[tmpv + 2]]) &&
1452 (ar_beams[i].p2 == &ar_nodes[ar_cabs[tmpv]] || ar_beams[i].p2 == &ar_nodes[ar_cabs[tmpv + 1]] || ar_beams[i].p2 == &ar_nodes[ar_cabs[tmpv + 2]]))
1453 {
1454 m_buoyance->sink = true;
1455 }
1456 }
1457 }
1458 }
1459
1460 // At last update the beam forces
1461 Vector3 f = dis;
1462 f *= (slen * inverted_dislen);
1463 ar_beams[i].p1->Forces += f;
1464 ar_beams[i].p2->Forces -= f;
1465 }
1466 }
1467}
1468
1470{
1471 for (int i = 0; i < static_cast<int>(ar_inter_beams.size()); i++)
1472 {
1473 if (!ar_inter_beams[i]->bm_disabled && ar_inter_beams[i]->bm_inter_actor)
1474 {
1475 // Calculate beam length
1476 Vector3 dis = ar_inter_beams[i]->p1->AbsPosition - ar_inter_beams[i]->p2->AbsPosition;
1477
1478 Real dislen = dis.squaredLength();
1479 Real inverted_dislen = fast_invSqrt(dislen);
1480
1481 dislen *= inverted_dislen;
1482
1483 // Calculate beam's deviation from normal
1484 Real difftoBeamL = dislen - ar_inter_beams[i]->L;
1485
1486 Real k = ar_inter_beams[i]->k;
1487 Real d = ar_inter_beams[i]->d;
1488
1489 if (ar_inter_beams[i]->bounded == ROPE && difftoBeamL < 0.0f)
1490 {
1491 k = 0.0f;
1492 d *= 0.1f;
1493 }
1494
1495 // Calculate beam's rate of change
1496 Vector3 v = ar_inter_beams[i]->p1->Velocity - ar_inter_beams[i]->p2->Velocity;
1497
1498 float slen = -k * (difftoBeamL) - d * v.dotProduct(dis) * inverted_dislen;
1499 ar_inter_beams[i]->stress = slen;
1500
1501 // Fast test for deformation
1502 float len = std::abs(slen);
1503 if (len > ar_inter_beams[i]->minmaxposnegstress)
1504 {
1505 if (ar_inter_beams[i]->bm_type == BEAM_NORMAL && ar_inter_beams[i]->bounded != SHOCK1 && k != 0.0f)
1506 {
1507 // Actual deformation tests
1508 if (slen > ar_inter_beams[i]->maxposstress && difftoBeamL < 0.0f) // compression
1509 {
1510 Real yield_length = ar_inter_beams[i]->maxposstress / k;
1511 Real deform = difftoBeamL + yield_length * (1.0f - ar_inter_beams[i]->plastic_coef);
1512 Real Lold = ar_inter_beams[i]->L;
1513 ar_inter_beams[i]->L += deform;
1514 ar_inter_beams[i]->L = std::max(MIN_BEAM_LENGTH, ar_inter_beams[i]->L);
1515 slen = slen - (slen - ar_inter_beams[i]->maxposstress) * 0.5f;
1516 len = slen;
1517 if (ar_inter_beams[i]->L > 0.0f && Lold > ar_inter_beams[i]->L)
1518 {
1519 ar_inter_beams[i]->maxposstress *= Lold / ar_inter_beams[i]->L;
1520 ar_inter_beams[i]->minmaxposnegstress = std::min(ar_inter_beams[i]->maxposstress, -ar_inter_beams[i]->maxnegstress);
1521 ar_inter_beams[i]->minmaxposnegstress = std::min(ar_inter_beams[i]->minmaxposnegstress, ar_inter_beams[i]->strength);
1522 }
1523 // For the compression case we do not remove any of the beam's
1524 // strength for structure stability reasons
1525 //ar_inter_beams[i]->strength += deform * k * 0.5f;
1527 {
1528 RoR::Str<300> msg;
1529 msg << "[RoR|Diag] YYY Beam " << i << " just deformed with extension force "
1530 << len << " / " << ar_inter_beams[i]->strength << ". ";
1531 LogBeamNodes(msg, (*ar_inter_beams[i]));
1532 RoR::Log(msg.ToCStr());
1533 }
1534 }
1535 else if (slen < ar_inter_beams[i]->maxnegstress && difftoBeamL > 0.0f) // expansion
1536 {
1537 Real yield_length = ar_inter_beams[i]->maxnegstress / k;
1538 Real deform = difftoBeamL + yield_length * (1.0f - ar_inter_beams[i]->plastic_coef);
1539 Real Lold = ar_inter_beams[i]->L;
1540 ar_inter_beams[i]->L += deform;
1541 slen = slen - (slen - ar_inter_beams[i]->maxnegstress) * 0.5f;
1542 len = -slen;
1543 if (Lold > 0.0f && ar_inter_beams[i]->L > Lold)
1544 {
1545 ar_inter_beams[i]->maxnegstress *= ar_inter_beams[i]->L / Lold;
1546 ar_inter_beams[i]->minmaxposnegstress = std::min(ar_inter_beams[i]->maxposstress, -ar_inter_beams[i]->maxnegstress);
1547 ar_inter_beams[i]->minmaxposnegstress = std::min(ar_inter_beams[i]->minmaxposnegstress, ar_inter_beams[i]->strength);
1548 }
1549 ar_inter_beams[i]->strength -= deform * k;
1551 {
1552 RoR::Str<300> msg;
1553 msg << "[RoR|Diag] YYY Beam " << i << " just deformed with extension force "
1554 << len << " / " << ar_inter_beams[i]->strength << ". ";
1555 LogBeamNodes(msg, (*ar_inter_beams[i]));
1556 RoR::Log(msg.ToCStr());
1557 }
1558 }
1559 }
1560
1561 // Test if the beam should break
1562 if (len > ar_inter_beams[i]->strength)
1563 {
1564 // Sound effect.
1565 // Sound volume depends on springs stored energy
1566 SOUND_MODULATE(ar_instance_id, SS_MOD_BREAK, 0.5 * k * difftoBeamL * difftoBeamL);
1568
1569 //Break the beam only when it is not connected to a node
1570 //which is a part of a collision triangle and has 2 "live" beams or less
1571 //connected to it.
1572 if (!((ar_inter_beams[i]->p1->nd_cab_node && GetNumActiveConnectedBeams(ar_inter_beams[i]->p1->pos) < 3) || (ar_inter_beams[i]->p2->nd_cab_node && GetNumActiveConnectedBeams(ar_inter_beams[i]->p2->pos) < 3)))
1573 {
1574 slen = 0.0f;
1575 ar_inter_beams[i]->bm_broken = true;
1576 ar_inter_beams[i]->bm_disabled = true;
1577
1579 {
1580 RoR::Str<200> msg;
1581 msg << "Beam " << i << " just broke with force " << len << " / " << ar_inter_beams[i]->strength << ". ";
1582 LogBeamNodes(msg, (*ar_inter_beams[i]));
1584 }
1585 }
1586 else
1587 {
1588 ar_inter_beams[i]->strength = 2.0f * ar_inter_beams[i]->minmaxposnegstress;
1589 }
1590 }
1591 }
1592
1593 // At last update the beam forces
1594 Vector3 f = dis;
1595 f *= (slen * inverted_dislen);
1596 ar_inter_beams[i]->p1->Forces += f;
1597 ar_inter_beams[i]->p2->Forces -= f;
1598 }
1599 }
1600}
1601
1603{
1604 const auto water = App::GetGameContext()->GetTerrain()->getWater();
1605 const float gravity = App::GetGameContext()->GetTerrain()->getGravity();
1606 m_water_contact = false;
1607
1608 for (NodeNum_t i = 0; i < ar_num_nodes; i++)
1609 {
1610 // COLLISION
1611 if (!ar_nodes[i].nd_no_ground_contact)
1612 {
1613 Vector3 oripos = ar_nodes[i].AbsPosition;
1615 contacted = contacted | App::GetGameContext()->GetTerrain()->GetCollisions()->nodeCollision(&ar_nodes[i], PHYSICS_DT);
1616 ar_nodes[i].nd_has_ground_contact = contacted;
1617 if (ar_nodes[i].nd_has_ground_contact || ar_nodes[i].nd_has_mesh_contact)
1618 {
1620 // Reverts: commit/d11a88142f737528638bd357c38d717c85cebba6#diff-4003254e55aec2c60d21228f375f2a2dL1153
1621 // Fixes: Gavril Omega Six sliding on ground on the simple2 spawn
1622 // ar_nodes[i].AbsPosition - oripos is always zero ... dark floating point magic
1623 ar_nodes[i].RelPosition += ar_nodes[i].AbsPosition - oripos;
1624 }
1625 }
1626
1627 if (i == ar_main_camera_node_pos)
1628 {
1629 // record g forces on cameras
1631 }
1632
1633 // integration
1634 if (!ar_nodes[i].nd_immovable)
1635 {
1640 }
1641
1642 // prepare next loop (optimisation)
1643 // we start forces from zero
1644 // start with gravity
1645 ar_nodes[i].Forces = Vector3(0, ar_nodes[i].mass * gravity, 0);
1646
1647 Real approx_speed = approx_sqrt(ar_nodes[i].Velocity.squaredLength());
1648
1649 // anti-explsion guard (mach 20)
1650 if (approx_speed > 6860 && !m_ongoing_reset)
1651 {
1652 ActorModifyRequest* rq = new ActorModifyRequest; // actor exploded, schedule reset
1653 rq->amr_actor = this->ar_instance_id;
1656 m_ongoing_reset = true;
1657 }
1658
1660 {
1661 // aerodynamics on steroids!
1663 }
1665 {
1666 // add viscous drag (turbulent model)
1667 Real defdragxspeed = DEFAULT_DRAG * approx_speed;
1668 Vector3 drag = -defdragxspeed * ar_nodes[i].Velocity;
1669 // plus: turbulences
1670 Real maxtur = defdragxspeed * approx_speed * 0.005f;
1671 drag += maxtur * Vector3(frand_11(), frand_11(), frand_11());
1672 ar_nodes[i].Forces += drag;
1673 }
1674
1675 if (water)
1676 {
1677 const bool is_under_water = water->IsUnderWater(ar_nodes[i].AbsPosition);
1678 if (is_under_water)
1679 {
1680 m_water_contact = true;
1681 if (ar_num_buoycabs == 0)
1682 {
1683 // water drag (turbulent)
1684 ar_nodes[i].Forces -= (DEFAULT_WATERDRAG * approx_speed) * ar_nodes[i].Velocity;
1685 // basic buoyance
1686 ar_nodes[i].Forces += ar_nodes[i].buoyancy * Vector3::UNIT_Y;
1687 }
1688 // engine stall
1689 if (i == ar_cinecam_node[0] && ar_engine)
1690 {
1692 }
1693 }
1694 ar_nodes[i].nd_under_water = is_under_water;
1695 }
1696 }
1697}
1698
1700{
1701 // Test eventbox collision and extra 'only wheel nodes' filtering condition
1702 // ------------------------------------------------------------------------
1703
1705 && (cbox->event_filter != EVENT_TRUCK_WHEELS || node.nd_tyre_node);
1706}
1707
1709{
1710 // Assumption: node positions and bounding boxes are up to date.
1711 // First, find all collision boxes which this actor's bounding box touches (potential collisions)
1712 // For each potential collision box:
1713 // * if a collision was already recorded, test the recorded node. If still colliding, do nothing.
1714 // * otherwise loop nodes until collision is found. If not, clear the collision record.
1715 // ----------------------------------------------------------------------------------------------
1716
1717 m_potential_eventboxes.clear();
1719
1721 {
1722 // Find existing collision record
1723 bool has_collision = false;
1724 bool do_callback_enter = true;
1725 bool do_callback_exit = false;
1726 auto itor = m_active_eventboxes.begin();
1727 while (itor != m_active_eventboxes.end())
1728 {
1729 if (itor->first == cbox)
1730 {
1731 // Existing record found - check if the node still collides
1732 has_collision = TestNodeEventBoxCollision(ar_nodes[itor->second], cbox);
1733 if (!has_collision)
1734 {
1735 // Erase the collision record
1736 itor = m_active_eventboxes.erase(itor);
1737 // Prevent invoking the same ENTER callback again
1738 do_callback_enter = false;
1739 do_callback_exit = true;
1740 }
1741 break;
1742 }
1743 else
1744 {
1745 itor++;
1746 }
1747 }
1748
1749 if (!has_collision)
1750 {
1751 // Find if any node collides
1752 for (NodeNum_t i = 0; i < ar_num_nodes; i++)
1753 {
1754 has_collision = TestNodeEventBoxCollision(ar_nodes[i], cbox);
1755 if (has_collision)
1756 {
1757 do_callback_exit = false;
1758 // Add new collision record
1759 m_active_eventboxes.push_back(std::make_pair(cbox, i));
1760 // Do the script callback
1761 if (do_callback_enter)
1762 {
1763 // IMPORTANT - this function is executed under physics thread pool, do not run script callbacks synchronously!
1764 eventsource_t& eventsource = App::GetGameContext()->GetTerrain()->GetCollisions()->getEventSource(cbox->eventsourcenum);
1765
1766 // The classic optional per-object script handler.
1767 ScriptCallbackArgs* args = new ScriptCallbackArgs( &eventsource, i );
1769
1770 // The new EVENTBOX_ENTER event.
1771 TRIGGER_EVENT_ASYNC(SE_EVENTBOX_ENTER, 0, ar_instance_id, ar_nodes[i].pos, 0, eventsource.es_instance_name, eventsource.es_box_name, "", "");
1772 }
1773 break;
1774 }
1775 }
1776 }
1777
1778 if (do_callback_exit)
1779 {
1780 // IMPORTANT - this function is executed under physics thread pool, do not run script callbacks synchronously!
1781 const eventsource_t& eventsource = App::GetGameContext()->GetTerrain()->GetCollisions()->getEventSource(cbox->eventsourcenum);
1782 TRIGGER_EVENT_ASYNC(SE_EVENTBOX_EXIT, 0, ar_instance_id, 0, 0, eventsource.es_instance_name, eventsource.es_box_name, "", "");
1783 }
1784 }
1785}
1786
1788{
1789 //locks - this is not active in network mode
1790 for (std::vector<hook_t>::iterator it = ar_hooks.begin(); it != ar_hooks.end(); it++)
1791 {
1792 //we need to do this here to avoid countdown speedup by triggers
1793 it->hk_timer = std::max(0.0f, it->hk_timer - PHYSICS_DT);
1794
1795 if (it->hk_lock_node && it->hk_locked == PRELOCK)
1796 {
1797 if (it->hk_beam->L < it->hk_min_length)
1798 {
1799 //shortlimit reached -> status LOCKED
1800 it->hk_locked = LOCKED;
1801 }
1802 else
1803 {
1804 //shorten the connecting beam slowly to locking minrange
1805 if (it->hk_beam->L > it->hk_lockspeed && fabs(it->hk_beam->stress) < it->hk_maxforce)
1806 {
1807 it->hk_beam->L = (it->hk_beam->L - it->hk_lockspeed);
1808 }
1809 else
1810 {
1811 if (fabs(it->hk_beam->stress) < it->hk_maxforce)
1812 {
1813 it->hk_beam->L = 0.001f;
1814 //locking minrange or stress exeeded -> status LOCKED
1815 it->hk_locked = LOCKED;
1816 }
1817 else
1818 {
1819 if (it->hk_nodisable)
1820 {
1821 //force exceed, but beam is set to nodisable, just lock it in this position
1822 it->hk_locked = LOCKED;
1823 }
1824 else
1825 {
1826 //force exceeded, reset the hook node
1831 }
1832 }
1833 }
1834 }
1835 }
1836 }
1837}
1838
1840{
1841 for (auto r : ar_ropes)
1842 {
1843 if (r.rp_locked == LOCKED && r.rp_locked_ropable)
1844 {
1845 auto locked_node = r.rp_locked_ropable->node;
1846 r.rp_beam->p2->AbsPosition = locked_node->AbsPosition;
1847 r.rp_beam->p2->RelPosition = locked_node->AbsPosition - ar_origin;
1848 r.rp_beam->p2->Velocity = locked_node->Velocity;
1849 locked_node->Forces += r.rp_beam->p2->Forces;
1850 r.rp_beam->p2->Forces = Vector3::ZERO;
1851 }
1852 }
1853}
void LogNodeId(RoR::Str< L > &msg, node_t *node)
void LogBeamNodes(RoR::Str< L > &msg, beam_t &beam)
const int BUOYANCY_VISUAL_UPDATE_INTERVAL
const int BUOYANCY_SAMPLING_INTERVAL
bool TestNodeEventBoxCollision(const node_t &node, collision_box_t *cbox)
Central state/object manager and communications hub.
#define TOSTRING(x)
Definition Application.h:57
float fast_invSqrt(const float v)
Definition ApproxMath.h:124
float approx_sqrt(const float y)
Definition ApproxMath.h:101
float frand_11()
Definition ApproxMath.h:55
float approx_pow(const float x, const float y)
Definition ApproxMath.h:91
Game state manager and message-queue provider.
static const int MAX_COMMANDS
maximum number of commands per actor
#define PHYSICS_DT
static const float RAD_PER_SEC_TO_RPM
Convert radian/second to RPM (60/2*PI)
#define SOUND_START(_ACTOR_, _TRIG_)
#define SOUND_STOP(_ACTOR_, _TRIG_)
#define SOUND_PLAY_ONCE(_ACTOR_, _TRIG_)
#define SOUND_MODULATE(_ACTOR_, _MOD_, _VALUE_)
bool ar_hydro_speed_coupling
Definition Actor.h:551
bool ar_parking_brake
Definition Actor.h:467
bool alb_mode
Anti-lock brake state; Enabled? {1/0}.
Definition Actor.h:410
float m_odometer_user
GUI state.
Definition Actor.h:652
wing_t * ar_wings
Definition Actor.h:360
int ar_num_screwprops
Definition Actor.h:391
int ar_num_wings
Definition Actor.h:361
EnginePtr ar_engine
Definition Actor.h:432
void updateSlideNodeForces(const Ogre::Real delta_time_sec)
calculate and apply Corrective forces
bool m_antilockbrake
GUI state.
Definition Actor.h:656
TransferCase * m_transfer_case
Physics.
Definition Actor.h:643
void CalcCommands(bool doUpdate)
float ar_hydro_elevator_state
Definition Actor.h:464
bool m_ongoing_reset
Hack to prevent position/rotation creep during interactive truck reset (aka LiveRepair).
Definition Actor.h:520
void CalcBeams(bool trigger_hooks)
float m_mouse_grab_move_force
Definition Actor.h:630
float ar_wheel_speed
Physics state; wheel speed in m/s.
Definition Actor.h:453
int GetNumActiveConnectedBeams(int nodeid)
Returns the number of active (non bounded) beams connected to a node.
Definition Actor.cpp:3913
BuoyCachedNodeID_t ar_cabs_buoy_cache_ids[MAX_CABS]
Definition Actor.h:393
CmdKeyArray ar_command_key
BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
Definition Actor.h:369
wheel_t ar_wheels[MAX_WHEELS]
Definition Actor.h:384
std::vector< rope_t > ar_ropes
Definition Actor.h:363
NodeNum_t m_mouse_grab_node
Sim state; node currently being dragged by user.
Definition Actor.h:628
float m_odometer_total
GUI state.
Definition Actor.h:651
std::vector< wheeldetacher_t > ar_wheeldetachers
Definition Actor.h:374
float tc_pulse_time
Definition Actor.h:505
node_t * ar_nodes
Definition Actor.h:330
int ar_buoycabs[MAX_CABS]
Definition Actor.h:400
int m_num_wheel_diffs
Physics attr.
Definition Actor.h:642
int m_num_proped_wheels
Physics attr, filled at spawn - Number of propelled wheels.
Definition Actor.h:616
void CalcForceFeedback(bool doUpdate)
std::vector< tie_t > ar_ties
Definition Actor.h:365
float ar_hydro_rudder_state
Definition Actor.h:462
void CalcBuoyance(bool doUpdate)
int m_num_axle_diffs
Physics attr.
Definition Actor.h:640
float m_stabilizer_shock_ratio
Physics state.
Definition Actor.h:637
CineCameraID_t ar_current_cinecam
Sim state; index of current CineCam (CINECAMERAID_INVALID if using 3rd-person camera)
Definition Actor.h:496
void CalcShocks(bool doUpdate, int num_steps)
float tc_wheelslip_constant
use ACTORSIMATTR_TC_WHEELSLIP_CONSTANT
Definition Actor.h:510
Differential * m_axle_diffs[1+MAX_WHEELS/2]
Physics.
Definition Actor.h:639
ScrewpropPtr ar_screwprops[MAX_SCREWPROPS]
Definition Actor.h:390
Replay * m_replay_handler
Definition Actor.h:627
NodeNum_t ar_main_camera_node_pos
Sim attr; ar_camera_node_pos[0] >= 0 ? ar_camera_node_pos[0] : 0.
Definition Actor.h:441
std::vector< Airbrake * > ar_airbrakes
Definition Actor.h:368
bool m_beam_break_debug_enabled
Logging state.
Definition Actor.h:703
bool ar_physics_paused
Actor physics individually paused by user.
Definition Actor.h:521
void CalcCabCollisions()
Definition Actor.cpp:2553
bool m_tractioncontrol
GUI state.
Definition Actor.h:657
float ar_hydro_dir_command
Definition Actor.h:456
int ar_buoycab_types[MAX_CABS]
Definition Actor.h:401
float m_fusealge_width
Physics attr; defined in truckfile.
Definition Actor.h:650
Airfoil * m_fusealge_airfoil
Physics attr; defined in truckfile.
Definition Actor.h:647
void CalcShocks3(int i, Ogre::Real difftoBeamL, Ogre::Real &k, Ogre::Real &d, Ogre::Real v)
Definition Actor.cpp:2685
node_t * m_fusealge_front
Physics attr; defined in truckfile.
Definition Actor.h:648
bool ar_disable_aerodyn_turbulent_drag
Physics state.
Definition Actor.h:549
float ar_wheel_spin
Physics state; wheel speed in radians/s.
Definition Actor.h:454
bool tc_mode
Enabled?
Definition Actor.h:504
std::unique_ptr< Buoyance > m_buoyance
Definition Actor.h:490
node_t * m_fusealge_back
Physics attr; defined in truckfile.
Definition Actor.h:649
float ar_hydro_elevator_command
Definition Actor.h:463
int m_stabilizer_shock_request
Physics state; values: { -1, 0, 1 }.
Definition Actor.h:638
float m_handbrake_force
Physics attr; defined in truckfile.
Definition Actor.h:646
float ar_hydro_dir_state
Definition Actor.h:457
bool ar_engine_hydraulics_ready
Sim state; does engine have enough RPM to power hydraulics?
Definition Actor.h:550
Ogre::Vector3 ar_origin
Physics state; base position for softbody nodes.
Definition Actor.h:438
ActorState ar_state
Definition Actor.h:518
float m_stabilizer_shock_sleep
Sim state.
Definition Actor.h:626
float ar_brake_force
Physics attr; filled at spawn.
Definition Actor.h:436
Ogre::Vector3 getDirection()
average actor velocity, calculated using the actor positions of the last two frames
Definition Actor.cpp:365
float tc_timer
Definition Actor.h:509
AeroEnginePtr ar_aeroengines[MAX_AEROENGINES]
Definition Actor.h:388
float ar_hydro_aileron_command
Definition Actor.h:459
int ar_num_shocks
Number of shock absorbers.
Definition Actor.h:356
float ar_total_mass
Calculated; total mass in Kg.
Definition Actor.h:325
float ar_hydro_aileron_state
Definition Actor.h:460
int ar_num_aeroengines
Definition Actor.h:389
float alb_ratio
Anti-lock brake attribute: Regulating force.
Definition Actor.h:408
int ar_num_beams
Definition Actor.h:349
bool m_beam_deform_debug_enabled
Logging state.
Definition Actor.h:704
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition Actor.h:429
float ar_avg_wheel_speed
Physics state; avg wheel speed in m/s.
Definition Actor.h:455
bool m_has_axles_section
Temporary (legacy parsing helper) until central diffs are implemented.
Definition Actor.h:658
rotator_t * ar_rotators
Definition Actor.h:358
Ogre::Real ar_brake
Physics state; braking intensity.
Definition Actor.h:452
Ogre::Vector3 m_camera_gforces_accu
Accumulator for 'camera' G-forces.
Definition Actor.h:633
void CalcShocks2(int i, Ogre::Real difftoBeamL, Ogre::Real &k, Ogre::Real &d, Ogre::Real v)
Definition Actor.cpp:2574
NodeNum_t ar_camera_node_pos[MAX_CAMERAS]
Physics attr; 'camera' = frame of reference; origin node.
Definition Actor.h:444
void CalcAnimators(hydrobeam_t const &hydrobeam, float &cstate, int &div)
Definition Actor.cpp:2164
Differential * m_wheel_diffs[MAX_WHEELS/2]
Physics.
Definition Actor.h:641
float alb_pulse_time
Anti-lock brake attribute;.
Definition Actor.h:411
int ar_cabs[MAX_CABS *3]
Definition Actor.h:392
bool m_water_contact
Scripting state.
Definition Actor.h:699
float ar_hydro_rudder_command
Definition Actor.h:461
int ar_num_nodes
Definition Actor.h:345
void CalcAircraftForces(bool doUpdate)
bool tc_pulse_state
Definition Actor.h:506
std::vector< beam_t * > ar_inter_beams
Beams connecting 2 actors.
Definition Actor.h:354
float alb_minspeed
Anti-lock brake attribute;.
Definition Actor.h:409
void CalcForcesEulerCompute(bool doUpdate, int num_steps)
Ogre::Vector3 m_mouse_grab_pos
Definition Actor.h:629
bool m_has_command_beams
Physics attr;.
Definition Actor.h:701
ground_model_t * ar_last_fuzzy_ground_model
GUI state.
Definition Actor.h:487
void CalcTriggers(int i, Ogre::Real difftoBeamL, bool update_hooks)
Definition Actor.cpp:2717
Ogre::Vector3 ar_fusedrag
Physics state.
Definition Actor.h:475
float tc_ratio
Regulating force.
Definition Actor.h:503
float alb_timer
Anti-lock brake state;.
Definition Actor.h:415
void CalcTruckEngine(bool doUpdate)
std::vector< hydrobeam_t > ar_hydros
Definition Actor.h:395
std::vector< std::pair< collision_box_t *, NodeNum_t > > m_active_eventboxes
Definition Actor.h:489
std::vector< hook_t > ar_hooks
Definition Actor.h:366
void UpdateBoundingBoxes()
Definition Actor.cpp:1201
int ar_num_wheels
Definition Actor.h:385
shock_t * ar_shocks
Shock absorbers.
Definition Actor.h:355
Ogre::Vector3 GetCameraRoll()
Definition Actor.h:307
bool CalcForcesEulerPrepare(bool doUpdate)
NodeNum_t ar_cinecam_node[MAX_CAMERAS]
Sim attr; Cine-camera node indexes.
Definition Actor.h:433
bool alb_pulse_state
Anti-lock brake state;.
Definition Actor.h:412
Ogre::Real ar_hydro_dir_wheel_display
Definition Actor.h:458
beam_t * ar_beams
Definition Actor.h:348
int ar_num_buoycabs
Definition Actor.h:402
struct RoR::Actor::VehicleForceSensors m_force_sensors
Data for ForceFeedback devices.
bool ar_has_active_shocks
Are there active stabilizer shocks?
Definition Actor.h:357
CollisionBoxPtrVec m_potential_eventboxes
Definition Actor.h:488
void CalcWheels(bool doUpdate, int num_steps)
int ar_num_rotators
Definition Actor.h:359
ActorType ar_driveable
Sim attr; marks vehicle type and features.
Definition Actor.h:431
virtual void updateForces(float dt, int doUpdate)=0
void getparams(float a, float cratio, float cdef, float *ocl, float *ocd, float *ocm)
Definition Airfoil.cpp:105
float CalcCmdKeyDelay(float cmd_input, float dt)
bool nodeCollision(node_t *node, float dt)
bool groundCollision(node_t *node, float dt)
void findPotentialEventBoxes(Actor *actor, CollisionBoxPtrVec &out_boxes)
eventsource_t & getEventSource(int pos)
Definition Collisions.h:225
bool isInside(Ogre::Vector3 pos, const Ogre::String &inst, const Ogre::String &box, float border=0)
@ CONSOLE_MSGTYPE_ACTOR
Parsing/spawn/simulation messages for actors.
Definition Console.h:63
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition Console.cpp:103
@ CONSOLE_SYSTEM_NOTICE
Definition Console.h:51
int di_idx_1
array location of wheel / axle 1
int di_idx_2
array location of wheel / axle 2
void CalcAxleTorque(DifferentialData &diff_data)
float di_delta_rotation
difference of rotational position between two wheels/axles... a kludge at best
float getCrankFactor()
Definition Engine.cpp:969
void setWheelSpin(float rpm)
Definition Engine.cpp:933
float getRPM()
Definition Engine.h:94
float getIdleRPM() const
('engoption' attr #8)
Definition Engine.h:78
void setPrime(bool p)
Definition Engine.cpp:923
void setHydroPump(float work)
Definition Engine.cpp:928
float getTorque()
Definition Engine.cpp:913
void UpdateEngine(float dt, int doUpdate)
Definition Engine.cpp:258
void stopEngine()
stall engine
Definition Engine.cpp:1059
bool isRunning()
Definition Engine.h:101
const TerrainPtr & GetTerrain()
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
void onPhysicsStep()
Definition Replay.cpp:183
bool isValid()
Definition Replay.h:55
void updateForces(int update)
Definition ScrewProp.cpp:54
void modulate(int actor_id, int mod, float value, int linkType=SL_DEFAULT, int linkItemID=-1)
void trigStart(int actor_id, int trig, int linkType=SL_DEFAULT, int linkItemID=-1)
void trigStop(int actor_id, int trig, int linkType=SL_DEFAULT, int linkItemID=-1)
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition Str.h:36
const char * ToCStr() const
Definition Str.h:46
Collisions * GetCollisions()
Definition Terrain.h:86
Wavefield * getWater()
Definition Terrain.h:87
float getGravity() const
Definition Terrain.h:101
bool tr_4wd_mode
Enables 4WD mode.
@ SS_TRIG_LINKED_COMMAND
@ SS_MOD_LINKED_COMMANDRATE
@ HYDRO_FLAG_DIR
Definition SimData.h:124
@ HYDRO_FLAG_SPEED
Definition SimData.h:123
@ HYDRO_FLAG_RUDDER
Definition SimData.h:126
@ HYDRO_FLAG_REV_ELEVATOR
Definition SimData.h:130
@ HYDRO_FLAG_AILERON
Definition SimData.h:125
@ HYDRO_FLAG_ELEVATOR
Definition SimData.h:127
@ HYDRO_FLAG_REV_AILERON
Definition SimData.h:128
@ HYDRO_FLAG_REV_RUDDER
Definition SimData.h:129
@ PRELOCK
prelocking, attraction forces in action
Definition SimData.h:76
@ LOCKED
lock locked.
Definition SimData.h:77
@ MACHINE
its a machine
Definition SimData.h:88
@ MSG_SIM_ACTOR_LINKING_REQUESTED
Payload = RoR::ActorLinkingRequest* (owner)
@ MSG_SIM_SCRIPT_CALLBACK_QUEUED
Payload = RoR::ScriptCallbackArgs* (owner)
@ MSG_SIM_MODIFY_ACTOR_REQUESTED
Payload = RoR::ActorModifyRequest* (owner)
static const float DEFAULT_SPRING
static const float DEFAULT_DRAG
static const float STAB_RATE
static const float DEFAULT_DAMP
static const float SUPPORT_BEAM_LIMIT_DEFAULT
static const float DEFAULT_WATERDRAG
static const float MIN_BEAM_LENGTH
minimum beam lenght is 10 centimeters
@ BEAM_HYDRO
Definition SimData.h:64
@ BEAM_NORMAL
Definition SimData.h:63
@ SHOCK3
shock3
Definition SimData.h:102
@ TRIGGER
trigger
Definition SimData.h:103
@ ROPE
Definition SimData.h:105
@ SUPPORTBEAM
Definition SimData.h:104
@ SHOCK2
shock2
Definition SimData.h:101
@ SHOCK1
either 'shock1' (with flag BEAM_HYDRO) or a wheel beam
Definition SimData.h:100
@ LOCAL_SIMULATED
simulated (local) actor
@ SHOCK_FLAG_LACTIVE
Definition SimData.h:190
@ SHOCK_FLAG_RACTIVE
Definition SimData.h:191
void TRIGGER_EVENT_ASYNC(scriptEvents type, int arg1, int arg2ex=0, int arg3ex=0, int arg4ex=0, std::string arg5ex="", std::string arg6ex="", std::string arg7ex="", std::string arg8ex="")
Asynchronously (via MSG_SIM_SCRIPT_EVENT_TRIGGERED) invoke script function eventCallbackEx(),...
SoundScriptManager * GetSoundScriptManager()
GameContext * GetGameContext()
CVar * io_analog_sensitivity
Console * GetConsole()
CVar * io_hydro_coupling
CVar * io_analog_smoothing
static const NodeNum_t NODENUM_INVALID
void Log(const char *msg)
The ultimate, application-wide logging function. Adds a line (any length) in 'RoR....
static const CineCameraID_t CINECAMERAID_INVALID
@ SE_EVENTBOX_EXIT
Actor or person left an eventbox; arguments: #1 type, #2 actorID (actor only), #3 unused,...
@ SE_EVENTBOX_ENTER
Actor or person entered an eventbox; arguments: #1 type, #2 actorID (actor only), #3 node ID (actor o...
@ EVENT_TRUCK_WHEELS
'truck_wheels' ~ Triggered only by wheel nodes of land vehicle (ActorType::TRUCK)
Definition SimData.h:52
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
@ FOOT_ONLY
= yes footbrake, no handbrake, no direction control – footbrake only, such as with the front wheels o...
@ FOOT_HAND_SKID_RIGHT
= yes footbrake, yes handbrake, yes direction control (braked when vehicle steers to the right)
@ FOOT_HAND_SKID_LEFT
= yes footbrake, yes handbrake, yes direction control (braked when vehicle steers to the left)
@ NONE
= no footbrake, no handbrake, no direction control – wheel is unbraked
Ogre::Vector3 accu_body_forces
Definition Actor.h:719
Estabilishing a physics linkage between 2 actors modifies a global linkage table and triggers immedia...
Definition SimData.h:909
ActorLinkingRequestType alr_type
Definition SimData.h:911
ActorInstanceID_t alr_actor_instance_id
Definition SimData.h:910
ActorInstanceID_t amr_actor
Definition SimData.h:875
Unified game event system - all requests and state changes are reported using a message.
Definition GameContext.h:52
Simulation: An edge in the softbody structure.
Definition SimData.h:305
node_t * p1
Definition SimData.h:309
float k
tensile spring
Definition SimData.h:311
bool bm_broken
Definition SimData.h:326
float debug_v
debug shock velocity
Definition SimData.h:340
float refL
reference length
Definition SimData.h:330
float minmaxposnegstress
Definition SimData.h:314
float plastic_coef
Definition SimData.h:319
float debug_d
debug shock damping
Definition SimData.h:339
shock_t * shock
Definition SimData.h:332
float longbound
Definition SimData.h:329
int detacher_group
Attribute: detacher group number (integer)
Definition SimData.h:320
float maxnegstress
Definition SimData.h:316
float shortbound
Definition SimData.h:328
float strength
Definition SimData.h:317
float stress
Definition SimData.h:318
float debug_k
debug shock spring_rate
Definition SimData.h:338
float d
damping factor
Definition SimData.h:312
float L
length
Definition SimData.h:313
bool bm_disabled
Definition SimData.h:325
node_t * p2
Definition SimData.h:310
float maxposstress
Definition SimData.h:315
CollisionEventFilter event_filter
Definition SimData.h:684
float cmb_engine_coupling
Attr from truckfile.
Definition SimData.h:539
uint16_t cmb_beam_index
Index to Actor::ar_beams array.
Definition SimData.h:538
bool cmb_is_contraction
Attribute defined at spawn.
Definition SimData.h:545
float cmb_center_length
Attr computed at spawn.
Definition SimData.h:540
bool cmb_needs_engine
Attribute defined in truckfile.
Definition SimData.h:547
bool cmb_is_force_restricted
Attribute defined in truckfile.
Definition SimData.h:546
float cmb_boundary_length
Attr; Maximum/minimum length proportional to orig. len.
Definition SimData.h:542
bool cmb_is_1press
Attribute defined in truckfile.
Definition SimData.h:550
float cmb_speed
Attr; Rate of contraction/extension.
Definition SimData.h:541
bool cmb_is_autocentering
Attribute defined in truckfile.
Definition SimData.h:548
bool cmb_is_1press_center
Attribute defined in truckfile.
Definition SimData.h:551
bool cmb_plays_sound
Attribute defined in truckfile.
Definition SimData.h:549
std::shared_ptr< commandbeam_state_t > cmb_state
Definition SimData.h:553
< Scripting
Definition Collisions.h:41
std::string es_box_name
Specified in ODEF file as "event".
Definition Collisions.h:43
std::string es_instance_name
Specified by user when calling "GameScript::spawnObject()".
Definition Collisions.h:42
< beams updating length based on simulation variables, generally known as actuators.
Definition SimData.h:571
BitMask_t hb_flags
Only for 'hydros'.
Definition SimData.h:575
float hb_speed
Rate of change.
Definition SimData.h:574
RoR::CmdKeyInertia hb_inertia
Definition SimData.h:578
uint16_t hb_beam_index
Index to Actor::ar_beams array.
Definition SimData.h:572
BitMask_t hb_anim_flags
Only for 'animators'.
Definition SimData.h:576
float hb_ref_length
Idle length in meters.
Definition SimData.h:573
Physics: A vertex in the softbody structure.
Definition SimData.h:260
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition SimData.h:267
Ogre::Real mass
Definition SimData.h:271
bool nd_has_ground_contact
Physics state.
Definition SimData.h:287
Ogre::Vector3 Velocity
Definition SimData.h:268
Ogre::Vector3 nd_last_collision_force
Physics state; last collision force.
Definition SimData.h:299
Ogre::Vector3 Forces
Definition SimData.h:269
bool nd_under_water
State; GFX hint.
Definition SimData.h:293
Ogre::Vector3 RelPosition
relative to the local physics origin (one origin per actor) (shaky)
Definition SimData.h:266
Ogre::Vector3 nd_last_collision_slip
Physics state; last collision slip vector.
Definition SimData.h:298
bool nd_cab_node
Attr; This node is part of collision triangle.
Definition SimData.h:282
ground_model_t * nd_last_collision_gm
Physics state; last collision 'ground model' (surface definition)
Definition SimData.h:300
Ogre::Real buoyancy
Definition SimData.h:272
bool nd_tyre_node
Attr; This node is part of a tyre (note some wheel types don't use rim nodes at all)
Definition SimData.h:284
NodeNum_t pos
This node's index in Actor::ar_nodes array.
Definition SimData.h:277
NodeNum_t axis2
Definition SimData.h:587
NodeNum_t axis1
rot axis
Definition SimData.h:586
NodeNum_t nodes2[4]
Definition SimData.h:585
NodeNum_t nodes1[4]
Definition SimData.h:584
float debug_rate
Definition SimData.h:593
float debug_aerror
Definition SimData.h:594
float sbd_damp
set beam default for damping
Definition SimData.h:375
float sbd_spring
set beam default for spring
Definition SimData.h:374
Ogre::Real wh_tc_coef
Sim state; Current traction control modulation ratio.
Definition SimData.h:416
Ogre::Real wh_speed
Current wheel speed in m/s.
Definition SimData.h:413
Ogre::Vector3 debug_vel
Definition SimData.h:441
Ogre::Real wh_last_torque
Last internal forces (engine / brakes / diffs)
Definition SimData.h:419
node_t * wh_axis_node_0
Definition SimData.h:408
node_t * wh_arm_node
Definition SimData.h:406
int wh_num_nodes
Definition SimData.h:401
float debug_rpm
Definition SimData.h:439
Ogre::Real wh_last_retorque
Last external forces (friction, ...)
Definition SimData.h:420
node_t * wh_nodes[50]
Definition SimData.h:402
node_t * wh_axis_node_1
Definition SimData.h:409
Ogre::Vector3 debug_force
Definition SimData.h:443
float wh_net_rp
Definition SimData.h:421
float debug_torque
Definition SimData.h:440
node_t * wh_near_attach_node
Definition SimData.h:407
Ogre::Real wh_mass
Total rotational mass of the wheel.
Definition SimData.h:417
Ogre::Real wh_avg_speed
Internal physics state; Do not read from this.
Definition SimData.h:414
Ogre::Real wh_radius
Definition SimData.h:411
Ogre::Vector3 debug_scaled_cforce
Definition SimData.h:444
bool wh_is_detached
Definition SimData.h:423
Ogre::Vector3 debug_slip
Definition SimData.h:442
Ogre::Real wh_alb_coef
Sim state; Current anti-lock brake modulation ratio.
Definition SimData.h:415
Ogre::Real wh_torque
Internal physics state; Do not read from this.
Definition SimData.h:418
FlexAirfoil * fa
Definition SimData.h:521