Merge branch 'physics-tweaks'

This commit is contained in:
hiker 2018-07-27 21:24:28 +10:00
commit 4e35fe8fb8
11 changed files with 241 additions and 64 deletions

View File

@ -101,12 +101,35 @@
any track/library pbject.
default-moveable-friction: Default friction to be used for any moveable,
e.g. karts, bowling balls, ...
solver-iteation: Number of solver iterations. A lower number reduces
the quality, but can reduce bouncing effect.
solver-split-impulse:: by default bullet solves for velocity and
position at the same time, which can introduce bounce. Setting
this to 1 can reduce bounce.
solver-split-impulse-threshold: Penetration threshold for using split
impulse (ignored if solver-split-impulse is false).
solver-mode: Bullet's solver mode is a bit mask, which can be modified.
This entry contains a space-separated list of mode-names to either
set or unset in this bit mask. Any name starting with a '-' indicate
that the bit is to be set to 0, otherwise the bit will be set.
This field takes two
values: the first value is 'and'ed with bullet's default values
(i.e. it can be used to unset bullet defaults), the second value
is 'or'ed (i.e. is used to set a bit). A value of -1 for 'and'
means to keep all bits. The valid names are listed in stk_config.cpp
and correspond to the definitions in btContactSolverInfo.h, e.g.:
'randomized_order' corresponds to the bit SOLVER_RANDMIZE_ORDER.
-->
<physics smooth-normals="true"
smooth-angle-limit="0.65"
fps="120"
default-track-friction="0.5"
default-moveable-friction="0.5" />
default-moveable-friction="0.5"
solver-iterations="4"
solver-split-impulse="true"
solver-split-impulse-threshold="-0.00001"
solver-mode=""/>
<!-- The title music. -->
<music title="main_theme.music"/>
@ -412,6 +435,11 @@
period, which results in less abrupt changes. If set to 0,
the impulse is only applied once.
resitution: restitution value to be used for the kart rigid bodies.
The restitution used depends on the speed to avoid physics issues
(a collision with high speed and high restitution will push the
kart high up into the air). The values specified are
speed:restitution pairs, the actual restitution will be
interpolated based on the points specified here.
bevel-factor: for each point of the chassis collision box one
additional point is added, resulting in a bevelled box shape.
The original Z coordinate of the chassis is multiplied by
@ -431,7 +459,7 @@
behaviour of the karts. -->
<collision impulse-type="normal"
impulse="3000" impulse-time="0.1" terrain-impulse="160"
restitution="1.0" bevel-factor="0.5 0.0 0.3"
restitution="0:1.0 5:1.0 20:0.2" bevel-factor="0.5 0.0 0.3"
physical-wheel-position="0" />
<!-- Skidding: increase: multiplicative increase of skidding factor in each frame.
@ -503,7 +531,7 @@
Use a slow but high quality colour compressor.
kColourClusterFit = (32),
Use a fast but low quality colour compressor.
kColourRangeFit = (64),
kColourRangeFit = (64),
Use a very slow but very high quality colour compressor.
kColourIterativeClusterFit = (256),
STK default the low quality.

55
src/config/stk_config.cpp Normal file → Executable file
View File

