* Prototyped a faster smooth algo, currently disabled.
* Wrote a new API for wrangling temporary customdata layers across pbvh types:
- SCULPT_temp_customlayer_ensure: makes sure a (named) customdata
layer exists. Works for multires; since these are temporary
layers we can safely allocate them in a temporary CustomData
structure (in ss->temp_vdata).
- SCULPT_temp_customlayer_get: initializes a special structure,
SculptCustomLayer, that is used to get per elem customdata.
- SCULPT_temp_cdata_get: Uses a SculptCustomLayer ref along with
a SculptVertexRef to look up the data.
This commit is contained in:
@@ -605,6 +605,8 @@ void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *da
|
||||
void CustomData_unmark_temporary_nocopy(struct CustomData *data);
|
||||
void CustomData_mark_temporary_nocopy(struct CustomData *data);
|
||||
|
||||
int CustomData_get_elem_size(CustomDataLayer *layer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "DNA_brush_enums.h"
|
||||
#include "DNA_customdata_types.h"
|
||||
#include "DNA_object_enums.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -471,6 +472,12 @@ typedef struct SculptSession {
|
||||
struct MPoly *mpoly;
|
||||
struct MLoop *mloop;
|
||||
|
||||
// only assigned in PBVH_FACES and PBVH_GRIDS
|
||||
CustomData *vdata, *edata, *ldata, *pdata;
|
||||
|
||||
// for grids
|
||||
CustomData temp_vdata, temp_pdata;
|
||||
|
||||
/* These contain the vertex and poly counts of the final mesh. */
|
||||
int totvert, totpoly;
|
||||
|
||||
|
||||
@@ -2107,6 +2107,11 @@ static const LayerTypeInfo *layerType_getInfo(int type)
|
||||
return &LAYERTYPEINFO[type];
|
||||
}
|
||||
|
||||
int CustomData_get_elem_size(CustomDataLayer *layer)
|
||||
{
|
||||
return layerType_getInfo(layer->type)->size;
|
||||
}
|
||||
|
||||
static const char *layerType_getName(int type)
|
||||
{
|
||||
if (type < 0 || type >= CD_NUMTYPES) {
|
||||
|
||||
@@ -1630,7 +1630,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
|
||||
Scene *scene = DEG_get_input_scene(depsgraph);
|
||||
Sculpt *sd = scene->toolsettings->sculpt;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
const Mesh *me = BKE_object_get_original_mesh(ob);
|
||||
Mesh *me = BKE_object_get_original_mesh(ob);
|
||||
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
|
||||
const bool use_face_sets = (ob->mode & OB_MODE_SCULPT) != 0;
|
||||
|
||||
@@ -1683,6 +1683,11 @@ static void sculpt_update_object(Depsgraph *depsgraph,
|
||||
ss->multires.level = 0;
|
||||
ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
|
||||
ss->vcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
|
||||
|
||||
ss->vdata = &me->vdata;
|
||||
ss->edata = &me->edata;
|
||||
ss->ldata = &me->ldata;
|
||||
ss->pdata = &me->pdata;
|
||||
}
|
||||
|
||||
/* Sculpt Face Sets. */
|
||||
@@ -2233,7 +2238,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
|
||||
pbvh = build_pbvh_for_dynamic_topology(ob);
|
||||
|
||||
ob->sculpt->pbvh = pbvh;
|
||||
//reorder mesh elements to improve memory cache performance
|
||||
// reorder mesh elements to improve memory cache performance
|
||||
SCULPT_reorder_bmesh(ob->sculpt);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -782,7 +782,8 @@ void BM_log_set_cd_offsets(BMLog *log, int cd_dyn_vert)
|
||||
log->cd_dyn_vert = cd_dyn_vert;
|
||||
}
|
||||
|
||||
void BM_log_set_bm(BMesh *bm, BMLog *log) {
|
||||
void BM_log_set_bm(BMesh *bm, BMLog *log)
|
||||
{
|
||||
log->bm = bm;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_heap.h"
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_math.h"
|
||||
@@ -1105,20 +1106,20 @@ bool BM_face_point_inside_test(const BMFace *f, const float co[3])
|
||||
* \note use_tag tags new flags and edges.
|
||||
*/
|
||||
void BM_face_triangulate(BMesh *bm,
|
||||
BMFace *f,
|
||||
BMFace **r_faces_new,
|
||||
int *r_faces_new_tot,
|
||||
BMEdge **r_edges_new,
|
||||
int *r_edges_new_tot,
|
||||
LinkNode **r_faces_double,
|
||||
const int quad_method,
|
||||
const int ngon_method,
|
||||
const bool use_tag,
|
||||
/* use for ngons only! */
|
||||
MemArena *pf_arena,
|
||||
BMFace *f,
|
||||
BMFace **r_faces_new,
|
||||
int *r_faces_new_tot,
|
||||
BMEdge **r_edges_new,
|
||||
int *r_edges_new_tot,
|
||||
LinkNode **r_faces_double,
|
||||
const int quad_method,
|
||||
const int ngon_method,
|
||||
const bool use_tag,
|
||||
/* use for ngons only! */
|
||||
MemArena *pf_arena,
|
||||
|
||||
/* use for MOD_TRIANGULATE_NGON_BEAUTY only! */
|
||||
struct Heap *pf_heap)
|
||||
/* use for MOD_TRIANGULATE_NGON_BEAUTY only! */
|
||||
struct Heap *pf_heap)
|
||||
{
|
||||
const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
|
||||
const bool use_beauty = (ngon_method == MOD_TRIANGULATE_NGON_BEAUTY);
|
||||
@@ -1467,7 +1468,6 @@ void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4])
|
||||
r_verts[3] = l->v;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* faster alternative to:
|
||||
* BM_iter_as_array(bm, BM_LOOPS_OF_FACE, f, (void **)l, 4);
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_ccg.h"
|
||||
#include "BKE_colortools.h"
|
||||
@@ -120,7 +121,6 @@ void SCULPT_vertex_random_access_ensure(SculptSession *ss)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Sculpt PBVH abstraction API
|
||||
*
|
||||
* This is read-only, for writing use PBVH vertex iterators. There vd.index matches
|
||||
@@ -168,7 +168,7 @@ const float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex)
|
||||
const float *SCULPT_vertex_co_get(SculptSession *ss, SculptVertRef index)
|
||||
{
|
||||
if (ss->bm) {
|
||||
return ((BMVert*)index.i)->co;
|
||||
return ((BMVert *)index.i)->co;
|
||||
}
|
||||
|
||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||
@@ -335,6 +335,132 @@ void SCULPT_vertex_persistent_normal_get(SculptSession *ss,
|
||||
SCULPT_vertex_normal_get(ss, index, no);
|
||||
}
|
||||
|
||||
static bool sculpt_temp_customlayer_get(SculptSession *ss,
|
||||
AttributeDomain domain,
|
||||
int proptype,
|
||||
char *name,
|
||||
SculptCustomLayer *out,
|
||||
bool autocreate)
|
||||
{
|
||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||
case PBVH_BMESH: {
|
||||
CustomData *cdata = NULL;
|
||||
|
||||
if (!ss->bm) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
cdata = &ss->bm->vdata;
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
cdata = &ss->bm->pdata;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = CustomData_get_named_layer_index(cdata, proptype, name);
|
||||
|
||||
if (idx < 0) {
|
||||
if (!autocreate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BM_data_layer_add_named(ss->bm, cdata, proptype, name);
|
||||
idx = CustomData_get_named_layer_index(cdata, proptype, name);
|
||||
cdata->layers[idx].flag |= CD_FLAG_TEMPORARY;
|
||||
SCULPT_dyntopo_node_layers_update_offsets(ss);
|
||||
}
|
||||
|
||||
out->data = NULL;
|
||||
out->is_cdlayer = true;
|
||||
out->layer = cdata->layers + idx;
|
||||
out->cd_offset = out->layer->offset;
|
||||
out->elemsize = CustomData_get_elem_size(out->layer);
|
||||
|
||||
break;
|
||||
}
|
||||
case PBVH_FACES: {
|
||||
CustomData *cdata = NULL;
|
||||
int totelem = 0;
|
||||
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
totelem = ss->totvert;
|
||||
cdata = ss->vdata;
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
totelem = ss->totfaces;
|
||||
cdata = ss->pdata;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = CustomData_get_named_layer_index(cdata, proptype, name);
|
||||
|
||||
if (idx < 0) {
|
||||
if (!autocreate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CustomData_add_layer_named(cdata, proptype, CD_CALLOC, NULL, totelem, name);
|
||||
idx = CustomData_get_named_layer_index(cdata, proptype, name);
|
||||
|
||||
cdata->layers[idx].flag |= CD_FLAG_TEMPORARY;
|
||||
}
|
||||
|
||||
out->data = NULL;
|
||||
out->is_cdlayer = true;
|
||||
out->layer = cdata->layers + idx;
|
||||
out->cd_offset = -1;
|
||||
out->data = out->layer->data;
|
||||
out->elemsize = CustomData_get_elem_size(out->layer);
|
||||
break;
|
||||
}
|
||||
case PBVH_GRIDS: {
|
||||
CustomData *cdata = NULL;
|
||||
int totelem = 0;
|
||||
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
totelem = BKE_pbvh_get_grid_num_vertices(ss->pbvh);
|
||||
cdata = &ss->temp_vdata;
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
// not supported
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = CustomData_get_named_layer_index(cdata, proptype, name);
|
||||
|
||||
if (idx < 0) {
|
||||
if (!autocreate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CustomData_add_layer_named(cdata, proptype, CD_CALLOC, NULL, totelem, name);
|
||||
idx = CustomData_get_named_layer_index(cdata, proptype, name);
|
||||
}
|
||||
|
||||
out->data = NULL;
|
||||
out->is_cdlayer = true;
|
||||
out->layer = cdata->layers + idx;
|
||||
out->cd_offset = -1;
|
||||
out->data = out->layer->data;
|
||||
out->elemsize = CustomData_get_elem_size(out->layer);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float SCULPT_vertex_mask_get(SculptSession *ss, SculptVertRef index)
|
||||
{
|
||||
BMVert *v;
|
||||
@@ -358,6 +484,21 @@ float SCULPT_vertex_mask_get(SculptSession *ss, SculptVertRef index)
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool SCULPT_temp_customlayer_ensure(SculptSession *ss,
|
||||
AttributeDomain domain,
|
||||
int proptype,
|
||||
char *name)
|
||||
{
|
||||
SculptCustomLayer scl;
|
||||
return sculpt_temp_customlayer_get(ss, domain, proptype, name, &scl, true);
|
||||
}
|
||||
|
||||
bool SCULPT_temp_customlayer_get(
|
||||
SculptSession *ss, AttributeDomain domain, int proptype, char *name, SculptCustomLayer *scl)
|
||||
{
|
||||
return sculpt_temp_customlayer_get(ss, domain, proptype, name, scl, true);
|
||||
}
|
||||
|
||||
SculptVertRef SCULPT_active_vertex_get(SculptSession *ss)
|
||||
{
|
||||
if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) {
|
||||
@@ -1095,10 +1236,9 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter,
|
||||
iter->size++;
|
||||
}
|
||||
|
||||
|
||||
static void sculpt_vertex_neighbor_add_nocheck(SculptVertexNeighborIter *iter,
|
||||
SculptVertRef neighbor,
|
||||
int neighbor_index)
|
||||
SculptVertRef neighbor,
|
||||
int neighbor_index)
|
||||
{
|
||||
if (iter->size >= iter->capacity) {
|
||||
iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
|
||||
@@ -1147,7 +1287,8 @@ static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss,
|
||||
iter, BKE_pbvh_make_vref((intptr_t)e->v2), BM_elem_index_get(e->v2));
|
||||
|
||||
e = e->v1_disk_link.next;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
sculpt_vertex_neighbor_add_nocheck(
|
||||
iter, BKE_pbvh_make_vref((intptr_t)e->v1), BM_elem_index_get(e->v1));
|
||||
|
||||
@@ -1527,7 +1668,9 @@ void SCULPT_floodfill_add_initial(SculptFloodFill *flood, SculptVertRef vertex)
|
||||
BLI_gsqueue_push(flood->queue, &vertex);
|
||||
}
|
||||
|
||||
void SCULPT_floodfill_add_and_skip_initial(SculptSession *ss, SculptFloodFill *flood, SculptVertRef vertex)
|
||||
void SCULPT_floodfill_add_and_skip_initial(SculptSession *ss,
|
||||
SculptFloodFill *flood,
|
||||
SculptVertRef vertex)
|
||||
{
|
||||
BLI_gsqueue_push(flood->queue, &vertex);
|
||||
BLI_BITMAP_ENABLE(flood->visited_vertices, BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex));
|
||||
@@ -1675,7 +1818,7 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool)
|
||||
static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush *brush)
|
||||
{
|
||||
return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(brush->sculpt_tool) &&
|
||||
(brush->topology_rake_factor > 0.0f) && (ss->bm != NULL);
|
||||
(brush->topology_rake_factor > 0.0f) && (ss->bm != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3492,14 +3635,12 @@ static void bmesh_topology_rake(
|
||||
|
||||
for (iteration = 0; iteration <= count; iteration++) {
|
||||
|
||||
SculptThreadedTaskData data = {
|
||||
.sd = sd,
|
||||
.ob = ob,
|
||||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
.strength = factor,
|
||||
.rake_projection = brush->topology_rake_projection
|
||||
};
|
||||
SculptThreadedTaskData data = {.sd = sd,
|
||||
.ob = ob,
|
||||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
.strength = factor,
|
||||
.rake_projection = brush->topology_rake_projection};
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
|
||||
|
||||
@@ -6882,10 +7023,11 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
|
||||
float spacing = (float)brush->topology_rake_spacing / 100.0f;
|
||||
|
||||
use_topology_rake = use_topology_rake &&
|
||||
paint_stroke_apply_subspacing(ss->cache->stroke,
|
||||
spacing,
|
||||
PAINT_MODE_SCULPT,
|
||||
&ss->cache->last_rake_t[SCULPT_get_symmetry_pass(ss)]);
|
||||
paint_stroke_apply_subspacing(
|
||||
ss->cache->stroke,
|
||||
spacing,
|
||||
PAINT_MODE_SCULPT,
|
||||
&ss->cache->last_rake_t[SCULPT_get_symmetry_pass(ss)]);
|
||||
}
|
||||
|
||||
if (use_topology_rake) {
|
||||
|
||||
@@ -138,6 +138,9 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm)
|
||||
MemArena *pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__);
|
||||
LinkNode *f_double = NULL;
|
||||
|
||||
BMFace **faces_array = NULL;
|
||||
BLI_array_declare(faces_array);
|
||||
|
||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||
if (f->len <= 3) {
|
||||
continue;
|
||||
@@ -146,9 +149,9 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm)
|
||||
bool sel = BM_elem_flag_test(f, BM_ELEM_SELECT);
|
||||
|
||||
int faces_array_tot = f->len;
|
||||
BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot);
|
||||
|
||||
printf("%d: ", f->head.hflag);
|
||||
BLI_array_clear(faces_array);
|
||||
BLI_array_grow_items(faces_array, faces_array_tot);
|
||||
// BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot);
|
||||
|
||||
BM_face_triangulate(bm,
|
||||
f,
|
||||
@@ -163,8 +166,6 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm)
|
||||
pf_arena,
|
||||
NULL);
|
||||
|
||||
printf("%d, ", f->head.hflag);
|
||||
|
||||
for (int i = 0; i < faces_array_tot; i++) {
|
||||
BMFace *f2 = faces_array[i];
|
||||
|
||||
@@ -181,8 +182,6 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm)
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
while (f_double) {
|
||||
LinkNode *next = f_double->next;
|
||||
BM_face_kill(bm, f_double->link);
|
||||
@@ -191,6 +190,7 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm)
|
||||
}
|
||||
|
||||
BLI_memarena_free(pf_arena);
|
||||
MEM_SAFE_FREE(faces_array);
|
||||
|
||||
ss->totfaces = ss->totpoly = ss->bm->totface;
|
||||
ss->totvert = ss->bm->totvert;
|
||||
|
||||
@@ -30,12 +30,16 @@
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_gsqueue.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_pbvh.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
struct AutomaskingCache;
|
||||
struct KeyBlock;
|
||||
struct Object;
|
||||
@@ -747,6 +751,14 @@ struct SculptRakeData {
|
||||
float follow_co[3];
|
||||
};
|
||||
|
||||
typedef struct SculptCustomLayer {
|
||||
bool is_cdlayer; // false for multires data
|
||||
void *data; // only valid for multires and face
|
||||
int elemsize;
|
||||
int cd_offset; // for bmesh
|
||||
CustomDataLayer *layer; // not for multires
|
||||
} SculptCustomLayer;
|
||||
|
||||
/* Single struct used by all BLI_task threaded callbacks, let's avoid adding 10's of those... */
|
||||
typedef struct SculptThreadedTaskData {
|
||||
struct bContext *C;
|
||||
@@ -866,6 +878,7 @@ typedef struct SculptThreadedTaskData {
|
||||
|
||||
float smooth_projection;
|
||||
float rake_projection;
|
||||
SculptCustomLayer *scl;
|
||||
} SculptThreadedTaskData;
|
||||
|
||||
/*************** Brush testing declarations ****************/
|
||||
@@ -1519,3 +1532,31 @@ int SCULPT_get_symmetry_pass(const SculptSession *ss);
|
||||
|
||||
void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
|
||||
void SCULPT_reorder_bmesh(SculptSession *ss);
|
||||
|
||||
static inline void *SCULPT_temp_cdata_get(SculptVertRef vertex, SculptCustomLayer *scl)
|
||||
{
|
||||
if (scl->data) {
|
||||
char *p = (char *)scl->data;
|
||||
return p + scl->elemsize * (int)vertex.i;
|
||||
}
|
||||
else {
|
||||
BMVert *v = (BMVert *)vertex.i;
|
||||
return BM_ELEM_CD_GET_VOID_P(v, scl->cd_offset);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
create a custom vertex or face attribute.
|
||||
always create all of your attributes together with SCULPT_temp_customlayer_ensure,
|
||||
|
||||
then initialize their SculptCustomLayer's with SCULPT_temp_customlayer_get
|
||||
afterwards. Otherwise customdata offsets will be wrong (for PBVH_BMESH).
|
||||
|
||||
return true on success. if false layer was not created.
|
||||
*/
|
||||
bool SCULPT_temp_customlayer_ensure(SculptSession *ss,
|
||||
AttributeDomain domain,
|
||||
int proptype,
|
||||
char *name);
|
||||
bool SCULPT_temp_customlayer_get(
|
||||
SculptSession *ss, AttributeDomain domain, int proptype, char *name, SculptCustomLayer *scl);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_mesh.h"
|
||||
@@ -141,9 +142,95 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
|
||||
}
|
||||
}
|
||||
|
||||
void SCULPT_neighbor_coords_average_interior_velocity(SculptSession *ss,
|
||||
float result[3],
|
||||
SculptVertRef vertex,
|
||||
float projection,
|
||||
SculptCustomLayer *scl)
|
||||
{
|
||||
float avg[3] = {0.0f, 0.0f, 0.0f};
|
||||
int total = 0;
|
||||
int neighbor_count = 0;
|
||||
const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex);
|
||||
const float *co = SCULPT_vertex_co_get(ss, vertex);
|
||||
float no[3];
|
||||
|
||||
if (projection > 0.0f) {
|
||||
SCULPT_vertex_normal_get(ss, vertex, no);
|
||||
}
|
||||
|
||||
float vel[3];
|
||||
|
||||
copy_v3_v3(vel, (float *)SCULPT_temp_cdata_get(vertex, scl));
|
||||
mul_v3_fl(vel, 0.4f);
|
||||
|
||||
SculptVertexNeighborIter ni;
|
||||
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
|
||||
neighbor_count++;
|
||||
|
||||
float tmp[3];
|
||||
bool ok = false;
|
||||
|
||||
float *vel2 = SCULPT_temp_cdata_get(ni.vertex, scl);
|
||||
|
||||
// propegate smooth velocities a bit
|
||||
madd_v3_v3fl(vel2, vel, 1.0f / (float)ni.size);
|
||||
|
||||
if (is_boundary) {
|
||||
/* Boundary vertices use only other boundary vertices. */
|
||||
if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
|
||||
copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Interior vertices use all neighbors. */
|
||||
copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (projection > 0.0f) {
|
||||
sub_v3_v3(tmp, co);
|
||||
float fac = dot_v3v3(tmp, no);
|
||||
madd_v3_v3fl(tmp, no, -fac * projection);
|
||||
add_v3_v3(avg, tmp);
|
||||
}
|
||||
else {
|
||||
add_v3_v3(avg, tmp);
|
||||
}
|
||||
|
||||
total++;
|
||||
}
|
||||
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
|
||||
|
||||
/* Do not modify corner vertices. */
|
||||
if (neighbor_count <= 2) {
|
||||
copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Avoid division by 0 when there are no neighbors. */
|
||||
if (total == 0) {
|
||||
copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
|
||||
return;
|
||||
}
|
||||
|
||||
mul_v3_v3fl(result, avg, 1.0f / total);
|
||||
|
||||
if (projection > 0.0f) {
|
||||
add_v3_v3(result, co);
|
||||
}
|
||||
}
|
||||
/* For bmesh: Average surrounding verts based on an orthogonality measure.
|
||||
* Naturally converges to a quad-like structure. */
|
||||
void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v, float projection)
|
||||
void SCULPT_bmesh_four_neighbor_average(float avg[3],
|
||||
float direction[3],
|
||||
BMVert *v,
|
||||
float projection)
|
||||
{
|
||||
|
||||
float avg_co[3] = {0.0f, 0.0f, 0.0f};
|
||||
@@ -161,7 +248,7 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
|
||||
float vec[3];
|
||||
sub_v3_v3v3(vec, v_other->co, v->co);
|
||||
|
||||
madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no)*projection);
|
||||
madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no) * projection);
|
||||
normalize_v3(vec);
|
||||
|
||||
/* fac is a measure of how orthogonal or parallel the edge is
|
||||
@@ -180,7 +267,7 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
|
||||
/* Preserve volume. */
|
||||
float vec[3];
|
||||
sub_v3_v3(avg, v->co);
|
||||
mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)*projection);
|
||||
mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no) * projection);
|
||||
sub_v3_v3(avg, vec);
|
||||
add_v3_v3(avg, v->co);
|
||||
}
|
||||
@@ -193,9 +280,9 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
|
||||
* account. */
|
||||
|
||||
void SCULPT_neighbor_coords_average(SculptSession *ss,
|
||||
float result[3],
|
||||
SculptVertRef vertex,
|
||||
float projection)
|
||||
float result[3],
|
||||
SculptVertRef vertex,
|
||||
float projection)
|
||||
{
|
||||
float avg[3] = {0.0f, 0.0f, 0.0f};
|
||||
float *co, no[3];
|
||||
@@ -497,6 +584,63 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
|
||||
}
|
||||
#endif
|
||||
|
||||
static void do_smooth_brush_task_cb_ex_scl(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
Sculpt *sd = data->sd;
|
||||
const Brush *brush = data->brush;
|
||||
float bstrength = data->strength;
|
||||
float projection = data->smooth_projection;
|
||||
|
||||
SculptCustomLayer *scl = data->scl;
|
||||
|
||||
PBVHVertexIter vd;
|
||||
|
||||
CLAMP(bstrength, 0.0f, 1.0f);
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, data->brush->falloff_shape);
|
||||
|
||||
const int thread_id = BLI_task_parallel_thread_id(tls);
|
||||
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
continue;
|
||||
}
|
||||
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
||||
brush,
|
||||
vd.co,
|
||||
sqrtf(test.dist),
|
||||
vd.no,
|
||||
vd.fno,
|
||||
(vd.mask ? *vd.mask : 0.0f),
|
||||
vd.vertex,
|
||||
thread_id);
|
||||
|
||||
float avg[3], val[3];
|
||||
|
||||
SCULPT_neighbor_coords_average_interior_velocity(ss, avg, vd.vertex, projection, scl);
|
||||
|
||||
sub_v3_v3v3(val, avg, vd.co);
|
||||
|
||||
float *vel = (float *)SCULPT_temp_cdata_get(vd.vertex, scl);
|
||||
interp_v3_v3v3(vel, vel, val, 0.5);
|
||||
|
||||
madd_v3_v3v3fl(val, vd.co, vel, fade);
|
||||
|
||||
SCULPT_clip(sd, ss, vd.co, val);
|
||||
|
||||
if (vd.mvert) {
|
||||
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
void SCULPT_smooth(Sculpt *sd,
|
||||
Object *ob,
|
||||
PBVHNode **nodes,
|
||||
@@ -519,6 +663,18 @@ void SCULPT_smooth(Sculpt *sd,
|
||||
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);
|
||||
}
|
||||
#else
|
||||
bool have_scl = false;
|
||||
#endif
|
||||
|
||||
if (type == PBVH_FACES && !ss->pmap) {
|
||||
BLI_assert(!"sculpt smooth: pmap missing");
|
||||
return;
|
||||
@@ -536,19 +692,23 @@ void SCULPT_smooth(Sculpt *sd,
|
||||
for (iteration = 0; iteration <= count; iteration++) {
|
||||
const float strength = (iteration != count) ? 1.0f : last;
|
||||
|
||||
SculptThreadedTaskData data = {
|
||||
.sd = sd,
|
||||
.ob = ob,
|
||||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
.smooth_mask = smooth_mask,
|
||||
.strength = strength,
|
||||
.smooth_projection = projection,
|
||||
};
|
||||
SculptThreadedTaskData data = {.sd = sd,
|
||||
.ob = ob,
|
||||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
.smooth_mask = smooth_mask,
|
||||
.strength = strength,
|
||||
.smooth_projection = projection,
|
||||
.scl = have_scl ? &scl : NULL};
|
||||
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
|
||||
BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
|
||||
if (have_scl) {
|
||||
BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex_scl, &settings);
|
||||
}
|
||||
else {
|
||||
BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
|
||||
}
|
||||
|
||||
#ifdef PROXY_ADVANCED
|
||||
BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode);
|
||||
|
||||
Reference in New Issue
Block a user