Rigid body physics: Move effector force update into substep loop.

The substep loop for rigid bodies causes unequal effects of force fields depedending on the substep setting, larger
substep counts cause a diminishing effect of force fields.
This is because the force to apply on a body is reset in Bullet after each step and needs to be recomputed. Without this
the body will just coast with constant velocity after the first substep. Since the per-step impulse with larger substep
counts is smaller, the effect is that more substeps cause a smaller total impulse.

The fix is to move external force calculation into the substep loop and update forces for each substep.

Note that this may be considered a breaking change, because the breaking commit rB1aa54d4921c2 has been in master for
a long time and after this fix force fields will generally have a much larger effect on rigid bodies (10x for the
default setting of 10 substeps).

Differential Revision: https://developer.blender.org/D15173
This commit is contained in:
Lukas Tönne 2022-06-22 06:37:45 +01:00
parent 75f0aaab3d
commit f4d8382c86
Notes: blender-bot 2023-02-14 04:24:05 +01:00
Referenced by issue #102988, Force fields no longer work correctly with ridged bodies in 3.3/3.4
Referenced by issue #98272, Regression: Physics force field strength affect varies inversely proportion to physics substeps per frame
1 changed files with 70 additions and 54 deletions

View File

@ -1712,54 +1712,6 @@ static void rigidbody_update_sim_ob(
RB_body_set_mass(rbo->shared->physics_object, 0.0f);
}
/* update influence of effectors - but don't do it on an effector */
/* only dynamic bodies need effector update */
else if (rbo->type == RBO_TYPE_ACTIVE &&
((ob->pd == NULL) || (ob->pd->forcefield == PFIELD_NULL))) {
EffectorWeights *effector_weights = rbw->effector_weights;
EffectedPoint epoint;
ListBase *effectors;
/* get effectors present in the group specified by effector_weights */
effectors = BKE_effectors_create(depsgraph, ob, NULL, effector_weights, false);
if (effectors) {
float eff_force[3] = {0.0f, 0.0f, 0.0f};
float eff_loc[3], eff_vel[3];
/* create dummy 'point' which represents last known position of object as result of sim */
/* XXX: this can create some inaccuracies with sim position,
* but is probably better than using un-simulated values? */
RB_body_get_position(rbo->shared->physics_object, eff_loc);
RB_body_get_linear_velocity(rbo->shared->physics_object, eff_vel);
pd_point_from_loc(scene, eff_loc, eff_vel, 0, &epoint);
/* Calculate net force of effectors, and apply to sim object:
* - we use 'central force' since apply force requires a "relative position"
* which we don't have... */
BKE_effectors_apply(effectors, NULL, effector_weights, &epoint, eff_force, NULL, NULL);
if (G.f & G_DEBUG) {
printf("\tapplying force (%f,%f,%f) to '%s'\n",
eff_force[0],
eff_force[1],
eff_force[2],
ob->id.name + 2);
}
/* activate object in case it is deactivated */
if (!is_zero_v3(eff_force)) {
RB_body_activate(rbo->shared->physics_object);
}
RB_body_apply_central_force(rbo->shared->physics_object, eff_force);
}
else if (G.f & G_DEBUG) {
printf("\tno forces to apply to '%s'\n", ob->id.name + 2);
}
/* cleanup */
BKE_effectors_free(effectors);
}
/* NOTE: passive objects don't need to be updated since they don't move */
/* NOTE: no other settings need to be explicitly updated here,
* since RNA setters take care of the rest :)
*/
@ -1986,6 +1938,69 @@ static void rigidbody_update_kinematic_obj_substep(ListBase *substep_targets, fl
}
}
static void rigidbody_update_external_forces(Depsgraph *depsgraph,
Scene *scene,
RigidBodyWorld *rbw)
{
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, ob) {
/* only update if rigid body exists */
RigidBodyOb *rbo = ob->rigidbody_object;
if (ob->type != OB_MESH || rbo->shared->physics_object == NULL) {
continue;
}
/* update influence of effectors - but don't do it on an effector */
/* only dynamic bodies need effector update */
if (rbo->type == RBO_TYPE_ACTIVE &&
((ob->pd == NULL) || (ob->pd->forcefield == PFIELD_NULL))) {
EffectorWeights *effector_weights = rbw->effector_weights;
EffectedPoint epoint;
ListBase *effectors;
/* get effectors present in the group specified by effector_weights */
effectors = BKE_effectors_create(depsgraph, ob, NULL, effector_weights, false);
if (effectors) {
float eff_force[3] = {0.0f, 0.0f, 0.0f};
float eff_loc[3], eff_vel[3];
/* create dummy 'point' which represents last known position of object as result of sim
*/
/* XXX: this can create some inaccuracies with sim position,
* but is probably better than using un-simulated values? */
RB_body_get_position(rbo->shared->physics_object, eff_loc);
RB_body_get_linear_velocity(rbo->shared->physics_object, eff_vel);
pd_point_from_loc(scene, eff_loc, eff_vel, 0, &epoint);
/* Calculate net force of effectors, and apply to sim object:
* - we use 'central force' since apply force requires a "relative position"
* which we don't have... */
BKE_effectors_apply(effectors, NULL, effector_weights, &epoint, eff_force, NULL, NULL);
if (G.f & G_DEBUG) {
printf("\tapplying force (%f,%f,%f) to '%s'\n",
eff_force[0],
eff_force[1],
eff_force[2],
ob->id.name + 2);
}
/* activate object in case it is deactivated */
if (!is_zero_v3(eff_force)) {
RB_body_activate(rbo->shared->physics_object);
}
RB_body_apply_central_force(rbo->shared->physics_object, eff_force);
}
else if (G.f & G_DEBUG) {
printf("\tno forces to apply to '%s'\n", ob->id.name + 2);
}
/* cleanup */
BKE_effectors_free(effectors);
}
/* NOTE: passive objects don't need to be updated since they don't move */
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
static void rigidbody_free_substep_data(ListBase *substep_targets)
{
LISTBASE_FOREACH (LinkData *, link, substep_targets) {
@ -2220,26 +2235,27 @@ void BKE_rigidbody_do_simulation(Depsgraph *depsgraph, Scene *scene, float ctime
BKE_ptcache_write(&pid, startframe);
}
/* update and validate simulation */
rigidbody_update_simulation(depsgraph, scene, rbw, false);
const float frame_diff = ctime - rbw->ltime;
/* calculate how much time elapsed since last step in seconds */
const float timestep = 1.0f / (float)FPS * frame_diff * rbw->time_scale;
const float substep = timestep / rbw->substeps_per_frame;
ListBase substep_targets = rigidbody_create_substep_data(rbw);
ListBase kinematic_substep_targets = rigidbody_create_substep_data(rbw);
const float interp_step = 1.0f / rbw->substeps_per_frame;
float cur_interp_val = interp_step;
/* update and validate simulation */
rigidbody_update_simulation(depsgraph, scene, rbw, false);
for (int i = 0; i < rbw->substeps_per_frame; i++) {
rigidbody_update_kinematic_obj_substep(&substep_targets, cur_interp_val);
rigidbody_update_external_forces(depsgraph, scene, rbw);
rigidbody_update_kinematic_obj_substep(&kinematic_substep_targets, cur_interp_val);
RB_dworld_step_simulation(rbw->shared->physics_world, substep, 0, substep);
cur_interp_val += interp_step;
}
rigidbody_free_substep_data(&substep_targets);
rigidbody_free_substep_data(&kinematic_substep_targets);
rigidbody_update_simulation_post_step(depsgraph, rbw);