@ -154,6 +154,9 @@ void STKConfig::load(const std::string &filename)
CHECK_NEG(m_network_state_frequeny, "network state-frequency" );
CHECK_NEG(m_network_steering_reduction,"network steering-reduction" );
CHECK_NEG(m_default_moveable_friction, "physics default-moveable-friction");
CHECK_NEG(m_solver_iterations, "physics: solver-iterations" );
CHECK_NEG(m_network_state_frequeny, "network solver-state-frequency" );
CHECK_NEG(m_solver_split_impulse_thresh,"physics: solver-split-impulse-threshold");
// Square distance to make distance checks cheaper (no sqrt)
m_default_kart_properties->checkAllSet(filename);
@ -166,11 +169,11 @@ void STKConfig::load(const std::string &filename)
*/
void STKConfig::init_defaults()
{
m_bomb_time = m_bomb_time_increase =
m_explosion_impulse_objects = m_music_credit_time =
m_delay_finish_time = m_skid_fadeout_time =
m_near_ground =
m_smooth_angle_limit = m_default_track_friction =
m_bomb_time = m_bomb_time_increase =
m_explosion_impulse_objects = m_music_credit_time =
m_delay_finish_time = m_skid_fadeout_time =
m_near_ground = m_solver_split_impulse_thresh =
m_smooth_angle_limit = m_default_track_friction =
m_default_moveable_friction = UNDEFINED;
m_item_switch_ticks = -100;
m_penalty_ticks = -100;
@ -196,8 +199,12 @@ void STKConfig::init_defaults()
m_donate_url = "";
m_password_reset_url = "";
m_network_state_frequeny = -100;
m_network_steering_reduction = 1.0f;
m_solver_iterations = -100;
m_solver_set_flags = 0;
m_solver_reset_flags = 0;
m_network_steering_reduction = -100;
m_title_music = NULL;
m_solver_split_impulse = false;
m_smooth_normals = false;
m_same_powerup_mode = POWERUP_MODE_ONLY_IF_SAME;
m_ai_acceleration = 1.0f;
@ -282,6 +289,42 @@ void STKConfig::getAllData(const XMLNode * root)
physics_node->get("default-moveable-friction",
&m_default_moveable_friction);
physics_node->get("fps", &m_physics_fps );
physics_node->get("solver-iterations", &m_solver_iterations );
physics_node->get("solver-split-impulse", &m_solver_split_impulse );
physics_node->get("solver-split-impulse-threshold",
&m_solver_split_impulse_thresh);
std::vector<std::string> solver_modes;
physics_node->get("solver-mode", &solver_modes );
m_solver_set_flags=0, m_solver_reset_flags = 0;
int *p;
for (auto mode : solver_modes)
{
std::string s = mode;
p = &m_solver_set_flags;
if (s[0] == '-')
{
s.erase(s.begin());
p = &m_solver_reset_flags;
}
s = StringUtils::toLowerCase(s);
if (s == "randmize_order" ) *p |= 1;
else if (s == "friction_separate" ) *p |= 2;
else if (s == "use_warmstarting" ) *p |= 4;
else if (s == "use_friction_warmstarting" ) *p |= 8;
else if (s == "use_2_friction_directions" ) *p |= 16;
else if (s == "enable_friction_direction_caching" ) *p |= 32;
else if (s == "disable_velocity_dependent_friction_direction") *p |= 64;
else if (s == "cache_friendly" ) *p |= 128;
else if (s == "simd" ) *p |= 256;
else if (s == "cuda" ) *p |= 512;
else
{
Log::fatal("STK-Config",
"Unknown option '%s' for solver-mode - ignored.",
s.c_str());
}
} // for mode in solver_modes
}
if (const XMLNode *startup_node= root->getNode("startup"))

View File

