From f4d8382c86ba760c5fccf7096ca50d6572b208bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 22 Jun 2022 06:37:45 +0100 Subject: 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 --- source/blender/blenkernel/intern/rigidbody.c | 124 +++++++++++++++------------ 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 60cc4ce83af..2f20b1e661b 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -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); -- cgit v1.2.3