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:
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
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue