Changeset View
Standalone View
source/blender/editors/sculpt_paint/sculpt.c
| Show First 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | |||||
| #include "bmesh.h" | #include "bmesh.h" | ||||
| #include "bmesh_tools.h" | #include "bmesh_tools.h" | ||||
| #include <math.h> | #include <math.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| /* Sculpt PBVH abstraction API */ | |||||
| static int sculpt_active_vertex_get(SculptSession *ss) | |||||
brecht: All these functions should be `static` since they are not exposed in any header file. | |||||
| { | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| return ss->active_vertex_index; | |||||
| case PBVH_BMESH: | |||||
| return ss->active_vertex_index; | |||||
| default: | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| static int sculpt_vertex_count_get(SculptSession *ss) | |||||
| { | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| return ss->totvert; | |||||
| case PBVH_BMESH: | |||||
| return BM_mesh_elem_count(BKE_pbvh_get_bmesh(ss->pbvh), BM_VERT); | |||||
| default: | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3]) | |||||
| { | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| normal_short_to_float_v3(no, ss->mvert[index].no); | |||||
| return; | |||||
| case PBVH_BMESH: | |||||
| copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no); | |||||
| default: | |||||
| return; | |||||
| } | |||||
| } | |||||
| static float *sculpt_vertex_co_get(SculptSession *ss, int index) | |||||
| { | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| return ss->mvert[index].co; | |||||
| case PBVH_BMESH: | |||||
| return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; | |||||
| default: | |||||
| return NULL; | |||||
| } | |||||
| } | |||||
| static void sculpt_vertex_co_set(SculptSession *ss, int index, float co[3]) | |||||
| { | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| copy_v3_v3(ss->mvert[index].co, co); | |||||
| return; | |||||
| case PBVH_BMESH: | |||||
| copy_v3_v3(BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co, co); | |||||
| return; | |||||
| default: | |||||
| return; | |||||
| } | |||||
| } | |||||
| static void sculpt_vertex_mask_set(SculptSession *ss, int index, float mask) | |||||
| { | |||||
| BMVert *v; | |||||
| float *mask_p; | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| ss->vmask[index] = mask; | |||||
| return; | |||||
| case PBVH_BMESH: | |||||
| v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); | |||||
| mask_p = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); | |||||
| *(mask_p) = mask; | |||||
| return; | |||||
| default: | |||||
| return; | |||||
| } | |||||
| } | |||||
| static float sculpt_vertex_mask_get(SculptSession *ss, int index) | |||||
| { | |||||
| BMVert *v; | |||||
| float *mask; | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| return ss->vmask[index]; | |||||
| case PBVH_BMESH: | |||||
| v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); | |||||
| mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); | |||||
| return *mask; | |||||
| default: | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| static void sculpt_vertex_tag_update(SculptSession *ss, int index) | |||||
| { | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE; | |||||
| return; | |||||
| case PBVH_BMESH: | |||||
| return; | |||||
| default: | |||||
| return; | |||||
| } | |||||
| } | |||||
| #define SCULPT_VERTEX_NEIGHBOUR_FIXED_CAPACITY 256 | |||||
| typedef struct SculptVertexNeighbourIter { | |||||
| int *neighbours; | |||||
| int size; | |||||
| int capacity; | |||||
| int neighbours_fixed[SCULPT_VERTEX_NEIGHBOUR_FIXED_CAPACITY]; | |||||
| int index; | |||||
| int i; | |||||
| } SculptVertexNeighbourIter; | |||||
Not Done Inline ActionsThis will not reallocate when iter->count reaches 2 * SCULPT_VERTEX_NEIGHBOUR_MAX. brecht: This will not reallocate when `iter->count` reaches `2 * SCULPT_VERTEX_NEIGHBOUR_MAX`. | |||||
| static void sculpt_vertex_neighbour_add(SculptVertexNeighbourIter *iter, int neighbour_index) | |||||
| { | |||||
| for (int i = 0; i < iter->size; i++) { | |||||
Done Inline ActionsCreating a gset and the associated memory allocations here is too slow. Since the typical number of neighbors is low, you can just store them in an array and loop over the array every time to check for duplicates. To avoid the memory allocation, SculptVertexNeighbourIter can contain a fixed size array of e.g. 256. int *neighbours would initially point to that. You can then use MEM_malloc to double the capacity when that number is exceeded, which would be almost never. The code to do that can also be shared between the bmesh and faces case, if you make a common sculpt_vertex_neighbours_add function for both to call. brecht: Creating a gset and the associated memory allocations here is too slow. Since the typical… | |||||
| if (iter->neighbours[i] == neighbour_index) { | |||||
| return; | |||||
| } | |||||
| } | |||||
| if (iter->size >= iter->capacity) { | |||||
| iter->capacity += SCULPT_VERTEX_NEIGHBOUR_FIXED_CAPACITY; | |||||
| if (iter->neighbours == iter->neighbours_fixed) { | |||||
| iter->neighbours = MEM_mallocN(iter->capacity * sizeof(int), "neighbour array"); | |||||
| memcpy(iter->neighbours, iter->neighbours_fixed, sizeof(int) * iter->size); | |||||
| } | |||||
| else { | |||||
| iter->neighbours = MEM_reallocN_id( | |||||
| iter->neighbours, iter->capacity * sizeof(int), "neighbour array"); | |||||
| } | |||||
| } | |||||
| iter->neighbours[iter->size] = neighbour_index; | |||||
| iter->size++; | |||||
| } | |||||
Not Done Inline ActionsDoing one allocation per vertex is still relatively slow, these things tend to work poorly especially with multithreading. brecht: Doing one allocation per vertex is still relatively slow, these things tend to work poorly… | |||||
| static void sculpt_vertex_neighbours_get_bmesh(SculptSession *ss, | |||||
| int index, | |||||
| SculptVertexNeighbourIter *iter) | |||||
| { | |||||
| BMVert *v = BM_vert_at_index(ss->bm, index); | |||||
| BMIter liter; | |||||
| BMLoop *l; | |||||
| iter->size = 0; | |||||
| iter->capacity = SCULPT_VERTEX_NEIGHBOUR_FIXED_CAPACITY; | |||||
| iter->neighbours = iter->neighbours_fixed; | |||||
| int i = 0; | |||||
| BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { | |||||
| const BMVert *adj_v[2] = {l->prev->v, l->next->v}; | |||||
| for (i = 0; i < ARRAY_SIZE(adj_v); i++) { | |||||
| const BMVert *v_other = adj_v[i]; | |||||
| if (BM_elem_index_get(v_other) != (int)index) { | |||||
| sculpt_vertex_neighbour_add(iter, BM_elem_index_get(v_other)); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| static void sculpt_vertex_neighbours_get_faces(SculptSession *ss, | |||||
| int index, | |||||
| SculptVertexNeighbourIter *iter) | |||||
| { | |||||
| int i; | |||||
| MeshElemMap *vert_map = &ss->pmap[(int)index]; | |||||
| iter->size = 0; | |||||
| iter->capacity = SCULPT_VERTEX_NEIGHBOUR_FIXED_CAPACITY; | |||||
| iter->neighbours = iter->neighbours_fixed; | |||||
| for (i = 0; i < ss->pmap[(int)index].count; i++) { | |||||
| const MPoly *p = &ss->mpoly[vert_map->indices[i]]; | |||||
| unsigned f_adj_v[2]; | |||||
| if (poly_get_adj_loops_from_vert(p, ss->mloop, (int)index, f_adj_v) != -1) { | |||||
| int j; | |||||
| for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { | |||||
| if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { | |||||
| if (f_adj_v[j] != (int)index) { | |||||
| sculpt_vertex_neighbour_add(iter, f_adj_v[j]); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| static void sculpt_vertex_neighbours_get(SculptSession *ss, | |||||
| int index, | |||||
| SculptVertexNeighbourIter *iter) | |||||
| { | |||||
| switch (BKE_pbvh_type(ss->pbvh)) { | |||||
| case PBVH_FACES: | |||||
| sculpt_vertex_neighbours_get_faces(ss, index, iter); | |||||
| return; | |||||
| case PBVH_BMESH: | |||||
| sculpt_vertex_neighbours_get_bmesh(ss, index, iter); | |||||
| return; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | |||||
| #define sculpt_vertex_neighbours_iter_begin(ss, v_index, neighbour_iterator) \ | |||||
| sculpt_vertex_neighbours_get(ss, v_index, &neighbour_iterator); \ | |||||
| for (neighbour_iterator.i = 0; neighbour_iterator.i < neighbour_iterator.size; \ | |||||
| neighbour_iterator.i++) { \ | |||||
| neighbour_iterator.index = ni.neighbours[ni.i]; | |||||
| #define sculpt_vertex_neighbours_iter_end(neighbour_iterator) \ | |||||
| } \ | |||||
| if (neighbour_iterator.neighbours != neighbour_iterator.neighbours_fixed) { \ | |||||
| MEM_freeN(neighbour_iterator.neighbours); \ | |||||
| } | |||||
| /** \name Tool Capabilities | /** \name Tool Capabilities | ||||
| * | * | ||||
| * Avoid duplicate checks, internal logic only, | * Avoid duplicate checks, internal logic only, | ||||
| * share logic with #rna_def_sculpt_capabilities where possible. | * share logic with #rna_def_sculpt_capabilities where possible. | ||||
| * | * | ||||
| * \{ */ | * \{ */ | ||||
| /* Check if there are any active modifiers in stack | /* Check if there are any active modifiers in stack | ||||
| ▲ Show 20 Lines • Show All 6,431 Lines • Show Last 20 Lines | |||||
All these functions should be static since they are not exposed in any header file.