@ -105,6 +105,19 @@ public:
/** Default friction to be used for any moveable, e.g. karts, balls. */
float m_default_moveable_friction;
/** Number of solver iterations. */
int m_solver_iterations;
/** If position and velocity constraints are solved separately. */
bool m_solver_split_impulse;
/** Threshold when to use the split impulse approach. */
float m_solver_split_impulse_thresh;
/** Bit flags to modify the solver mode. Bits set in set_flags are
* added to the solver mode, bits set in reset_flags are removed. */
int m_solver_set_flags, m_solver_reset_flags;
int m_max_skidmarks; /**<Maximum number of skid marks/kart. */
float m_skid_fadeout_time; /**<Time till skidmarks fade away. */
float m_near_ground; /**<Determines when a kart is not near

View File

@ -723,7 +723,7 @@ void Kart::createPhysics()
btTransform trans;
trans.setIdentity();
createBody(mass, trans, &m_kart_chassis,
m_kart_properties->getRestitution());
m_kart_properties->getRestitution(0.0f));
std::vector<float> ang_fact = m_kart_properties->getStabilityAngularFactor();
// The angular factor (with X and Z values <1) helps to keep the kart
// upright, especially in case of a collision.
@ -1273,6 +1273,13 @@ void Kart::eliminate()
*/
void Kart::update(int ticks)
{
// Make the restitution depend on speed: this avoids collision issues,
// otherwise a collision with high speed can see a kart being push
// high up in the air (and out of control). So for higher speed we
// reduce the restitution, meaning the karts will get less of a push
// based on the collision speed.
m_body->setRestitution(m_kart_properties->getRestitution(fabsf(m_speed)));
// Reset any instand speed increase in the bullet kart
m_vehicle->setMinSpeed(0);

View File

@ -82,7 +82,7 @@ KartProperties::KartProperties(const std::string &filename)
// Set all other values to undefined, so that it can later be tested
// if everything is defined properly.
m_wheel_base = m_friction_slip = m_collision_terrain_impulse =
m_collision_impulse = m_restitution = m_collision_impulse_time =
m_collision_impulse = m_collision_impulse_time =
m_max_lean = m_lean_speed = m_physical_wheel_position = UNDEFINED;
m_terrain_impulse_type = IMPULSE_NONE;
@ -499,7 +499,7 @@ void KartProperties::getAllData(const XMLNode * root)
void KartProperties::checkAllSet(const std::string &filename)
{
#define CHECK_NEG( a,strA) if(a<=UNDEFINED) { \
Log::fatal("[KartProperties]", \
Log::fatal("KartProperties", \
"Missing default value for '%s' in '%s'.", \
strA,filename.c_str()); \
}
@ -508,9 +508,11 @@ void KartProperties::checkAllSet(const std::string &filename)
CHECK_NEG(m_collision_terrain_impulse, "collision terrain-impulse" );
CHECK_NEG(m_collision_impulse, "collision impulse" );
CHECK_NEG(m_collision_impulse_time, "collision impulse-time" );
CHECK_NEG(m_restitution, "collision restitution" );
CHECK_NEG(m_physical_wheel_position, "collision physical-wheel-position");
if(m_restitution.size()<1)
Log::fatal("KartProperties", "Missing restitution value.");
for(unsigned int i=0; i<RaceManager::DIFFICULTY_COUNT; i++)
m_ai_properties[i]->checkAllSet(filename);
} // checkAllSet

View File

@ -201,9 +201,8 @@ private:
/** How long the collision impulse should be applied. */
float m_collision_impulse_time;
/** The restitution factor to be used in collsions for this kart. */
float m_restitution;
/** Restitution depending on speed. */
InterpolationArray m_restitution;
void load (const std::string &filename,
const std::string &node);
@ -340,7 +339,7 @@ public:
// ------------------------------------------------------------------------
/** Returns the restitution factor for this kart. */
float getRestitution () const { return m_restitution; }
float getRestitution(float speed) const { return m_restitution.get(speed);}
// ------------------------------------------------------------------------
/** Returns a pointer to the AI properties. */

View File

@ -123,6 +123,7 @@ BareNetworkString* KartRewinder::saveState(std::vector<std::string>* ru)
buffer->addFloat(m_vehicle->getMinSpeed());
buffer->addFloat(m_vehicle->getTimedRotationTime());
buffer->add(m_vehicle->getTimedRotation());
buffer->addUInt8(m_vehicle->getCushioningDisableTime());
// 2) Steering and other player controls
// -------------------------------------
@ -189,6 +190,8 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count)
float time_rot = buffer->getFloat();
// Set timed rotation divides by time_rot
m_vehicle->setTimedRotation(time_rot, time_rot*buffer->getVec3());
m_vehicle->setCushioningDisableTime(buffer->getUInt8());
// For the raycast to determine the current material under the kart
// the m_hardPointWS of the wheels is used. So after a rewind we
// must restore the m_hardPointWS to the new values, otherwise they

View File

