Sculpt dyntopo: Fix boundary brush for multires
This commit fixes boundary brush for multires which broke two commits ago. This required implementing the geodesic api for PBVH_GRIDS, which I did by building topology maps in a rather. . . haphazard fashion. Basically I built a vert->edge map and then used it to derive a pseudo edge to quads mapping (it maps edges to all the verts in the two surrounding quads except the edge's own verts). Just for fun I enabled geodesic mode in mask expand; it seems to work.
This commit is contained in:
parent
ab632243e6
commit
b5100e73c8
|
@ -1 +1 @@
|
|||
Subproject commit 78107f78694f47ee6e50a7eb7c16b506af921199
|
||||
Subproject commit 985f6d8c304630c155133e9b368fdb7a29cac216
|
|
@ -421,7 +421,7 @@ static float *calc_boundary_tangent(SculptSession *ss, SculptBoundary *boundary)
|
|||
return (float *)tangents;
|
||||
}
|
||||
|
||||
ATTR_NO_OPT static void sculpt_boundary_cotan_init(SculptSession *ss, SculptBoundary *boundary)
|
||||
static void sculpt_boundary_cotan_init(SculptSession *ss, SculptBoundary *boundary)
|
||||
{
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
boundary->boundary_cotangents = MEM_calloc_arrayN(
|
||||
|
|
|
@ -930,7 +930,7 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob,
|
|||
BLI_bitmap *enabled_vertices)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
BLI_assert(ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH));
|
||||
BLI_assert(ELEM(BKE_pbvh_type(ss->pbvh), PBVH_GRIDS, PBVH_FACES, PBVH_BMESH));
|
||||
|
||||
GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
|
||||
BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false);
|
||||
|
@ -1051,7 +1051,7 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
|
|||
BLI_BITMAP_ENABLE(enabled_vertices, i);
|
||||
}
|
||||
|
||||
if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH)) {
|
||||
if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_GRIDS, PBVH_FACES, PBVH_BMESH)) {
|
||||
sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices);
|
||||
}
|
||||
else {
|
||||
|
@ -1112,9 +1112,12 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
|
|||
|
||||
switch (falloff_type) {
|
||||
case SCULPT_EXPAND_FALLOFF_GEODESIC:
|
||||
expand_cache->vert_falloff = sculpt_expand_geodesic_falloff_create(sd, ob, v);
|
||||
/*
|
||||
expand_cache->vert_falloff = has_topology_info ?
|
||||
sculpt_expand_geodesic_falloff_create(sd, ob, v) :
|
||||
sculpt_expand_spherical_falloff_create(ob, v);
|
||||
*/
|
||||
break;
|
||||
case SCULPT_EXPAND_FALLOFF_TOPOLOGY:
|
||||
expand_cache->vert_falloff = sculpt_expand_topology_falloff_create(sd, ob, v);
|
||||
|
|
|
@ -23,10 +23,16 @@
|
|||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_array.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_linklist_stack.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_memarena.h"
|
||||
#include "BLI_mempool.h"
|
||||
#include "BLI_sort_utils.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -144,6 +150,80 @@ static bool sculpt_geodesic_mesh_test_dist_add(MVert *mvert,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Propagate distance from v1 and v2 to v0. */
|
||||
static bool sculpt_geodesic_grids_test_dist_add(SculptSession *ss,
|
||||
const int v0,
|
||||
const int v1,
|
||||
const int v2,
|
||||
float *dists,
|
||||
GSet *initial_vertices,
|
||||
SculptVertRef *r_closest_verts,
|
||||
float (*cos)[3])
|
||||
{
|
||||
if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_assert(dists[v1] != FLT_MAX);
|
||||
if (dists[v0] <= dists[v1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float *co0 = cos ? cos[v0] :
|
||||
SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v0));
|
||||
const float *co1 = cos ? cos[v1] :
|
||||
SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v1));
|
||||
const float *co2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ?
|
||||
(cos ? cos[v2] :
|
||||
SCULPT_vertex_co_get(
|
||||
ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v2))) :
|
||||
NULL;
|
||||
|
||||
float dist0;
|
||||
if (v2 != SCULPT_GEODESIC_VERTEX_NONE) {
|
||||
BLI_assert(dists[v2] != FLT_MAX);
|
||||
if (dists[v0] <= dists[v2]) {
|
||||
return false;
|
||||
}
|
||||
dist0 = geodesic_distance_propagate_across_triangle(co0, co1, co2, dists[v1], dists[v2]);
|
||||
}
|
||||
else {
|
||||
float vec[3];
|
||||
sub_v3_v3v3(vec, co1, co0);
|
||||
dist0 = dists[v1] + len_v3(vec);
|
||||
}
|
||||
|
||||
if (dist0 < dists[v0]) {
|
||||
dists[v0] = dist0;
|
||||
|
||||
if (r_closest_verts) {
|
||||
bool tag1 = r_closest_verts[v1].i != -1LL;
|
||||
bool tag2 = v2 != SCULPT_GEODESIC_VERTEX_NONE && r_closest_verts[v2].i != -1LL;
|
||||
|
||||
float l1 = len_v3v3(co0, co1);
|
||||
float l2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? len_v3v3(co0, co2) : 0.0f;
|
||||
|
||||
if (tag1 && tag2) {
|
||||
if (l1 < l2) {
|
||||
r_closest_verts[v0] = r_closest_verts[v1];
|
||||
}
|
||||
else {
|
||||
r_closest_verts[v0] = r_closest_verts[v2];
|
||||
}
|
||||
}
|
||||
else if (tag2) {
|
||||
r_closest_verts[v0] = r_closest_verts[v2];
|
||||
}
|
||||
else if (tag1) {
|
||||
r_closest_verts[v0] = r_closest_verts[v1];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define BMESH_INITIAL_VERT_TAG BM_ELEM_TAG_ALT
|
||||
|
||||
static bool sculpt_geodesic_mesh_test_dist_add_bmesh(BMVert *v0,
|
||||
|
@ -598,6 +678,302 @@ static float *SCULPT_geodesic_bmesh_create(Object *ob,
|
|||
return dists;
|
||||
}
|
||||
|
||||
BLI_INLINE void *hash_edge(int v1, int v2, int totvert)
|
||||
{
|
||||
if (v1 > v2) {
|
||||
SWAP(int, v1, v2);
|
||||
}
|
||||
|
||||
intptr_t ret = (intptr_t)v1 + (intptr_t)v2 * (intptr_t)totvert;
|
||||
return (void *)ret;
|
||||
}
|
||||
|
||||
typedef struct TempEdge {
|
||||
int v1, v2;
|
||||
} TempEdge;
|
||||
|
||||
int find_quad(TempEdge *edges, MeshElemMap *vmap, int v1, int v2, int v3)
|
||||
{
|
||||
for (int i = 0; i < vmap[v1].count; i++) {
|
||||
TempEdge *te = edges + vmap[v1].indices[i];
|
||||
int v = v1 == te->v1 ? te->v2 : te->v1;
|
||||
|
||||
if (v == v2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < vmap[v].count; j++) {
|
||||
TempEdge *te2 = edges + vmap[v].indices[j];
|
||||
int v4 = v == te2->v1 ? te2->v2 : te2->v1;
|
||||
|
||||
if (v4 == v3) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static float *SCULPT_geodesic_grids_create(Object *ob,
|
||||
GSet *initial_vertices,
|
||||
const float limit_radius,
|
||||
SculptVertRef *r_closest_verts,
|
||||
float (*cos)[3])
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Mesh *mesh = BKE_object_get_original_mesh(ob);
|
||||
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
|
||||
const float limit_radius_sq = limit_radius * limit_radius;
|
||||
|
||||
float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
|
||||
|
||||
/* Both contain edge indices encoded as *void. */
|
||||
BLI_LINKSTACK_DECLARE(queue, void *);
|
||||
BLI_LINKSTACK_DECLARE(queue_next, void *);
|
||||
|
||||
BLI_LINKSTACK_INIT(queue);
|
||||
BLI_LINKSTACK_INIT(queue_next);
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) {
|
||||
if (r_closest_verts) {
|
||||
r_closest_verts[i] = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
}
|
||||
|
||||
dists[i] = 0.0f;
|
||||
}
|
||||
else {
|
||||
if (r_closest_verts) {
|
||||
r_closest_verts[i].i = -1LL;
|
||||
}
|
||||
|
||||
dists[i] = FLT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
/* Masks vertices that are further than limit radius from an initial vertex. As there is no need
|
||||
* to define a distance to them the algorithm can stop earlier by skipping them. */
|
||||
BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex");
|
||||
GSetIterator gs_iter;
|
||||
|
||||
if (limit_radius == FLT_MAX) {
|
||||
/* In this case, no need to loop through all initial vertices to check distances as they are
|
||||
* all going to be affected. */
|
||||
BLI_bitmap_set_all(affected_vertex, true, totvert);
|
||||
}
|
||||
else {
|
||||
/* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When
|
||||
* this optimization is needed, it is expected for the tool to request the distance to a low
|
||||
* number of vertices (usually just 1 or 2). */
|
||||
GSET_ITER (gs_iter, initial_vertices) {
|
||||
const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
|
||||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, v);
|
||||
const float *v_co = cos ? cos[v] : SCULPT_vertex_co_get(ss, vertex);
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
const float *v_co2 = cos ? cos[i] :
|
||||
SCULPT_vertex_co_get(
|
||||
ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
|
||||
if (len_squared_v3v3(v_co, v_co2) <= limit_radius_sq) {
|
||||
BLI_BITMAP_ENABLE(affected_vertex, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SculptVertexNeighborIter ni;
|
||||
|
||||
TempEdge *edges = NULL;
|
||||
BLI_array_declare(edges);
|
||||
GHash *ehash = BLI_ghash_ptr_new("geodesic multigrids ghash");
|
||||
|
||||
MeshElemMap *vmap = MEM_calloc_arrayN(totvert, sizeof(*vmap), "geodesic grids vmap");
|
||||
|
||||
int totedge = 0;
|
||||
MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "geodesic grids memarena");
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
|
||||
MeshElemMap *map = vmap + i;
|
||||
|
||||
int val = SCULPT_vertex_valence_get(ss, vertex);
|
||||
map->count = val;
|
||||
map->indices = BLI_memarena_alloc(ma, sizeof(int) * val);
|
||||
|
||||
int j = 0;
|
||||
|
||||
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
|
||||
void *ekey = hash_edge(i, ni.index, totvert);
|
||||
void **val;
|
||||
|
||||
if (!BLI_ghash_ensure_p(ehash, ekey, &val)) {
|
||||
*val = POINTER_FROM_INT(totedge);
|
||||
|
||||
TempEdge te = {i, ni.index};
|
||||
BLI_array_append(edges, te);
|
||||
totedge++;
|
||||
}
|
||||
|
||||
map->indices[j] = POINTER_AS_INT(*val);
|
||||
j++;
|
||||
}
|
||||
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
|
||||
}
|
||||
|
||||
int(*e_otherv_map)[4] = MEM_malloc_arrayN(totedge, sizeof(*e_otherv_map), "e_otherv_map");
|
||||
|
||||
// create an edge map of opposite edge verts in (up to 2) adjacent faces
|
||||
for (int i = 0; i < totedge; i++) {
|
||||
int v1a = -1, v2a = -1;
|
||||
int v1b = -1, v2b = -1;
|
||||
|
||||
TempEdge *te = edges + i;
|
||||
SculptVertexNeighborIter ni2;
|
||||
|
||||
for (int j = 0; j < vmap[te->v1].count; j++) {
|
||||
TempEdge *te2 = edges + vmap[te->v1].indices[j];
|
||||
int v3 = te->v1 == te2->v1 ? te2->v2 : te2->v1;
|
||||
|
||||
if (v3 == te->v2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int p = find_quad(edges, vmap, te->v1, te->v2, v3);
|
||||
|
||||
if (p != -1) {
|
||||
v1a = p;
|
||||
v1b = v3;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < vmap[te->v2].count; j++) {
|
||||
TempEdge *te2 = edges + vmap[te->v2].indices[j];
|
||||
int v3 = te->v2 == te2->v1 ? te2->v2 : te2->v1;
|
||||
|
||||
if (v3 == te->v1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int p = find_quad(edges, vmap, te->v1, te->v2, v3);
|
||||
|
||||
if (p != -1) {
|
||||
if (v1a != -1) {
|
||||
v2a = p;
|
||||
v2b = v3;
|
||||
}
|
||||
else {
|
||||
v1a = p;
|
||||
v1b = v3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e_otherv_map[i][0] = v1a;
|
||||
e_otherv_map[i][1] = v1b;
|
||||
e_otherv_map[i][2] = v2a;
|
||||
e_otherv_map[i][3] = v2b;
|
||||
}
|
||||
|
||||
BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag");
|
||||
|
||||
/* Add edges adjacent to an initial vertex to the queue. */
|
||||
for (int i = 0; i < totedge; i++) {
|
||||
const int v1 = edges[i].v1;
|
||||
const int v2 = edges[i].v2;
|
||||
|
||||
if (!BLI_BITMAP_TEST(affected_vertex, v1) && !BLI_BITMAP_TEST(affected_vertex, v2)) {
|
||||
continue;
|
||||
}
|
||||
if (dists[v1] != FLT_MAX || dists[v2] != FLT_MAX) {
|
||||
BLI_LINKSTACK_PUSH(queue, POINTER_FROM_INT(i));
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
while (BLI_LINKSTACK_SIZE(queue)) {
|
||||
const int e = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
|
||||
int v1 = edges[e].v1;
|
||||
int v2 = edges[e].v2;
|
||||
|
||||
if (dists[v1] == FLT_MAX || dists[v2] == FLT_MAX) {
|
||||
if (dists[v1] > dists[v2]) {
|
||||
SWAP(int, v1, v2);
|
||||
}
|
||||
sculpt_geodesic_grids_test_dist_add(ss,
|
||||
v2,
|
||||
v1,
|
||||
SCULPT_GEODESIC_VERTEX_NONE,
|
||||
dists,
|
||||
initial_vertices,
|
||||
r_closest_verts,
|
||||
cos);
|
||||
}
|
||||
|
||||
TempEdge *te = edges + e;
|
||||
|
||||
for (int pi = 0; pi < 4; pi++) {
|
||||
int v_other = e_otherv_map[e][pi];
|
||||
|
||||
if (v_other == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// XXX not sure how to handle face sets here - joeedh
|
||||
// if (ss->face_sets[poly] <= 0) {
|
||||
// continue;
|
||||
//}
|
||||
|
||||
if (sculpt_geodesic_grids_test_dist_add(
|
||||
ss, v_other, v1, v2, dists, initial_vertices, r_closest_verts, cos)) {
|
||||
for (int edge_map_index = 0; edge_map_index < vmap[v_other].count; edge_map_index++) {
|
||||
const int e_other = vmap[v_other].indices[edge_map_index];
|
||||
int ev_other;
|
||||
if (edges[e_other].v1 == (uint)v_other) {
|
||||
ev_other = edges[e_other].v2;
|
||||
}
|
||||
else {
|
||||
ev_other = edges[e_other].v1;
|
||||
}
|
||||
|
||||
if (e_other != e && !BLI_BITMAP_TEST(edge_tag, e_other) &&
|
||||
(dists[ev_other] != FLT_MAX)) {
|
||||
if (BLI_BITMAP_TEST(affected_vertex, v_other) ||
|
||||
BLI_BITMAP_TEST(affected_vertex, ev_other)) {
|
||||
BLI_BITMAP_ENABLE(edge_tag, e_other);
|
||||
BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(e_other));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
|
||||
const int e = POINTER_AS_INT(lnk->link);
|
||||
BLI_BITMAP_DISABLE(edge_tag, e);
|
||||
}
|
||||
|
||||
BLI_LINKSTACK_SWAP(queue, queue_next);
|
||||
|
||||
} while (BLI_LINKSTACK_SIZE(queue));
|
||||
|
||||
BLI_LINKSTACK_FREE(queue);
|
||||
BLI_LINKSTACK_FREE(queue_next);
|
||||
MEM_SAFE_FREE(edge_tag);
|
||||
MEM_SAFE_FREE(affected_vertex);
|
||||
|
||||
BLI_memarena_free(ma);
|
||||
BLI_ghash_free(ehash, NULL, NULL);
|
||||
MEM_SAFE_FREE(edges);
|
||||
MEM_SAFE_FREE(vmap);
|
||||
MEM_SAFE_FREE(e_otherv_map);
|
||||
|
||||
return dists;
|
||||
}
|
||||
|
||||
/* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the
|
||||
* distance to each vertex. In this case, only one of the initial vertices will be used to
|
||||
* calculate the distance. */
|
||||
|
@ -606,7 +982,7 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
|
|||
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Mesh *mesh = BKE_object_get_original_mesh(ob);
|
||||
const int totvert = mesh->totvert;
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
|
||||
SculptVertRef first_affected = {SCULPT_GEODESIC_VERTEX_NONE};
|
||||
|
||||
|
@ -647,7 +1023,9 @@ float *SCULPT_geodesic_distances_create(Object *ob,
|
|||
return SCULPT_geodesic_bmesh_create(
|
||||
ob, initial_vertices, limit_radius, r_closest_verts, vertco_override);
|
||||
case PBVH_GRIDS:
|
||||
return SCULPT_geodesic_fallback_create(ob, initial_vertices);
|
||||
return SCULPT_geodesic_grids_create(
|
||||
ob, initial_vertices, limit_radius, r_closest_verts, vertco_override);
|
||||
// return SCULPT_geodesic_fallback_create(ob, initial_vertices);
|
||||
}
|
||||
BLI_assert(false);
|
||||
return NULL;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 82e4b979ab424cad429a751a9a90c0e0c6ea077e
|
||||
Subproject commit 08de10dbd8234c242b1896a6813d2a6335288e74
|
Loading…
Reference in New Issue