Cloth: allow forces to act parallel to cloth.

Currently all force effectors can only act on cloth when the force is
perpendicular to the surface. This makes sense for wind, but not for
other forces; and the user may want even wind to have some friction.

This changes effector code to output two force vectors - although
you of course can pass the same pointer for both. The force is split
between the two outputs based on a new per-effector setting.

Differential Revision: https://developer.blender.org/D8017
This commit is contained in:
Alexander Gavrilov 2020-06-12 21:33:38 +03:00
parent f382109f38
commit 9e70129952
16 changed files with 155 additions and 36 deletions

View File

@ -330,6 +330,7 @@ def basic_force_field_settings_ui(self, field):
col.prop(field, "use_gravity_falloff", text="Gravitation")
col.prop(field, "use_absorption")
col.prop(field, "wind_factor")
def basic_force_field_falloff_ui(self, field):

View File

@ -130,6 +130,7 @@ void BKE_effectors_apply(struct ListBase *effectors,
struct EffectorWeights *weights,
struct EffectedPoint *point,
float *force,
float *wind_force,
float *impulse);
void BKE_effectors_free(struct ListBase *lb);

View File

@ -1388,6 +1388,7 @@ void boid_body(BoidBrainData *bbd, ParticleData *pa)
bbd->part->effector_weights,
&epoint,
force,
NULL,
NULL);
if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) {

View File

@ -5112,7 +5112,7 @@ static void dynamic_paint_prepare_effect_cb(void *__restrict userdata,
EffectedPoint epoint;
pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint);
epoint.vel_to_sec = 1.0f;
BKE_effectors_apply(effectors, NULL, surface->effector_weights, &epoint, forc, NULL);
BKE_effectors_apply(effectors, NULL, surface->effector_weights, &epoint, forc, NULL, NULL);
}
/* if global gravity is enabled, add it too */

View File

