Sculpt: experimental bending constraints for cloth

brush.

See "bending" checkmark in the cloth settings.
This commit is contained in:
Joseph Eagar 2021-10-10 14:37:26 -07:00
parent 3b167c257d
commit 3083e3fd26
11 changed files with 1404 additions and 192 deletions

View File

@ -23,6 +23,14 @@ from bpy.types import Menu, Panel
classes = []
builtin_channel_categories = ["Cloth Tool",
"Color",
"Clay",
"Pose Tool",
"Basic",
"Smoothing",
"Stroke",
"Automasking"]
class DynamicBrushCategoryPanel(Panel):
bl_space_type = 'VIEW_3D'
@ -184,6 +192,11 @@ classes.append(CLASSNAME)
exec(code)
#pre create category panels in correct order
for cat in builtin_channel_categories:
DynamicPaintPanelGen.ensureCategory(cat, cat, parent="VIEW3D_PT_tools_brush_settings_channels", prefix="VIEW3D_PT_brush_category_")
DynamicPaintPanelGen.ensureCategory(cat, cat, prefix="VIEW3D_PT_brush_category_edit_",
parent="VIEW3D_PT_tools_brush_settings_channels_preview")
channel_name_map = {
"size": "radius",
@ -194,7 +207,8 @@ channel_name_map = {
"boundary_smooth_factor": "boundary_smooth",
"autosmooth_fset_slide": "fset_slide",
"topology_rake_factor": "topology_rake",
"use_locked_size": "radius_unit"
"use_locked_size": "radius_unit",
"use_cloth_collision" : "cloth_use_collision"
}
expand_channels = {"direction", "radius_unit", "automasking"}

View File

@ -302,43 +302,73 @@ typedef enum eSculptClothConstraintType {
SCULPT_CLOTH_CONSTRAINT_PIN = 3,
} eSculptClothConstraintType;
typedef struct SculptClothLengthConstraint {
/* Elements that are affected by the constraint. */
/* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always
* deformed. Element b could be another vertex of the same mesh or any other position (arbitrary
* point, position for a previous state). In that case, elem_index_a and elem_index_b should be
* the same to avoid affecting two different vertices when solving the constraints.
* *elem_position points to the position which is owned by the element. */
int elem_index_a;
float *elem_position_a;
typedef struct SculptClothConstraint {
signed char ctype, thread_nr;
int elem_index_b;
float *elem_position_b;
/* 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. */
short node;
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;
int thread_nr;
/* Elements that are affected by the constraint. */
/* Element a should always be a mesh vertex
* with the index stored in elem_index_a as
* it is \
* always deformed. Element b could be
* another vertex of the same mesh or any
* other position \
* (arbitrary point, position for a previous
* state). In that case, elem_index_a and \
* elem_index_b should be the same to avoid
* affecting two different vertices when
* solving the \
* constraints. *elem_position points to the
* position which is owned by the element. */
struct {
int index;
float *co;
} elems[];
} SculptClothConstraint;
#define MAKE_CONSTRAINT_STRUCT(totelem) \
signed char ctype, thread_nr; \
short node; \
float strength; \
struct { \
int index; \
float *position; \
} elems[totelem]
typedef struct SculptClothLengthConstraint {
MAKE_CONSTRAINT_STRUCT(2);
float length;
eSculptClothConstraintType type;
} SculptClothLengthConstraint;
typedef struct SculptClothTaskData {
SculptClothLengthConstraint **length_constraints;
int tot_length_constraints;
} SculptClothTaskData;
typedef struct SculptClothBendConstraint {
MAKE_CONSTRAINT_STRUCT(4);
float rest_angle, stiffness;
} SculptClothBendConstraint;
struct SculptClothTaskData;
typedef struct SculptClothSimulation {
SculptClothLengthConstraint *length_constraints;
int tot_length_constraints;
SculptClothConstraint *constraints[2];
int tot_constraints[2];
int capacity_constraints[2];
struct EdgeSet *created_length_constraints;
int capacity_length_constraints;
struct EdgeSet *created_bend_constraints;
float *length_constraint_tweak;
SculptClothTaskData *constraint_tasks;
SculptClothBendConstraint *bend_constraints;
int tot_bend_constraints, capacity_bend_constraints;
struct SculptClothTaskData *constraint_tasks;
/* final task always run in main thread, after all the others
* have completed
@ -380,6 +410,9 @@ typedef struct SculptClothSimulation {
int cd_pers_co;
int cd_pers_no;
int cd_pers_disp;
bool use_bending;
float bend_stiffness;
} SculptClothSimulation;
typedef struct SculptVertexInfo {

View File

@ -403,7 +403,9 @@ MAKE_BOOL(cloth_use_collision, "Enable Collision", "Collide with objects during
MAKE_BOOL(use_frontface, "Use Front-Face", "Brush only affects vertexes that face the viewer", false)
MAKE_BOOL(cloth_pin_simulation_boundary, "Pin Simulation Boundary",
"Lock the position of the vertices in the simulation falloff area to avoid artifacts and "
"create a softer transition with unaffected areas", false)
"create a softer transition with unaffected areas", true)
MAKE_BOOL(cloth_solve_bending, "Bending", "Solve for bending", false)
MAKE_FLOAT(cloth_bending_stiffness, "Bending Stiffness", "", 0.5f, 0.0f, 1.0f)
MAKE_FLOAT(boundary_offset, "Boundary Origin Offset",
"Offset of the boundary origin in relation to the brush radius", 0.05f, 0.0f, 10.0f)

View File

@ -1586,8 +1586,8 @@ void BKE_builtin_commandlist_create(Brush *brush,
float cloth_radius_mul = 1.0f;
if (is_cloth && (ch = BRUSHSET_LOOKUP(chset, cloth_sim_limit))) {
autosmooth_scale *= ch->fvalue;
cloth_radius_mul = ch->fvalue;
cloth_radius_mul += ch->fvalue;
autosmooth_scale *= cloth_radius_mul;
}
float autosmooth_spacing;

View File

@ -329,6 +329,8 @@ static bool check_builtin_init()
SETCAT(cloth_constraint_softbody_strength, "Cloth Tool");
SETCAT(cloth_use_collision, "Cloth Tool");
SETCAT(cloth_pin_simulation_boundary, "Cloth Tool");
SETCAT(cloth_solve_bending, "Cloth Tool");
SETCAT(cloth_bending_stiffness, "Cloth Tool");
SETCAT(pose_offset, "Pose Tool");
SETCAT(pose_smooth_iterations, "Pose Tool");
@ -1151,6 +1153,8 @@ void BKE_brush_builtin_patch(Brush *brush, int tool)
ADDCH(slide_deform_type);
break;
case SCULPT_TOOL_CLOTH:
ADDCH(cloth_solve_bending);
ADDCH(cloth_bending_stiffness);
ADDCH(cloth_mass);
ADDCH(cloth_damping);
ADDCH(cloth_sim_limit);
@ -1161,6 +1165,7 @@ void BKE_brush_builtin_patch(Brush *brush, int tool)
ADDCH(cloth_deform_type);
ADDCH(cloth_use_collision);
ADDCH(cloth_pin_simulation_boundary);
ADDCH(cloth_constraint_softbody_strength);
break;
case SCULPT_TOOL_SNAKE_HOOK:
@ -1408,6 +1413,10 @@ void BKE_brush_channelset_ui_init(Brush *brush, int tool)
SHOWCTX(height);
break;
case SCULPT_TOOL_CLOTH:
SHOWWRK(spacing);
SHOWCTX(spacing);
SHOWWRK(cloth_solve_bending);
SHOWWRK(cloth_bending_stiffness);
SHOWWRK(cloth_deform_type);
SHOWWRK(cloth_force_falloff_type);
SHOWWRK(cloth_simulation_area_type);
@ -1711,6 +1720,7 @@ void BKE_brush_builtin_create(Brush *brush, int tool)
GETCH(dyntopo_disabled)->ivalue = true;
break;
case SCULPT_TOOL_CLOTH:
BRUSHSET_SET_BOOL(chset, cloth_pin_simulation_boundary, true);
BRUSHSET_SET_BOOL(chset, use_space_attenuation, false);
GETCH(radius)->mappings[BRUSH_MAPPING_PRESSURE].flag &= ~BRUSH_MAPPING_ENABLED;
GETCH(strength)->mappings[BRUSH_MAPPING_PRESSURE].flag &= ~BRUSH_MAPPING_ENABLED;

View File

@ -1836,6 +1836,14 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 36)) {
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
if (brush->channels && brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
BKE_brush_channelset_ui_init(brush, brush->sculpt_tool);
}
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -36,6 +36,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_memarena.h"
#include "BLI_rand.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@ -273,7 +274,7 @@ void SCULPT_face_random_access_ensure(SculptSession *ss)
}
}
int SCULPT_vertex_count_get(SculptSession *ss)
int SCULPT_vertex_count_get(const SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@ -820,9 +821,15 @@ bool SCULPT_temp_customlayer_ensure(SculptSession *ss,
SculptLayerParams *params)
{
SculptCustomLayer scl;
// call SCULPT_update_customdata_refs before and after,
// thoeretically it can allocate new layers
SCULPT_update_customdata_refs(ss);
bool ret = sculpt_temp_customlayer_get(ss, domain, proptype, name, &scl, true, params);
SCULPT_update_customdata_refs(ss);
return ret;
}
@ -1710,8 +1717,6 @@ int SCULPT_face_set_next_available_get(SculptSession *ss)
/* Sculpt Neighbor Iterators */
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter,
SculptVertRef neighbor,
SculptEdgeRef edge,
@ -1727,19 +1732,21 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter,
iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
if (iter->neighbors == iter->neighbors_fixed) {
iter->neighbors = MEM_mallocN(iter->capacity * sizeof(struct _SculptNeighborRef),
"neighbor array");
iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
iter->neighbors = MEM_mallocN(iter->capacity * sizeof(*iter->neighbors), "neighbor array");
iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(*iter->neighbor_indices),
"neighbor array");
memcpy(
iter->neighbors, iter->neighbors_fixed, sizeof(struct _SculptNeighborRef) * iter->size);
memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size);
memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(*iter->neighbors) * iter->size);
memcpy(iter->neighbor_indices,
iter->neighbor_indices_fixed,
sizeof(*iter->neighbor_indices) * iter->size);
}
else {
iter->neighbors = MEM_reallocN_id(
iter->neighbors, iter->capacity * sizeof(struct _SculptNeighborRef), "neighbor array");
iter->neighbor_indices = MEM_reallocN_id(
iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array");
iter->neighbors, iter->capacity * sizeof(*iter->neighbors), "neighbor array");
iter->neighbor_indices = MEM_reallocN_id(iter->neighbor_indices,
iter->capacity * sizeof(*iter->neighbor_indices),
"neighbor array");
}
}
@ -1758,19 +1765,22 @@ static void sculpt_vertex_neighbor_add_nocheck(SculptVertexNeighborIter *iter,
iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
if (iter->neighbors == iter->neighbors_fixed) {
iter->neighbors = MEM_mallocN(iter->capacity * sizeof(struct _SculptNeighborRef),
"neighbor array");
iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
iter->neighbors = MEM_mallocN(iter->capacity * sizeof(*iter->neighbors), "neighbor array");
iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(*iter->neighbor_indices),
"neighbor array");
memcpy(
iter->neighbors, iter->neighbors_fixed, sizeof(struct _SculptNeighborRef) * iter->size);
memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size);
memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(*iter->neighbors) * iter->size);
memcpy(iter->neighbor_indices,
iter->neighbor_indices_fixed,
sizeof(*iter->neighbor_indices) * iter->size);
}
else {
iter->neighbors = MEM_reallocN_id(
iter->neighbors, iter->capacity * sizeof(struct _SculptNeighborRef), "neighbor array");
iter->neighbor_indices = MEM_reallocN_id(
iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array");
iter->neighbors, iter->capacity * sizeof(*iter->neighbors), "neighbor array");
iter->neighbor_indices = MEM_reallocN_id(iter->neighbor_indices,
iter->capacity * sizeof(*iter->neighbor_indices),
"neighbor array");
}
}
@ -1794,6 +1804,7 @@ static void sculpt_vertex_neighbors_get_bmesh(const SculptSession *ss,
iter->neighbors = iter->neighbors_fixed;
iter->neighbor_indices = iter->neighbor_indices_fixed;
iter->i = 0;
iter->no_free = false;
// cache profiling revealed a hotspot here, don't use BM_ITER
BMEdge *e = v->e;
@ -1846,6 +1857,7 @@ static void sculpt_vertex_neighbors_get_faces(const SculptSession *ss,
iter->neighbor_indices = iter->neighbor_indices_fixed;
iter->is_duplicate = false;
iter->has_edge = true;
iter->no_free = false;
for (int i = 0; i < ss->pmap[index].count; i++) {
if (ss->face_sets[vert_map->indices[i]] < 0) {
@ -1908,6 +1920,7 @@ static void sculpt_vertex_neighbors_get_faces_vemap(const SculptSession *ss,
iter->neighbors = iter->neighbors_fixed;
iter->neighbor_indices = iter->neighbor_indices_fixed;
iter->is_duplicate = false;
iter->no_free = false;
for (int i = 0; i < vert_map->count; i++) {
const MEdge *me = &ss->medge[vert_map->indices[i]];
@ -1963,6 +1976,7 @@ static void sculpt_vertex_neighbors_get_grids(const SculptSession *ss,
iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
iter->neighbors = iter->neighbors_fixed;
iter->neighbor_indices = iter->neighbor_indices_fixed;
iter->no_free = false;
for (int i = 0; i < neighbors.size; i++) {
int idx = neighbors.coords[i].grid_index * key->grid_area +
@ -1987,11 +2001,175 @@ static void sculpt_vertex_neighbors_get_grids(const SculptSession *ss,
}
}
#define SCULPT_NEIGHBORS_CACHE
#ifdef SCULPT_NEIGHBORS_CACHE
typedef struct NeighborCacheItem {
struct _SculptNeighborRef *neighbors;
int *neighbors_indices;
short num_duplicates, valence;
bool has_edge;
} NeighborCacheItem;
typedef struct NeighborCache {
MemArena **arenas;
NeighborCacheItem **cache;
NeighborCacheItem **duplicates;
int totvert, totthread;
} NeighborCache;
static void neighbor_cache_free(NeighborCache *ncache)
{
for (int i = 0; i < ncache->totthread; i++) {
BLI_memarena_free(ncache->arenas[i]);
}
MEM_SAFE_FREE(ncache->arenas);
MEM_SAFE_FREE(ncache->cache);
MEM_SAFE_FREE(ncache->duplicates);
MEM_SAFE_FREE(ncache);
}
static bool neighbor_cache_begin(const SculptSession *ss)
{
if (!ss->cache) {
return false;
}
// return false;
Brush *brush = ss->cache->brush;
if (brush && SCULPT_stroke_is_dynamic_topology(ss, brush)) {
return false;
}
if (ss->cache->ncache) {
return true;
}
int totvert = SCULPT_vertex_count_get(ss);
NeighborCache *ncache = MEM_callocN(sizeof(NeighborCache), "NeighborCache");
ncache->cache = MEM_calloc_arrayN(totvert, sizeof(void *), "Cache Items");
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
ncache->duplicates = MEM_calloc_arrayN(totvert, sizeof(void *), "Cache Items");
}
int totthread = BLI_task_scheduler_num_threads() * 4;
ncache->arenas = MEM_malloc_arrayN(totthread, sizeof(void *), "neighbor cache->arenas");
ncache->totthread = totthread;
for (int i = 0; i < totthread; i++) {
ncache->arenas[i] = BLI_memarena_new(1 << 17, "neighbor cache");
}
ncache->totvert = totvert;
NeighborCache *old = ss->cache->ncache;
atomic_cas_ptr(&ss->cache->ncache, NULL, ncache);
if (ss->cache->ncache != ncache) {
// another thread got here first?
neighbor_cache_free(ncache);
return false;
}
return true;
}
static NeighborCacheItem *neighbor_cache_get(const SculptSession *ss,
SculptVertRef vertex,
const bool include_duplicates)
{
int i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
NeighborCache *ncache = ss->cache->ncache;
if (include_duplicates && !ncache->duplicates) {
ncache->duplicates = MEM_calloc_arrayN(ncache->totvert, sizeof(void *), "ncache->duplicages");
}
NeighborCacheItem **cache = include_duplicates ? ncache->duplicates : ncache->cache;
int thread_nr = BLI_task_parallel_thread_id(NULL);
if (!cache[i]) {
NeighborCacheItem *item = BLI_memarena_calloc(ncache->arenas[thread_nr], sizeof(*item));
SculptVertexNeighborIter ni = {0};
ni.neighbors = ni.neighbors_fixed;
ni.neighbor_indices = ni.neighbor_indices_fixed;
ni.capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
// use vemap if it exists, so result is in disk cycle order
if (ss->vemap) {
sculpt_vertex_neighbors_get_faces_vemap(ss, vertex, &ni);
}
else {
sculpt_vertex_neighbors_get_faces(ss, vertex, &ni);
}
break;
case PBVH_BMESH:
sculpt_vertex_neighbors_get_bmesh(ss, vertex, &ni);
break;
case PBVH_GRIDS:
sculpt_vertex_neighbors_get_grids(ss, vertex, include_duplicates, &ni);
break;
}
item->num_duplicates = ni.num_duplicates;
item->has_edge = ni.has_edge;
item->valence = ni.size;
item->neighbors = BLI_memarena_alloc(ncache->arenas[thread_nr],
sizeof(*item->neighbors) * ni.size);
item->neighbors_indices = BLI_memarena_alloc(ncache->arenas[thread_nr],
sizeof(*item->neighbors_indices) * ni.size);
memcpy(item->neighbors, ni.neighbors, sizeof(*item->neighbors) * ni.size);
memcpy(
item->neighbors_indices, ni.neighbor_indices, sizeof(*item->neighbors_indices) * ni.size);
// if (atomic_cas_ptr(&cache[i], NULL, item) != item) {
// another thread got here first
//}
atomic_cas_ptr(&cache[i], NULL, item);
}
return cache[i];
}
#endif
void SCULPT_vertex_neighbors_get(const SculptSession *ss,
const SculptVertRef vertex,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
{
#ifdef SCULPT_NEIGHBORS_CACHE
if (neighbor_cache_begin(ss)) {
NeighborCacheItem *item = neighbor_cache_get(ss, vertex, include_duplicates);
// memset(iter, 0, sizeof(*iter));
iter->no_free = true;
iter->neighbors = item->neighbors;
iter->neighbor_indices = item->neighbors_indices;
iter->num_duplicates = item->num_duplicates;
iter->size = iter->capacity = item->valence;
iter->has_edge = item->has_edge;
iter->is_duplicate = false;
return;
}
#endif
iter->no_free = false;
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
// use vemap if it exists, so result is in disk cycle order
@ -9344,8 +9522,12 @@ typedef struct BrushRunCommandData {
float radius_max;
} BrushRunCommandData;
static void get_nodes_undo(
Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, BrushRunCommandData *data)
static void get_nodes_undo(Sculpt *sd,
Object *ob,
Brush *brush,
UnifiedPaintSettings *ups,
BrushRunCommandData *data,
int tool)
{
PBVHNode **nodes = NULL;
int totnode = 0;
@ -9360,13 +9542,13 @@ static void get_nodes_undo(
*/
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
}
else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
else if (tool == SCULPT_TOOL_CLOTH) {
nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode);
}
else {
/* With these options enabled not all required nodes are inside the original brush radius,
* so the brush can produce artifacts in some situations. */
if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) {
if (tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) {
radius_scale = MAX2(radius_scale, 2.0f);
}
nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode);
@ -9376,8 +9558,8 @@ static void get_nodes_undo(
* vertices and uses regular coords undo. */
/* It also assigns the paint_face_set here as it needs to be done regardless of the stroke type
* and the number of nodes under the brush influence. */
if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS &&
SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) {
if (tool == SCULPT_TOOL_DRAW_FACE_SETS && SCULPT_stroke_is_first_brush_step(ss->cache) &&
!ss->cache->alt_smooth) {
// faceset undo node is created below for pbvh_bmesh
if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
@ -9421,7 +9603,7 @@ static void get_nodes_undo(
// dyntopo can't push undo nodes inside a thread
if (ss->bm) {
if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
if (ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
for (int i = 0; i < totnode; i++) {
int other = brush->vcol_boundary_factor > 0.0f ? SCULPT_UNDO_COORDS : -1;
@ -9429,7 +9611,7 @@ static void get_nodes_undo(
BKE_pbvh_node_mark_update_color(nodes[i]);
}
}
else if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
else if (tool == SCULPT_TOOL_DRAW_FACE_SETS) {
for (int i = 0; i < totnode; i++) {
if (ss->cache->alt_smooth) {
SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, SCULPT_UNDO_COORDS);
@ -9491,7 +9673,14 @@ static void SCULPT_run_command(
BrushRunCommandData *data = userdata;
BrushCommand *cmd = data->cmd;
get_nodes_undo(sd, ob, ss->cache->brush, ups, data);
float radius = BRUSHSET_GET_FLOAT(cmd->params_mapped, radius, NULL);
radius = paint_calc_object_space_radius(ss->cache->vc, ss->cache->true_location, radius);
ss->cache->radius = radius;
ss->cache->radius_squared = radius * radius;
ss->cache->initial_radius = radius;
get_nodes_undo(sd, ob, ss->cache->brush, ups, data, cmd->tool);
PBVHNode **nodes = data->nodes;
int totnode = data->totnode;
@ -9507,13 +9696,6 @@ static void SCULPT_run_command(
BKE_brush_channelset_apply_mapping(cmd->params_mapped, &ss->cache->input_mapping);
BKE_brush_channelset_clear_inherit(cmd->params_mapped);
float radius = BRUSHSET_GET_FLOAT(cmd->params_mapped, radius, NULL);
radius = paint_calc_object_space_radius(ss->cache->vc, ss->cache->true_location, radius);
ss->cache->radius = radius;
ss->cache->radius_squared = radius * radius;
ss->cache->initial_radius = radius;
radius_scale = 1.0f;
*brush2 = *brush;
@ -10522,6 +10704,13 @@ void SCULPT_cache_free(SculptSession *ss, StrokeCache *cache)
MEM_SAFE_FREE(cache->dial);
MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp);
#ifdef SCULPT_NEIGHBORS_CACHE
if (ss->cache->ncache) {
neighbor_cache_free(ss->cache->ncache);
ss->cache->ncache = NULL;
}
#endif
if (ss->custom_layers[SCULPT_SCL_LAYER_DISP]) {
SCULPT_temp_customlayer_release(ss, ss->custom_layers[SCULPT_SCL_LAYER_DISP]);
MEM_freeN(ss->custom_layers[SCULPT_SCL_LAYER_DISP]);
@ -13841,6 +14030,20 @@ static void sculpt_mask_by_color_full_mesh(Object *object,
MEM_SAFE_FREE(nodes);
}
void SCULPT_ensure_epmap(SculptSession *ss)
{
if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH && !ss->epmap) {
BKE_mesh_edge_poly_map_create(&ss->epmap,
&ss->epmap_mem,
ss->medge,
ss->totedges,
ss->mpoly,
ss->totfaces,
ss->mloop,
ss->totloops);
}
}
static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);

File diff suppressed because it is too large Load Diff

View File

@ -1409,8 +1409,9 @@ static void sculpt_expand_cancel(bContext *C, wmOperator *UNUSED(op))
/**
* Callback to update mask data per PBVH node.
*/
ATTR_NO_OPT static void sculpt_expand_mask_update_task_cb(
void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls))
static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;

View File

@ -143,7 +143,7 @@ void SCULPT_face_random_access_ensure(struct SculptSession *ss);
int SCULPT_vertex_valence_get(const struct SculptSession *ss, SculptVertRef vertex);
int SCULPT_vertex_count_get(struct SculptSession *ss);
int SCULPT_vertex_count_get(const struct SculptSession *ss);
const float *SCULPT_vertex_co_get(struct SculptSession *ss, SculptVertRef index);
void SCULPT_vertex_normal_get(SculptSession *ss, SculptVertRef index, float no[3]);
float SCULPT_vertex_mask_get(struct SculptSession *ss, SculptVertRef index);
@ -172,7 +172,8 @@ struct _SculptNeighborRef {
SculptEdgeRef edge;
};
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 12
typedef struct SculptVertexNeighborIter {
/* Storage */
struct _SculptNeighborRef *neighbors;
@ -193,6 +194,7 @@ typedef struct SculptVertexNeighborIter {
int index;
bool has_edge; // does this iteration step have an edge, fake neighbors do not
bool is_duplicate;
bool no_free;
} SculptVertexNeighborIter;
void SCULPT_vertex_neighbors_get(const struct SculptSession *ss,
@ -227,8 +229,18 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss,
#define SCULPT_VERTEX_NEIGHBORS_ITER_END(neighbor_iterator) \
} \
if (neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \
if (!neighbor_iterator.no_free && \
neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \
MEM_freeN(neighbor_iterator.neighbors); \
MEM_freeN(neighbor_iterator.neighbor_indices); \
} \
((void)0)
#define SCULPT_VERTEX_NEIGHBORS_ITER_FREE(neighbor_iterator) \
if (neighbor_iterator.neighbors && !neighbor_iterator.no_free && \
neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \
MEM_freeN(neighbor_iterator.neighbors); \
MEM_freeN(neighbor_iterator.neighbor_indices); \
} \
((void)0)
@ -371,6 +383,8 @@ bool SCULPT_stroke_is_main_symmetry_pass(struct StrokeCache *cache);
bool SCULPT_stroke_is_first_brush_step(struct StrokeCache *cache);
bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cache);
void SCULPT_ensure_epmap(SculptSession *ss);
/* Sculpt Original Data */
typedef struct {
struct BMLog *bm_log;
@ -1067,6 +1081,7 @@ typedef struct SculptThreadedTaskData {
float fset_slide, bound_smooth;
float crease_pinch_factor;
bool use_curvature;
float vel_smooth_fac;
} SculptThreadedTaskData;
/*************** Brush testing declarations ****************/
@ -1376,7 +1391,10 @@ typedef struct StrokeCache {
struct BrushCommandList *commandlist;
bool use_plane_trim;
struct NeighborCache *ncache;
} StrokeCache;
/* Sculpt Filters */
typedef enum SculptFilterOrientation {
SCULPT_FILTER_ORIENTATION_LOCAL = 0,

View File

@ -134,6 +134,20 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
areas = BLI_array_alloca(areas, val);
BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
float totarea = 0.0f;
for (int i = 0; i < val; i++) {
totarea += areas[i];
}
totarea = totarea != 0.0f ? 1.0f / totarea : 0.0f;
float df = 0.25f / (float)val;
for (int i = 0; i < val; i++) {
areas[i] = (areas[i] * totarea) + df;
}
}
float *b1 = NULL, btot = 0.0f, b1_orig;
@ -443,6 +457,17 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss,
areas = BLI_array_alloca(areas, val * 2);
BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
float totarea = 0.0f;
for (int i = 0; i < val; i++) {
totarea += areas[i];
}
totarea = totarea != 0.0f ? 1.0f / totarea : 0.0f;
for (int i = 0; i < val; i++) {
areas[i] *= totarea;
}
}
copy_v3_v3(dir, col);
@ -990,6 +1015,10 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
float bstrength = data->strength;
float projection = data->smooth_projection;
if (!data->nodes[n]) {
return;
}
PBVHVertexIter vd;
CLAMP(bstrength, 0.0f, 1.0f);
@ -1017,12 +1046,15 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
// const float slide_fset = BKE_brush_fset_slide_get(ss->scene, ss->cache->brush);
SculptCustomLayer *bound_scl = data->scl2;
SculptCustomLayer *vel_scl = data->scl;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
modified = true;
// check origdata to be sure we don't mess it up
SCULPT_vertex_check_origdata(ss, vd.vertex);
@ -1068,15 +1100,28 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
BRUSH_SMOOTH_PRESERVE_FACE_SETS}));
sub_v3_v3v3(val, avg, co);
if (vel_scl) {
float *vel = SCULPT_temp_cdata_get(vd.vertex, vel_scl);
float oldval[3];
copy_v3_v3(oldval, val);
madd_v3_v3fl(val, vel, data->vel_smooth_fac);
interp_v3_v3v3(vel, vel, oldval, 0.25);
mul_v3_fl(vel, 0.95);
// madd_v3_v3fl(vel, dvel, 0.125);
}
madd_v3_v3v3fl(val, co, val, fade);
SCULPT_clip(sd, ss, co, val);
// interp_v3_v3v3(vel, vel, val, 0.5);
}
}
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
modified = true;
}
BKE_pbvh_vertex_iter_end;
@ -1087,6 +1132,10 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_node_mark_update(data->nodes[n]);
}
else {
// not modified? remove from future iterations
data->nodes[n] = NULL;
}
}
#endif
@ -1102,7 +1151,10 @@ void SCULPT_smooth(Sculpt *sd,
SculptSession *ss = ob->sculpt;
Brush *brush = ss->cache && ss->cache->brush ? ss->cache->brush : BKE_paint_brush(&sd->paint);
const int max_iterations = 4;
const float vel_smooth_cutoff = 0.75;
const bool do_vel_smooth = bstrength > vel_smooth_cutoff;
const int max_iterations = (int)(4.0f * ceilf(bstrength));
const float fract = 1.0f / max_iterations;
PBVHType type = BKE_pbvh_type(ss->pbvh);
int iteration, count;
@ -1114,22 +1166,34 @@ void SCULPT_smooth(Sculpt *sd,
BKE_pbvh_update_all_tri_areas(ss->pbvh);
}
CLAMP(bstrength, 0.0f, 1.0f);
SculptLayerParams params = {.permanent = false, .simple_array = false};
SculptCustomLayer vel_scl;
count = (int)(bstrength * max_iterations);
last = max_iterations * (bstrength - count * fract);
SculptCustomLayer scl;
#if 0
bool have_scl = smooth_mask ? false :
SCULPT_temp_customlayer_ensure(
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel");
if (have_scl) {
SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &scl, false);
if (do_vel_smooth) {
SCULPT_temp_customlayer_ensure(
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &params);
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &vel_scl, &params);
}
float bstrength2 = bstrength;
CLAMP(bstrength2, 0.0f, 1.0f);
count = (int)(bstrength2 * max_iterations);
last = max_iterations * (bstrength2 - count * fract);
if (do_vel_smooth && last == 0.0f) {
count--;
last = 1.0f;
count = MAX2(count, 1);
}
// increase strength of last to compensate for velocity smooth in previous iterations
if (count > 1) {
// last = sqrtf(last);
last = 1.0f;
}
#else
bool have_scl = false;
#endif
if (type == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "sculpt smooth: pmap missing");
@ -1155,8 +1219,15 @@ void SCULPT_smooth(Sculpt *sd,
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist", &params);
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist", bound_scl, &params);
if (do_vel_smooth) {
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &vel_scl, &params);
}
}
nodes = nodes ? MEM_dupallocN(nodes) : NULL;
#ifdef PROXY_ADVANCED
int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK;
BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask);
@ -1166,6 +1237,19 @@ void SCULPT_smooth(Sculpt *sd,
for (iteration = 0; iteration <= count; iteration++) {
const float strength = (iteration != count) ? 1.0f : last;
// turn off velocity smooth for final iteration or two to smooth out ripples
bool do_vel = do_vel_smooth && iteration != count;
if (count > 3) {
do_vel = do_vel && iteration != count - 1;
}
float vel_fac = 1.0f;
if (do_vel) {
vel_fac = (bstrength - vel_smooth_cutoff) / 2.0f;
vel_fac = min_ff(vel_fac, 1.0f);
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
@ -1176,8 +1260,9 @@ void SCULPT_smooth(Sculpt *sd,
.smooth_projection = projection,
.fset_slide = fset_slide,
.bound_smooth = bound_smooth,
.scl = have_scl ? &scl : NULL,
.scl = do_vel ? &vel_scl : NULL,
.scl2 = bound_scl,
.vel_smooth_fac = vel_fac,
.do_origco = do_origco,
};