diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py index 210d850ad06..db33fda3b17 100644 --- a/release/scripts/startup/bl_ui/properties_physics_common.py +++ b/release/scripts/startup/bl_ui/properties_physics_common.py @@ -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): diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h index 6935b3aecce..0518ce8ffa3 100644 --- a/source/blender/blenkernel/BKE_effect.h +++ b/source/blender/blenkernel/BKE_effect.h @@ -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); diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c index 639437f8251..d5064629451 100644 --- a/source/blender/blenkernel/intern/boids.c +++ b/source/blender/blenkernel/intern/boids.c @@ -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)) { diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index dae8a59fe43..2e1fa519284 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -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 */ diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 7ed04c6976a..235c834fde9 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -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); } } } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index b75592836e0..521210e7bb5 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -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); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index c201cb83c44..94b677c649c 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -2262,6 +2262,7 @@ static void do_path_effectors(ParticleSimulationData *sim, sim->psys->part->effector_weights, &epoint, force, + NULL, NULL); mul_v3_fl(force, diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 606291a9ae0..d09f9a8eb09 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -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); diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 02ce0c6f996..4752782eaeb 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -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], diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 68d0822a223..9c7abbdf876 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -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); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 6211c58d7d4..0648264466d 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -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. */ } } diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h index 7f022f104e6..78f645deaa2 100644 --- a/source/blender/makesdna/DNA_object_force_types.h +++ b/source/blender/makesdna/DNA_object_force_types.h @@ -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; diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 30e0ba19608..0a4e714c2ef 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -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"); diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp index 18fab5215a6..3b6e39ef48f 100644 --- a/source/blender/physics/intern/BPH_mass_spring.cpp +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -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 { diff --git a/source/blender/physics/intern/implicit.h b/source/blender/physics/intern/implicit.h index 69b50f7fa48..8bc09755180 100644 --- a/source/blender/physics/intern/implicit.h +++ b/source/blender/physics/intern/implicit.h @@ -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, diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c index 063c224f158..5ec4c750d5d 100644 --- a/source/blender/physics/intern/implicit_blender.c +++ b/source/blender/physics/intern/implicit_blender.c @@ -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)