* Fix crash in boundary sculpt tool
* For some reason boundary tool sometimes fails to match verts to boundary verts. It now simply freezes them in place if that happens
This commit is contained in:
parent
384a0930d1
commit
dabff9af7d
|
@ -427,7 +427,7 @@ typedef struct SculptBoundary {
|
|||
/* Bend Deform type. */
|
||||
struct {
|
||||
float (*pivot_rotation_axis)[3];
|
||||
float (*pivot_positions)[3];
|
||||
float (*pivot_positions)[4];
|
||||
} bend;
|
||||
|
||||
/* Slide Deform type. */
|
||||
|
|
|
@ -98,3 +98,14 @@
|
|||
#else
|
||||
# define ATTR_ALIGN(x) __attribute__((aligned(x)))
|
||||
#endif
|
||||
|
||||
/* Disable optimization for a function (for debugging use only)*/
|
||||
#ifdef __clang__
|
||||
#define ATTR_NO_OPT __attribute__((optnone))
|
||||
#elif __MSC_VER
|
||||
# define ATTR_NO_OPT __pragma(optimize("", off))
|
||||
#elif __GNUC__
|
||||
#define ATTR_NO_OPT _Pragma(optimize, "O0")
|
||||
#else
|
||||
#define ATTR_NO_OPT
|
||||
#endif
|
||||
|
|
|
@ -7328,6 +7328,7 @@ void SCULPT_cache_free(StrokeCache *cache)
|
|||
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
|
||||
if (cache->boundaries[i]) {
|
||||
SCULPT_boundary_data_free(cache->boundaries[i]);
|
||||
cache->boundaries[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,9 +58,24 @@
|
|||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if 1
|
||||
# ifdef NDEBUG
|
||||
# define NDEBUG_UNDEFD
|
||||
# undef NDEBUG
|
||||
# endif
|
||||
|
||||
# include "BLI_assert.h"
|
||||
|
||||
# ifdef NDEBUG_UNDEFD
|
||||
# define NDEBUG 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define BOUNDARY_VERTEX_NONE -1
|
||||
#define BOUNDARY_STEPS_NONE -1
|
||||
|
||||
#define TSTN 4
|
||||
|
||||
typedef struct BoundaryInitialVertexFloodFillData {
|
||||
SculptVertRef initial_vertex;
|
||||
int initial_vertex_index;
|
||||
|
@ -71,6 +86,31 @@ typedef struct BoundaryInitialVertexFloodFillData {
|
|||
int *floodfill_steps;
|
||||
float radius_sq;
|
||||
} BoundaryInitialVertexFloodFillData;
|
||||
#if 0
|
||||
__attribute__((optnone)) static bool validVert(SculptSession *ss, SculptVertRef v)
|
||||
{
|
||||
if (v.i == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH && v.i < 1000000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int totvert = SCULPT_vertex_count_get(ss);
|
||||
int idx = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
|
||||
|
||||
return idx >= 0 && idx < totvert;
|
||||
}
|
||||
__attribute__((optnone)) static void validateVert(SculptSession *ss, SculptVertRef v)
|
||||
{
|
||||
if (!validVert(ss, v)) {
|
||||
printf("Error! %p\n", v.i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// XXX remove all calls to validateVert before final merge
|
||||
#define validateVert(ss, vertex)
|
||||
|
||||
static bool boundary_initial_vertex_floodfill_cb(SculptSession *ss,
|
||||
SculptVertRef from_vref,
|
||||
|
@ -80,6 +120,9 @@ static bool boundary_initial_vertex_floodfill_cb(SculptSession *ss,
|
|||
{
|
||||
BoundaryInitialVertexFloodFillData *data = userdata;
|
||||
|
||||
validateVert(ss, from_vref);
|
||||
validateVert(ss, to_vref);
|
||||
|
||||
int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref);
|
||||
int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref);
|
||||
|
||||
|
@ -114,6 +157,7 @@ static SculptVertRef sculpt_boundary_get_closest_boundary_vertex(
|
|||
const int initial_vertex_index,
|
||||
const float radius)
|
||||
{
|
||||
validateVert(ss, initial_vertex);
|
||||
|
||||
if (SCULPT_vertex_is_boundary(ss, initial_vertex)) {
|
||||
return initial_vertex;
|
||||
|
@ -132,7 +176,7 @@ static SculptVertRef sculpt_boundary_get_closest_boundary_vertex(
|
|||
};
|
||||
|
||||
fdata.floodfill_steps = MEM_calloc_arrayN(
|
||||
SCULPT_vertex_count_get(ss), sizeof(int), "floodfill steps");
|
||||
SCULPT_vertex_count_get(ss), sizeof(int) * TSTN, "floodfill steps");
|
||||
|
||||
SCULPT_floodfill_execute(ss, &flood, boundary_initial_vertex_floodfill_cb, &fdata);
|
||||
SCULPT_floodfill_free(&flood);
|
||||
|
@ -146,11 +190,11 @@ static SculptVertRef sculpt_boundary_get_closest_boundary_vertex(
|
|||
* deformations usually need in the boundary. */
|
||||
static int BOUNDARY_INDICES_BLOCK_SIZE = 300;
|
||||
|
||||
static void sculpt_boundary_index_add(SculptSession *ss,
|
||||
SculptBoundary *boundary,
|
||||
const SculptVertRef new_index,
|
||||
const float distance,
|
||||
GSet *included_vertices)
|
||||
ATTR_NO_OPT static void sculpt_boundary_index_add(SculptSession *ss,
|
||||
SculptBoundary *boundary,
|
||||
const SculptVertRef new_index,
|
||||
const float distance,
|
||||
GSet *included_vertices)
|
||||
{
|
||||
|
||||
boundary->vertices[boundary->num_vertices] = new_index;
|
||||
|
@ -167,10 +211,12 @@ static void sculpt_boundary_index_add(SculptSession *ss,
|
|||
if (boundary->num_vertices >= boundary->vertices_capacity) {
|
||||
boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
|
||||
boundary->vertices = MEM_reallocN_id(boundary->vertices,
|
||||
boundary->vertices_capacity * sizeof(SculptVertRef),
|
||||
boundary->vertices_capacity * sizeof(SculptVertRef) *
|
||||
TSTN,
|
||||
"boundary vertrefs");
|
||||
boundary->vertex_indices = MEM_reallocN_id(
|
||||
boundary->vertex_indices, boundary->vertices_capacity * sizeof(int), "boundary indices");
|
||||
boundary->vertex_indices = MEM_reallocN_id(boundary->vertex_indices,
|
||||
boundary->vertices_capacity * sizeof(int) * TSTN,
|
||||
"boundary indices");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -186,7 +232,8 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary,
|
|||
if (boundary->num_edges >= boundary->edges_capacity) {
|
||||
boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
|
||||
boundary->edges = MEM_reallocN_id(boundary->edges,
|
||||
boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge),
|
||||
boundary->edges_capacity *
|
||||
sizeof(SculptBoundaryPreviewEdge) * TSTN,
|
||||
"boundary edges");
|
||||
}
|
||||
};
|
||||
|
@ -260,9 +307,10 @@ static bool boundary_floodfill_cb(
|
|||
boundary->distance[from_v_i] + edge_len :
|
||||
0.0f;
|
||||
sculpt_boundary_index_add(ss, boundary, to_v, distance_boundary_to_dst, data->included_vertices);
|
||||
if (!is_duplicate) {
|
||||
//if (!is_duplicate) {
|
||||
sculpt_boundary_preview_edge_add(boundary, from_v, to_v);
|
||||
}
|
||||
//}
|
||||
|
||||
return sculpt_boundary_is_vertex_in_editable_boundary(ss, to_v);
|
||||
}
|
||||
|
||||
|
@ -272,17 +320,19 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
|
|||
const SculptVertRef initial_boundary_index)
|
||||
{
|
||||
|
||||
validateVert(ss, initial_boundary_index);
|
||||
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
boundary->vertices = MEM_malloc_arrayN(
|
||||
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptVertRef), "boundary vrefs");
|
||||
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptVertRef) * TSTN, "boundary vrefs");
|
||||
boundary->vertex_indices = MEM_malloc_arrayN(
|
||||
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices");
|
||||
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int) * TSTN, "boundary indices");
|
||||
|
||||
if (init_boundary_distances) {
|
||||
boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances");
|
||||
boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float) * TSTN, "boundary distances");
|
||||
}
|
||||
boundary->edges = MEM_malloc_arrayN(
|
||||
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges");
|
||||
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge) * TSTN, "boundary edges");
|
||||
|
||||
GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE);
|
||||
SculptFloodFill flood;
|
||||
|
@ -327,17 +377,19 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
|
|||
* needed to go from a boundary vertex to an interior vertex and which vertex of the boundary is
|
||||
* the closest one.
|
||||
*/
|
||||
static void sculpt_boundary_edit_data_init(SculptSession *ss,
|
||||
SculptBoundary *boundary,
|
||||
const SculptVertRef initial_vertex,
|
||||
const float radius)
|
||||
ATTR_NO_OPT static void sculpt_boundary_edit_data_init(SculptSession *ss,
|
||||
SculptBoundary *boundary,
|
||||
const SculptVertRef initial_vertex,
|
||||
const float radius)
|
||||
{
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
|
||||
const bool has_duplicates = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS;
|
||||
|
||||
validateVert(ss, initial_vertex);
|
||||
|
||||
boundary->edit_info = MEM_malloc_arrayN(
|
||||
totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info");
|
||||
totvert, sizeof(SculptBoundaryEditInfo) * TSTN, "Boundary edit info");
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
boundary->edit_info[i].original_vertex.i = BOUNDARY_VERTEX_NONE;
|
||||
|
@ -366,8 +418,7 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
|
|||
int index = ni_duplis.index;
|
||||
|
||||
boundary->edit_info[index].original_vertex = boundary->vertices[i];
|
||||
boundary->edit_info[index].original_vertex_i = BKE_pbvh_vertex_index_to_table(
|
||||
ss->pbvh, boundary->vertices[i]);
|
||||
boundary->edit_info[index].original_vertex_i = boundary->vertex_indices[i];
|
||||
}
|
||||
}
|
||||
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
|
||||
|
@ -392,6 +443,8 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
|
|||
BLI_gsqueue_pop(current_iteration, &from_v);
|
||||
const int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
|
||||
|
||||
validateVert(ss, from_v);
|
||||
|
||||
SculptVertexNeighborIter ni;
|
||||
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
|
||||
|
||||
|
@ -402,6 +455,10 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
|
|||
}
|
||||
boundary->edit_info[ni.index].original_vertex =
|
||||
boundary->edit_info[from_v_i].original_vertex;
|
||||
boundary->edit_info[ni.index].original_vertex_i =
|
||||
boundary->edit_info[from_v_i].original_vertex_i;
|
||||
|
||||
validateVert(ss, boundary->edit_info[ni.index].original_vertex);
|
||||
|
||||
BLI_BITMAP_ENABLE(visited_vertices, ni.index);
|
||||
|
||||
|
@ -416,6 +473,7 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
|
|||
|
||||
BLI_gsqueue_push(next_iteration, &ni.vertex);
|
||||
|
||||
validateVert(ss, ni.vertex);
|
||||
|
||||
/* When copying the data to the neighbor for the next iteration, it has to be copied to
|
||||
* all its duplicates too. This is because it is not possible to know if the updated
|
||||
|
@ -424,9 +482,13 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
|
|||
if (has_duplicates) {
|
||||
SculptVertexNeighborIter ni_duplis;
|
||||
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni_duplis) {
|
||||
validateVert(ss, ni_duplis.vertex);
|
||||
|
||||
if (ni_duplis.is_duplicate) {
|
||||
boundary->edit_info[ni_duplis.index].original_vertex =
|
||||
boundary->edit_info[from_v_i].original_vertex;
|
||||
boundary->edit_info[ni_duplis.index].original_vertex_i =
|
||||
boundary->edit_info[from_v_i].original_vertex_i;
|
||||
boundary->edit_info[ni_duplis.index].num_propagation_steps =
|
||||
boundary->edit_info[from_v_i].num_propagation_steps + 1;
|
||||
}
|
||||
|
@ -538,6 +600,11 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// XXX
|
||||
if (ss->bm) {
|
||||
ss->bm->elem_index_dirty |= BM_VERT;
|
||||
}
|
||||
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
SCULPT_boundary_info_ensure(object);
|
||||
|
||||
|
@ -554,7 +621,7 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data");
|
||||
SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary) * TSTN, "Boundary edit data");
|
||||
|
||||
const bool init_boundary_distances = brush ? brush->boundary_falloff_type !=
|
||||
BRUSH_BOUNDARY_FALLOFF_CONSTANT :
|
||||
|
@ -575,6 +642,7 @@ void SCULPT_boundary_data_free(SculptBoundary *boundary)
|
|||
MEM_SAFE_FREE(boundary->vertices);
|
||||
MEM_SAFE_FREE(boundary->edges);
|
||||
MEM_SAFE_FREE(boundary->distance);
|
||||
|
||||
MEM_SAFE_FREE(boundary->edit_info);
|
||||
MEM_SAFE_FREE(boundary->bend.pivot_positions);
|
||||
MEM_SAFE_FREE(boundary->bend.pivot_rotation_axis);
|
||||
|
@ -586,13 +654,13 @@ void SCULPT_boundary_data_free(SculptBoundary *boundary)
|
|||
* SculptBoundaryEditInfo. They calculate the data using the vertices that have the
|
||||
* max_propagation_steps value and them this data is copied to the rest of the vertices using the
|
||||
* original vertex index. */
|
||||
static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *boundary)
|
||||
ATTR_NO_OPT static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *boundary)
|
||||
{
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN(
|
||||
totvert, 3 * sizeof(float), "pivot rotation axis");
|
||||
totvert, 3 * sizeof(float) * TSTN, "pivot rotation axis");
|
||||
boundary->bend.pivot_positions = MEM_calloc_arrayN(
|
||||
totvert, 3 * sizeof(float), "pivot positions");
|
||||
totvert, 4 * sizeof(float) * TSTN, "pivot positions");
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
|
@ -601,17 +669,61 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo
|
|||
continue;
|
||||
}
|
||||
|
||||
if (boundary->edit_info[i].original_vertex_i == BOUNDARY_VERTEX_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float dir[3];
|
||||
float normal[3];
|
||||
SCULPT_vertex_normal_get(ss, vertex, normal);
|
||||
sub_v3_v3v3(dir,
|
||||
SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
|
||||
SCULPT_vertex_co_get(ss, vertex));
|
||||
|
||||
validateVert(ss, boundary->edit_info[i].original_vertex);
|
||||
|
||||
#if 0
|
||||
/*strategy to increase accuracy for non-quad topologies:
|
||||
use principle curvature direction. Since SCULPT_curvature_dir_get
|
||||
can also be somewhat noisy we average it with dir.
|
||||
*/
|
||||
|
||||
float cdir[3];
|
||||
SCULPT_curvature_dir_get(ss, vertex, cdir);
|
||||
normalize_v3(dir);
|
||||
normalize_v3(cdir);
|
||||
|
||||
if (dot_v3v3(dir, cdir) < 0.0f) {
|
||||
negate_v3(cdir);
|
||||
}
|
||||
|
||||
add_v3_v3(dir, cdir);
|
||||
#endif
|
||||
|
||||
cross_v3_v3v3(
|
||||
boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i], dir, normal);
|
||||
normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]);
|
||||
copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i],
|
||||
SCULPT_vertex_co_get(ss, vertex));
|
||||
copy_v3_v3(boundary->bend.pivot_positions[i], SCULPT_vertex_co_get(ss, vertex));
|
||||
|
||||
add_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i],
|
||||
SCULPT_vertex_co_get(ss, vertex));
|
||||
boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] += 1.0f;
|
||||
}
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
if (boundary->bend.pivot_positions[i][3] > 0.0f) {
|
||||
float mul = 1.0f / boundary->bend.pivot_positions[i][3];
|
||||
boundary->bend.pivot_positions[i][0] *= mul;
|
||||
boundary->bend.pivot_positions[i][1] *= mul;
|
||||
boundary->bend.pivot_positions[i][2] *= mul;
|
||||
boundary->bend.pivot_positions[i][3] = 1.0f;
|
||||
}
|
||||
else { // pathological case, at least prevent from rotating around origin
|
||||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
float *co = SCULPT_vertex_co_get(ss, vertex);
|
||||
|
||||
copy_v3_v3(boundary->bend.pivot_positions[i], co);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
|
@ -629,7 +741,8 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo
|
|||
static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary)
|
||||
{
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions");
|
||||
boundary->slide.directions = MEM_calloc_arrayN(
|
||||
totvert, 3 * sizeof(float) * TSTN, "slide directions");
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
|
@ -657,7 +770,7 @@ static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *b
|
|||
{
|
||||
zero_v3(boundary->twist.pivot_position);
|
||||
float(*poly_verts)[3] = MEM_malloc_arrayN(
|
||||
boundary->num_vertices, sizeof(float) * 3, "poly verts");
|
||||
boundary->num_vertices, sizeof(float) * 3 * TSTN, "poly verts");
|
||||
for (int i = 0; i < boundary->num_vertices; i++) {
|
||||
add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i]));
|
||||
copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i]));
|
||||
|
@ -729,6 +842,9 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
|
|||
const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
|
||||
float t_orig_co[3];
|
||||
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
|
||||
|
||||
validateVert(ss, vd.vertex);
|
||||
|
||||
sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]);
|
||||
rotate_v3_v3v3fl(target_co,
|
||||
t_orig_co,
|
||||
|
|
Loading…
Reference in New Issue