@ -321,7 +321,6 @@ void MainLoop::run()
left_over_time += getLimitedDt();
int num_steps = stk_config->time2Ticks(left_over_time);
float dt = stk_config->ticks2Time(1);
left_over_time -= num_steps * dt ;
@ -371,8 +370,6 @@ void MainLoop::run()
input_manager->update(frame_duration);
GUIEngine::update(frame_duration);
PROFILER_POP_CPU_MARKER();
if (World::getWorld() && history->replayHistory())
history->updateReplay(World::getWorld()->getTicksSinceStart());
PROFILER_PUSH_CPU_MARKER("Music", 0x7F, 0x00, 0x00);
SFXManager::get()->update();
PROFILER_POP_CPU_MARKER();
@ -408,8 +405,11 @@ void MainLoop::run()
}
m_ticks_adjustment.unlock();
for (int i = 0; i < num_steps; i++)
for(int i=0; i<num_steps; i++)
{
if (World::getWorld() && history->replayHistory())
history->updateReplay(World::getWorld()->getTicksSinceStart());
PROFILER_PUSH_CPU_MARKER("Protocol manager update",
0x7F, 0x00, 0x7F);
if (auto pm = ProtocolManager::lock())

View File

@ -26,6 +26,10 @@
#include "karts/kart.hpp"
#include "karts/kart_model.hpp"
#include "karts/kart_properties.hpp"
#undef DEBUG_CUSHIONING
#ifdef DEBUG_CUSHIONING
#include "modes/world.hpp"
#endif
#include "physics/triangle_mesh.hpp"
#include "tracks/terrain_info.hpp"
#include "tracks/track.hpp"
@ -125,6 +129,7 @@ void btKart::reset()
m_time_additional_rotation = 0;
m_max_speed = -1.0f;
m_min_speed = 0.0f;
m_cushioning_disable_time = 0;
// Set the brakes so that karts don't slide downhill
setAllBrakes(5.0f);
@ -434,48 +439,7 @@ void btKart::updateAllWheelPositions()
void btKart::updateVehicle( btScalar step )
{
updateAllWheelTransformsWS();
// Test if the kart is falling so fast
// that the chassis might hit the track
// ------------------------------------
bool needs_cushioning_test = false;
for(int i=0; i<m_wheelInfo.size(); i++)
{
btWheelInfo &wheel = m_wheelInfo[i];
if(!wheel.m_was_on_ground && wheel.m_raycastInfo.m_isInContact)
{
needs_cushioning_test = true;
break;
}
}
if(needs_cushioning_test)
{
const btVector3 &v = m_chassisBody->getLinearVelocity();
btVector3 down(0, 1, 0);
btVector3 v_down = (v * down) * down;
// Estimate what kind of downward speed can be compensated by the
// suspension. Atm the parameters are set that the suspension is
// actually capped at max suspension force, so the maximum
// speed that can be caught by the suspension without the chassis
// hitting the ground can be based on that. Note that there are
// 4 suspensions, all adding together.
btScalar max_compensate_speed = m_wheelInfo[0].m_maxSuspensionForce
* m_chassisBody->getInvMass()
* step * 4;
// If the downward speed is too fast to be caught by the suspension,
// slow down the falling speed by applying an appropriately impulse:
if(-v_down.getY() > max_compensate_speed)
{
btVector3 impulse = down * (-v_down.getY() - max_compensate_speed)
/ m_chassisBody->getInvMass()*0.5f;
//float v_old = m_chassisBody->getLinearVelocity().getY();
//float x = m_wheelInfo[0].m_raycastInfo.m_isInContact ? m_wheelInfo[0].m_raycastInfo.m_contactPointWS.getY() : -100;
m_chassisBody->applyCentralImpulse(impulse);
//Log::verbose("physics", "Cushioning %f from %f m/s to %f m/s wheel %f kart %f", impulse.getY(),
// v_old, m_chassisBody->getLinearVelocity().getY(), x,
// m_chassisBody->getWorldTransform().getOrigin().getY()
// );
}
}
for(int i=0; i<m_wheelInfo.size(); i++)
m_wheelInfo[i].m_was_on_ground = m_wheelInfo[i].m_raycastInfo.m_isInContact;
@ -530,6 +494,93 @@ void btKart::updateVehicle( btScalar step )
}
// Test if the kart is falling so fast
// that the chassis might hit the track
// ------------------------------------
int wheel_index = 0;
float min_susp = m_wheelInfo[0].m_raycastInfo.m_suspensionLength;
for (int i = 1; i<m_wheelInfo.size(); i++)
{
btWheelInfo &wheel = m_wheelInfo[i];
if (wheel.m_raycastInfo.m_suspensionLength < min_susp)
{
min_susp = wheel.m_raycastInfo.m_suspensionLength;
wheel_index = i;
}
}
// Cushioning test: if the kart is falling fast, the suspension might
// not be strong enough to prevent the chassis from hitting the ground.
// Try to detect this upcoming crash, and apply an upward impulse if
// necessary that will slow down the falling speed.
if(m_cushioning_disable_time>0) m_cushioning_disable_time --;
bool needed_cushioning = false;
btVector3 v =
m_chassisBody->getVelocityInLocalPoint(m_wheelInfo[wheel_index]
.m_chassisConnectionPointCS);
btVector3 down = -m_chassisBody->getGravity();
down.normalize();
btVector3 v_down = (v * down) * down;
btScalar offset=0.1f;
#ifdef DEBUG_CUSHIONING
Log::verbose("physics",
"World %d wheel %d lsuspl %f vdown %f overall speed %f lenght %f",
World::getWorld()->getTimeTicks(),
wheel_index,
m_wheelInfo[wheel_index].m_raycastInfo.m_suspensionLength,
-v_down.getY(),
-v_down.getY() + 9.8*step,
step * (-v_down.getY() + 9.8*step)+offset);
#endif
// If the kart is falling, estimate the distance the kart will fall
// in the next time step: the speed gets increased by the gravity*dt.
// This approximation is still not good enough (either because of
// kart rotation that can be changed, or perhaps because of the
// collision threshold used by bullet) - i.e. it would sometimes not
// predict the upcoming collision correcty - so we add an offset
// to the predicted kart movement, which was found experimentally:
btScalar gravity = m_chassisBody->getGravity().length();
if (v_down.getY()<0 && m_cushioning_disable_time==0 &&
m_wheelInfo[wheel_index].m_raycastInfo.m_suspensionLength
< step * (-v_down.getY()+gravity*step)+offset)
{
// Disable more cushioning for 1 second. This avoids the problem
// of hovering: a kart gets cushioned on a down-sloping area, still
// moves forwards, gets cushioned again etc. --> kart is hovering
// and not controllable.
m_cushioning_disable_time = 120;
needed_cushioning = true;
btVector3 impulse = down * (-v_down.getY() + gravity*step)
/ m_chassisBody->getInvMass();
#ifdef DEBUG_CUSHIONING
float v_old = m_chassisBody->getLinearVelocity().getY();
#endif
m_chassisBody->applyCentralImpulse(impulse);
#ifdef DEBUG_CUSHIONING
Log::verbose("physics",
"World %d Cushioning imp %f vdown %f from %f m/s to %f m/s "
"contact %f kart %f susp %f relspeed %f",
World::getWorld()->getTimeTicks(),
impulse.getY(),
-v_down.getY(),
v_old,
m_chassisBody->getLinearVelocity().getY(),
m_wheelInfo[wheel_index].m_raycastInfo.m_isInContact ?
m_wheelInfo[wheel_index].m_raycastInfo.m_contactPointWS.getY()
: -100,
m_chassisBody->getWorldTransform().getOrigin().getY(),
m_wheelInfo[wheel_index].m_raycastInfo.m_suspensionLength,
m_chassisBody->getVelocityInLocalPoint(m_wheelInfo[wheel_index]
.m_chassisConnectionPointCS)
);
#endif
}
// Update friction (i.e. forward force)
// ------------------------------------
updateFriction( step);
@ -537,7 +588,7 @@ void btKart::updateVehicle( btScalar step )
// If configured, add a force to keep karts on the track
// -----------------------------------------------------
float dif = m_kart->getKartProperties()->getStabilityDownwardImpulseFactor();
if(dif!=0 && m_num_wheels_on_ground==4)
if(dif!=0 && m_num_wheels_on_ground==4 && !needed_cushioning)
{
float f = -fabsf(m_kart->getSpeed()) * dif;
btVector3 downwards_impulse = m_chassisBody->getWorldTransform().getBasis()
@ -648,6 +699,10 @@ void btKart::updateSuspension(btScalar deltaTime)
// a force pulling the axis down (towards the ground). Note that it
// is already guaranteed that either both or no wheels on one axis
// are on the ground, so we have to test only one of the wheels
// In hindsight it turns out that this code basically adds
// additional gravity when a kart is flying. So if this code would
// be removed some jumps (esp. Enterprise) do not work as expected
// anymore.
wheel_info.m_wheelsSuspensionForce =
-m_kart->getKartProperties()->getStabilityTrackConnectionAccel()
* chassisMass;

View File

@ -93,6 +93,9 @@ private:
/** Number of wheels that touch the ground. */
int m_num_wheels_on_ground;
/** Number of time steps during which cushioning is disabled. */
unsigned int m_cushioning_disable_time;
/** Index of the right axis. */
int m_indexRightAxis;
/** Index of the up axis. */
@ -245,6 +248,19 @@ public:
// ------------------------------------------------------------------------
float getTimedRotationTime() const { return m_time_additional_rotation; }
// ------------------------------------------------------------------------
/** Returns the time cushioning is disabled. Used for networking state
* saving. */
unsigned int getCushioningDisableTime() const
{
return m_cushioning_disable_time;
} // getCushioningDisableTime
// ------------------------------------------------------------------------
/** Sets the cushioning disable time. Used for networking state saving. */
void setCushioningDisableTime(unsigned int cdt)
{
m_cushioning_disable_time = cdt;
} // setCushioningDisableTime
// ------------------------------------------------------------------------
/** Sets the maximum speed for this kart. */
void setMaxSpeed(float new_max_speed)
{

View File

@ -74,6 +74,17 @@ void Physics::init(const Vec3 &world_min, const Vec3 &world_max)
0.0f));
m_debug_drawer = new IrrDebugDrawer();
m_dynamics_world->setDebugDrawer(m_debug_drawer);
// Get the solver settings from the config file
btContactSolverInfo& info = m_dynamics_world->getSolverInfo();
info.m_numIterations = stk_config->m_solver_iterations;
info.m_splitImpulse = stk_config->m_solver_split_impulse;
info.m_splitImpulsePenetrationThreshold =
stk_config->m_solver_split_impulse_thresh;
// Modify the mode according to the bits of the solver mode:
info.m_solverMode = (info.m_solverMode & (~stk_config->m_solver_reset_flags))
| stk_config->m_solver_set_flags;
} // init
//-----------------------------------------------------------------------------