Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py4
-rw-r--r--source/blender/blenkernel/BKE_paint.h21
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c16
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c188
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h19
-rw-r--r--source/blender/makesdna/DNA_brush_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_brush.c5
8 files changed, 201 insertions, 55 deletions
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 135ba802655..c601dec87a9 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -660,9 +660,11 @@ def brush_settings(layout, context, brush, popover=False):
elif sculpt_tool == 'CLOTH':
layout.separator()
layout.prop(brush, "cloth_simulation_area_type")
- if brush.cloth_simulation_area_type == 'LOCAL':
+ if brush.cloth_simulation_area_type != 'GLOBAL':
layout.prop(brush, "cloth_sim_limit")
layout.prop(brush, "cloth_sim_falloff")
+
+ if brush.cloth_simulation_area_type == 'LOCAL':
layout.prop(brush, "use_cloth_pin_simulation_boundary")
layout.separator()
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 653c8a752d5..dc2a42e3988 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -256,6 +256,18 @@ typedef struct SculptPoseIKChain {
/* Cloth Brush */
+/* Cloth Simulation. */
+typedef enum eSculptClothNodeSimState {
+ /* Constraints were not built for this node, so it can't be simulated. */
+ SCULPT_CLOTH_NODE_UNINITIALIZED,
+
+ /* There are constraints for the geometry in this node, but it should not be simulated. */
+ SCULPT_CLOTH_NODE_INACTIVE,
+
+ /* There are constraints for this node and they should be used by the solver. */
+ SCULPT_CLOTH_NODE_ACTIVE,
+} eSculptClothNodeSimState;
+
typedef enum eSculptClothConstraintType {
/* Constraint that creates the structure of the cloth. */
SCULPT_CLOTH_CONSTRAINT_STRUCTURAL = 0,
@@ -282,6 +294,10 @@ typedef struct SculptClothLengthConstraint {
float length;
float strength;
+ /* Index in SculptClothSimulation.node_state of the node from where this constraint was created.
+ * This constraints will only be used by the solver if the state is active. */
+ int node;
+
eSculptClothConstraintType type;
} SculptClothLengthConstraint;
@@ -308,6 +324,11 @@ typedef struct SculptClothSimulation {
float (*last_iteration_pos)[3];
struct ListBase *collider_list;
+
+ int totnode;
+ /* PBVHNode pointer as a key, index in SculptClothSimulation.node_state as value. */
+ struct GHash *node_state_index;
+ eSculptClothNodeSimState *node_state;
} SculptClothSimulation;
typedef struct SculptPersistentBase {
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 2b6ecdcd640..2e24c2533c5 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -1663,7 +1663,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
/* Cloth brush local simulation areas. */
if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
- brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) {
+ brush->cloth_simulation_area_type != BRUSH_CLOTH_SIMULATION_AREA_GLOBAL) {
const float white[3] = {1.0f, 1.0f, 1.0f};
const float zero_v[3] = {0.0f};
/* This functions sets its own drawing space in order to draw the simulation limits when the
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 70404f055e9..f9dbfaa5da9 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -5646,6 +5646,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
}
+ if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC) {
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .sd = sd,
+ .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)),
+ .original = false,
+ .ignore_fully_ineffective = false,
+ .center = ss->cache->location,
+ };
+ BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
+ }
else {
/* Gobal simulation, get all nodes. */
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
@@ -5724,10 +5735,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
if (!ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(ss, 1.0f, 0.0f, false, true);
SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
- SCULPT_cloth_brush_build_nodes_constraints(
- sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, FLT_MAX);
}
SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);
+ SCULPT_cloth_brush_ensure_nodes_constraints(
+ sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, FLT_MAX);
}
bool invert = ss->cache->pen_flip || ss->cache->invert || brush->flag & BRUSH_DIR_IN;
@@ -5870,6 +5881,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) {
if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
+ SCULPT_cloth_sim_activate_nodes(ss->cache->cloth_sim, nodes, totnode);
SCULPT_cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 3d0e5791f0d..7b14f48c6a8 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -103,6 +103,22 @@
#include <stdlib.h>
#include <string.h>
+static void cloth_brush_simulation_location_get(SculptSession *ss,
+ const Brush *brush,
+ float r_location[3])
+{
+ if (!ss->cache || !brush) {
+ zero_v3(r_location);
+ return;
+ }
+
+ if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) {
+ copy_v3_v3(r_location, ss->cache->initial_location);
+ return;
+ }
+ copy_v3_v3(r_location, ss->cache->location);
+}
+
static float cloth_brush_simulation_falloff_get(const Brush *brush,
const float radius,
const float location[3],
@@ -162,6 +178,7 @@ static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim)
static void cloth_brush_add_length_constraint(SculptSession *ss,
SculptClothSimulation *cloth_sim,
+ const int node_index,
const int v1,
const int v2,
const bool use_persistent)
@@ -172,6 +189,8 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
length_constraint->elem_index_a = v1;
length_constraint->elem_index_b = v2;
+ length_constraint->node = node_index;
+
length_constraint->elem_position_a = cloth_sim->pos[v1];
length_constraint->elem_position_b = cloth_sim->pos[v2];
@@ -197,6 +216,7 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
}
static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim,
+ const int node_index,
const int v,
const float strength)
{
@@ -206,6 +226,8 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim
length_constraint->elem_index_a = v;
length_constraint->elem_index_b = v;
+ length_constraint->node = node_index;
+
length_constraint->elem_position_a = cloth_sim->pos[v];
length_constraint->elem_position_b = cloth_sim->init_pos[v];
@@ -221,6 +243,7 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim
}
static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_sim,
+ const int node_index,
const int v,
const float strength)
{
@@ -230,6 +253,8 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_
length_constraint->elem_index_a = v;
length_constraint->elem_index_b = v;
+ length_constraint->node = node_index;
+
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_DEFORMATION;
length_constraint->elem_position_a = cloth_sim->pos[v];
@@ -250,11 +275,20 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
+ PBVHNode *node = data->nodes[n];
+
+ const int node_index = (int)(BLI_ghash_lookup(data->cloth_sim->node_state_index, node));
+ if (data->cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_UNINITIALIZED) {
+ /* The simulation already contains constraints for this node. */
+ return;
+ }
PBVHVertexIter vd;
const bool pin_simulation_boundary = ss->cache != NULL && brush != NULL &&
- brush->flag2 & BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY;
+ brush->flag2 & BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY &&
+ brush->cloth_simulation_area_type !=
+ BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC;
const bool use_persistent = brush != NULL && brush->flag & BRUSH_PERSISTENT;
@@ -273,7 +307,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
data->cloth_sim_radius * data->cloth_sim_radius :
FLT_MAX;
- BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
{
const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location);
if (len_squared < cloth_sim_radius_squared) {
@@ -291,7 +325,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
if (brush->cloth_constraint_softbody_strength > 0.0f) {
cloth_brush_add_softbody_constraint(
- data->cloth_sim, vd.index, brush->cloth_constraint_softbody_strength);
+ data->cloth_sim, node_index, vd.index, brush->cloth_constraint_softbody_strength);
}
/* As we don't know the order of the neighbor vertices, we create all possible combinations
@@ -303,8 +337,12 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
for (int c_j = 0; c_j < tot_indices; c_j++) {
if (c_i != c_j && !cloth_brush_sim_has_length_constraint(
data->cloth_sim, build_indices[c_i], build_indices[c_j])) {
- cloth_brush_add_length_constraint(
- ss, data->cloth_sim, build_indices[c_i], build_indices[c_j], use_persistent);
+ cloth_brush_add_length_constraint(ss,
+ data->cloth_sim,
+ node_index,
+ build_indices[c_i],
+ build_indices[c_j],
+ use_persistent);
}
}
}
@@ -317,13 +355,13 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
/* When the grab brush brush is used as part of the cloth brush, deformation constraints
* are created with different strengths and only inside the radius of the brush. */
const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius);
- cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade);
+ cloth_brush_add_deformation_constraint(data->cloth_sim, node_index, vd.index, fade);
}
else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) {
/* Cloth Snake Hook creates deformation constraint with fixed strength because the strength
* is controlled per iteration using cloth_sim->deformation_strength. */
cloth_brush_add_deformation_constraint(
- data->cloth_sim, vd.index, CLOTH_DEFORMATION_SNAKEHOOK_STRENGTH);
+ data->cloth_sim,node_index, vd.index, CLOTH_DEFORMATION_SNAKEHOOK_STRENGTH);
}
}
else if (data->cloth_sim->deformation_pos) {
@@ -331,7 +369,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
* their own code when modifying the deformation coordinates of the simulation, so
* deformation constraints are created with a fixed strength for all vertices. */
cloth_brush_add_deformation_constraint(
- data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
+ data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
}
if (pin_simulation_boundary) {
@@ -341,7 +379,8 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
if (sim_falloff < 1.0f) {
/* Create constraints with more strength the closer the vertex is to the simulation
* boundary. */
- cloth_brush_add_softbody_constraint(data->cloth_sim, vd.index, 1.0f - sim_falloff);
+ cloth_brush_add_softbody_constraint(
+ data->cloth_sim, node_index, vd.index, 1.0f - sim_falloff);
}
}
}
@@ -411,8 +450,10 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
float force[3];
+ float sim_location[3];
+ cloth_brush_simulation_location_get(ss, brush, sim_location);
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, sim_location, cloth_sim->init_pos[vd.index]);
float current_vertex_location[3];
if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
@@ -646,17 +687,25 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
+
+ PBVHNode *node = data->nodes[n];
PBVHVertexIter vd;
SculptClothSimulation *cloth_sim = data->cloth_sim;
const float time_step = data->cloth_time_step;
+
+ const int node_index = (int)(BLI_ghash_lookup(data->cloth_sim->node_state_index, node));
+ if (data->cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_ACTIVE) {
+ return;
+ }
+
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- const float sim_factor = ss->cache ? cloth_brush_simulation_falloff_get(
- brush,
- ss->cache->radius,
- ss->cache->initial_location,
- cloth_sim->init_pos[vd.index]) :
- 1.0f;
+ float sim_location[3];
+ cloth_brush_simulation_location_get(ss, brush, sim_location);
+ const float sim_factor =
+ ss->cache ? cloth_brush_simulation_falloff_get(
+ brush, ss->cache->radius, sim_location, cloth_sim->init_pos[vd.index]) :
+ 1.0f;
if (sim_factor > 0.0f) {
int i = vd.index;
float temp[3];
@@ -692,6 +741,9 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
}
}
BKE_pbvh_vertex_iter_end;
+
+ /* Disable the simulation on this node, it needs to be enabled again to continue. */
+ cloth_sim->node_state[node_index] = SCULPT_CLOTH_NODE_INACTIVE;
}
static void cloth_brush_satisfy_constraints(SculptSession *ss,
@@ -700,8 +752,13 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
{
for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) {
for (int i = 0; i < cloth_sim->tot_length_constraints; i++) {
-
const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i];
+
+ if (cloth_sim->node_state[constraint->node] != SCULPT_CLOTH_NODE_ACTIVE) {
+ /* Skip all constraints that were created for inactive nodes. */
+ continue;
+ }
+
const int v1 = constraint->elem_index_a;
const int v2 = constraint->elem_index_b;
@@ -729,18 +786,21 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) *
SCULPT_automasking_factor_get(ss, v2);
- const float sim_factor_v1 = ss->cache ? cloth_brush_simulation_falloff_get(
- brush,
- ss->cache->radius,
- ss->cache->initial_location,
- cloth_sim->init_pos[v1]) :
- 1.0f;
- const float sim_factor_v2 = ss->cache ? cloth_brush_simulation_falloff_get(
- brush,
- ss->cache->radius,
- ss->cache->initial_location,
- cloth_sim->init_pos[v2]) :
- 1.0f;
+ float sim_location[3];
+ cloth_brush_simulation_location_get(ss, brush, sim_location);
+
+ const float sim_factor_v1 = ss->cache ?
+ cloth_brush_simulation_falloff_get(brush,
+ ss->cache->radius,
+ sim_location,
+ cloth_sim->init_pos[v1]) :
+ 1.0f;
+ const float sim_factor_v2 = ss->cache ?
+ cloth_brush_simulation_falloff_get(brush,
+ ss->cache->radius,
+ sim_location,
+ cloth_sim->init_pos[v2]) :
+ 1.0f;
float deformation_strength = 1.0f;
if (constraint->type == SCULPT_CLOTH_CONSTRAINT_DEFORMATION) {
@@ -871,6 +931,25 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
0, totnode, &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings);
}
+/* Allocates nodes state and initializes them to Uninitialized, so constraints can be created for
+ * them. */
+static void cloth_sim_initialize_default_node_state(SculptSession *ss,
+ SculptClothSimulation *cloth_sim)
+{
+ PBVHNode **nodes;
+ int totnode;
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ cloth_sim->node_state = MEM_malloc_arrayN(
+ totnode, sizeof(eSculptClothNodeSimState), "node sim state");
+ cloth_sim->node_state_index = BLI_ghash_ptr_new("node sim state indices");
+ for (int i = 0; i < totnode; i++) {
+ cloth_sim->node_state[i] = SCULPT_CLOTH_NODE_UNINITIALIZED;
+ BLI_ghash_insert(cloth_sim->node_state_index, nodes[i], POINTER_FROM_INT(i));
+ }
+ MEM_SAFE_FREE(nodes);
+}
+
/* Public functions. */
SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
const float cloth_mass,
@@ -912,10 +991,12 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph);
}
+ cloth_sim_initialize_default_node_state(ss, cloth_sim);
+
return cloth_sim;
}
-void SCULPT_cloth_brush_build_nodes_constraints(
+void SCULPT_cloth_brush_ensure_nodes_constraints(
Sculpt *sd,
Object *ob,
PBVHNode **nodes,
@@ -975,6 +1056,17 @@ void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSim
}
}
+void SCULPT_cloth_sim_activate_nodes(SculptClothSimulation *cloth_sim,
+ PBVHNode **nodes,
+ int totnode)
+{
+ /* Activate the nodes inside the simulation area. */
+ for (int n = 0; n < totnode; n++) {
+ const int node_index = (int)(BLI_ghash_lookup(cloth_sim->node_state_index, nodes[n]));
+ cloth_sim->node_state[node_index] = SCULPT_CLOTH_NODE_ACTIVE;
+ }
+}
+
/* Main Brush Function. */
void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
@@ -998,19 +1090,21 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
SCULPT_is_cloth_deform_brush(brush));
SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
}
-
- /* Build the constraints. */
- const float radius = ss->cache->initial_radius;
- const float limit = radius + (radius * brush->cloth_sim_limit);
- SCULPT_cloth_brush_build_nodes_constraints(
- sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, limit);
-
return;
}
+ /* Ensure the constraints for the nodes. */
+ const float radius = ss->cache->initial_radius;
+ const float limit = radius + (radius * brush->cloth_sim_limit);
+ SCULPT_cloth_brush_ensure_nodes_constraints(
+ sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, limit);
+
/* Store the initial state in the simulation. */
SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);
+ /* Enable the nodes that should be simulated. */
+ SCULPT_cloth_sim_activate_nodes(ss->cache->cloth_sim, nodes, totnode);
+
/* Apply forces to the vertices. */
cloth_brush_apply_brush_foces(sd, ob, nodes, totnode);
@@ -1029,6 +1123,8 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim)
MEM_SAFE_FREE(cloth_sim->deformation_pos);
MEM_SAFE_FREE(cloth_sim->init_pos);
MEM_SAFE_FREE(cloth_sim->deformation_strength);
+ MEM_SAFE_FREE(cloth_sim->node_state);
+ BLI_ghash_free(cloth_sim->node_state_index, NULL, NULL);
if (cloth_sim->collider_list) {
BKE_collider_cache_free(&cloth_sim->collider_list);
}
@@ -1313,6 +1409,10 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, cloth_filter_apply_forces_task_cb, &settings);
+ /* Activate all nodes. */
+ SCULPT_cloth_sim_activate_nodes(
+ ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode);
+
/* Update and write the simulation to the nodes. */
SCULPT_cloth_brush_do_simulation_step(
sd, ob, ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode);
@@ -1363,13 +1463,13 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_cloth_brush_simulation_init(ss, ss->filter_cache->cloth_sim);
float origin[3] = {0.0f, 0.0f, 0.0f};
- SCULPT_cloth_brush_build_nodes_constraints(sd,
- ob,
- ss->filter_cache->nodes,
- ss->filter_cache->totnode,
- ss->filter_cache->cloth_sim,
- origin,
- FLT_MAX);
+ SCULPT_cloth_brush_ensure_nodes_constraints(sd,
+ ob,
+ ss->filter_cache->nodes,
+ ss->filter_cache->totnode,
+ ss->filter_cache->cloth_sim,
+ origin,
+ FLT_MAX);
const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
if (use_face_sets) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index b846e009369..e6710e27115 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -372,6 +372,11 @@ struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct Sculpt
const bool needs_deform_coords);
void SCULPT_cloth_brush_simulation_init(struct SculptSession *ss,
struct SculptClothSimulation *cloth_sim);
+
+void SCULPT_cloth_sim_activate_nodes(struct SculptClothSimulation *cloth_sim,
+ PBVHNode **nodes,
+ int totnode);
+
void SCULPT_cloth_brush_store_simulation_state(struct SculptSession *ss,
struct SculptClothSimulation *cloth_sim);
@@ -381,13 +386,13 @@ void SCULPT_cloth_brush_do_simulation_step(struct Sculpt *sd,
struct PBVHNode **nodes,
int totnode);
-void SCULPT_cloth_brush_build_nodes_constraints(struct Sculpt *sd,
- struct Object *ob,
- struct PBVHNode **nodes,
- int totnode,
- struct SculptClothSimulation *cloth_sim,
- float initial_location[3],
- const float radius);
+void SCULPT_cloth_brush_ensure_nodes_constraints(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode,
+ struct SculptClothSimulation *cloth_sim,
+ float initial_location[3],
+ const float radius);
void SCULPT_cloth_simulation_limits_draw(const uint gpuattr,
const struct Brush *brush,
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 6c3ffc09919..07186425c3f 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -359,6 +359,7 @@ typedef enum eBrushClothForceFalloffType {
typedef enum eBrushClothSimulationAreaType {
BRUSH_CLOTH_SIMULATION_AREA_LOCAL = 0,
BRUSH_CLOTH_SIMULATION_AREA_GLOBAL = 1,
+ BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC = 2,
} eBrushClothSimulationAreaType;
typedef enum eBrushPoseDeformType {
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 7586bdf2d3f..82db67e0612 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -2099,6 +2099,11 @@ static void rna_def_brush(BlenderRNA *brna)
"Local",
"Simulates only a specific area around the brush limited by a fixed radius"},
{BRUSH_CLOTH_SIMULATION_AREA_GLOBAL, "GLOBAL", 0, "Global", "Simulates the entire mesh"},
+ {BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC,
+ "DYNAMIC",
+ 0,
+ "Dynamic",
+ "The active simulation area moves with the brush"},
{0, NULL, 0, NULL, NULL},
};