@ -108,7 +108,8 @@ PartDeflect *BKE_partdeflect_new(int type)
break;
case PFIELD_WIND:
pd->shape = PFIELD_SHAPE_PLANE;
pd->f_flow = 1.0f; /* realistic wind behavior */
pd->f_flow = 1.0f; /* realistic wind behavior */
pd->f_wind_factor = 1.0f; /* only act perpendicularly to a surface */
break;
case PFIELD_TEXTURE:
pd->f_size = 1.0f;
@ -1072,7 +1073,8 @@ static void do_physical_effector(EffectorCache *eff,
* scene = scene where it runs in, for time and stuff
* lb = listbase with objects that take part in effecting
* opco = global coord, as input
* force = force accumulator
* force = accumulator for force
* wind_force = accumulator for force only acting perpendicular to a surface
* speed = actual current speed which can be altered
* cur_time = "external" time in frames, is constant for static particles
* loc_time = "local" time in frames, range <0-1> for the lifetime of particle
@ -1085,6 +1087,7 @@ void BKE_effectors_apply(ListBase *effectors,
EffectorWeights *weights,
EffectedPoint *point,
float *force,
float *wind_force,
float *impulse)
{
/*
@ -1120,22 +1123,27 @@ void BKE_effectors_apply(ListBase *effectors,
if (efd.falloff > 0.0f) {
efd.falloff *= eff_calc_visibility(colliders, eff, &efd, point);
}
if (efd.falloff <= 0.0f) {
/* don't do anything */
}
else if (eff->pd->forcefield == PFIELD_TEXTURE) {
do_texture_effector(eff, &efd, point, force);
}
else {
float temp1[3] = {0, 0, 0}, temp2[3];
copy_v3_v3(temp1, force);
if (efd.falloff > 0.0f) {
float out_force[3] = {0, 0, 0};
do_physical_effector(eff, &efd, point, force);
if (eff->pd->forcefield == PFIELD_TEXTURE) {
do_texture_effector(eff, &efd, point, out_force);
}
else {
do_physical_effector(eff, &efd, point, out_force);
/* for softbody backward compatibility */
if (point->flag & PE_WIND_AS_SPEED && impulse) {
sub_v3_v3v3(temp2, force, temp1);
sub_v3_v3v3(impulse, impulse, temp2);
/* for softbody backward compatibility */
if (point->flag & PE_WIND_AS_SPEED && impulse) {
sub_v3_v3v3(impulse, impulse, out_force);
}
}
if (wind_force) {
madd_v3_v3fl(force, out_force, 1.0f - eff->pd->f_wind_factor);
madd_v3_v3fl(wind_force, out_force, eff->pd->f_wind_factor);
}
else {
add_v3_v3(force, out_force);
}
}
}

View File

@ -3175,7 +3175,7 @@ static void update_effectors_task_cb(void *__restrict userdata,
/* do effectors */
pd_point_from_loc(data->scene, voxel_center, vel, index, &epoint);
BKE_effectors_apply(data->effectors, NULL, mds->effector_weights, &epoint, retvel, NULL);
BKE_effectors_apply(data->effectors, NULL, mds->effector_weights, &epoint, retvel, NULL, NULL);
/* convert retvel to local space */
mag = len_v3(retvel);

View File

@ -2262,6 +2262,7 @@ static void do_path_effectors(ParticleSimulationData *sim,
sim->psys->part->effector_weights,
&epoint,
force,
NULL,
NULL);
mul_v3_fl(force,

View File

@ -2243,8 +2243,13 @@ static void basic_force_cb(void *efdata_v, ParticleKey *state, float *force, flo
/* add effectors */
pd_point_from_particle(efdata->sim, efdata->pa, state, &epoint);
if (part->type != PART_HAIR || part->effector_weights->flag & EFF_WEIGHT_DO_HAIR) {
BKE_effectors_apply(
sim->psys->effectors, sim->colliders, part->effector_weights, &epoint, force, impulse);
BKE_effectors_apply(sim->psys->effectors,
sim->colliders,
part->effector_weights,
&epoint,
force,
NULL,
impulse);
}
mul_v3_fl(force, efdata->ptex.field);

View File

@ -1647,7 +1647,7 @@ static void rigidbody_update_sim_ob(
/* 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);
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],

View File

@ -1479,7 +1479,8 @@ static void _scan_for_ext_spring_forces(
mid_v3_v3v3(pos, sb->bpoint[bs->v1].pos, sb->bpoint[bs->v2].pos);
mid_v3_v3v3(vel, sb->bpoint[bs->v1].vec, sb->bpoint[bs->v2].vec);
pd_point_from_soft(scene, pos, vel, -1, &epoint);
BKE_effectors_apply(effectors, NULL, sb->effector_weights, &epoint, force, speed);
BKE_effectors_apply(
effectors, NULL, sb->effector_weights, &epoint, force, NULL, speed);
mul_v3_fl(speed, windfactor);
add_v3_v3(vel, speed);
@ -2107,7 +2108,7 @@ static int _softbody_calc_forces_slice_in_a_thread(Scene *scene,
float eval_sb_fric_force_scale = sb_fric_force_scale(ob);
pd_point_from_soft(scene, bp->pos, bp->vec, sb->bpoint - bp, &epoint);
BKE_effectors_apply(effectors, NULL, sb->effector_weights, &epoint, force, speed);
BKE_effectors_apply(effectors, NULL, sb->effector_weights, &epoint, force, NULL, speed);
/* apply forcefield*/
mul_v3_fl(force, fieldfactor * eval_sb_fric_force_scale);

View File

@ -5075,6 +5075,23 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
* \note Keep this message at the bottom of the function.
*/
{
/* Set the cloth wind factor to 1 for old forces. */
if (!DNA_struct_elem_find(fd->filesdna, "PartDeflect", "float", "f_wind_factor")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ob->pd) {
ob->pd->f_wind_factor = 1.0f;
}
}
LISTBASE_FOREACH (ParticleSettings *, part, &bmain->particles) {
if (part->pd) {
part->pd->f_wind_factor = 1.0f;
}
if (part->pd2) {
part->pd2->f_wind_factor = 1.0f;
}
}
}
/* Keep this block, even when empty. */
}
}

View File

@ -92,6 +92,10 @@ typedef struct PartDeflect {
* How much force is converted into "air flow", i.e.
* force used as the velocity of surrounding medium. */
float f_flow;
/** How much force is reduced when acting parallel to a surface, e.g. cloth. */
float f_wind_factor;
char _pad0[4];
/** Noise size for noise effector, restlength for harmonic effector. */
float f_size;

View File

@ -1517,6 +1517,15 @@ static void rna_def_field(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Flow", "Convert effector force into air flow velocity");
RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
prop = RNA_def_property(srna, "wind_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "f_wind_factor");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(
prop,
"Wind Factor",
"How much the force is reduced when acting parallel to a surface, e.g. cloth");
RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
/* different ui range to above */
prop = RNA_def_property(srna, "inflow", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "f_flow");

View File

@ -683,23 +683,42 @@ static void cloth_calc_force(
/* handle external forces like wind */
if (effectors) {
bool is_not_hair = (clmd->hairdata == NULL) && (cloth->primitive_num > 0);
bool has_wind = false, has_force = false;
/* cache per-vertex forces to avoid redundant calculation */
float(*winvec)[3] = (float(*)[3])MEM_callocN(sizeof(float[3]) * mvert_num, "effector forces");
float(*winvec)[3] = (float(*)[3])MEM_callocN(sizeof(float[3]) * mvert_num * 2,
"effector forces");
float(*forcevec)[3] = is_not_hair ? winvec + mvert_num : winvec;
for (i = 0; i < cloth->mvert_num; i++) {
float x[3], v[3];
EffectedPoint epoint;
BPH_mass_spring_get_motion_state(data, i, x, v);
pd_point_from_loc(scene, x, v, i, &epoint);
BKE_effectors_apply(
effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL);
BKE_effectors_apply(effectors,
NULL,
clmd->sim_parms->effector_weights,
&epoint,
forcevec[i],
winvec[i],
NULL);
has_wind = has_wind || !is_zero_v3(winvec[i]);
has_force = has_force || !is_zero_v3(forcevec[i]);
}
/* Hair has only edges. */
if ((clmd->hairdata == NULL) && (cloth->primitive_num > 0)) {
if (is_not_hair) {
for (i = 0; i < cloth->primitive_num; i++) {
const MVertTri *vt = &tri[i];
BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec);
if (has_wind) {
BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec);
}
if (has_force) {
BPH_mass_spring_force_face_extern(data, vt->tri[0], vt->tri[1], vt->tri[2], forcevec);
}
}
}
else {

View File

@ -126,9 +126,12 @@ void BPH_mass_spring_force_drag(struct Implicit_Data *data, float drag);
/* Custom external force */
void BPH_mass_spring_force_extern(
struct Implicit_Data *data, int i, const float f[3], float dfdx[3][3], float dfdv[3][3]);
/* Wind force, acting on a face */
/* Wind force, acting on a face (only generates pressure from the normal component) */
void BPH_mass_spring_force_face_wind(
struct Implicit_Data *data, int v1, int v2, int v3, const float (*winvec)[3]);
/* Arbitrary per-unit-area vector force field acting on a face. */
void BPH_mass_spring_force_face_extern(
struct Implicit_Data *data, int v1, int v2, int v3, const float (*forcevec)[3]);
/* Wind force, acting on an edge */
void BPH_mass_spring_force_edge_wind(struct Implicit_Data *data,
int v1,

View File

@ -1469,22 +1469,71 @@ void BPH_mass_spring_force_face_wind(
Implicit_Data *data, int v1, int v2, int v3, const float (*winvec)[3])
{
const float effector_scale = 0.02f;
int vs[3] = {v1, v2, v3};
float win[3], nor[3], area;
float factor;
float factor, base_force;
float force[3];
/* calculate face normal and area */
area = calc_nor_area_tri(nor, data->X[v1], data->X[v2], data->X[v3]);
/* The force is calculated and split up evenly for each of the three face verts */
factor = effector_scale * area / 3.0f;
world_to_root_v3(data, v1, win, winvec[v1]);
madd_v3_v3fl(data->F[v1], nor, factor * dot_v3v3(win, nor));
/* Calculate wind pressure at each vertex by projecting the wind field on the normal. */
for (int i = 0; i < 3; i++) {
world_to_root_v3(data, vs[i], win, winvec[vs[i]]);
world_to_root_v3(data, v2, win, winvec[v2]);
madd_v3_v3fl(data->F[v2], nor, factor * dot_v3v3(win, nor));
force[i] = dot_v3v3(win, nor);
}
world_to_root_v3(data, v3, win, winvec[v3]);
madd_v3_v3fl(data->F[v3], nor, factor * dot_v3v3(win, nor));
/* Compute per-vertex force values from local pressures.
* From integrating the pressure over the triangle and deriving
* equivalent vertex forces, it follows that:
*
* force[idx] = (sum(pressure) + pressure[idx]) * area / 12
*
* Effectively, 1/4 of the pressure acts just on its vertex,
* while 3/4 is split evenly over all three.
*/
mul_v3_fl(force, factor / 4.0f);
base_force = force[0] + force[1] + force[2];
/* add pressure to each of the face verts */
madd_v3_v3fl(data->F[v1], nor, base_force + force[0]);
madd_v3_v3fl(data->F[v2], nor, base_force + force[1]);
madd_v3_v3fl(data->F[v3], nor, base_force + force[2]);
}
void BPH_mass_spring_force_face_extern(
Implicit_Data *data, int v1, int v2, int v3, const float (*forcevec)[3])
{
const float effector_scale = 0.02f;
int vs[3] = {v1, v2, v3};
float nor[3], area;
float factor, base_force[3];
float force[3][3];
/* calculate face normal and area */
area = calc_nor_area_tri(nor, data->X[v1], data->X[v2], data->X[v3]);
/* The force is calculated and split up evenly for each of the three face verts */
factor = effector_scale * area / 3.0f;
/* Compute common and per-vertex force vectors from the original inputs. */
zero_v3(base_force);
for (int i = 0; i < 3; i++) {
world_to_root_v3(data, vs[i], force[i], forcevec[vs[i]]);
mul_v3_fl(force[i], factor / 4.0f);
add_v3_v3(base_force, force[i]);
}
/* Apply the common and vertex components to all vertices. */
for (int i = 0; i < 3; i++) {
add_v3_v3(force[i], base_force);
add_v3_v3(data->F[vs[i]], force[i]);
}
}
float BPH_tri_tetra_volume_signed_6x(Implicit_Data *data, int v1, int v2, int v3)