Implemented internal hair pressure to prevent hair from collapsing in

on itself.

This uses the same voxel structure as the hair smoothing algorithm.
A slightly different method was suggested in the original paper
(Volumetric Methods for Simulation and Rendering of Hair), but this is
based on directing hair based on a target density, which is another
way of implementing global goals. Our own approach is to define a
pressure threshold above which the hair is repelled in the density
gradient direction to simulate internal pressure from collisions.
This commit is contained in:
Lukas Tönne 2014-08-28 14:55:55 +02:00
parent 345c7b144d
commit ba8b8ec998
4 changed files with 62 additions and 6 deletions

@ -316,6 +316,8 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
sub.prop(cloth, "bending_stiffness", text="Bending") sub.prop(cloth, "bending_stiffness", text="Bending")
sub.prop(cloth, "internal_friction", slider=True) sub.prop(cloth, "internal_friction", slider=True)
sub.prop(cloth, "collider_friction", slider=True) sub.prop(cloth, "collider_friction", slider=True)
sub.prop(cloth, "pressure", slider=True)
sub.prop(cloth, "pressure_threshold", slider=True)
col = split.column() col = split.column()
col.label(text="Damping:") col.label(text="Damping:")

@ -1275,7 +1275,8 @@ BLI_INLINE int hair_grid_interp_weights(int res, const float gmin[3], const floa
return offset; return offset;
} }
BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid, int res, const float gmin[3], const float scale[3], const float vec[3], float *density, float velocity[3]) BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid, int res, const float gmin[3], const float scale[3], const float vec[3],
float *density, float velocity[3], float density_gradient[3])
{ {
HairGridVert data[8]; HairGridVert data[8];
float uvw[3], muvw[3]; float uvw[3], muvw[3];
@ -1311,6 +1312,22 @@ BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid, int res, const f
uvw[1]*( muvw[0]*data[6].velocity[k] + uvw[0]*data[7].velocity[k] ) ); uvw[1]*( muvw[0]*data[6].velocity[k] + uvw[0]*data[7].velocity[k] ) );
} }
} }
if (density_gradient) {
density_gradient[0] = muvw[1] * muvw[2] * ( data[0].density - data[1].density ) +
uvw[1] * muvw[2] * ( data[2].density - data[3].density ) +
muvw[1] * uvw[2] * ( data[4].density - data[5].density ) +
uvw[1] * uvw[2] * ( data[6].density - data[7].density );
density_gradient[1] = muvw[2] * muvw[0] * ( data[0].density - data[2].density ) +
uvw[2] * muvw[0] * ( data[4].density - data[6].density ) +
muvw[2] * uvw[0] * ( data[1].density - data[3].density ) +
uvw[2] * uvw[0] * ( data[5].density - data[7].density );
density_gradient[2] = muvw[2] * muvw[0] * ( data[0].density - data[4].density ) +
uvw[2] * muvw[0] * ( data[1].density - data[5].density ) +
muvw[2] * uvw[0] * ( data[2].density - data[6].density ) +
uvw[2] * uvw[0] * ( data[3].density - data[7].density );
}
} }
static void hair_velocity_smoothing(const HairGridVert *hairgrid, const float gmin[3], const float scale[3], float smoothfac, static void hair_velocity_smoothing(const HairGridVert *hairgrid, const float gmin[3], const float scale[3], float smoothfac,
@ -1321,9 +1338,7 @@ static void hair_velocity_smoothing(const HairGridVert *hairgrid, const float gm
for (v = 0; v < numverts; v++) { for (v = 0; v < numverts; v++) {
float density, velocity[3]; float density, velocity[3];
hair_grid_interpolate(hairgrid, hair_grid_res, gmin, scale, lX[v], &density, velocity); hair_grid_interpolate(hairgrid, hair_grid_res, gmin, scale, lX[v], &density, velocity, NULL);
if (len_v3(velocity) > 10.0f)
printf("a bit too much\n");
sub_v3_v3(velocity, lV[v]); sub_v3_v3(velocity, lV[v]);
madd_v3_v3fl(lF[v], velocity, smoothfac); madd_v3_v3fl(lF[v], velocity, smoothfac);
@ -1346,6 +1361,26 @@ static void hair_velocity_collision(const HairGridVert *collgrid, const float gm
} }
} }
static void hair_pressure_force(const HairGridVert *hairgrid, const float gmin[3], const float scale[3], float pressurefac, float minpressure,
lfVector *lF, lfVector *lX, unsigned int numverts)
{
int v;
/* calculate forces */
for (v = 0; v < numverts; v++) {
float density, gradient[3], gradlen;
hair_grid_interpolate(hairgrid, hair_grid_res, gmin, scale, lX[v], &density, NULL, gradient);
gradlen = normalize_v3(gradient) - minpressure;
if (gradlen < 0.0f)
continue;
mul_v3_fl(gradient, gradlen);
madd_v3_v3fl(lF[v], gradient, pressurefac);
}
}
static void hair_volume_get_boundbox(lfVector *lX, unsigned int numverts, float gmin[3], float gmax[3]) static void hair_volume_get_boundbox(lfVector *lX, unsigned int numverts, float gmin[3], float gmax[3])
{ {
int i; int i;
@ -1531,6 +1566,11 @@ static void hair_volume_forces(ClothModifierData *clmd, lfVector *lF, lfVector *
/* 2.0f is an experimental value that seems to give good results */ /* 2.0f is an experimental value that seems to give good results */
float smoothfac = 2.0f * clmd->sim_parms->velocity_smooth; float smoothfac = 2.0f * clmd->sim_parms->velocity_smooth;
float collfac = 2.0f * clmd->sim_parms->collider_friction; float collfac = 2.0f * clmd->sim_parms->collider_friction;
float pressfac = clmd->sim_parms->pressure;
float minpress = clmd->sim_parms->pressure_threshold;
if (smoothfac <= 0.0f && collfac <= 0.0f && pressfac <= 0.0f)
return;
hair_volume_get_boundbox(lX, numverts, gmin, gmax); hair_volume_get_boundbox(lX, numverts, gmin, gmax);
hair_grid_get_scale(hair_grid_res, gmin, gmax, scale); hair_grid_get_scale(hair_grid_res, gmin, gmax, scale);
@ -1540,6 +1580,7 @@ static void hair_volume_forces(ClothModifierData *clmd, lfVector *lF, lfVector *
hair_velocity_smoothing(hairgrid, gmin, scale, smoothfac, lF, lX, lV, numverts); hair_velocity_smoothing(hairgrid, gmin, scale, smoothfac, lF, lX, lV, numverts);
hair_velocity_collision(collgrid, gmin, scale, collfac, lF, lX, lV, numverts); hair_velocity_collision(collgrid, gmin, scale, collfac, lF, lX, lV, numverts);
hair_pressure_force(hairgrid, gmin, scale, pressfac, minpress, lF, lX, numverts);
MEM_freeN(hairgrid); MEM_freeN(hairgrid);
MEM_freeN(collgrid); MEM_freeN(collgrid);
@ -1645,7 +1686,6 @@ static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), lfVec
init_lfvector(lF, gravity, numverts); init_lfvector(lF, gravity, numverts);
if (clmd->sim_parms->velocity_smooth > 0.0f || clmd->sim_parms->collider_friction > 0.0f)
hair_volume_forces(clmd, lF, lX, lV, numverts); hair_volume_forces(clmd, lF, lX, lV, numverts);
/* multiply lF with mass matrix /* multiply lF with mass matrix

@ -69,6 +69,8 @@ typedef struct ClothSimSettings {
float goalspring; float goalspring;
float goalfrict; float goalfrict;
float velocity_smooth; /* smoothing of velocities for hair */ float velocity_smooth; /* smoothing of velocities for hair */
float pressure; /* pressure factor from hair density */
float pressure_threshold; /* minimum density for hair pressure */
float collider_friction; /* friction with colliders */ float collider_friction; /* friction with colliders */
float vel_damping; /* damp the velocity to speed up getting to the resting position */ float vel_damping; /* damp the velocity to speed up getting to the resting position */
float shrink_min; /* min amount to shrink cloth by 0.0f (no shrink) - 1.0f (shrink to nothing) */ float shrink_min; /* min amount to shrink cloth by 0.0f (no shrink) - 1.0f (shrink to nothing) */

@ -325,6 +325,18 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Collider Friction", ""); RNA_def_property_ui_text(prop, "Collider Friction", "");
RNA_def_property_update(prop, 0, "rna_cloth_update"); RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "pressure", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "pressure");
RNA_def_property_range(prop, 0.0f, 1000.0f);
RNA_def_property_ui_text(prop, "Internal Pressure", "Generate outward force based on hair density");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "pressure_threshold", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "pressure_threshold");
RNA_def_property_range(prop, 0.0f, 1000.0f);
RNA_def_property_ui_text(prop, "Internal Pressure Threshold", "No pressure force is generated below this pressure value");
RNA_def_property_update(prop, 0, "rna_cloth_update");
/* mass */ /* mass */
prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_NONE); prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_NONE);