Fix Cloth Brush grab deformation mode
The grab mode was not correctly implemented, so the way it was working was confusing for users. - Grab delta was calculated in increments from the last stroke position, so it did not match the behavior of a grab brush. I refactored the grab delta calculation to make this change more explicit. - Grab displacement was not calculated from the original coordinates - Grab was using an incorrect strength Grab is now setting the position of the affected vertices directly and the constraints solve the rest of the cloth. I also tried to implement an alternative version based on applying forces to move the vertices to the grab position, but I think this is more controllable and the grab falloff can be adjusted by tweaking the simulation falloff. Reviewed By: sergey Differential Revision: https://developer.blender.org/D7756
This commit is contained in:
parent
9aea7dc7c6
commit
18f33f293b
@ -230,8 +230,7 @@ static bool paint_tool_require_location(Brush *brush, ePaintMode mode)
|
|||||||
SCULPT_TOOL_THUMB)) {
|
SCULPT_TOOL_THUMB)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
|
else if (SCULPT_is_cloth_deform_brush(brush)) {
|
||||||
brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2091,9 +2091,13 @@ static float brush_strength(const Sculpt *sd,
|
|||||||
case SCULPT_TOOL_LAYER:
|
case SCULPT_TOOL_LAYER:
|
||||||
return alpha * flip * pressure * overlap * feather;
|
return alpha * flip * pressure * overlap * feather;
|
||||||
case SCULPT_TOOL_CLOTH:
|
case SCULPT_TOOL_CLOTH:
|
||||||
/* Expand is more sensible to strength as it keeps expanding the cloth when sculpting over
|
if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
|
||||||
* the same vertices. */
|
/* Grab deform uses the same falloff as a regular grab brush. */
|
||||||
if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_EXPAND) {
|
return root_alpha * feather;
|
||||||
|
}
|
||||||
|
else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_EXPAND) {
|
||||||
|
/* Expand is more sensible to strength as it keeps expanding the cloth when sculpting over
|
||||||
|
* the same vertices. */
|
||||||
return 0.1f * alpha * flip * pressure * overlap * feather;
|
return 0.1f * alpha * flip * pressure * overlap * feather;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -6199,6 +6203,34 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* In these brushes the grab delta is calculated always from the initial stroke location, which is
|
||||||
|
* generally used to create grab deformations. */
|
||||||
|
static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
|
||||||
|
{
|
||||||
|
return ELEM(brush->sculpt_tool,
|
||||||
|
SCULPT_TOOL_GRAB,
|
||||||
|
SCULPT_TOOL_POSE,
|
||||||
|
SCULPT_TOOL_THUMB,
|
||||||
|
SCULPT_TOOL_ELASTIC_DEFORM) ||
|
||||||
|
SCULPT_is_cloth_deform_brush(brush);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In these brushes the grab delta is calculated from the previous stroke location, which is used
|
||||||
|
* to calculate to orientate the brush tip and deformation towards the stroke direction. */
|
||||||
|
static bool sculpt_needs_delta_for_tip_orientation(Brush *brush)
|
||||||
|
{
|
||||||
|
if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
|
||||||
|
return !SCULPT_is_cloth_deform_brush(brush);
|
||||||
|
}
|
||||||
|
return ELEM(brush->sculpt_tool,
|
||||||
|
SCULPT_TOOL_CLAY_STRIPS,
|
||||||
|
SCULPT_TOOL_PINCH,
|
||||||
|
SCULPT_TOOL_MULTIPLANE_SCRAPE,
|
||||||
|
SCULPT_TOOL_CLAY_THUMB,
|
||||||
|
SCULPT_TOOL_NUDGE,
|
||||||
|
SCULPT_TOOL_SNAKE_HOOK);
|
||||||
|
}
|
||||||
|
|
||||||
static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Brush *brush)
|
static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Brush *brush)
|
||||||
{
|
{
|
||||||
SculptSession *ss = ob->sculpt;
|
SculptSession *ss = ob->sculpt;
|
||||||
@ -6242,38 +6274,27 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
|
|||||||
|
|
||||||
/* Compute delta to move verts by. */
|
/* Compute delta to move verts by. */
|
||||||
if (!cache->first_time) {
|
if (!cache->first_time) {
|
||||||
switch (tool) {
|
if (sculpt_needs_delta_from_anchored_origin(brush)) {
|
||||||
case SCULPT_TOOL_GRAB:
|
sub_v3_v3v3(delta, grab_location, cache->old_grab_location);
|
||||||
case SCULPT_TOOL_POSE:
|
invert_m4_m4(imat, ob->obmat);
|
||||||
case SCULPT_TOOL_THUMB:
|
mul_mat3_m4_v3(imat, delta);
|
||||||
case SCULPT_TOOL_ELASTIC_DEFORM:
|
add_v3_v3(cache->grab_delta, delta);
|
||||||
sub_v3_v3v3(delta, grab_location, cache->old_grab_location);
|
}
|
||||||
invert_m4_m4(imat, ob->obmat);
|
else if (sculpt_needs_delta_for_tip_orientation(brush)) {
|
||||||
mul_mat3_m4_v3(imat, delta);
|
if (brush->flag & BRUSH_ANCHORED) {
|
||||||
add_v3_v3(cache->grab_delta, delta);
|
float orig[3];
|
||||||
break;
|
mul_v3_m4v3(orig, ob->obmat, cache->orig_grab_location);
|
||||||
case SCULPT_TOOL_CLAY_STRIPS:
|
sub_v3_v3v3(cache->grab_delta, grab_location, orig);
|
||||||
case SCULPT_TOOL_PINCH:
|
}
|
||||||
case SCULPT_TOOL_CLOTH:
|
else {
|
||||||
case SCULPT_TOOL_MULTIPLANE_SCRAPE:
|
|
||||||
case SCULPT_TOOL_CLAY_THUMB:
|
|
||||||
case SCULPT_TOOL_NUDGE:
|
|
||||||
case SCULPT_TOOL_SNAKE_HOOK:
|
|
||||||
if (brush->flag & BRUSH_ANCHORED) {
|
|
||||||
float orig[3];
|
|
||||||
mul_v3_m4v3(orig, ob->obmat, cache->orig_grab_location);
|
|
||||||
sub_v3_v3v3(cache->grab_delta, grab_location, orig);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
|
|
||||||
}
|
|
||||||
invert_m4_m4(imat, ob->obmat);
|
|
||||||
mul_mat3_m4_v3(imat, cache->grab_delta);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Use for 'Brush.topology_rake_factor'. */
|
|
||||||
sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
|
sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
|
||||||
break;
|
}
|
||||||
|
invert_m4_m4(imat, ob->obmat);
|
||||||
|
mul_mat3_m4_v3(imat, cache->grab_delta);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Use for 'Brush.topology_rake_factor'. */
|
||||||
|
sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -6294,18 +6315,14 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
|
|||||||
copy_v3_v3(cache->anchored_location, cache->true_location);
|
copy_v3_v3(cache->anchored_location, cache->true_location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tool == SCULPT_TOOL_ELASTIC_DEFORM) {
|
else if (tool == SCULPT_TOOL_ELASTIC_DEFORM || SCULPT_is_cloth_deform_brush(brush)) {
|
||||||
copy_v3_v3(cache->anchored_location, cache->true_location);
|
copy_v3_v3(cache->anchored_location, cache->true_location);
|
||||||
}
|
}
|
||||||
else if (tool == SCULPT_TOOL_THUMB) {
|
else if (tool == SCULPT_TOOL_THUMB) {
|
||||||
copy_v3_v3(cache->anchored_location, cache->orig_grab_location);
|
copy_v3_v3(cache->anchored_location, cache->orig_grab_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ELEM(tool,
|
if (sculpt_needs_delta_from_anchored_origin(brush)) {
|
||||||
SCULPT_TOOL_GRAB,
|
|
||||||
SCULPT_TOOL_THUMB,
|
|
||||||
SCULPT_TOOL_ELASTIC_DEFORM,
|
|
||||||
SCULPT_TOOL_POSE)) {
|
|
||||||
/* Location stays the same for finding vertices in brush radius. */
|
/* Location stays the same for finding vertices in brush radius. */
|
||||||
copy_v3_v3(cache->true_location, cache->orig_grab_location);
|
copy_v3_v3(cache->true_location, cache->orig_grab_location);
|
||||||
|
|
||||||
@ -6376,9 +6393,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
|
|||||||
|
|
||||||
if (cache->first_time ||
|
if (cache->first_time ||
|
||||||
!((brush->flag & BRUSH_ANCHORED) || (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) ||
|
!((brush->flag & BRUSH_ANCHORED) || (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) ||
|
||||||
(brush->sculpt_tool == SCULPT_TOOL_ROTATE) ||
|
(brush->sculpt_tool == SCULPT_TOOL_ROTATE) || SCULPT_is_cloth_deform_brush(brush))) {
|
||||||
(brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
|
|
||||||
brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB))) {
|
|
||||||
RNA_float_get_array(ptr, "location", cache->true_location);
|
RNA_float_get_array(ptr, "location", cache->true_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,8 +212,9 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
|
|||||||
const float *grab_delta = data->grab_delta;
|
const float *grab_delta = data->grab_delta;
|
||||||
float(*imat)[4] = data->mat;
|
float(*imat)[4] = data->mat;
|
||||||
|
|
||||||
const bool use_falloff_plane = brush->cloth_force_falloff_type ==
|
const bool use_falloff_plane = !SCULPT_is_cloth_deform_brush(brush) &&
|
||||||
BRUSH_CLOTH_FORCE_FALLOFF_PLANE;
|
brush->cloth_force_falloff_type ==
|
||||||
|
BRUSH_CLOTH_FORCE_FALLOFF_PLANE;
|
||||||
|
|
||||||
PBVHVertexIter vd;
|
PBVHVertexIter vd;
|
||||||
const float bstrength = ss->cache->bstrength;
|
const float bstrength = ss->cache->bstrength;
|
||||||
@ -245,14 +246,29 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
|
|||||||
gravity, ss->cache->gravity_direction, -ss->cache->radius * data->sd->gravity_factor);
|
gravity, ss->cache->gravity_direction, -ss->cache->radius * data->sd->gravity_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Original data for deform brushes. */
|
||||||
|
SculptOrigVertData orig_data;
|
||||||
|
if (SCULPT_is_cloth_deform_brush(brush)) {
|
||||||
|
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
|
||||||
|
}
|
||||||
|
|
||||||
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
||||||
{
|
{
|
||||||
float force[3];
|
float force[3];
|
||||||
const float sim_factor = cloth_brush_simulation_falloff_get(
|
const float sim_factor = cloth_brush_simulation_falloff_get(
|
||||||
brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]);
|
brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]);
|
||||||
|
|
||||||
|
float current_vertex_location[3];
|
||||||
|
if (SCULPT_is_cloth_deform_brush(brush)) {
|
||||||
|
SCULPT_orig_vert_data_update(&orig_data, &vd);
|
||||||
|
copy_v3_v3(current_vertex_location, orig_data.co);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
copy_v3_v3(current_vertex_location, vd.co);
|
||||||
|
}
|
||||||
|
|
||||||
/* When using the plane falloff mode the falloff is not constrained by the brush radius. */
|
/* When using the plane falloff mode the falloff is not constrained by the brush radius. */
|
||||||
if (sculpt_brush_test_sq_fn(&test, vd.co) || use_falloff_plane) {
|
if (sculpt_brush_test_sq_fn(&test, current_vertex_location) || use_falloff_plane) {
|
||||||
|
|
||||||
float dist = sqrtf(test.dist);
|
float dist = sqrtf(test.dist);
|
||||||
|
|
||||||
@ -263,7 +279,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
|
|||||||
const float fade = sim_factor * bstrength *
|
const float fade = sim_factor * bstrength *
|
||||||
SCULPT_brush_strength_factor(ss,
|
SCULPT_brush_strength_factor(ss,
|
||||||
brush,
|
brush,
|
||||||
vd.co,
|
current_vertex_location,
|
||||||
dist,
|
dist,
|
||||||
vd.no,
|
vd.no,
|
||||||
vd.fno,
|
vd.fno,
|
||||||
@ -292,7 +308,10 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
|
|||||||
mul_v3_v3fl(force, offset, -fade);
|
mul_v3_v3fl(force, offset, -fade);
|
||||||
break;
|
break;
|
||||||
case BRUSH_CLOTH_DEFORM_GRAB:
|
case BRUSH_CLOTH_DEFORM_GRAB:
|
||||||
mul_v3_v3fl(force, grab_delta, fade);
|
/* Grab writes the positions in the simulation directly without applying forces. */
|
||||||
|
madd_v3_v3v3fl(
|
||||||
|
cloth_sim->pos[vd.index], orig_data.co, ss->cache->grab_delta_symmetry, fade);
|
||||||
|
zero_v3(force);
|
||||||
break;
|
break;
|
||||||
case BRUSH_CLOTH_DEFORM_PINCH_POINT:
|
case BRUSH_CLOTH_DEFORM_PINCH_POINT:
|
||||||
if (use_falloff_plane) {
|
if (use_falloff_plane) {
|
||||||
|
@ -333,6 +333,13 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr,
|
|||||||
struct SculptSession *ss,
|
struct SculptSession *ss,
|
||||||
const float outline_col[3],
|
const float outline_col[3],
|
||||||
float outline_alpha);
|
float outline_alpha);
|
||||||
|
|
||||||
|
BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush)
|
||||||
|
{
|
||||||
|
return brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
|
||||||
|
brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB;
|
||||||
|
}
|
||||||
|
|
||||||
/* Pose Brush. */
|
/* Pose Brush. */
|
||||||
void SCULPT_do_pose_brush(struct Sculpt *sd,
|
void SCULPT_do_pose_brush(struct Sculpt *sd,
|
||||||
struct Object *ob,
|
struct Object *ob,
|
||||||
|
Loading…
Reference in New Issue
Block a user