Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt.cc
- This file was moved from source/blender/editors/sculpt_paint/sculpt.c.
| /* SPDX-License-Identifier: GPL-2.0-or-later | /* SPDX-License-Identifier: GPL-2.0-or-later | ||||
| * Copyright 2006 by Nicholas Bishop. All rights reserved. */ | * Copyright 2006 by Nicholas Bishop. All rights reserved. */ | ||||
| /** \file | /** \file | ||||
| * \ingroup edsculpt | * \ingroup edsculpt | ||||
| * Implements the Sculpt Mode tools. | * Implements the Sculpt Mode tools. | ||||
| */ | */ | ||||
| #include <cmath> | |||||
| #include <cstdlib> | |||||
| #include <cstring> | |||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_dial_2d.h" | #include "BLI_dial_2d.h" | ||||
| #include "BLI_ghash.h" | #include "BLI_ghash.h" | ||||
| #include "BLI_gsqueue.h" | #include "BLI_gsqueue.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_task.h" | #include "BLI_task.h" | ||||
| ▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
| #include "paint_intern.h" | #include "paint_intern.h" | ||||
| #include "sculpt_intern.h" | #include "sculpt_intern.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "bmesh.h" | #include "bmesh.h" | ||||
| #include <math.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Sculpt PBVH Abstraction API | /** \name Sculpt PBVH Abstraction API | ||||
| * | * | ||||
| * This is read-only, for writing use PBVH vertex iterators. There vd.index matches | * This is read-only, for writing use PBVH vertex iterators. There vd.index matches | ||||
| * the indices used here. | * the indices used here. | ||||
| * | * | ||||
| * For multi-resolution, the same vertex in multiple grids is counted multiple times, with | * For multi-resolution, the same vertex in multiple grids is counted multiple times, with | ||||
| * different index for each grid. | * different index for each grid. | ||||
| Show All 36 Lines | switch (BKE_pbvh_type(ss->pbvh)) { | ||||
| case PBVH_GRIDS: { | case PBVH_GRIDS: { | ||||
| const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | ||||
| const int grid_index = vertex.i / key->grid_area; | const int grid_index = vertex.i / key->grid_area; | ||||
| const int vertex_index = vertex.i - grid_index * key->grid_area; | const int vertex_index = vertex.i - grid_index * key->grid_area; | ||||
| CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; | CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; | ||||
| return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index)); | return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index)); | ||||
| } | } | ||||
| } | } | ||||
| return NULL; | return nullptr; | ||||
| } | } | ||||
| bool SCULPT_has_loop_colors(const Object *ob) | bool SCULPT_has_loop_colors(const Object *ob) | ||||
| { | { | ||||
| Mesh *me = BKE_object_get_original_mesh(ob); | Mesh *me = BKE_object_get_original_mesh(ob); | ||||
| const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); | const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); | ||||
| return layer && BKE_id_attribute_domain(&me->id, layer) == ATTR_DOMAIN_CORNER; | return layer && BKE_id_attribute_domain(&me->id, layer) == ATTR_DOMAIN_CORNER; | ||||
| ▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | switch (BKE_pbvh_type(ss->pbvh)) { | ||||
| case PBVH_BMESH: | case PBVH_BMESH: | ||||
| copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, vertex)); | copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, vertex)); | ||||
| break; | break; | ||||
| case PBVH_GRIDS: { | case PBVH_GRIDS: { | ||||
| const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | ||||
| const int grid_index = vertex.i / key->grid_area; | const int grid_index = vertex.i / key->grid_area; | ||||
| const int vertex_index = vertex.i - grid_index * key->grid_area; | const int vertex_index = vertex.i - grid_index * key->grid_area; | ||||
| SubdivCCGCoord coord = {.grid_index = grid_index, | SubdivCCGCoord coord{}; | ||||
| .x = vertex_index % key->grid_size, | coord.grid_index = grid_index; | ||||
| .y = vertex_index / key->grid_size}; | coord.x = vertex_index % key->grid_size; | ||||
| coord.y = vertex_index / key->grid_size; | |||||
| BKE_subdiv_ccg_eval_limit_point(ss->subdiv_ccg, &coord, r_co); | BKE_subdiv_ccg_eval_limit_point(ss->subdiv_ccg, &coord, r_co); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) | void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | MVert *SCULPT_mesh_deformed_mverts_get(SculptSession *ss) | ||||
| switch (BKE_pbvh_type(ss->pbvh)) { | switch (BKE_pbvh_type(ss->pbvh)) { | ||||
| case PBVH_FACES: | case PBVH_FACES: | ||||
| if (ss->shapekey_active || ss->deform_modifiers_active) { | if (ss->shapekey_active || ss->deform_modifiers_active) { | ||||
| return BKE_pbvh_get_verts(ss->pbvh); | return BKE_pbvh_get_verts(ss->pbvh); | ||||
| } | } | ||||
| return ss->mvert; | return ss->mvert; | ||||
| case PBVH_BMESH: | case PBVH_BMESH: | ||||
| case PBVH_GRIDS: | case PBVH_GRIDS: | ||||
| return NULL; | return nullptr; | ||||
| } | } | ||||
| return NULL; | return nullptr; | ||||
| } | } | ||||
| float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, | float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, | ||||
| const int deform_target, | const int deform_target, | ||||
| PBVHVertexIter *iter) | PBVHVertexIter *iter) | ||||
| { | { | ||||
| switch (deform_target) { | switch (deform_target) { | ||||
| case BRUSH_DEFORM_TARGET_GEOMETRY: | case BRUSH_DEFORM_TARGET_GEOMETRY: | ||||
| ▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible) | ||||
| } | } | ||||
| } | } | ||||
| bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex) | bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex) | ||||
| { | { | ||||
| switch (BKE_pbvh_type(ss->pbvh)) { | switch (BKE_pbvh_type(ss->pbvh)) { | ||||
| case PBVH_FACES: { | case PBVH_FACES: { | ||||
| const bool *hide_vert = BKE_pbvh_get_vert_hide(ss->pbvh); | const bool *hide_vert = BKE_pbvh_get_vert_hide(ss->pbvh); | ||||
| return hide_vert == NULL || !hide_vert[vertex.i]; | return hide_vert == nullptr || !hide_vert[vertex.i]; | ||||
| } | } | ||||
| case PBVH_BMESH: | case PBVH_BMESH: | ||||
| return !BM_elem_flag_test((BMVert *)vertex.i, BM_ELEM_HIDDEN); | return !BM_elem_flag_test((BMVert *)vertex.i, BM_ELEM_HIDDEN); | ||||
| case PBVH_GRIDS: { | case PBVH_GRIDS: { | ||||
| const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | ||||
| const int grid_index = vertex.i / key->grid_area; | const int grid_index = vertex.i / key->grid_area; | ||||
| const int vertex_index = vertex.i - grid_index * key->grid_area; | const int vertex_index = vertex.i - grid_index * key->grid_area; | ||||
| BLI_bitmap **grid_hidden = BKE_pbvh_get_grid_visibility(ss->pbvh); | BLI_bitmap **grid_hidden = BKE_pbvh_get_grid_visibility(ss->pbvh); | ||||
| if (grid_hidden && grid_hidden[grid_index]) { | if (grid_hidden && grid_hidden[grid_index]) { | ||||
| return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index); | return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible) | void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible) | ||||
| { | { | ||||
| BLI_assert(ss->face_sets != NULL); | BLI_assert(ss->face_sets != nullptr); | ||||
| BLI_assert(ss->hide_poly != NULL); | BLI_assert(ss->hide_poly != nullptr); | ||||
| switch (BKE_pbvh_type(ss->pbvh)) { | switch (BKE_pbvh_type(ss->pbvh)) { | ||||
| case PBVH_FACES: | case PBVH_FACES: | ||||
| case PBVH_GRIDS: | case PBVH_GRIDS: | ||||
| for (int i = 0; i < ss->totfaces; i++) { | for (int i = 0; i < ss->totfaces; i++) { | ||||
| if (ss->face_sets[i] != face_set) { | if (ss->face_sets[i] != face_set) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| ss->hide_poly[i] = !visible; | ss->hide_poly[i] = !visible; | ||||
| } | } | ||||
| break; | break; | ||||
| case PBVH_BMESH: | case PBVH_BMESH: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| void SCULPT_face_visibility_all_invert(SculptSession *ss) | void SCULPT_face_visibility_all_invert(SculptSession *ss) | ||||
| { | { | ||||
| BLI_assert(ss->face_sets != NULL); | BLI_assert(ss->face_sets != nullptr); | ||||
| BLI_assert(ss->hide_poly != NULL); | BLI_assert(ss->hide_poly != nullptr); | ||||
| switch (BKE_pbvh_type(ss->pbvh)) { | switch (BKE_pbvh_type(ss->pbvh)) { | ||||
| case PBVH_FACES: | case PBVH_FACES: | ||||
| case PBVH_GRIDS: | case PBVH_GRIDS: | ||||
| for (int i = 0; i < ss->totfaces; i++) { | for (int i = 0; i < ss->totfaces; i++) { | ||||
| ss->hide_poly[i] = !ss->hide_poly[i]; | ss->hide_poly[i] = !ss->hide_poly[i]; | ||||
| } | } | ||||
| break; | break; | ||||
| case PBVH_BMESH: { | case PBVH_BMESH: { | ||||
| BMIter iter; | BMIter iter; | ||||
| BMFace *f; | BMFace *f; | ||||
| BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { | BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { | ||||
| BM_elem_flag_toggle(f, BM_ELEM_HIDDEN); | BM_elem_flag_toggle(f, BM_ELEM_HIDDEN); | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible) | void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible) | ||||
| { | { | ||||
| switch (BKE_pbvh_type(ss->pbvh)) { | switch (BKE_pbvh_type(ss->pbvh)) { | ||||
| case PBVH_FACES: | case PBVH_FACES: | ||||
| case PBVH_GRIDS: | case PBVH_GRIDS: | ||||
| BLI_assert(ss->hide_poly != NULL); | BLI_assert(ss->hide_poly != nullptr); | ||||
| memset(ss->hide_poly, !visible, sizeof(bool) * ss->totfaces); | memset(ss->hide_poly, !visible, sizeof(bool) * ss->totfaces); | ||||
| break; | break; | ||||
| case PBVH_BMESH: { | case PBVH_BMESH: { | ||||
| BMIter iter; | BMIter iter; | ||||
| BMFace *f; | BMFace *f; | ||||
| BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { | BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { | ||||
| BM_elem_flag_set(f, BM_ELEM_HIDDEN, !visible); | BM_elem_flag_set(f, BM_ELEM_HIDDEN, !visible); | ||||
| ▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef vertex) | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set) | void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set) | ||||
| { | { | ||||
| switch (BKE_pbvh_type(ss->pbvh)) { | switch (BKE_pbvh_type(ss->pbvh)) { | ||||
| case PBVH_FACES: { | case PBVH_FACES: { | ||||
| BLI_assert(ss->face_sets != NULL); | BLI_assert(ss->face_sets != nullptr); | ||||
| const MeshElemMap *vert_map = &ss->pmap[vertex.i]; | const MeshElemMap *vert_map = &ss->pmap[vertex.i]; | ||||
| for (int j = 0; j < vert_map->count; j++) { | for (int j = 0; j < vert_map->count; j++) { | ||||
| const int poly_index = vert_map->indices[j]; | const int poly_index = vert_map->indices[j]; | ||||
| if (ss->hide_poly && ss->hide_poly[poly_index]) { | if (ss->hide_poly && ss->hide_poly[poly_index]) { | ||||
| /* Skip hidden faces connected to the vertex. */ | /* Skip hidden faces connected to the vertex. */ | ||||
| continue; | continue; | ||||
| } | } | ||||
| ss->face_sets[poly_index] = face_set; | ss->face_sets[poly_index] = face_set; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case PBVH_BMESH: | case PBVH_BMESH: | ||||
| break; | break; | ||||
| case PBVH_GRIDS: { | case PBVH_GRIDS: { | ||||
| BLI_assert(ss->face_sets != NULL); | BLI_assert(ss->face_sets != nullptr); | ||||
| const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | ||||
| const int grid_index = vertex.i / key->grid_area; | const int grid_index = vertex.i / key->grid_area; | ||||
| const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); | const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); | ||||
| if (ss->hide_poly && ss->hide_poly[face_index]) { | if (ss->hide_poly && ss->hide_poly[face_index]) { | ||||
| /* Skip the vertex if it's in a hidden face. */ | /* Skip the vertex if it's in a hidden face. */ | ||||
| return; | return; | ||||
| } | } | ||||
| ss->face_sets[face_index] = face_set; | ss->face_sets[face_index] = face_set; | ||||
| ▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | case PBVH_BMESH: | ||||
| return true; | return true; | ||||
| case PBVH_GRIDS: { | case PBVH_GRIDS: { | ||||
| if (!ss->face_sets) { | if (!ss->face_sets) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | ||||
| const int grid_index = vertex.i / key->grid_area; | const int grid_index = vertex.i / key->grid_area; | ||||
| const int vertex_index = vertex.i - grid_index * key->grid_area; | const int vertex_index = vertex.i - grid_index * key->grid_area; | ||||
| const SubdivCCGCoord coord = {.grid_index = grid_index, | SubdivCCGCoord coord{}; | ||||
| .x = vertex_index % key->grid_size, | coord.grid_index = grid_index; | ||||
| .y = vertex_index / key->grid_size}; | coord.x = vertex_index % key->grid_size; | ||||
| coord.y = vertex_index / key->grid_size; | |||||
| int v1, v2; | int v1, v2; | ||||
| const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( | const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( | ||||
| ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); | ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); | ||||
| switch (adjacency) { | switch (adjacency) { | ||||
| case SUBDIV_CCG_ADJACENT_VERTEX: | case SUBDIV_CCG_ADJACENT_VERTEX: | ||||
| return sculpt_check_unique_face_set_in_base_mesh(ss, v1); | return sculpt_check_unique_face_set_in_base_mesh(ss, v1); | ||||
| case SUBDIV_CCG_ADJACENT_EDGE: | case SUBDIV_CCG_ADJACENT_EDGE: | ||||
| return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2); | return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2); | ||||
| ▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if (iter->neighbors[i].i == neighbor.i) { | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| if (iter->size >= iter->capacity) { | if (iter->size >= iter->capacity) { | ||||
| iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; | iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; | ||||
| if (iter->neighbors == iter->neighbors_fixed) { | if (iter->neighbors == iter->neighbors_fixed) { | ||||
| iter->neighbors = MEM_mallocN(iter->capacity * sizeof(PBVHVertRef), "neighbor array"); | iter->neighbors = static_cast<PBVHVertRef *>( | ||||
| MEM_mallocN(iter->capacity * sizeof(PBVHVertRef), "neighbor array")); | |||||
| memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(PBVHVertRef) * iter->size); | memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(PBVHVertRef) * iter->size); | ||||
| } | } | ||||
| else { | else { | ||||
| iter->neighbors = MEM_reallocN_id( | iter->neighbors = static_cast<PBVHVertRef *>(MEM_reallocN_id( | ||||
| iter->neighbors, iter->capacity * sizeof(PBVHVertRef), "neighbor array"); | iter->neighbors, iter->capacity * sizeof(PBVHVertRef), "neighbor array")); | ||||
| } | } | ||||
| if (iter->neighbor_indices == iter->neighbor_indices_fixed) { | if (iter->neighbor_indices == iter->neighbor_indices_fixed) { | ||||
| iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); | iter->neighbor_indices = static_cast<int *>( | ||||
| MEM_mallocN(iter->capacity * sizeof(int), "neighbor array")); | |||||
| memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size); | memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size); | ||||
| } | } | ||||
| else { | else { | ||||
| iter->neighbor_indices = MEM_reallocN_id( | iter->neighbor_indices = static_cast<int *>( | ||||
| iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array"); | MEM_reallocN_id(iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array")); | ||||
| } | } | ||||
| } | } | ||||
| iter->neighbors[iter->size] = neighbor; | iter->neighbors[iter->size] = neighbor; | ||||
| iter->neighbor_indices[iter->size] = neighbor_index; | iter->neighbor_indices[iter->size] = neighbor_index; | ||||
| iter->size++; | iter->size++; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | if (poly_get_adj_loops_from_vert(p, ss->mloop, vertex.i, f_adj_v) != -1) { | ||||
| if (f_adj_v[j] != vertex.i) { | if (f_adj_v[j] != vertex.i) { | ||||
| sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]); | sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (ss->fake_neighbors.use_fake_neighbors) { | if (ss->fake_neighbors.use_fake_neighbors) { | ||||
| BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); | BLI_assert(ss->fake_neighbors.fake_neighbor_index != nullptr); | ||||
| if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { | if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { | ||||
| sculpt_vertex_neighbor_add( | sculpt_vertex_neighbor_add( | ||||
| iter, | iter, | ||||
| BKE_pbvh_make_vref(ss->fake_neighbors.fake_neighbor_index[vertex.i]), | BKE_pbvh_make_vref(ss->fake_neighbors.fake_neighbor_index[vertex.i]), | ||||
| ss->fake_neighbors.fake_neighbor_index[vertex.i]); | ss->fake_neighbors.fake_neighbor_index[vertex.i]); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, | static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, | ||||
| const PBVHVertRef vertex, | const PBVHVertRef vertex, | ||||
| const bool include_duplicates, | const bool include_duplicates, | ||||
| SculptVertexNeighborIter *iter) | SculptVertexNeighborIter *iter) | ||||
| { | { | ||||
| /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, | /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, | ||||
| * maybe provide coordinate and mask pointers directly rather than converting | * maybe provide coordinate and mask pointers directly rather than converting | ||||
| * back and forth between #CCGElem and global index. */ | * back and forth between #CCGElem and global index. */ | ||||
| const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | ||||
| const int grid_index = vertex.i / key->grid_area; | const int grid_index = vertex.i / key->grid_area; | ||||
| const int vertex_index = vertex.i - grid_index * key->grid_area; | const int vertex_index = vertex.i - grid_index * key->grid_area; | ||||
| SubdivCCGCoord coord = {.grid_index = grid_index, | SubdivCCGCoord coord{}; | ||||
| .x = vertex_index % key->grid_size, | coord.grid_index = grid_index; | ||||
| .y = vertex_index / key->grid_size}; | coord.x = vertex_index % key->grid_size; | ||||
| coord.y = vertex_index / key->grid_size; | |||||
| SubdivCCGNeighbors neighbors; | SubdivCCGNeighbors neighbors; | ||||
| BKE_subdiv_ccg_neighbor_coords_get(ss->subdiv_ccg, &coord, include_duplicates, &neighbors); | BKE_subdiv_ccg_neighbor_coords_get(ss->subdiv_ccg, &coord, include_duplicates, &neighbors); | ||||
| iter->size = 0; | iter->size = 0; | ||||
| iter->num_duplicates = neighbors.num_duplicates; | iter->num_duplicates = neighbors.num_duplicates; | ||||
| iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; | iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; | ||||
| iter->neighbors = iter->neighbors_fixed; | iter->neighbors = iter->neighbors_fixed; | ||||
| iter->neighbor_indices = iter->neighbor_indices_fixed; | iter->neighbor_indices = iter->neighbor_indices_fixed; | ||||
| for (int i = 0; i < neighbors.size; i++) { | for (int i = 0; i < neighbors.size; i++) { | ||||
| int v = neighbors.coords[i].grid_index * key->grid_area + | int v = neighbors.coords[i].grid_index * key->grid_area + | ||||
| neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x; | neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x; | ||||
| sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); | sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); | ||||
| } | } | ||||
| if (ss->fake_neighbors.use_fake_neighbors) { | if (ss->fake_neighbors.use_fake_neighbors) { | ||||
| BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); | BLI_assert(ss->fake_neighbors.fake_neighbor_index != nullptr); | ||||
| if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { | if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { | ||||
| int v = ss->fake_neighbors.fake_neighbor_index[vertex.i]; | int v = ss->fake_neighbors.fake_neighbor_index[vertex.i]; | ||||
| sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); | sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); | ||||
| } | } | ||||
| } | } | ||||
| if (neighbors.coords != neighbors.coords_fixed) { | if (neighbors.coords != neighbors.coords_fixed) { | ||||
| MEM_freeN(neighbors.coords); | MEM_freeN(neighbors.coords); | ||||
| Show All 37 Lines | case PBVH_BMESH: { | ||||
| BMVert *v = (BMVert *)vertex.i; | BMVert *v = (BMVert *)vertex.i; | ||||
| return BM_vert_is_boundary(v); | return BM_vert_is_boundary(v); | ||||
| } | } | ||||
| case PBVH_GRIDS: { | case PBVH_GRIDS: { | ||||
| const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); | ||||
| const int grid_index = vertex.i / key->grid_area; | const int grid_index = vertex.i / key->grid_area; | ||||
| const int vertex_index = vertex.i - grid_index * key->grid_area; | const int vertex_index = vertex.i - grid_index * key->grid_area; | ||||
| const SubdivCCGCoord coord = {.grid_index = grid_index, | SubdivCCGCoord coord{}; | ||||
| .x = vertex_index % key->grid_size, | coord.grid_index = grid_index; | ||||
| .y = vertex_index / key->grid_size}; | coord.x = vertex_index % key->grid_size; | ||||
| coord.y = vertex_index / key->grid_size; | |||||
| int v1, v2; | int v1, v2; | ||||
| const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( | const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( | ||||
| ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); | ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); | ||||
| switch (adjacency) { | switch (adjacency) { | ||||
| case SUBDIV_CCG_ADJACENT_VERTEX: | case SUBDIV_CCG_ADJACENT_VERTEX: | ||||
| return sculpt_check_boundary_vertex_in_base_mesh(ss, v1); | return sculpt_check_boundary_vertex_in_base_mesh(ss, v1); | ||||
| case SUBDIV_CCG_ADJACENT_EDGE: | case SUBDIV_CCG_ADJACENT_EDGE: | ||||
| return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && | return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && | ||||
| Show All 40 Lines | if (symm & symm_it) { | ||||
| if (vco[i] * pco[i] < 0.0f) { | if (vco[i] * pco[i] < 0.0f) { | ||||
| is_in_symmetry_area = false; | is_in_symmetry_area = false; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return is_in_symmetry_area; | return is_in_symmetry_area; | ||||
| } | } | ||||
| typedef struct NearestVertexTLSData { | struct NearestVertexTLSData { | ||||
| PBVHVertRef nearest_vertex; | PBVHVertRef nearest_vertex; | ||||
| float nearest_vertex_distance_squared; | float nearest_vertex_distance_squared; | ||||
| } NearestVertexTLSData; | }; | ||||
| static void do_nearest_vertex_get_task_cb(void *__restrict userdata, | static void do_nearest_vertex_get_task_cb(void *__restrict userdata, | ||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict tls) | const TaskParallelTLS *__restrict tls) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| NearestVertexTLSData *nvtd = tls->userdata_chunk; | NearestVertexTLSData *nvtd = static_cast<NearestVertexTLSData *>(tls->userdata_chunk); | ||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | ||||
| float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); | float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); | ||||
| if (distance_squared < nvtd->nearest_vertex_distance_squared && | if (distance_squared < nvtd->nearest_vertex_distance_squared && | ||||
| distance_squared < data->max_distance_squared) { | distance_squared < data->max_distance_squared) { | ||||
| nvtd->nearest_vertex = vd.vertex; | nvtd->nearest_vertex = vd.vertex; | ||||
| nvtd->nearest_vertex_distance_squared = distance_squared; | nvtd->nearest_vertex_distance_squared = distance_squared; | ||||
| } | } | ||||
| } | } | ||||
| BKE_pbvh_vertex_iter_end; | BKE_pbvh_vertex_iter_end; | ||||
| } | } | ||||
| static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata), | static void nearest_vertex_get_reduce(const void *__restrict /*userdata*/, | ||||
| void *__restrict chunk_join, | void *__restrict chunk_join, | ||||
| void *__restrict chunk) | void *__restrict chunk) | ||||
| { | { | ||||
| NearestVertexTLSData *join = chunk_join; | NearestVertexTLSData *join = static_cast<NearestVertexTLSData *>(chunk_join); | ||||
| NearestVertexTLSData *nvtd = chunk; | NearestVertexTLSData *nvtd = static_cast<NearestVertexTLSData *>(chunk); | ||||
| if (join->nearest_vertex.i == PBVH_REF_NONE) { | if (join->nearest_vertex.i == PBVH_REF_NONE) { | ||||
| join->nearest_vertex = nvtd->nearest_vertex; | join->nearest_vertex = nvtd->nearest_vertex; | ||||
| join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; | join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; | ||||
| } | } | ||||
| else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { | else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { | ||||
| join->nearest_vertex = nvtd->nearest_vertex; | join->nearest_vertex = nvtd->nearest_vertex; | ||||
| join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; | join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; | ||||
| } | } | ||||
| } | } | ||||
| PBVHVertRef SCULPT_nearest_vertex_get( | PBVHVertRef SCULPT_nearest_vertex_get( | ||||
| Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original) | Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| PBVHNode **nodes = NULL; | PBVHNode **nodes = nullptr; | ||||
| int totnode; | int totnode; | ||||
| SculptSearchSphereData data = { | SculptSearchSphereData data{}; | ||||
| .ss = ss, | data.sd = sd; | ||||
| .sd = sd, | data.radius_squared = max_distance * max_distance; | ||||
| .radius_squared = max_distance * max_distance, | data.original = use_original; | ||||
| .original = use_original, | data.center = co; | ||||
| .center = co, | |||||
| }; | |||||
| BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); | BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); | ||||
| if (totnode == 0) { | if (totnode == 0) { | ||||
| return BKE_pbvh_make_vref(PBVH_REF_NONE); | return BKE_pbvh_make_vref(PBVH_REF_NONE); | ||||
| } | } | ||||
| SculptThreadedTaskData task_data = { | SculptThreadedTaskData task_data{}; | ||||
| .sd = sd, | task_data.sd = sd; | ||||
| .ob = ob, | task_data.ob = ob; | ||||
| .nodes = nodes, | task_data.nodes = nodes; | ||||
| .max_distance_squared = max_distance * max_distance, | task_data.max_distance_squared = max_distance * max_distance; | ||||
| }; | |||||
| copy_v3_v3(task_data.nearest_vertex_search_co, co); | copy_v3_v3(task_data.nearest_vertex_search_co, co); | ||||
| NearestVertexTLSData nvtd; | NearestVertexTLSData nvtd; | ||||
| nvtd.nearest_vertex.i = PBVH_REF_NONE; | nvtd.nearest_vertex.i = PBVH_REF_NONE; | ||||
| nvtd.nearest_vertex_distance_squared = FLT_MAX; | nvtd.nearest_vertex_distance_squared = FLT_MAX; | ||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| Show All 17 Lines | bool SCULPT_is_vertex_inside_brush_radius_symm(const float vertex[3], | ||||
| float radius, | float radius, | ||||
| char symm) | char symm) | ||||
| { | { | ||||
| for (char i = 0; i <= symm; ++i) { | for (char i = 0; i <= symm; ++i) { | ||||
| if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { | if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| float location[3]; | float location[3]; | ||||
| flip_v3_v3(location, br_co, (char)i); | flip_v3_v3(location, br_co, ePaintSymmetryFlags(i)); | ||||
| if (len_squared_v3v3(location, vertex) < radius * radius) { | if (len_squared_v3v3(location, vertex) < radius * radius) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| void SCULPT_tag_update_overlays(bContext *C) | void SCULPT_tag_update_overlays(bContext *C) | ||||
| ▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | for (char i = 0; i <= symm; ++i) { | ||||
| PBVHVertRef v = {PBVH_REF_NONE}; | PBVHVertRef v = {PBVH_REF_NONE}; | ||||
| if (i == 0) { | if (i == 0) { | ||||
| v = vertex; | v = vertex; | ||||
| } | } | ||||
| else if (radius > 0.0f) { | else if (radius > 0.0f) { | ||||
| float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius; | float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius; | ||||
| float location[3]; | float location[3]; | ||||
| flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i); | flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), ePaintSymmetryFlags(i)); | ||||
| v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false); | v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false); | ||||
| } | } | ||||
| if (v.i != PBVH_REF_NONE) { | if (v.i != PBVH_REF_NONE) { | ||||
| SCULPT_floodfill_add_initial(flood, v); | SCULPT_floodfill_add_initial(flood, v); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Show All 10 Lines | for (char i = 0; i <= symm; ++i) { | ||||
| PBVHVertRef v = {PBVH_REF_NONE}; | PBVHVertRef v = {PBVH_REF_NONE}; | ||||
| if (i == 0) { | if (i == 0) { | ||||
| v = SCULPT_active_vertex_get(ss); | v = SCULPT_active_vertex_get(ss); | ||||
| } | } | ||||
| else if (radius > 0.0f) { | else if (radius > 0.0f) { | ||||
| float location[3]; | float location[3]; | ||||
| flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), i); | flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), ePaintSymmetryFlags(i)); | ||||
| v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false); | v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false); | ||||
| } | } | ||||
| if (v.i != PBVH_REF_NONE) { | if (v.i != PBVH_REF_NONE) { | ||||
| SCULPT_floodfill_add_initial(flood, v); | SCULPT_floodfill_add_initial(flood, v); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Show All 33 Lines | while (!BLI_gsqueue_is_empty(flood->queue)) { | ||||
| SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); | SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); | ||||
| } | } | ||||
| } | } | ||||
| void SCULPT_floodfill_free(SculptFloodFill *flood) | void SCULPT_floodfill_free(SculptFloodFill *flood) | ||||
| { | { | ||||
| MEM_SAFE_FREE(flood->visited_verts); | MEM_SAFE_FREE(flood->visited_verts); | ||||
| BLI_gsqueue_free(flood->queue); | BLI_gsqueue_free(flood->queue); | ||||
| flood->queue = NULL; | flood->queue = nullptr; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| static bool sculpt_tool_has_cube_tip(const char sculpt_tool) | static bool sculpt_tool_has_cube_tip(const char sculpt_tool) | ||||
| { | { | ||||
| return ELEM( | return ELEM( | ||||
| sculpt_tool, SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_PAINT, SCULPT_TOOL_MULTIPLANE_SCRAPE); | sculpt_tool, SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_PAINT, SCULPT_TOOL_MULTIPLANE_SCRAPE); | ||||
| Show All 32 Lines | return ELEM(sculpt_tool, | ||||
| SCULPT_TOOL_PAINT, | SCULPT_TOOL_PAINT, | ||||
| SCULPT_TOOL_SMEAR, | SCULPT_TOOL_SMEAR, | ||||
| SCULPT_TOOL_DRAW_FACE_SETS); | SCULPT_TOOL_DRAW_FACE_SETS); | ||||
| } | } | ||||
| static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush *brush) | static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush *brush) | ||||
| { | { | ||||
| return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(brush->sculpt_tool) && | 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 != nullptr); | ||||
| } | } | ||||
| /** | /** | ||||
| * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action | * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action | ||||
| */ | */ | ||||
| static int sculpt_brush_needs_normal(const SculptSession *ss, Sculpt *sd, const Brush *brush) | static int sculpt_brush_needs_normal(const SculptSession *ss, Sculpt *sd, const Brush *brush) | ||||
| { | { | ||||
| return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && | return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && | ||||
| Show All 21 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Sculpt Init/Update | /** \name Sculpt Init/Update | ||||
| * \{ */ | * \{ */ | ||||
| typedef enum StrokeFlags { | enum StrokeFlags { | ||||
| CLIP_X = 1, | CLIP_X = 1, | ||||
| CLIP_Y = 2, | CLIP_Y = 2, | ||||
| CLIP_Z = 4, | CLIP_Z = 4, | ||||
| } StrokeFlags; | }; | ||||
| void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode) | void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| BMesh *bm = ss->bm; | BMesh *bm = ss->bm; | ||||
| memset(data, 0, sizeof(*data)); | memset(data, 0, sizeof(*data)); | ||||
| data->unode = unode; | data->unode = unode; | ||||
| Show All 38 Lines | if (orig_data->bm_log) { | ||||
| orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); | orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); | ||||
| } | } | ||||
| else { | else { | ||||
| orig_data->mask = orig_data->vmasks[iter->i]; | orig_data->mask = orig_data->vmasks[iter->i]; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_rake_data_update(struct SculptRakeData *srd, const float co[3]) | static void sculpt_rake_data_update(SculptRakeData *srd, const float co[3]) | ||||
| { | { | ||||
| float rake_dist = len_v3v3(srd->follow_co, co); | float rake_dist = len_v3v3(srd->follow_co, co); | ||||
| if (rake_dist > srd->follow_dist) { | if (rake_dist > srd->follow_dist) { | ||||
| interp_v3_v3v3(srd->follow_co, srd->follow_co, co, rake_dist - srd->follow_dist); | interp_v3_v3v3(srd->follow_co, srd->follow_co, co, rake_dist - srd->follow_dist); | ||||
| } | } | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| Show All 18 Lines | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Sculpt Paint Mesh | /** \name Sculpt Paint Mesh | ||||
| * \{ */ | * \{ */ | ||||
| static void paint_mesh_restore_co_task_cb(void *__restrict userdata, | static void paint_mesh_restore_co_task_cb(void *__restrict userdata, | ||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict UNUSED(tls)) | const TaskParallelTLS *__restrict /*tls*/) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| SculptUndoNode *unode; | SculptUndoNode *unode; | ||||
| SculptUndoType type; | SculptUndoType type; | ||||
| switch (data->brush->sculpt_tool) { | switch (data->brush->sculpt_tool) { | ||||
| case SCULPT_TOOL_MASK: | case SCULPT_TOOL_MASK: | ||||
| type = SCULPT_UNDO_MASK; | type = SCULPT_UNDO_MASK; | ||||
| ▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
| static void paint_mesh_restore_co(Sculpt *sd, Object *ob) | static void paint_mesh_restore_co(Sculpt *sd, Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| Brush *brush = BKE_paint_brush(&sd->paint); | Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| PBVHNode **nodes; | PBVHNode **nodes; | ||||
| int totnode; | int totnode; | ||||
| BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); | BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode); | ||||
| /** | /** | ||||
| * Disable multi-threading when dynamic-topology is enabled. Otherwise, | * Disable multi-threading when dynamic-topology is enabled. Otherwise, | ||||
| * new entries might be inserted by #SCULPT_undo_push_node() into the #GHash | * new entries might be inserted by #SCULPT_undo_push_node() into the #GHash | ||||
| * used internally by #BM_log_original_vert_co() by a different thread. See T33787. | * used internally by #BM_log_original_vert_co() by a different thread. See T33787. | ||||
| */ | */ | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data{}; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .ob = ob, | data.ob = ob; | ||||
| .brush = brush, | data.brush = brush; | ||||
| .nodes = nodes, | data.nodes = nodes; | ||||
| }; | |||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true && !ss->bm, totnode); | BKE_pbvh_parallel_range_settings(&settings, true && !ss->bm, totnode); | ||||
| BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); | BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); | ||||
| BKE_pbvh_node_color_buffer_free(ss->pbvh); | BKE_pbvh_node_color_buffer_free(ss->pbvh); | ||||
| MEM_SAFE_FREE(nodes); | MEM_SAFE_FREE(nodes); | ||||
| ▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | void SCULPT_brush_test_init(SculptSession *ss, SculptBrushTest *test) | ||||
| if (ss->cache) { | if (ss->cache) { | ||||
| copy_v3_v3(test->location, ss->cache->location); | copy_v3_v3(test->location, ss->cache->location); | ||||
| test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; | test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; | ||||
| test->radial_symmetry_pass = ss->cache->radial_symmetry_pass; | test->radial_symmetry_pass = ss->cache->radial_symmetry_pass; | ||||
| copy_m4_m4(test->symm_rot_mat_inv, ss->cache->symm_rot_mat_inv); | copy_m4_m4(test->symm_rot_mat_inv, ss->cache->symm_rot_mat_inv); | ||||
| } | } | ||||
| else { | else { | ||||
| copy_v3_v3(test->location, ss->cursor_location); | copy_v3_v3(test->location, ss->cursor_location); | ||||
| test->mirror_symmetry_pass = 0; | test->mirror_symmetry_pass = ePaintSymmetryFlags(0); | ||||
| test->radial_symmetry_pass = 0; | test->radial_symmetry_pass = 0; | ||||
| unit_m4(test->symm_rot_mat_inv); | unit_m4(test->symm_rot_mat_inv); | ||||
| } | } | ||||
| /* Just for initialize. */ | /* Just for initialize. */ | ||||
| test->dist = 0.0f; | test->dist = 0.0f; | ||||
| /* Only for 2D projection. */ | /* Only for 2D projection. */ | ||||
| zero_v4(test->plane_view); | zero_v4(test->plane_view); | ||||
| zero_v4(test->plane_tool); | zero_v4(test->plane_tool); | ||||
| if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { | if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { | ||||
| test->clip_rv3d = rv3d; | test->clip_rv3d = rv3d; | ||||
| } | } | ||||
| else { | else { | ||||
| test->clip_rv3d = NULL; | test->clip_rv3d = nullptr; | ||||
| } | } | ||||
| } | } | ||||
| BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const float co[3]) | BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const float co[3]) | ||||
| { | { | ||||
| RegionView3D *rv3d = test->clip_rv3d; | RegionView3D *rv3d = test->clip_rv3d; | ||||
| if (!rv3d) { | if (!rv3d) { | ||||
| return false; | return false; | ||||
| ▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | static bool sculpt_brush_test_cyl(SculptBrushTest *test, | ||||
| return false; | return false; | ||||
| } | } | ||||
| #endif | #endif | ||||
| /* ===== Sculpting ===== | /* ===== Sculpting ===== | ||||
| */ | */ | ||||
| static float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle) | static float calc_overlap(StrokeCache *cache, | ||||
| const ePaintSymmetryFlags symm, | |||||
| const char axis, | |||||
| const float angle) | |||||
| { | { | ||||
| float mirror[3]; | float mirror[3]; | ||||
| float distsq; | float distsq; | ||||
| flip_v3_v3(mirror, cache->true_location, symm); | flip_v3_v3(mirror, cache->true_location, symm); | ||||
| if (axis != 0) { | if (axis != 0) { | ||||
| float mat[3][3]; | float mat[3][3]; | ||||
| axis_angle_to_mat3_single(mat, axis, angle); | axis_angle_to_mat3_single(mat, axis, angle); | ||||
| mul_m3_v3(mat, mirror); | mul_m3_v3(mat, mirror); | ||||
| } | } | ||||
| distsq = len_squared_v3v3(mirror, cache->true_location); | distsq = len_squared_v3v3(mirror, cache->true_location); | ||||
| if (distsq <= 4.0f * (cache->radius_squared)) { | if (distsq <= 4.0f * (cache->radius_squared)) { | ||||
| return (2.0f * (cache->radius) - sqrtf(distsq)) / (2.0f * (cache->radius)); | return (2.0f * (cache->radius) - sqrtf(distsq)) / (2.0f * (cache->radius)); | ||||
| } | } | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| static float calc_radial_symmetry_feather(Sculpt *sd, | static float calc_radial_symmetry_feather(Sculpt *sd, | ||||
| StrokeCache *cache, | StrokeCache *cache, | ||||
| const char symm, | const ePaintSymmetryFlags symm, | ||||
| const char axis) | const char axis) | ||||
| { | { | ||||
| float overlap = 0.0f; | float overlap = 0.0f; | ||||
| for (int i = 1; i < sd->radial_symm[axis - 'X']; i++) { | for (int i = 1; i < sd->radial_symm[axis - 'X']; i++) { | ||||
| const float angle = 2.0f * M_PI * i / sd->radial_symm[axis - 'X']; | const float angle = 2.0f * M_PI * i / sd->radial_symm[axis - 'X']; | ||||
| overlap += calc_overlap(cache, symm, axis, angle); | overlap += calc_overlap(cache, symm, axis, angle); | ||||
| } | } | ||||
| Show All 10 Lines | static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) | ||||
| const int symm = cache->symmetry; | const int symm = cache->symmetry; | ||||
| overlap = 0.0f; | overlap = 0.0f; | ||||
| for (int i = 0; i <= symm; i++) { | for (int i = 0; i <= symm; i++) { | ||||
| if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { | if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| overlap += calc_overlap(cache, i, 0, 0); | overlap += calc_overlap(cache, ePaintSymmetryFlags(i), 0, 0); | ||||
| overlap += calc_radial_symmetry_feather(sd, cache, i, 'X'); | overlap += calc_radial_symmetry_feather(sd, cache, ePaintSymmetryFlags(i), 'X'); | ||||
| overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y'); | overlap += calc_radial_symmetry_feather(sd, cache, ePaintSymmetryFlags(i), 'Y'); | ||||
| overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z'); | overlap += calc_radial_symmetry_feather(sd, cache, ePaintSymmetryFlags(i), 'Z'); | ||||
| } | } | ||||
| return 1.0f / overlap; | return 1.0f / overlap; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Calculate Normal and Center | /** \name Calculate Normal and Center | ||||
| * | * | ||||
| * Calculate geometry surrounding the brush center. | * Calculate geometry surrounding the brush center. | ||||
| * (optionally using original coordinates). | * (optionally using original coordinates). | ||||
| * | * | ||||
| * Functions are: | * Functions are: | ||||
| * - #SCULPT_calc_area_center | * - #SCULPT_calc_area_center | ||||
| * - #SCULPT_calc_area_normal | * - #SCULPT_calc_area_normal | ||||
| * - #SCULPT_calc_area_normal_and_center | * - #SCULPT_calc_area_normal_and_center | ||||
| * | * | ||||
| * \note These are all _very_ similar, when changing one, check others. | * \note These are all _very_ similar, when changing one, check others. | ||||
| * \{ */ | * \{ */ | ||||
| typedef struct AreaNormalCenterTLSData { | struct AreaNormalCenterTLSData { | ||||
| /* 0 = towards view, 1 = flipped */ | /* 0 = towards view, 1 = flipped */ | ||||
| float area_cos[2][3]; | float area_cos[2][3]; | ||||
| float area_nos[2][3]; | float area_nos[2][3]; | ||||
| int count_no[2]; | int count_no[2]; | ||||
| int count_co[2]; | int count_co[2]; | ||||
| } AreaNormalCenterTLSData; | }; | ||||
| static void calc_area_normal_and_center_task_cb(void *__restrict userdata, | static void calc_area_normal_and_center_task_cb(void *__restrict userdata, | ||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict tls) | const TaskParallelTLS *__restrict tls) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| AreaNormalCenterTLSData *anctd = tls->userdata_chunk; | AreaNormalCenterTLSData *anctd = static_cast<AreaNormalCenterTLSData *>(tls->userdata_chunk); | ||||
| const bool use_area_nos = data->use_area_nos; | const bool use_area_nos = data->use_area_nos; | ||||
| const bool use_area_cos = data->use_area_cos; | const bool use_area_cos = data->use_area_cos; | ||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| SculptUndoNode *unode = NULL; | SculptUndoNode *unode = nullptr; | ||||
| bool use_original = false; | bool use_original = false; | ||||
| bool normal_test_r, area_test_r; | bool normal_test_r, area_test_r; | ||||
| if (ss->cache && ss->cache->original) { | if (ss->cache && ss->cache->original) { | ||||
| unode = SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); | unode = SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); | ||||
| use_original = (unode->co || unode->bm_entry); | use_original = (unode->co || unode->bm_entry); | ||||
| } | } | ||||
| Show All 34 Lines | static void calc_area_normal_and_center_task_cb(void *__restrict userdata, | ||||
| /* When the mesh is edited we can't rely on original coords | /* When the mesh is edited we can't rely on original coords | ||||
| * (original mesh may not even have verts in brush radius). */ | * (original mesh may not even have verts in brush radius). */ | ||||
| if (use_original && data->has_bm_orco) { | if (use_original && data->has_bm_orco) { | ||||
| float(*orco_coords)[3]; | float(*orco_coords)[3]; | ||||
| int(*orco_tris)[3]; | int(*orco_tris)[3]; | ||||
| int orco_tris_num; | int orco_tris_num; | ||||
| BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords, NULL); | BKE_pbvh_node_get_bm_orco_data( | ||||
| data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords, nullptr); | |||||
| for (int i = 0; i < orco_tris_num; i++) { | for (int i = 0; i < orco_tris_num; i++) { | ||||
| const float *co_tri[3] = { | const float *co_tri[3] = { | ||||
| orco_coords[orco_tris[i][0]], | orco_coords[orco_tris[i][0]], | ||||
| orco_coords[orco_tris[i][1]], | orco_coords[orco_tris[i][1]], | ||||
| orco_coords[orco_tris[i][2]], | orco_coords[orco_tris[i][2]], | ||||
| }; | }; | ||||
| float co[3]; | float co[3]; | ||||
| ▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | ||||
| add_v3_v3(anctd->area_nos[flip_index], no); | add_v3_v3(anctd->area_nos[flip_index], no); | ||||
| anctd->count_no[flip_index] += 1; | anctd->count_no[flip_index] += 1; | ||||
| } | } | ||||
| } | } | ||||
| BKE_pbvh_vertex_iter_end; | BKE_pbvh_vertex_iter_end; | ||||
| } | } | ||||
| } | } | ||||
| static void calc_area_normal_and_center_reduce(const void *__restrict UNUSED(userdata), | static void calc_area_normal_and_center_reduce(const void *__restrict /*userdata*/, | ||||
| void *__restrict chunk_join, | void *__restrict chunk_join, | ||||
| void *__restrict chunk) | void *__restrict chunk) | ||||
| { | { | ||||
| AreaNormalCenterTLSData *join = chunk_join; | AreaNormalCenterTLSData *join = static_cast<AreaNormalCenterTLSData *>(chunk_join); | ||||
| AreaNormalCenterTLSData *anctd = chunk; | AreaNormalCenterTLSData *anctd = static_cast<AreaNormalCenterTLSData *>(chunk); | ||||
| /* For flatten center. */ | /* For flatten center. */ | ||||
| add_v3_v3(join->area_cos[0], anctd->area_cos[0]); | add_v3_v3(join->area_cos[0], anctd->area_cos[0]); | ||||
| add_v3_v3(join->area_cos[1], anctd->area_cos[1]); | add_v3_v3(join->area_cos[1], anctd->area_cos[1]); | ||||
| /* For area normal. */ | /* For area normal. */ | ||||
| add_v3_v3(join->area_nos[0], anctd->area_nos[0]); | add_v3_v3(join->area_nos[0], anctd->area_nos[0]); | ||||
| add_v3_v3(join->area_nos[1], anctd->area_nos[1]); | add_v3_v3(join->area_nos[1], anctd->area_nos[1]); | ||||
| /* Weights. */ | /* Weights. */ | ||||
| add_v2_v2_int(join->count_no, anctd->count_no); | add_v2_v2_int(join->count_no, anctd->count_no); | ||||
| add_v2_v2_int(join->count_co, anctd->count_co); | add_v2_v2_int(join->count_co, anctd->count_co); | ||||
| } | } | ||||
| void SCULPT_calc_area_center( | void SCULPT_calc_area_center( | ||||
| Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]) | Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]) | ||||
| { | { | ||||
| const Brush *brush = BKE_paint_brush(&sd->paint); | const Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); | const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); | ||||
| int n; | int n; | ||||
| /* Intentionally set 'sd' to NULL since we share logic with vertex paint. */ | /* Intentionally set 'sd' to nullptr since we share logic with vertex paint. */ | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data{}; | ||||
| .sd = NULL, | data.sd = nullptr; | ||||
| .ob = ob, | data.ob = ob; | ||||
| .brush = brush, | data.brush = brush; | ||||
| .nodes = nodes, | data.nodes = nodes; | ||||
| .totnode = totnode, | data.totnode = totnode; | ||||
| .has_bm_orco = has_bm_orco, | data.has_bm_orco = has_bm_orco; | ||||
| .use_area_cos = true, | data.use_area_cos = true; | ||||
| }; | |||||
| AreaNormalCenterTLSData anctd = {{{0}}}; | AreaNormalCenterTLSData anctd = {{{0}}}; | ||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| settings.func_reduce = calc_area_normal_and_center_reduce; | settings.func_reduce = calc_area_normal_and_center_reduce; | ||||
| settings.userdata_chunk = &anctd; | settings.userdata_chunk = &anctd; | ||||
| settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); | settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); | ||||
| Show All 32 Lines | bool SCULPT_pbvh_calc_area_normal(const Brush *brush, | ||||
| PBVHNode **nodes, | PBVHNode **nodes, | ||||
| int totnode, | int totnode, | ||||
| bool use_threading, | bool use_threading, | ||||
| float r_area_no[3]) | float r_area_no[3]) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); | const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); | ||||
| /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ | /* Intentionally set 'sd' to nullptr since this is used for vertex paint too. */ | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data{}; | ||||
| .sd = NULL, | data.sd = nullptr; | ||||
| .ob = ob, | data.ob = ob; | ||||
| .brush = brush, | data.brush = brush; | ||||
| .nodes = nodes, | data.nodes = nodes; | ||||
| .totnode = totnode, | data.totnode = totnode; | ||||
| .has_bm_orco = has_bm_orco, | data.has_bm_orco = has_bm_orco; | ||||
| .use_area_nos = true, | data.use_area_nos = true; | ||||
| .any_vertex_sampled = false, | data.any_vertex_sampled = false; | ||||
| }; | |||||
| AreaNormalCenterTLSData anctd = {{{0}}}; | AreaNormalCenterTLSData anctd = {{{0}}}; | ||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, use_threading, totnode); | BKE_pbvh_parallel_range_settings(&settings, use_threading, totnode); | ||||
| settings.func_reduce = calc_area_normal_and_center_reduce; | settings.func_reduce = calc_area_normal_and_center_reduce; | ||||
| settings.userdata_chunk = &anctd; | settings.userdata_chunk = &anctd; | ||||
| settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); | settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); | ||||
| Show All 12 Lines | |||||
| void SCULPT_calc_area_normal_and_center( | void SCULPT_calc_area_normal_and_center( | ||||
| Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) | Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) | ||||
| { | { | ||||
| const Brush *brush = BKE_paint_brush(&sd->paint); | const Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); | const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); | ||||
| int n; | int n; | ||||
| /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ | /* Intentionally set 'sd' to nullptr since this is used for vertex paint too. */ | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data{}; | ||||
| .sd = NULL, | data.sd = nullptr; | ||||
| .ob = ob, | data.ob = ob; | ||||
| .brush = brush, | data.brush = brush; | ||||
| .nodes = nodes, | data.nodes = nodes; | ||||
| .totnode = totnode, | data.totnode = totnode; | ||||
| .has_bm_orco = has_bm_orco, | data.has_bm_orco = has_bm_orco; | ||||
| .use_area_cos = true, | data.use_area_cos = true; | ||||
| .use_area_nos = true, | data.use_area_nos = true; | ||||
| }; | |||||
| AreaNormalCenterTLSData anctd = {{{0}}}; | AreaNormalCenterTLSData anctd = {{{0}}}; | ||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| settings.func_reduce = calc_area_normal_and_center_reduce; | settings.func_reduce = calc_area_normal_and_center_reduce; | ||||
| settings.userdata_chunk = &anctd; | settings.userdata_chunk = &anctd; | ||||
| settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); | settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); | ||||
| Show All 37 Lines | |||||
| * Return modified brush strength. Includes the direction of the brush, positive | * Return modified brush strength. Includes the direction of the brush, positive | ||||
| * values pull vertices, negative values push. Uses tablet pressure and a | * values pull vertices, negative values push. Uses tablet pressure and a | ||||
| * special multiplier found experimentally to scale the strength factor. | * special multiplier found experimentally to scale the strength factor. | ||||
| */ | */ | ||||
| static float brush_strength(const Sculpt *sd, | static float brush_strength(const Sculpt *sd, | ||||
| const StrokeCache *cache, | const StrokeCache *cache, | ||||
| const float feather, | const float feather, | ||||
| const UnifiedPaintSettings *ups, | const UnifiedPaintSettings *ups, | ||||
| const PaintModeSettings *UNUSED(paint_mode_settings)) | const PaintModeSettings * /*paint_mode_settings*/) | ||||
| { | { | ||||
| const Scene *scene = cache->vc->scene; | const Scene *scene = cache->vc->scene; | ||||
| const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); | const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); | ||||
| /* Primary strength input; square it to make lower values more sensitive. */ | /* Primary strength input; square it to make lower values more sensitive. */ | ||||
| const float root_alpha = BKE_brush_alpha_get(scene, brush); | const float root_alpha = BKE_brush_alpha_get(scene, brush); | ||||
| const float alpha = root_alpha * root_alpha; | const float alpha = root_alpha * root_alpha; | ||||
| const float dir = (brush->flag & BRUSH_DIR_IN) ? -1.0f : 1.0f; | const float dir = (brush->flag & BRUSH_DIR_IN) ? -1.0f : 1.0f; | ||||
| ▲ Show 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | float SCULPT_brush_strength_factor(SculptSession *ss, | ||||
| /* Auto-masking. */ | /* Auto-masking. */ | ||||
| avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex, automask_data); | avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex, automask_data); | ||||
| return avg; | return avg; | ||||
| } | } | ||||
| bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v) | bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v) | ||||
| { | { | ||||
| SculptSearchSphereData *data = data_v; | SculptSearchSphereData *data = static_cast<SculptSearchSphereData *>(data_v); | ||||
| const float *center; | const float *center; | ||||
| float nearest[3]; | float nearest[3]; | ||||
| if (data->center) { | if (data->center) { | ||||
| center = data->center; | center = data->center; | ||||
| } | } | ||||
| else { | else { | ||||
| center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location; | center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location; | ||||
| } | } | ||||
| Show All 29 Lines | bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v) | ||||
| sub_v3_v3v3(t, center, nearest); | sub_v3_v3v3(t, center, nearest); | ||||
| return len_squared_v3(t) < data->radius_squared; | return len_squared_v3(t) < data->radius_squared; | ||||
| } | } | ||||
| bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v) | bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v) | ||||
| { | { | ||||
| SculptSearchCircleData *data = data_v; | SculptSearchCircleData *data = static_cast<SculptSearchCircleData *>(data_v); | ||||
| float bb_min[3], bb_max[3]; | float bb_min[3], bb_max[3]; | ||||
| if (data->ignore_fully_ineffective) { | if (data->ignore_fully_ineffective) { | ||||
| if (BKE_pbvh_node_fully_masked_get(node)) { | if (BKE_pbvh_node_fully_masked_get(node)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob, | static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob, | ||||
| Sculpt *sd, | Sculpt *sd, | ||||
| bool use_original, | bool use_original, | ||||
| int *r_totnode) | int *r_totnode) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| PBVHNode **nodes = NULL; | PBVHNode **nodes = nullptr; | ||||
| SculptSearchSphereData data = { | SculptSearchSphereData data{}; | ||||
| .ss = ss, | data.ss = ss; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .radius_squared = ss->cursor_radius, | data.radius_squared = ss->cursor_radius; | ||||
| .original = use_original, | data.original = use_original; | ||||
| .ignore_fully_ineffective = false, | data.ignore_fully_ineffective = false; | ||||
| .center = NULL, | data.center = nullptr; | ||||
| }; | |||||
| BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); | BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); | ||||
| return nodes; | return nodes; | ||||
| } | } | ||||
| static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, | static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, | ||||
| Sculpt *sd, | Sculpt *sd, | ||||
| const Brush *brush, | const Brush *brush, | ||||
| bool use_original, | bool use_original, | ||||
| float radius_scale, | float radius_scale, | ||||
| int *r_totnode) | int *r_totnode) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| PBVHNode **nodes = NULL; | PBVHNode **nodes = nullptr; | ||||
| /* Build a list of all nodes that are potentially within the cursor or brush's area of influence. | /* Build a list of all nodes that are potentially within the cursor or brush's area of influence. | ||||
| */ | */ | ||||
| if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { | if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { | ||||
| SculptSearchSphereData data = { | SculptSearchSphereData data{}; | ||||
| .ss = ss, | data.ss = ss; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .radius_squared = square_f(ss->cache->radius * radius_scale), | data.radius_squared = square_f(ss->cache->radius * radius_scale); | ||||
| .original = use_original, | data.original = use_original; | ||||
| .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK, | data.ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK; | ||||
| .center = NULL, | data.center = nullptr; | ||||
| }; | |||||
| BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); | BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); | ||||
| } | } | ||||
| else { | else { | ||||
| struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; | DistRayAABB_Precalc dist_ray_to_aabb_precalc; | ||||
| dist_squared_ray_to_aabb_v3_precalc( | dist_squared_ray_to_aabb_v3_precalc( | ||||
| &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); | &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); | ||||
| SculptSearchCircleData data = { | SculptSearchCircleData data{}; | ||||
| .ss = ss, | data.ss = ss; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .radius_squared = ss->cache ? square_f(ss->cache->radius * radius_scale) : | data.radius_squared = ss->cache ? square_f(ss->cache->radius * radius_scale) : | ||||
| ss->cursor_radius, | ss->cursor_radius; | ||||
| .original = use_original, | data.original = use_original; | ||||
| .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, | data.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc; | ||||
| .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK, | data.ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK; | ||||
| }; | |||||
| BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode); | BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode); | ||||
| } | } | ||||
| return nodes; | return nodes; | ||||
| } | } | ||||
| /* Calculate primary direction of movement for many brushes. */ | /* Calculate primary direction of movement for many brushes. */ | ||||
| static void calc_sculpt_normal( | static void calc_sculpt_normal( | ||||
| Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) | Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) | ||||
| ▲ Show 20 Lines • Show All 181 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Generic Brush Plane & Symmetry Utilities | /** \name Generic Brush Plane & Symmetry Utilities | ||||
| * \{ */ | * \{ */ | ||||
| typedef struct { | struct SculptRaycastData { | ||||
| SculptSession *ss; | SculptSession *ss; | ||||
| const float *ray_start; | const float *ray_start; | ||||
| const float *ray_normal; | const float *ray_normal; | ||||
| bool hit; | bool hit; | ||||
| float depth; | float depth; | ||||
| bool original; | bool original; | ||||
| PBVHVertRef active_vertex; | PBVHVertRef active_vertex; | ||||
| float *face_normal; | float *face_normal; | ||||
| int active_face_grid_index; | int active_face_grid_index; | ||||
| struct IsectRayPrecalc isect_precalc; | IsectRayPrecalc isect_precalc; | ||||
| } SculptRaycastData; | }; | ||||
| typedef struct { | struct SculptFindNearestToRayData { | ||||
| SculptSession *ss; | SculptSession *ss; | ||||
| const float *ray_start, *ray_normal; | const float *ray_start, *ray_normal; | ||||
| bool hit; | bool hit; | ||||
| float depth; | float depth; | ||||
| float dist_sq_to_ray; | float dist_sq_to_ray; | ||||
| bool original; | bool original; | ||||
| } SculptFindNearestToRayData; | }; | ||||
| ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3]) | ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3]) | ||||
| { | { | ||||
| ePaintSymmetryAreas symm_area = PAINT_SYMM_AREA_DEFAULT; | ePaintSymmetryAreas symm_area = ePaintSymmetryAreas(PAINT_SYMM_AREA_DEFAULT); | ||||
| if (co[0] < 0.0f) { | if (co[0] < 0.0f) { | ||||
| symm_area |= PAINT_SYMM_AREA_X; | symm_area |= PAINT_SYMM_AREA_X; | ||||
| } | } | ||||
| if (co[1] < 0.0f) { | if (co[1] < 0.0f) { | ||||
| symm_area |= PAINT_SYMM_AREA_Y; | symm_area |= PAINT_SYMM_AREA_Y; | ||||
| } | } | ||||
| if (co[2] < 0.0f) { | if (co[2] < 0.0f) { | ||||
| symm_area |= PAINT_SYMM_AREA_Z; | symm_area |= PAINT_SYMM_AREA_Z; | ||||
| } | } | ||||
| return symm_area; | return symm_area; | ||||
| } | } | ||||
| void SCULPT_flip_v3_by_symm_area(float v[3], | void SCULPT_flip_v3_by_symm_area(float v[3], | ||||
| const ePaintSymmetryFlags symm, | const ePaintSymmetryFlags symm, | ||||
| const ePaintSymmetryAreas symmarea, | const ePaintSymmetryAreas symmarea, | ||||
| const float pivot[3]) | const float pivot[3]) | ||||
| { | { | ||||
| for (int i = 0; i < 3; i++) { | for (int i = 0; i < 3; i++) { | ||||
| ePaintSymmetryFlags symm_it = 1 << i; | ePaintSymmetryFlags symm_it = ePaintSymmetryFlags(1 << i); | ||||
| if (!(symm & symm_it)) { | if (!(symm & symm_it)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (symmarea & symm_it) { | if (symmarea & symm_it) { | ||||
| flip_v3(v, symm_it); | flip_v3(v, symm_it); | ||||
| } | } | ||||
| if (pivot[i] < 0.0f) { | if (pivot[i] < 0.0f) { | ||||
| flip_v3(v, symm_it); | flip_v3(v, symm_it); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void SCULPT_flip_quat_by_symm_area(float quat[4], | void SCULPT_flip_quat_by_symm_area(float quat[4], | ||||
| const ePaintSymmetryFlags symm, | const ePaintSymmetryFlags symm, | ||||
| const ePaintSymmetryAreas symmarea, | const ePaintSymmetryAreas symmarea, | ||||
| const float pivot[3]) | const float pivot[3]) | ||||
| { | { | ||||
| for (int i = 0; i < 3; i++) { | for (int i = 0; i < 3; i++) { | ||||
| ePaintSymmetryFlags symm_it = 1 << i; | ePaintSymmetryFlags symm_it = ePaintSymmetryFlags(1 << i); | ||||
| if (!(symm & symm_it)) { | if (!(symm & symm_it)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (symmarea & symm_it) { | if (symmarea & symm_it) { | ||||
| flip_qt(quat, symm_it); | flip_qt(quat, symm_it); | ||||
| } | } | ||||
| if (pivot[i] < 0.0f) { | if (pivot[i] < 0.0f) { | ||||
| flip_qt(quat, symm_it); | flip_qt(quat, symm_it); | ||||
| ▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Sculpt Gravity Brush | /** \name Sculpt Gravity Brush | ||||
| * \{ */ | * \{ */ | ||||
| static void do_gravity_task_cb_ex(void *__restrict userdata, | static void do_gravity_task_cb_ex(void *__restrict userdata, | ||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict tls) | const TaskParallelTLS *__restrict tls) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| const Brush *brush = data->brush; | const Brush *brush = data->brush; | ||||
| float *offset = data->offset; | float *offset = data->offset; | ||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| float(*proxy)[3]; | float(*proxy)[3]; | ||||
| proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; | proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; | ||||
| Show All 11 Lines | const float fade = SCULPT_brush_strength_factor(ss, | ||||
| brush, | brush, | ||||
| vd.co, | vd.co, | ||||
| sqrtf(test.dist), | sqrtf(test.dist), | ||||
| vd.no, | vd.no, | ||||
| vd.fno, | vd.fno, | ||||
| vd.mask ? *vd.mask : 0.0f, | vd.mask ? *vd.mask : 0.0f, | ||||
| vd.vertex, | vd.vertex, | ||||
| thread_id, | thread_id, | ||||
| NULL); | nullptr); | ||||
| mul_v3_v3fl(proxy[vd.i], offset, fade); | mul_v3_v3fl(proxy[vd.i], offset, fade); | ||||
| if (vd.mvert) { | if (vd.mvert) { | ||||
| BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); | BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); | ||||
| } | } | ||||
| } | } | ||||
| BKE_pbvh_vertex_iter_end; | BKE_pbvh_vertex_iter_end; | ||||
| Show All 9 Lines | static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength) | ||||
| mul_v3_v3fl(gravity_vector, ss->cache->gravity_direction, -ss->cache->radius_squared); | mul_v3_v3fl(gravity_vector, ss->cache->gravity_direction, -ss->cache->radius_squared); | ||||
| /* Offset with as much as possible factored in already. */ | /* Offset with as much as possible factored in already. */ | ||||
| mul_v3_v3v3(offset, gravity_vector, ss->cache->scale); | mul_v3_v3v3(offset, gravity_vector, ss->cache->scale); | ||||
| mul_v3_fl(offset, bstrength); | mul_v3_fl(offset, bstrength); | ||||
| /* Threaded loop over nodes. */ | /* Threaded loop over nodes. */ | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data{}; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .ob = ob, | data.ob = ob; | ||||
| .brush = brush, | data.brush = brush; | ||||
| .nodes = nodes, | data.nodes = nodes; | ||||
| .offset = offset, | data.offset = offset; | ||||
| }; | |||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); | BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Sculpt Brush Utilities | /** \name Sculpt Brush Utilities | ||||
| * \{ */ | * \{ */ | ||||
| void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) | void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) | ||||
| { | { | ||||
| Mesh *me = (Mesh *)ob->data; | Mesh *me = (Mesh *)ob->data; | ||||
| float(*ofs)[3] = NULL; | float(*ofs)[3] = nullptr; | ||||
| int a; | int a; | ||||
| const int kb_act_idx = ob->shapenr - 1; | const int kb_act_idx = ob->shapenr - 1; | ||||
| KeyBlock *currkey; | |||||
| /* For relative keys editing of base should update other keys. */ | /* For relative keys editing of base should update other keys. */ | ||||
| if (BKE_keyblock_is_basis(me->key, kb_act_idx)) { | if (BKE_keyblock_is_basis(me->key, kb_act_idx)) { | ||||
| ofs = BKE_keyblock_convert_to_vertcos(ob, kb); | ofs = BKE_keyblock_convert_to_vertcos(ob, kb); | ||||
| /* Calculate key coord offsets (from previous location). */ | /* Calculate key coord offsets (from previous location). */ | ||||
| for (a = 0; a < me->totvert; a++) { | for (a = 0; a < me->totvert; a++) { | ||||
| sub_v3_v3v3(ofs[a], vertCos[a], ofs[a]); | sub_v3_v3v3(ofs[a], vertCos[a], ofs[a]); | ||||
| } | } | ||||
| /* Apply offsets on other keys. */ | /* Apply offsets on other keys. */ | ||||
| for (currkey = me->key->block.first; currkey; currkey = currkey->next) { | LISTBASE_FOREACH (KeyBlock *, currkey, &me->key->block) { | ||||
| if ((currkey != kb) && (currkey->relative == kb_act_idx)) { | if ((currkey != kb) && (currkey->relative == kb_act_idx)) { | ||||
| BKE_keyblock_update_from_offset(ob, currkey, ofs); | BKE_keyblock_update_from_offset(ob, currkey, ofs); | ||||
| } | } | ||||
| } | } | ||||
| MEM_freeN(ofs); | MEM_freeN(ofs); | ||||
| } | } | ||||
| Show All 12 Lines | |||||
| } | } | ||||
| /* NOTE: we do the topology update before any brush actions to avoid | /* NOTE: we do the topology update before any brush actions to avoid | ||||
| * issues with the proxies. The size of the proxy can't change, so | * issues with the proxies. The size of the proxy can't change, so | ||||
| * topology must be updated first. */ | * topology must be updated first. */ | ||||
| static void sculpt_topology_update(Sculpt *sd, | static void sculpt_topology_update(Sculpt *sd, | ||||
| Object *ob, | Object *ob, | ||||
| Brush *brush, | Brush *brush, | ||||
| UnifiedPaintSettings *UNUSED(ups), | UnifiedPaintSettings * /*ups*/, | ||||
| PaintModeSettings *UNUSED(paint_mode_settings)) | PaintModeSettings * /*paint_mode_settings*/) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| int n, totnode; | int n, totnode; | ||||
| /* Build a list of all nodes that are potentially within the brush's area of influence. */ | /* Build a list of all nodes that are potentially within the brush's area of influence. */ | ||||
| const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : | const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : | ||||
| ss->cache->original; | ss->cache->original; | ||||
| const float radius_scale = 1.25f; | const float radius_scale = 1.25f; | ||||
| PBVHNode **nodes = sculpt_pbvh_gather_generic( | PBVHNode **nodes = sculpt_pbvh_gather_generic( | ||||
| ob, sd, brush, use_original, radius_scale, &totnode); | ob, sd, brush, use_original, radius_scale, &totnode); | ||||
| /* Only act if some verts are inside the brush area. */ | /* Only act if some verts are inside the brush area. */ | ||||
| if (totnode == 0) { | if (totnode == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| /* Free index based vertex info as it will become invalid after modifying the topology during the | /* Free index based vertex info as it will become invalid after modifying the topology during the | ||||
| * stroke. */ | * stroke. */ | ||||
| MEM_SAFE_FREE(ss->vertex_info.boundary); | MEM_SAFE_FREE(ss->vertex_info.boundary); | ||||
| MEM_SAFE_FREE(ss->vertex_info.connected_component); | MEM_SAFE_FREE(ss->vertex_info.connected_component); | ||||
| PBVHTopologyUpdateMode mode = 0; | PBVHTopologyUpdateMode mode = PBVHTopologyUpdateMode(0); | ||||
| float location[3]; | float location[3]; | ||||
| if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) { | if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) { | ||||
| if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { | if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { | ||||
| mode |= PBVH_Subdivide; | mode |= PBVH_Subdivide; | ||||
| } | } | ||||
| if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) { | if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) { | ||||
| Show All 28 Lines | static void sculpt_topology_update(Sculpt *sd, | ||||
| /* Update average stroke position. */ | /* Update average stroke position. */ | ||||
| copy_v3_v3(location, ss->cache->true_location); | copy_v3_v3(location, ss->cache->true_location); | ||||
| mul_m4_v3(ob->object_to_world, location); | mul_m4_v3(ob->object_to_world, location); | ||||
| } | } | ||||
| static void do_brush_action_task_cb(void *__restrict userdata, | static void do_brush_action_task_cb(void *__restrict userdata, | ||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict UNUSED(tls)) | const TaskParallelTLS *__restrict /*tls*/) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| bool need_coords = ss->cache->supports_gravity; | bool need_coords = ss->cache->supports_gravity; | ||||
| /* Face Sets modifications do a single undo push */ | /* Face Sets modifications do a single undo push */ | ||||
| if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { | if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { | ||||
| BKE_pbvh_node_mark_redraw(data->nodes[n]); | BKE_pbvh_node_mark_redraw(data->nodes[n]); | ||||
| /* Draw face sets in smooth mode moves the vertices. */ | /* Draw face sets in smooth mode moves the vertices. */ | ||||
| Show All 39 Lines | if (SCULPT_tool_is_paint(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) { | ||||
| BKE_pbvh_ensure_node_loops(ss->pbvh); | BKE_pbvh_ensure_node_loops(ss->pbvh); | ||||
| } | } | ||||
| /* Build a list of all nodes that are potentially within the brush's area of influence */ | /* Build a list of all nodes that are potentially within the brush's area of influence */ | ||||
| if (SCULPT_tool_needs_all_pbvh_nodes(brush)) { | if (SCULPT_tool_needs_all_pbvh_nodes(brush)) { | ||||
| /* These brushes need to update all nodes as they are not constrained by the brush radius */ | /* These brushes need to update all nodes as they are not constrained by the brush radius */ | ||||
| BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); | BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode); | ||||
| } | } | ||||
| else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { | else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { | ||||
| nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode); | nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode); | ||||
| } | } | ||||
| else { | else { | ||||
| const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : | const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : | ||||
| ss->cache->original; | ss->cache->original; | ||||
| float radius_scale = 1.0f; | float radius_scale = 1.0f; | ||||
| Show All 21 Lines | static void do_brush_action(Sculpt *sd, | ||||
| * and the number of nodes under the brush influence. */ | * and the number of nodes under the brush influence. */ | ||||
| if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && | if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && | ||||
| SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) { | SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) { | ||||
| /* Dynamic-topology does not support Face Sets data, so it can't store/restore it from undo. */ | /* Dynamic-topology does not support Face Sets data, so it can't store/restore it from undo. */ | ||||
| /* TODO(pablodp606): This check should be done in the undo code and not here, but the rest of | /* TODO(pablodp606): This check should be done in the undo code and not here, but the rest of | ||||
| * the sculpt code is not checking for unsupported undo types that may return a null node. */ | * the sculpt code is not checking for unsupported undo types that may return a null node. */ | ||||
| if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { | if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { | ||||
| SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS); | SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_FACE_SETS); | ||||
| } | } | ||||
| if (ss->cache->invert) { | if (ss->cache->invert) { | ||||
| /* When inverting the brush, pick the paint face mask ID from the mesh. */ | /* When inverting the brush, pick the paint face mask ID from the mesh. */ | ||||
| ss->cache->paint_face_set = SCULPT_active_face_set_get(ss); | ss->cache->paint_face_set = SCULPT_active_face_set_get(ss); | ||||
| } | } | ||||
| else { | else { | ||||
| /* By default create a new Face Sets. */ | /* By default create a new Face Sets. */ | ||||
| Show All 10 Lines | if (SCULPT_stroke_is_first_brush_step(ss->cache)) { | ||||
| if (SCULPT_is_automasking_enabled(sd, ss, brush)) { | if (SCULPT_is_automasking_enabled(sd, ss, brush)) { | ||||
| ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); | ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); | ||||
| ss->last_automasking_settings_hash = SCULPT_automasking_settings_hash( | ss->last_automasking_settings_hash = SCULPT_automasking_settings_hash( | ||||
| ob, ss->cache->automasking); | ob, ss->cache->automasking); | ||||
| } | } | ||||
| /* Initialize surface smooth cache. */ | /* Initialize surface smooth cache. */ | ||||
| if ((brush->sculpt_tool == SCULPT_TOOL_SMOOTH) && | if ((brush->sculpt_tool == SCULPT_TOOL_SMOOTH) && | ||||
| (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE)) { | (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE)) { | ||||
| BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL); | BLI_assert(ss->cache->surface_smooth_laplacian_disp == nullptr); | ||||
| ss->cache->surface_smooth_laplacian_disp = MEM_callocN( | ss->cache->surface_smooth_laplacian_disp = static_cast<float(*)[3]>( | ||||
| sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b"); | MEM_callocN(sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b")); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* Only act if some verts are inside the brush area. */ | /* Only act if some verts are inside the brush area. */ | ||||
| if (totnode == 0) { | if (totnode == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| float location[3]; | float location[3]; | ||||
| if (!use_pixels) { | if (!use_pixels) { | ||||
| SculptThreadedTaskData task_data = { | SculptThreadedTaskData task_data{}; | ||||
| .sd = sd, | task_data.sd = sd; | ||||
| .ob = ob, | task_data.ob = ob; | ||||
| .brush = brush, | task_data.brush = brush; | ||||
| .nodes = nodes, | task_data.nodes = nodes; | ||||
| }; | |||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); | BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); | ||||
| } | } | ||||
| if (sculpt_brush_needs_normal(ss, sd, brush)) { | if (sculpt_brush_needs_normal(ss, sd, brush)) { | ||||
| update_sculpt_normal(sd, ob, nodes, totnode); | update_sculpt_normal(sd, ob, nodes, totnode); | ||||
| ▲ Show 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | static void do_brush_action(Sculpt *sd, | ||||
| /* Update last stroke position. */ | /* Update last stroke position. */ | ||||
| ups->last_stroke_valid = true; | ups->last_stroke_valid = true; | ||||
| } | } | ||||
| /* Flush displacement from deformed PBVH vertex to original mesh. */ | /* Flush displacement from deformed PBVH vertex to original mesh. */ | ||||
| static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd) | static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| Mesh *me = ob->data; | Mesh *me = static_cast<Mesh *>(ob->data); | ||||
| float disp[3], newco[3]; | float disp[3], newco[3]; | ||||
| int index = vd->vert_indices[vd->i]; | int index = vd->vert_indices[vd->i]; | ||||
| sub_v3_v3v3(disp, vd->co, ss->deform_cos[index]); | sub_v3_v3v3(disp, vd->co, ss->deform_cos[index]); | ||||
| mul_m3_v3(ss->deform_imats[index], disp); | mul_m3_v3(ss->deform_imats[index], disp); | ||||
| add_v3_v3v3(newco, disp, ss->orig_cos[index]); | add_v3_v3v3(newco, disp, ss->orig_cos[index]); | ||||
| copy_v3_v3(ss->deform_cos[index], vd->co); | copy_v3_v3(ss->deform_cos[index], vd->co); | ||||
| copy_v3_v3(ss->orig_cos[index], newco); | copy_v3_v3(ss->orig_cos[index], newco); | ||||
| MVert *verts = BKE_mesh_verts_for_write(me); | MVert *verts = BKE_mesh_verts_for_write(me); | ||||
| if (!ss->shapekey_active) { | if (!ss->shapekey_active) { | ||||
| copy_v3_v3(verts[index].co, newco); | copy_v3_v3(verts[index].co, newco); | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_combine_proxies_task_cb(void *__restrict userdata, | static void sculpt_combine_proxies_task_cb(void *__restrict userdata, | ||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict UNUSED(tls)) | const TaskParallelTLS *__restrict /*tls*/) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| Sculpt *sd = data->sd; | Sculpt *sd = data->sd; | ||||
| Object *ob = data->ob; | Object *ob = data->ob; | ||||
| const bool use_orco = data->use_proxies_orco; | const bool use_orco = data->use_proxies_orco; | ||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| PBVHProxyNode *proxies; | PBVHProxyNode *proxies; | ||||
| int proxy_count; | int proxy_count; | ||||
| float(*orco)[3] = NULL; | float(*orco)[3] = nullptr; | ||||
| if (use_orco && !ss->bm) { | if (use_orco && !ss->bm) { | ||||
| orco = SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS)->co; | orco = SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS)->co; | ||||
| } | } | ||||
| BKE_pbvh_node_get_proxies(data->nodes[n], &proxies, &proxy_count); | BKE_pbvh_node_get_proxies(data->nodes[n], &proxies, &proxy_count); | ||||
| BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | ||||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | const bool use_orco = ELEM(brush->sculpt_tool, | ||||
| SCULPT_TOOL_ROTATE, | SCULPT_TOOL_ROTATE, | ||||
| SCULPT_TOOL_THUMB, | SCULPT_TOOL_THUMB, | ||||
| SCULPT_TOOL_ELASTIC_DEFORM, | SCULPT_TOOL_ELASTIC_DEFORM, | ||||
| SCULPT_TOOL_BOUNDARY, | SCULPT_TOOL_BOUNDARY, | ||||
| SCULPT_TOOL_POSE); | SCULPT_TOOL_POSE); | ||||
| BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); | BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data{}; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .ob = ob, | data.ob = ob; | ||||
| .brush = brush, | data.brush = brush; | ||||
| .nodes = nodes, | data.nodes = nodes; | ||||
| .use_proxies_orco = use_orco, | data.use_proxies_orco = use_orco; | ||||
| }; | |||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); | BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); | ||||
| MEM_SAFE_FREE(nodes); | MEM_SAFE_FREE(nodes); | ||||
| } | } | ||||
| void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob) | void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| PBVHNode **nodes; | PBVHNode **nodes; | ||||
| int totnode; | int totnode; | ||||
| BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); | BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data{}; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .ob = ob, | data.ob = ob; | ||||
| .nodes = nodes, | data.nodes = nodes; | ||||
| .use_proxies_orco = false, | data.use_proxies_orco = false; | ||||
| }; | |||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); | BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); | ||||
| MEM_SAFE_FREE(nodes); | MEM_SAFE_FREE(nodes); | ||||
| } | } | ||||
| Show All 22 Lines | static void sculpt_update_keyblock(Object *ob) | ||||
| if (vertCos != ss->orig_cos) { | if (vertCos != ss->orig_cos) { | ||||
| MEM_freeN(vertCos); | MEM_freeN(vertCos); | ||||
| } | } | ||||
| } | } | ||||
| static void SCULPT_flush_stroke_deform_task_cb(void *__restrict userdata, | static void SCULPT_flush_stroke_deform_task_cb(void *__restrict userdata, | ||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict UNUSED(tls)) | const TaskParallelTLS *__restrict /*tls*/) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| Object *ob = data->ob; | Object *ob = data->ob; | ||||
| float(*vertCos)[3] = data->vertCos; | float(*vertCos)[3] = data->vertCos; | ||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | ||||
| sculpt_flush_pbvhvert_deform(ob, &vd); | sculpt_flush_pbvhvert_deform(ob, &vd); | ||||
| Show All 15 Lines | void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used) | ||||
| if (is_proxy_used && ss->deform_modifiers_active) { | if (is_proxy_used && ss->deform_modifiers_active) { | ||||
| /* This brushes aren't using proxies, so sculpt_combine_proxies() wouldn't propagate needed | /* This brushes aren't using proxies, so sculpt_combine_proxies() wouldn't propagate needed | ||||
| * deformation to original base. */ | * deformation to original base. */ | ||||
| int totnode; | int totnode; | ||||
| Mesh *me = (Mesh *)ob->data; | Mesh *me = (Mesh *)ob->data; | ||||
| PBVHNode **nodes; | PBVHNode **nodes; | ||||
| float(*vertCos)[3] = NULL; | float(*vertCos)[3] = nullptr; | ||||
| if (ss->shapekey_active) { | if (ss->shapekey_active) { | ||||
| vertCos = MEM_mallocN(sizeof(*vertCos) * me->totvert, "flushStrokeDeofrm keyVerts"); | vertCos = static_cast<float(*)[3]>( | ||||
| MEM_mallocN(sizeof(*vertCos) * me->totvert, "flushStrokeDeofrm keyVerts")); | |||||
| /* Mesh could have isolated verts which wouldn't be in BVH, to deal with this we copy old | /* Mesh could have isolated verts which wouldn't be in BVH, to deal with this we copy old | ||||
| * coordinates over new ones and then update coordinates for all vertices from BVH. */ | * coordinates over new ones and then update coordinates for all vertices from BVH. */ | ||||
| memcpy(vertCos, ss->orig_cos, sizeof(*vertCos) * me->totvert); | memcpy(vertCos, ss->orig_cos, sizeof(*vertCos) * me->totvert); | ||||
| } | } | ||||
| BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); | BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode); | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data{}; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .ob = ob, | data.ob = ob; | ||||
| .brush = brush, | data.brush = brush; | ||||
| .nodes = nodes, | data.nodes = nodes; | ||||
| .vertCos = vertCos, | data.vertCos = vertCos; | ||||
| }; | |||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| BLI_task_parallel_range(0, totnode, &data, SCULPT_flush_stroke_deform_task_cb, &settings); | BLI_task_parallel_range(0, totnode, &data, SCULPT_flush_stroke_deform_task_cb, &settings); | ||||
| if (vertCos) { | if (vertCos) { | ||||
| SCULPT_vertcos_to_key(ob, ss->shapekey_active, vertCos); | SCULPT_vertcos_to_key(ob, ss->shapekey_active, vertCos); | ||||
| MEM_freeN(vertCos); | MEM_freeN(vertCos); | ||||
| } | } | ||||
| MEM_SAFE_FREE(nodes); | MEM_SAFE_FREE(nodes); | ||||
| } | } | ||||
| else if (ss->shapekey_active) { | else if (ss->shapekey_active) { | ||||
| sculpt_update_keyblock(ob); | sculpt_update_keyblock(ob); | ||||
| } | } | ||||
| } | } | ||||
| void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, | void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, | ||||
| const char symm, | const ePaintSymmetryFlags symm, | ||||
| const char axis, | const char axis, | ||||
| const float angle) | const float angle) | ||||
| { | { | ||||
| flip_v3_v3(cache->location, cache->true_location, symm); | flip_v3_v3(cache->location, cache->true_location, symm); | ||||
| flip_v3_v3(cache->last_location, cache->true_last_location, symm); | flip_v3_v3(cache->last_location, cache->true_last_location, symm); | ||||
| flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm); | flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm); | ||||
| flip_v3_v3(cache->view_normal, cache->true_view_normal, symm); | flip_v3_v3(cache->view_normal, cache->true_view_normal, symm); | ||||
| Show All 33 Lines | if (cache->supports_gravity) { | ||||
| mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction); | mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction); | ||||
| } | } | ||||
| if (cache->is_rake_rotation_valid) { | if (cache->is_rake_rotation_valid) { | ||||
| flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm); | flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm); | ||||
| } | } | ||||
| } | } | ||||
| typedef void (*BrushActionFunc)(Sculpt *sd, | using BrushActionFunc = void (*)(Sculpt *sd, | ||||
| Object *ob, | Object *ob, | ||||
| Brush *brush, | Brush *brush, | ||||
| UnifiedPaintSettings *ups, | UnifiedPaintSettings *ups, | ||||
| PaintModeSettings *paint_mode_settings); | PaintModeSettings *paint_mode_settings); | ||||
| static void do_tiled(Sculpt *sd, | static void do_tiled(Sculpt *sd, | ||||
| Object *ob, | Object *ob, | ||||
| Brush *brush, | Brush *brush, | ||||
| UnifiedPaintSettings *ups, | UnifiedPaintSettings *ups, | ||||
| PaintModeSettings *paint_mode_settings, | PaintModeSettings *paint_mode_settings, | ||||
| BrushActionFunc action) | BrushActionFunc action) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| static void do_radial_symmetry(Sculpt *sd, | static void do_radial_symmetry(Sculpt *sd, | ||||
| Object *ob, | Object *ob, | ||||
| Brush *brush, | Brush *brush, | ||||
| UnifiedPaintSettings *ups, | UnifiedPaintSettings *ups, | ||||
| PaintModeSettings *paint_mode_settings, | PaintModeSettings *paint_mode_settings, | ||||
| BrushActionFunc action, | BrushActionFunc action, | ||||
| const char symm, | const ePaintSymmetryFlags symm, | ||||
| const int axis, | const int axis, | ||||
| const float UNUSED(feather)) | const float /*feather*/) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| for (int i = 1; i < sd->radial_symm[axis - 'X']; i++) { | for (int i = 1; i < sd->radial_symm[axis - 'X']; i++) { | ||||
| const float angle = 2.0f * M_PI * i / sd->radial_symm[axis - 'X']; | const float angle = 2.0f * M_PI * i / sd->radial_symm[axis - 'X']; | ||||
| ss->cache->radial_symmetry_pass = i; | ss->cache->radial_symmetry_pass = i; | ||||
| SCULPT_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); | SCULPT_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); | ||||
| do_tiled(sd, ob, brush, ups, paint_mode_settings, action); | do_tiled(sd, ob, brush, ups, paint_mode_settings, action); | ||||
| Show All 32 Lines | static void do_symmetrical_brush_actions(Sculpt *sd, | ||||
| cache->symmetry = symm; | cache->symmetry = symm; | ||||
| /* `symm` is a bit combination of XYZ - | /* `symm` is a bit combination of XYZ - | ||||
| * 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ | * 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ | ||||
| for (int i = 0; i <= symm; i++) { | for (int i = 0; i <= symm; i++) { | ||||
| if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { | if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| cache->mirror_symmetry_pass = i; | const ePaintSymmetryFlags symm = ePaintSymmetryFlags(i); | ||||
| cache->mirror_symmetry_pass = symm; | |||||
| cache->radial_symmetry_pass = 0; | cache->radial_symmetry_pass = 0; | ||||
| SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); | SCULPT_cache_calc_brushdata_symm(cache, symm, 0, 0); | ||||
| do_tiled(sd, ob, brush, ups, paint_mode_settings, action); | do_tiled(sd, ob, brush, ups, paint_mode_settings, action); | ||||
| do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'X', feather); | do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, symm, 'X', feather); | ||||
| do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'Y', feather); | do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, symm, 'Y', feather); | ||||
| do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'Z', feather); | do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, symm, 'Z', feather); | ||||
| } | } | ||||
| } | } | ||||
| bool SCULPT_mode_poll(bContext *C) | bool SCULPT_mode_poll(bContext *C) | ||||
| { | { | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| return ob && ob->mode & OB_MODE_SCULPT; | return ob && ob->mode & OB_MODE_SCULPT; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | void SCULPT_cache_free(StrokeCache *cache) | ||||
| } | } | ||||
| MEM_freeN(cache); | MEM_freeN(cache); | ||||
| } | } | ||||
| /* Initialize mirror modifier clipping. */ | /* Initialize mirror modifier clipping. */ | ||||
| static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss) | static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss) | ||||
| { | { | ||||
| ModifierData *md; | |||||
| unit_m4(ss->cache->clip_mirror_mtx); | unit_m4(ss->cache->clip_mirror_mtx); | ||||
| for (md = ob->modifiers.first; md; md = md->next) { | LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { | ||||
| if (!(md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime))) { | if (!(md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime))) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| MirrorModifierData *mmd = (MirrorModifierData *)md; | MirrorModifierData *mmd = (MirrorModifierData *)md; | ||||
| if (!(mmd->flag & MOD_MIR_CLIPPING)) { | if (!(mmd->flag & MOD_MIR_CLIPPING)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | else { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* Initialize the stroke cache invariants from operator properties. */ | /* Initialize the stroke cache invariants from operator properties. */ | ||||
| static void sculpt_update_cache_invariants( | static void sculpt_update_cache_invariants( | ||||
| bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2]) | bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2]) | ||||
| { | { | ||||
| StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); | StrokeCache *cache = static_cast<StrokeCache *>( | ||||
| MEM_callocN(sizeof(StrokeCache), "stroke cache")); | |||||
| ToolSettings *tool_settings = CTX_data_tool_settings(C); | ToolSettings *tool_settings = CTX_data_tool_settings(C); | ||||
| UnifiedPaintSettings *ups = &tool_settings->unified_paint_settings; | UnifiedPaintSettings *ups = &tool_settings->unified_paint_settings; | ||||
| Brush *brush = BKE_paint_brush(&sd->paint); | Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| ViewContext *vc = paint_stroke_view_context(op->customdata); | ViewContext *vc = paint_stroke_view_context(static_cast<PaintStroke *>(op->customdata)); | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| float mat[3][3]; | float mat[3][3]; | ||||
| float viewDir[3] = {0.0f, 0.0f, 1.0f}; | float viewDir[3] = {0.0f, 0.0f, 1.0f}; | ||||
| float max_scale; | float max_scale; | ||||
| int mode; | int mode; | ||||
| ss->cache = cache; | ss->cache = cache; | ||||
| ▲ Show 20 Lines • Show All 537 Lines • ▼ Show 20 Lines | void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) | static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) | ||||
| { | { | ||||
| if (BKE_pbvh_node_get_tmin(node) >= *tmin) { | if (BKE_pbvh_node_get_tmin(node) >= *tmin) { | ||||
| return; | return; | ||||
| } | } | ||||
| SculptRaycastData *srd = data_v; | SculptRaycastData *srd = static_cast<SculptRaycastData *>(data_v); | ||||
| float(*origco)[3] = NULL; | float(*origco)[3] = nullptr; | ||||
| bool use_origco = false; | bool use_origco = false; | ||||
| if (srd->original && srd->ss->cache) { | if (srd->original && srd->ss->cache) { | ||||
| if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { | if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { | ||||
| use_origco = true; | use_origco = true; | ||||
| } | } | ||||
| else { | else { | ||||
| /* Intersect with coordinates from before we started stroke. */ | /* Intersect with coordinates from before we started stroke. */ | ||||
| SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS); | SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS); | ||||
| origco = (unode) ? unode->co : NULL; | origco = (unode) ? unode->co : nullptr; | ||||
| use_origco = origco ? true : false; | use_origco = origco ? true : false; | ||||
| } | } | ||||
| } | } | ||||
| if (BKE_pbvh_node_raycast(srd->ss->pbvh, | if (BKE_pbvh_node_raycast(srd->ss->pbvh, | ||||
| node, | node, | ||||
| origco, | origco, | ||||
| use_origco, | use_origco, | ||||
| Show All 9 Lines | static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *tmin) | static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *tmin) | ||||
| { | { | ||||
| if (BKE_pbvh_node_get_tmin(node) >= *tmin) { | if (BKE_pbvh_node_get_tmin(node) >= *tmin) { | ||||
| return; | return; | ||||
| } | } | ||||
| SculptFindNearestToRayData *srd = data_v; | SculptFindNearestToRayData *srd = static_cast<SculptFindNearestToRayData *>(data_v); | ||||
| float(*origco)[3] = NULL; | float(*origco)[3] = nullptr; | ||||
| bool use_origco = false; | bool use_origco = false; | ||||
| if (srd->original && srd->ss->cache) { | if (srd->original && srd->ss->cache) { | ||||
| if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { | if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { | ||||
| use_origco = true; | use_origco = true; | ||||
| } | } | ||||
| else { | else { | ||||
| /* Intersect with coordinates from before we started stroke. */ | /* Intersect with coordinates from before we started stroke. */ | ||||
| SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS); | SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS); | ||||
| origco = (unode) ? unode->co : NULL; | origco = (unode) ? unode->co : nullptr; | ||||
| use_origco = origco ? true : false; | use_origco = origco ? true : false; | ||||
| } | } | ||||
| } | } | ||||
| if (BKE_pbvh_node_find_nearest_to_ray(srd->ss->pbvh, | if (BKE_pbvh_node_find_nearest_to_ray(srd->ss->pbvh, | ||||
| node, | node, | ||||
| origco, | origco, | ||||
| use_origco, | use_origco, | ||||
| Show All 11 Lines | float SCULPT_raycast_init(ViewContext *vc, | ||||
| float ray_start[3], | float ray_start[3], | ||||
| float ray_end[3], | float ray_end[3], | ||||
| float ray_normal[3], | float ray_normal[3], | ||||
| bool original) | bool original) | ||||
| { | { | ||||
| float obimat[4][4]; | float obimat[4][4]; | ||||
| float dist; | float dist; | ||||
| Object *ob = vc->obact; | Object *ob = vc->obact; | ||||
| RegionView3D *rv3d = vc->region->regiondata; | RegionView3D *rv3d = static_cast<RegionView3D *>(vc->region->regiondata); | ||||
| View3D *v3d = vc->v3d; | View3D *v3d = vc->v3d; | ||||
| /* TODO: what if the segment is totally clipped? (return == 0). */ | /* TODO: what if the segment is totally clipped? (return == 0). */ | ||||
| ED_view3d_win_to_segment_clipped( | ED_view3d_win_to_segment_clipped( | ||||
| vc->depsgraph, vc->region, vc->v3d, mval, ray_start, ray_end, true); | vc->depsgraph, vc->region, vc->v3d, mval, ray_start, ray_end, true); | ||||
| invert_m4_m4(obimat, ob->object_to_world); | invert_m4_m4(obimat, ob->object_to_world); | ||||
| mul_m4_v3(obimat, ray_start); | mul_m4_v3(obimat, ray_start); | ||||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | if (!ss->pbvh) { | ||||
| zero_v3(out->active_vertex_co); | zero_v3(out->active_vertex_co); | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* PBVH raycast to get active vertex and face normal. */ | /* PBVH raycast to get active vertex and face normal. */ | ||||
| depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original); | depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original); | ||||
| SCULPT_stroke_modifiers_check(C, ob, brush); | SCULPT_stroke_modifiers_check(C, ob, brush); | ||||
| SculptRaycastData srd = { | SculptRaycastData srd{}; | ||||
| .original = original, | srd.original = original; | ||||
| .ss = ob->sculpt, | srd.ss = ob->sculpt; | ||||
| .hit = false, | srd.hit = false; | ||||
| .ray_start = ray_start, | srd.ray_start = ray_start; | ||||
| .ray_normal = ray_normal, | srd.ray_normal = ray_normal; | ||||
| .depth = depth, | srd.depth = depth; | ||||
| .face_normal = face_normal, | srd.face_normal = face_normal; | ||||
| }; | |||||
| isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); | isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); | ||||
| BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); | BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); | ||||
| /* Cursor is not over the mesh, return default values. */ | /* Cursor is not over the mesh, return default values. */ | ||||
| if (!srd.hit) { | if (!srd.hit) { | ||||
| zero_v3(out->location); | zero_v3(out->location); | ||||
| zero_v3(out->normal); | zero_v3(out->normal); | ||||
| zero_v3(out->active_vertex_co); | zero_v3(out->active_vertex_co); | ||||
| ▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | bool SCULPT_stroke_get_location(bContext *C, | ||||
| if (hit) { | if (hit) { | ||||
| return hit; | return hit; | ||||
| } | } | ||||
| if (!ELEM(brush->falloff_shape, PAINT_FALLOFF_SHAPE_TUBE)) { | if (!ELEM(brush->falloff_shape, PAINT_FALLOFF_SHAPE_TUBE)) { | ||||
| return hit; | return hit; | ||||
| } | } | ||||
| SculptFindNearestToRayData srd = { | SculptFindNearestToRayData srd{}; | ||||
| .original = original, | srd.original = original; | ||||
| .ss = ob->sculpt, | srd.ss = ob->sculpt; | ||||
| .hit = false, | srd.hit = false; | ||||
| .ray_start = ray_start, | srd.ray_start = ray_start; | ||||
| .ray_normal = ray_normal, | srd.ray_normal = ray_normal; | ||||
| .depth = FLT_MAX, | srd.depth = FLT_MAX; | ||||
| .dist_sq_to_ray = FLT_MAX, | srd.dist_sq_to_ray = FLT_MAX; | ||||
| }; | |||||
| BKE_pbvh_find_nearest_to_ray( | BKE_pbvh_find_nearest_to_ray( | ||||
| ss->pbvh, sculpt_find_nearest_to_ray_cb, &srd, ray_start, ray_normal, srd.original); | ss->pbvh, sculpt_find_nearest_to_ray_cb, &srd, ray_start, ray_normal, srd.original); | ||||
| if (srd.hit) { | if (srd.hit) { | ||||
| hit = true; | hit = true; | ||||
| copy_v3_v3(out, ray_normal); | copy_v3_v3(out, ray_normal); | ||||
| mul_v3_fl(out, srd.depth); | mul_v3_fl(out, srd.depth); | ||||
| add_v3_v3(out, ray_start); | add_v3_v3(out, ray_start); | ||||
| } | } | ||||
| return hit; | return hit; | ||||
| } | } | ||||
| static void sculpt_brush_init_tex(Sculpt *sd, SculptSession *ss) | static void sculpt_brush_init_tex(Sculpt *sd, SculptSession *ss) | ||||
| { | { | ||||
| Brush *brush = BKE_paint_brush(&sd->paint); | Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| MTex *mtex = &brush->mtex; | MTex *mtex = &brush->mtex; | ||||
| /* Init mtex nodes. */ | /* Init mtex nodes. */ | ||||
| if (mtex->tex && mtex->tex->nodetree) { | if (mtex->tex && mtex->tex->nodetree) { | ||||
| /* Has internal flag to detect it only does it once. */ | /* Has internal flag to detect it only does it once. */ | ||||
| ntreeTexBeginExecTree(mtex->tex->nodetree); | ntreeTexBeginExecTree(mtex->tex->nodetree); | ||||
| } | } | ||||
| if (ss->tex_pool == NULL) { | if (ss->tex_pool == nullptr) { | ||||
| ss->tex_pool = BKE_image_pool_new(); | ss->tex_pool = BKE_image_pool_new(); | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) | static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| ToolSettings *tool_settings = CTX_data_tool_settings(C); | ToolSettings *tool_settings = CTX_data_tool_settings(C); | ||||
| ▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) | ||||
| MultiresModifierData *mmd = ss->multires.modifier; | MultiresModifierData *mmd = ss->multires.modifier; | ||||
| RegionView3D *rv3d = CTX_wm_region_view3d(C); | RegionView3D *rv3d = CTX_wm_region_view3d(C); | ||||
| if (rv3d) { | if (rv3d) { | ||||
| /* Mark for faster 3D viewport redraws. */ | /* Mark for faster 3D viewport redraws. */ | ||||
| rv3d->rflag |= RV3D_PAINTING; | rv3d->rflag |= RV3D_PAINTING; | ||||
| } | } | ||||
| if (mmd != NULL) { | if (mmd != nullptr) { | ||||
| multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); | multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); | ||||
| } | } | ||||
| if ((update_flags & SCULPT_UPDATE_IMAGE) != 0) { | if ((update_flags & SCULPT_UPDATE_IMAGE) != 0) { | ||||
| ED_region_tag_redraw(region); | ED_region_tag_redraw(region); | ||||
| if (update_flags == SCULPT_UPDATE_IMAGE) { | if (update_flags == SCULPT_UPDATE_IMAGE) { | ||||
| /* Early exit when only need to update the images. We don't want to tag any geometry updates | /* Early exit when only need to update the images. We don't want to tag any geometry updates | ||||
| * that would rebuilt the PBVH. */ | * that would rebuilt the PBVH. */ | ||||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
| void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags) | void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags) | ||||
| { | { | ||||
| /* After we are done drawing the stroke, check if we need to do a more | /* After we are done drawing the stroke, check if we need to do a more | ||||
| * expensive depsgraph tag to update geometry. */ | * expensive depsgraph tag to update geometry. */ | ||||
| wmWindowManager *wm = CTX_wm_manager(C); | wmWindowManager *wm = CTX_wm_manager(C); | ||||
| RegionView3D *current_rv3d = CTX_wm_region_view3d(C); | RegionView3D *current_rv3d = CTX_wm_region_view3d(C); | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| Mesh *mesh = ob->data; | Mesh *mesh = static_cast<Mesh *>(ob->data); | ||||
| /* Always needed for linked duplicates. */ | /* Always needed for linked duplicates. */ | ||||
| bool need_tag = (ID_REAL_USERS(&mesh->id) > 1); | bool need_tag = (ID_REAL_USERS(&mesh->id) > 1); | ||||
| if (current_rv3d) { | if (current_rv3d) { | ||||
| current_rv3d->rflag &= ~RV3D_PAINTING; | current_rv3d->rflag &= ~RV3D_PAINTING; | ||||
| } | } | ||||
| LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { | LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { | ||||
| bScreen *screen = WM_window_get_active_screen(win); | bScreen *screen = WM_window_get_active_screen(win); | ||||
| LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { | LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { | ||||
| SpaceLink *sl = area->spacedata.first; | SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first); | ||||
| if (sl->spacetype != SPACE_VIEW3D) { | if (sl->spacetype != SPACE_VIEW3D) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Tag all 3D viewports for redraw now that we are done. Others | /* Tag all 3D viewports for redraw now that we are done. Others | ||||
| * viewports did not get a full redraw, and anti-aliasing for the | * viewports did not get a full redraw, and anti-aliasing for the | ||||
| * current viewport was deactivated. */ | * current viewport was deactivated. */ | ||||
| LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { | LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { | ||||
| if (region->regiontype == RGN_TYPE_WINDOW) { | if (region->regiontype == RGN_TYPE_WINDOW) { | ||||
| RegionView3D *rv3d = region->regiondata; | RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); | ||||
| if (rv3d != current_rv3d) { | if (rv3d != current_rv3d) { | ||||
| need_tag |= !BKE_sculptsession_use_pbvh_draw(ob, rv3d); | need_tag |= !BKE_sculptsession_use_pbvh_draw(ob, rv3d); | ||||
| } | } | ||||
| ED_region_tag_redraw(region); | ED_region_tag_redraw(region); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (update_flags & SCULPT_UPDATE_IMAGE) { | if (update_flags & SCULPT_UPDATE_IMAGE) { | ||||
| LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { | LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { | ||||
| SpaceLink *sl = area->spacedata.first; | SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first); | ||||
| if (sl->spacetype != SPACE_IMAGE) { | if (sl->spacetype != SPACE_IMAGE) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); | ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Show All 29 Lines | void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags) | ||||
| if (need_tag) { | if (need_tag) { | ||||
| DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); | DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); | ||||
| } | } | ||||
| } | } | ||||
| /* Returns whether the mouse/stylus is over the mesh (1) | /* Returns whether the mouse/stylus is over the mesh (1) | ||||
| * or over the background (0). */ | * or over the background (0). */ | ||||
| static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2]) | static bool over_mesh(bContext *C, wmOperator * /*op*/, const float mval[2]) | ||||
| { | { | ||||
| float co_dummy[3]; | float co_dummy[3]; | ||||
| return SCULPT_stroke_get_location(C, co_dummy, mval, false); | return SCULPT_stroke_get_location(C, co_dummy, mval, false); | ||||
| } | } | ||||
| static void sculpt_stroke_undo_begin(const bContext *C, wmOperator *op) | static void sculpt_stroke_undo_begin(const bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| Show All 39 Lines | case PBVH_GRIDS: | ||||
| return false; | return false; | ||||
| } | } | ||||
| BLI_assert_msg(0, "PBVH corruption, type was invalid."); | BLI_assert_msg(0, "PBVH corruption, type was invalid."); | ||||
| return false; | return false; | ||||
| } | } | ||||
| static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mval[2]) | static bool sculpt_stroke_test_start(bContext *C, wmOperator *op, const float mval[2]) | ||||
| { | { | ||||
| /* Don't start the stroke until `mval` goes over the mesh. | /* Don't start the stroke until `mval` goes over the mesh. | ||||
| * NOTE: `mval` will only be null when re-executing the saved stroke. | * NOTE: `mval` will only be null when re-executing the saved stroke. | ||||
| * We have exception for 'exec' strokes since they may not set `mval`, | * We have exception for 'exec' strokes since they may not set `mval`, | ||||
| * only 'location', see: T52195. */ | * only 'location', see: T52195. */ | ||||
| if (((op->flag & OP_IS_INVOKE) == 0) || (mval == NULL) || over_mesh(C, op, mval)) { | if (((op->flag & OP_IS_INVOKE) == 0) || (mval == nullptr) || over_mesh(C, op, mval)) { | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | ||||
| Brush *brush = BKE_paint_brush(&sd->paint); | Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| ToolSettings *tool_settings = CTX_data_tool_settings(C); | ToolSettings *tool_settings = CTX_data_tool_settings(C); | ||||
| /* NOTE: This should be removed when paint mode is available. Paint mode can force based on the | /* NOTE: This should be removed when paint mode is available. Paint mode can force based on the | ||||
| * canvas it is painting on. (ref. use_sculpt_texture_paint). */ | * canvas it is painting on. (ref. use_sculpt_texture_paint). */ | ||||
| Show All 18 Lines | if (((op->flag & OP_IS_INVOKE) == 0) || (mval == nullptr) || over_mesh(C, op, mval)) { | ||||
| ss->cache->stroke_id = ss->stroke_id; | ss->cache->stroke_id = ss->stroke_id; | ||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static void sculpt_stroke_update_step(bContext *C, | static void sculpt_stroke_update_step(bContext *C, | ||||
| wmOperator *UNUSED(op), | wmOperator * /*op*/, | ||||
| struct PaintStroke *stroke, | PaintStroke *stroke, | ||||
| PointerRNA *itemptr) | PointerRNA *itemptr) | ||||
| { | { | ||||
| UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; | UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; | ||||
| Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| const Brush *brush = BKE_paint_brush(&sd->paint); | const Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| ToolSettings *tool_settings = CTX_data_tool_settings(C); | ToolSettings *tool_settings = CTX_data_tool_settings(C); | ||||
| ▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | static void sculpt_brush_exit_tex(Sculpt *sd) | ||||
| Brush *brush = BKE_paint_brush(&sd->paint); | Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| MTex *mtex = &brush->mtex; | MTex *mtex = &brush->mtex; | ||||
| if (mtex->tex && mtex->tex->nodetree) { | if (mtex->tex && mtex->tex->nodetree) { | ||||
| ntreeTexEndExecTree(mtex->tex->nodetree->execdata); | ntreeTexEndExecTree(mtex->tex->nodetree->execdata); | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(stroke)) | static void sculpt_stroke_done(const bContext *C, PaintStroke * /*stroke*/) | ||||
| { | { | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | ||||
| ToolSettings *tool_settings = CTX_data_tool_settings(C); | ToolSettings *tool_settings = CTX_data_tool_settings(C); | ||||
| /* Finished. */ | /* Finished. */ | ||||
| if (!ss->cache) { | if (!ss->cache) { | ||||
| Show All 15 Lines | static void sculpt_stroke_done(const bContext *C, PaintStroke * /*stroke*/) | ||||
| } | } | ||||
| if (SCULPT_is_automasking_enabled(sd, ss, brush)) { | if (SCULPT_is_automasking_enabled(sd, ss, brush)) { | ||||
| SCULPT_automasking_cache_free(ss->cache->automasking); | SCULPT_automasking_cache_free(ss->cache->automasking); | ||||
| } | } | ||||
| BKE_pbvh_node_color_buffer_free(ss->pbvh); | BKE_pbvh_node_color_buffer_free(ss->pbvh); | ||||
| SCULPT_cache_free(ss->cache); | SCULPT_cache_free(ss->cache); | ||||
| ss->cache = NULL; | ss->cache = nullptr; | ||||
| sculpt_stroke_undo_end(C, brush); | sculpt_stroke_undo_end(C, brush); | ||||
| if (brush->sculpt_tool == SCULPT_TOOL_MASK) { | if (brush->sculpt_tool == SCULPT_TOOL_MASK) { | ||||
| SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); | SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); | ||||
| } | } | ||||
| else if (brush->sculpt_tool == SCULPT_TOOL_PAINT) { | else if (brush->sculpt_tool == SCULPT_TOOL_PAINT) { | ||||
| if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { | if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { | ||||
| Show All 9 Lines | static void sculpt_stroke_done(const bContext *C, PaintStroke * /*stroke*/) | ||||
| } | } | ||||
| WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); | WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); | ||||
| sculpt_brush_exit_tex(sd); | sculpt_brush_exit_tex(sd); | ||||
| } | } | ||||
| static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) | static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| { | { | ||||
| struct PaintStroke *stroke; | PaintStroke *stroke; | ||||
| int ignore_background_click; | int ignore_background_click; | ||||
| int retval; | int retval; | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| /* Test that ob is visible; otherwise we won't be able to get evaluated data | /* Test that ob is visible; otherwise we won't be able to get evaluated data | ||||
| * from the depsgraph. We do this here instead of SCULPT_mode_poll | * from the depsgraph. We do this here instead of SCULPT_mode_poll | ||||
| * to avoid falling through to the translate operator in the | * to avoid falling through to the translate operator in the | ||||
| * global view3d keymap. | * global view3d keymap. | ||||
| Show All 25 Lines | if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) { | ||||
| ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); | ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); | ||||
| } | } | ||||
| stroke = paint_stroke_new(C, | stroke = paint_stroke_new(C, | ||||
| op, | op, | ||||
| SCULPT_stroke_get_location, | SCULPT_stroke_get_location, | ||||
| sculpt_stroke_test_start, | sculpt_stroke_test_start, | ||||
| sculpt_stroke_update_step, | sculpt_stroke_update_step, | ||||
| NULL, | nullptr, | ||||
| sculpt_stroke_done, | sculpt_stroke_done, | ||||
| event->type); | event->type); | ||||
| op->customdata = stroke; | op->customdata = stroke; | ||||
| /* For tablet rotation. */ | /* For tablet rotation. */ | ||||
| ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); | ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); | ||||
| const float mval[2] = {float(event->mval[0]), float(event->mval[1])}; | |||||
| if (ignore_background_click && !over_mesh(C, op, (const float[2]){UNPACK2(event->mval)})) { | if (ignore_background_click && !over_mesh(C, op, mval)) { | ||||
| paint_stroke_free(C, op, op->customdata); | paint_stroke_free(C, op, static_cast<PaintStroke *>(op->customdata)); | ||||
| return OPERATOR_PASS_THROUGH; | return OPERATOR_PASS_THROUGH; | ||||
| } | } | ||||
| retval = op->type->modal(C, op, event); | retval = op->type->modal(C, op, event); | ||||
| if (ELEM(retval, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { | if (ELEM(retval, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { | ||||
| paint_stroke_free(C, op, op->customdata); | paint_stroke_free(C, op, static_cast<PaintStroke *>(op->customdata)); | ||||
| return retval; | return retval; | ||||
| } | } | ||||
| /* Add modal handler. */ | /* Add modal handler. */ | ||||
| WM_event_add_modal_handler(C, op); | WM_event_add_modal_handler(C, op); | ||||
| OPERATOR_RETVAL_CHECK(retval); | OPERATOR_RETVAL_CHECK(retval); | ||||
| BLI_assert(retval == OPERATOR_RUNNING_MODAL); | BLI_assert(retval == OPERATOR_RUNNING_MODAL); | ||||
| return OPERATOR_RUNNING_MODAL; | return OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) | static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| sculpt_brush_stroke_init(C, op); | sculpt_brush_stroke_init(C, op); | ||||
| op->customdata = paint_stroke_new(C, | op->customdata = paint_stroke_new(C, | ||||
| op, | op, | ||||
| SCULPT_stroke_get_location, | SCULPT_stroke_get_location, | ||||
| sculpt_stroke_test_start, | sculpt_stroke_test_start, | ||||
| sculpt_stroke_update_step, | sculpt_stroke_update_step, | ||||
| NULL, | nullptr, | ||||
| sculpt_stroke_done, | sculpt_stroke_done, | ||||
| 0); | 0); | ||||
| /* Frees op->customdata. */ | /* Frees op->customdata. */ | ||||
| paint_stroke_exec(C, op, op->customdata); | paint_stroke_exec(C, op, static_cast<PaintStroke *>(op->customdata)); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) | static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | ||||
| const Brush *brush = BKE_paint_brush(&sd->paint); | const Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| /* XXX Canceling strokes that way does not work with dynamic topology, | /* XXX Canceling strokes that way does not work with dynamic topology, | ||||
| * user will have to do real undo for now. See T46456. */ | * user will have to do real undo for now. See T46456. */ | ||||
| if (ss->cache && !SCULPT_stroke_is_dynamic_topology(ss, brush)) { | if (ss->cache && !SCULPT_stroke_is_dynamic_topology(ss, brush)) { | ||||
| paint_mesh_restore_co(sd, ob); | paint_mesh_restore_co(sd, ob); | ||||
| } | } | ||||
| paint_stroke_cancel(C, op, op->customdata); | paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata)); | ||||
| if (ss->cache) { | if (ss->cache) { | ||||
| SCULPT_cache_free(ss->cache); | SCULPT_cache_free(ss->cache); | ||||
| ss->cache = NULL; | ss->cache = nullptr; | ||||
| } | } | ||||
| sculpt_brush_exit_tex(sd); | sculpt_brush_exit_tex(sd); | ||||
| } | } | ||||
| static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) | static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| { | { | ||||
| bool started = op->customdata && paint_stroke_started((struct PaintStroke *)op->customdata); | bool started = op->customdata && paint_stroke_started((PaintStroke *)op->customdata); | ||||
| int retval = paint_stroke_modal(C, op, event, (struct PaintStroke **)&op->customdata); | int retval = paint_stroke_modal(C, op, event, (PaintStroke **)&op->customdata); | ||||
| if (!started && ELEM(retval, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { | if (!started && ELEM(retval, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { | ||||
| /* Did the stroke never start? If so push a blank sculpt undo | /* Did the stroke never start? If so push a blank sculpt undo | ||||
| * step to prevent a global undo step (which is triggered by the | * step to prevent a global undo step (which is triggered by the | ||||
| * #OPTYPE_UNDO flag in #SCULPT_OT_brush_stroke). | * #OPTYPE_UNDO flag in #SCULPT_OT_brush_stroke). | ||||
| * | * | ||||
| * Having blank global undo steps interleaved with sculpt steps | * Having blank global undo steps interleaved with sculpt steps | ||||
| * corrupts the DynTopo undo stack. | * corrupts the DynTopo undo stack. | ||||
| * See T101430. | * See T101430. | ||||
| * | * | ||||
| * NOTE: simply returning #OPERATOR_CANCELLED was not | * NOTE: simply returning #OPERATOR_CANCELLED was not | ||||
| * sufficient to prevent this. */ | * sufficient to prevent this. */ | ||||
| Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | Sculpt *sd = CTX_data_tool_settings(C)->sculpt; | ||||
| Brush *brush = BKE_paint_brush(&sd->paint); | Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| sculpt_stroke_undo_begin(C, op); | sculpt_stroke_undo_begin(C, op); | ||||
| sculpt_stroke_undo_end(C, brush); | sculpt_stroke_undo_end(C, brush); | ||||
| } | } | ||||
| return retval; | return retval; | ||||
| } | } | ||||
| static void sculpt_redo_empty_ui(bContext *UNUSED(C), wmOperator *UNUSED(op)) | static void sculpt_redo_empty_ui(bContext * /*C*/, wmOperator * /*op*/) | ||||
| { | { | ||||
| } | } | ||||
| void SCULPT_OT_brush_stroke(wmOperatorType *ot) | void SCULPT_OT_brush_stroke(wmOperatorType *ot) | ||||
| { | { | ||||
| /* Identifiers. */ | /* Identifiers. */ | ||||
| ot->name = "Sculpt"; | ot->name = "Sculpt"; | ||||
| ot->idname = "SCULPT_OT_brush_stroke"; | ot->idname = "SCULPT_OT_brush_stroke"; | ||||
| ▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | if (ss->vertex_info.connected_component) { | ||||
| return ss->vertex_info.connected_component[vertex.i]; | return ss->vertex_info.connected_component[vertex.i]; | ||||
| } | } | ||||
| return SCULPT_TOPOLOGY_ID_DEFAULT; | return SCULPT_TOPOLOGY_ID_DEFAULT; | ||||
| } | } | ||||
| static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist) | static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist) | ||||
| { | { | ||||
| const int totvert = SCULPT_vertex_count_get(ss); | const int totvert = SCULPT_vertex_count_get(ss); | ||||
| ss->fake_neighbors.fake_neighbor_index = MEM_malloc_arrayN( | ss->fake_neighbors.fake_neighbor_index = static_cast<int *>( | ||||
| totvert, sizeof(int), "fake neighbor"); | MEM_malloc_arrayN(totvert, sizeof(int), "fake neighbor")); | ||||
| for (int i = 0; i < totvert; i++) { | for (int i = 0; i < totvert; i++) { | ||||
| ss->fake_neighbors.fake_neighbor_index[i] = FAKE_NEIGHBOR_NONE; | ss->fake_neighbors.fake_neighbor_index[i] = FAKE_NEIGHBOR_NONE; | ||||
| } | } | ||||
| ss->fake_neighbors.current_max_distance = max_dist; | ss->fake_neighbors.current_max_distance = max_dist; | ||||
| } | } | ||||
| static void SCULPT_fake_neighbor_add(SculptSession *ss, PBVHVertRef v_a, PBVHVertRef v_b) | static void SCULPT_fake_neighbor_add(SculptSession *ss, PBVHVertRef v_a, PBVHVertRef v_b) | ||||
| { | { | ||||
| int v_index_a = BKE_pbvh_vertex_to_index(ss->pbvh, v_a); | int v_index_a = BKE_pbvh_vertex_to_index(ss->pbvh, v_a); | ||||
| int v_index_b = BKE_pbvh_vertex_to_index(ss->pbvh, v_b); | int v_index_b = BKE_pbvh_vertex_to_index(ss->pbvh, v_b); | ||||
| if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) { | if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) { | ||||
| ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b; | ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b; | ||||
| ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a; | ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a; | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_pose_fake_neighbors_free(SculptSession *ss) | static void sculpt_pose_fake_neighbors_free(SculptSession *ss) | ||||
| { | { | ||||
| MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index); | MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index); | ||||
| } | } | ||||
| typedef struct NearestVertexFakeNeighborTLSData { | struct NearestVertexFakeNeighborTLSData { | ||||
| PBVHVertRef nearest_vertex; | PBVHVertRef nearest_vertex; | ||||
| float nearest_vertex_distance_squared; | float nearest_vertex_distance_squared; | ||||
| int current_topology_id; | int current_topology_id; | ||||
| } NearestVertexFakeNeighborTLSData; | }; | ||||
| static void do_fake_neighbor_search_task_cb(void *__restrict userdata, | static void do_fake_neighbor_search_task_cb(void *__restrict userdata, | ||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict tls) | const TaskParallelTLS *__restrict tls) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| NearestVertexFakeNeighborTLSData *nvtd = tls->userdata_chunk; | NearestVertexFakeNeighborTLSData *nvtd = static_cast<NearestVertexFakeNeighborTLSData *>( | ||||
| tls->userdata_chunk); | |||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { | ||||
| int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.vertex); | int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.vertex); | ||||
| if (vd_topology_id != nvtd->current_topology_id && | if (vd_topology_id != nvtd->current_topology_id && | ||||
| ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) { | ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) { | ||||
| float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); | float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); | ||||
| if (distance_squared < nvtd->nearest_vertex_distance_squared && | if (distance_squared < nvtd->nearest_vertex_distance_squared && | ||||
| distance_squared < data->max_distance_squared) { | distance_squared < data->max_distance_squared) { | ||||
| nvtd->nearest_vertex = vd.vertex; | nvtd->nearest_vertex = vd.vertex; | ||||
| nvtd->nearest_vertex_distance_squared = distance_squared; | nvtd->nearest_vertex_distance_squared = distance_squared; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| BKE_pbvh_vertex_iter_end; | BKE_pbvh_vertex_iter_end; | ||||
| } | } | ||||
| static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata), | static void fake_neighbor_search_reduce(const void *__restrict /*userdata*/, | ||||
| void *__restrict chunk_join, | void *__restrict chunk_join, | ||||
| void *__restrict chunk) | void *__restrict chunk) | ||||
| { | { | ||||
| NearestVertexFakeNeighborTLSData *join = chunk_join; | NearestVertexFakeNeighborTLSData *join = static_cast<NearestVertexFakeNeighborTLSData *>( | ||||
| NearestVertexFakeNeighborTLSData *nvtd = chunk; | chunk_join); | ||||
| NearestVertexFakeNeighborTLSData *nvtd = static_cast<NearestVertexFakeNeighborTLSData *>(chunk); | |||||
| if (join->nearest_vertex.i == PBVH_REF_NONE) { | if (join->nearest_vertex.i == PBVH_REF_NONE) { | ||||
| join->nearest_vertex = nvtd->nearest_vertex; | join->nearest_vertex = nvtd->nearest_vertex; | ||||
| join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; | join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; | ||||
| } | } | ||||
| else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { | else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { | ||||
| join->nearest_vertex = nvtd->nearest_vertex; | join->nearest_vertex = nvtd->nearest_vertex; | ||||
| join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; | join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; | ||||
| } | } | ||||
| } | } | ||||
| static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd, | static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd, | ||||
| Object *ob, | Object *ob, | ||||
| const PBVHVertRef vertex, | const PBVHVertRef vertex, | ||||
| float max_distance) | float max_distance) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| PBVHNode **nodes = NULL; | PBVHNode **nodes = nullptr; | ||||
| int totnode; | int totnode; | ||||
| SculptSearchSphereData data = { | SculptSearchSphereData data{}; | ||||
| .ss = ss, | data.ss = ss; | ||||
| .sd = sd, | data.sd = sd; | ||||
| .radius_squared = max_distance * max_distance, | data.radius_squared = max_distance * max_distance; | ||||
| .original = false, | data.original = false; | ||||
| .center = SCULPT_vertex_co_get(ss, vertex), | data.center = SCULPT_vertex_co_get(ss, vertex); | ||||
| }; | |||||
| BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); | BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); | ||||
| if (totnode == 0) { | if (totnode == 0) { | ||||
| return BKE_pbvh_make_vref(PBVH_REF_NONE); | return BKE_pbvh_make_vref(PBVH_REF_NONE); | ||||
| } | } | ||||
| SculptThreadedTaskData task_data = { | SculptThreadedTaskData task_data{}; | ||||
| .sd = sd, | task_data.sd = sd; | ||||
| .ob = ob, | task_data.ob = ob; | ||||
| .nodes = nodes, | task_data.nodes = nodes; | ||||
| .max_distance_squared = max_distance * max_distance, | task_data.max_distance_squared = max_distance * max_distance; | ||||
| }; | |||||
| copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, vertex)); | copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, vertex)); | ||||
| NearestVertexFakeNeighborTLSData nvtd; | NearestVertexFakeNeighborTLSData nvtd; | ||||
| nvtd.nearest_vertex.i = -1; | nvtd.nearest_vertex.i = -1; | ||||
| nvtd.nearest_vertex_distance_squared = FLT_MAX; | nvtd.nearest_vertex_distance_squared = FLT_MAX; | ||||
| nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, vertex); | nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, vertex); | ||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| settings.func_reduce = fake_neighbor_search_reduce; | settings.func_reduce = fake_neighbor_search_reduce; | ||||
| settings.userdata_chunk = &nvtd; | settings.userdata_chunk = &nvtd; | ||||
| settings.userdata_chunk_size = sizeof(NearestVertexFakeNeighborTLSData); | settings.userdata_chunk_size = sizeof(NearestVertexFakeNeighborTLSData); | ||||
| BLI_task_parallel_range(0, totnode, &task_data, do_fake_neighbor_search_task_cb, &settings); | BLI_task_parallel_range(0, totnode, &task_data, do_fake_neighbor_search_task_cb, &settings); | ||||
| MEM_SAFE_FREE(nodes); | MEM_SAFE_FREE(nodes); | ||||
| return nvtd.nearest_vertex; | return nvtd.nearest_vertex; | ||||
| } | } | ||||
| typedef struct SculptTopologyIDFloodFillData { | struct SculptTopologyIDFloodFillData { | ||||
| int next_id; | int next_id; | ||||
| } SculptTopologyIDFloodFillData; | }; | ||||
| static bool SCULPT_connected_components_floodfill_cb(SculptSession *ss, | static bool SCULPT_connected_components_floodfill_cb( | ||||
| PBVHVertRef from_v, | SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool /*is_duplicate*/, void *userdata) | ||||
| PBVHVertRef to_v, | |||||
| bool UNUSED(is_duplicate), | |||||
| void *userdata) | |||||
| { | { | ||||
| SculptTopologyIDFloodFillData *data = userdata; | SculptTopologyIDFloodFillData *data = static_cast<SculptTopologyIDFloodFillData *>(userdata); | ||||
| int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); | int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); | ||||
| int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); | int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); | ||||
| ss->vertex_info.connected_component[from_v_i] = data->next_id; | ss->vertex_info.connected_component[from_v_i] = data->next_id; | ||||
| ss->vertex_info.connected_component[to_v_i] = data->next_id; | ss->vertex_info.connected_component[to_v_i] = data->next_id; | ||||
| return true; | return true; | ||||
| } | } | ||||
| void SCULPT_connected_components_ensure(Object *ob) | void SCULPT_connected_components_ensure(Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| /* Topology IDs already initialized. They only need to be recalculated when the PBVH is | /* Topology IDs already initialized. They only need to be recalculated when the PBVH is | ||||
| * rebuild. | * rebuild. | ||||
| */ | */ | ||||
| if (ss->vertex_info.connected_component) { | if (ss->vertex_info.connected_component) { | ||||
| return; | return; | ||||
| } | } | ||||
| const int totvert = SCULPT_vertex_count_get(ss); | const int totvert = SCULPT_vertex_count_get(ss); | ||||
| ss->vertex_info.connected_component = MEM_malloc_arrayN(totvert, sizeof(int), "topology ID"); | ss->vertex_info.connected_component = static_cast<int *>( | ||||
| MEM_malloc_arrayN(totvert, sizeof(int), "topology ID")); | |||||
| for (int i = 0; i < totvert; i++) { | for (int i = 0; i < totvert; i++) { | ||||
| ss->vertex_info.connected_component[i] = SCULPT_TOPOLOGY_ID_NONE; | ss->vertex_info.connected_component[i] = SCULPT_TOPOLOGY_ID_NONE; | ||||
| } | } | ||||
| int next_id = 0; | int next_id = 0; | ||||
| for (int i = 0; i < totvert; i++) { | for (int i = 0; i < totvert; i++) { | ||||
| PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); | PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); | ||||
| Show All 19 Lines | void SCULPT_boundary_info_ensure(Object *object) | ||||
| } | } | ||||
| Mesh *base_mesh = BKE_mesh_from_object(object); | Mesh *base_mesh = BKE_mesh_from_object(object); | ||||
| const MEdge *edges = BKE_mesh_edges(base_mesh); | const MEdge *edges = BKE_mesh_edges(base_mesh); | ||||
| const MPoly *polys = BKE_mesh_polys(base_mesh); | const MPoly *polys = BKE_mesh_polys(base_mesh); | ||||
| const MLoop *loops = BKE_mesh_loops(base_mesh); | const MLoop *loops = BKE_mesh_loops(base_mesh); | ||||
| ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); | ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); | ||||
| int *adjacent_faces_edge_count = MEM_calloc_arrayN( | int *adjacent_faces_edge_count = static_cast<int *>( | ||||
| base_mesh->totedge, sizeof(int), "Adjacent face edge count"); | MEM_calloc_arrayN(base_mesh->totedge, sizeof(int), "Adjacent face edge count")); | ||||
| for (int p = 0; p < base_mesh->totpoly; p++) { | for (int p = 0; p < base_mesh->totpoly; p++) { | ||||
| const MPoly *poly = &polys[p]; | const MPoly *poly = &polys[p]; | ||||
| for (int l = 0; l < poly->totloop; l++) { | for (int l = 0; l < poly->totloop; l++) { | ||||
| const MLoop *loop = &loops[l + poly->loopstart]; | const MLoop *loop = &loops[l + poly->loopstart]; | ||||
| adjacent_faces_edge_count[loop->e]++; | adjacent_faces_edge_count[loop->e]++; | ||||
| } | } | ||||
| } | } | ||||
| Show All 37 Lines | if (ss->fake_neighbors.fake_neighbor_index[i] == FAKE_NEIGHBOR_NONE) { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void SCULPT_fake_neighbors_enable(Object *ob) | void SCULPT_fake_neighbors_enable(Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); | BLI_assert(ss->fake_neighbors.fake_neighbor_index != nullptr); | ||||
| ss->fake_neighbors.use_fake_neighbors = true; | ss->fake_neighbors.use_fake_neighbors = true; | ||||
| } | } | ||||
| void SCULPT_fake_neighbors_disable(Object *ob) | void SCULPT_fake_neighbors_disable(Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); | BLI_assert(ss->fake_neighbors.fake_neighbor_index != nullptr); | ||||
| ss->fake_neighbors.use_fake_neighbors = false; | ss->fake_neighbors.use_fake_neighbors = false; | ||||
| } | } | ||||
| void SCULPT_fake_neighbors_free(Object *ob) | void SCULPT_fake_neighbors_free(Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| sculpt_pose_fake_neighbors_free(ss); | sculpt_pose_fake_neighbors_free(ss); | ||||
| } | } | ||||
| void SCULPT_automasking_node_begin(Object *ob, | void SCULPT_automasking_node_begin(Object *ob, | ||||
| const SculptSession *UNUSED(ss), | const SculptSession * /*ss*/, | ||||
| AutomaskingCache *automasking, | AutomaskingCache *automasking, | ||||
| AutomaskingNodeData *automask_data, | AutomaskingNodeData *automask_data, | ||||
| PBVHNode *node) | PBVHNode *node) | ||||
| { | { | ||||
| if (!automasking) { | if (!automasking) { | ||||
| memset(automask_data, 0, sizeof(*automask_data)); | memset(automask_data, 0, sizeof(*automask_data)); | ||||
| return; | return; | ||||
| } | } | ||||
| automask_data->node = node; | automask_data->node = node; | ||||
| automask_data->have_orig_data = automasking->settings.flags & | automask_data->have_orig_data = automasking->settings.flags & | ||||
| (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL); | (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL); | ||||
| if (automask_data->have_orig_data) { | if (automask_data->have_orig_data) { | ||||
| SCULPT_orig_vert_data_init(&automask_data->orig_data, ob, node, SCULPT_UNDO_COORDS); | SCULPT_orig_vert_data_init(&automask_data->orig_data, ob, node, SCULPT_UNDO_COORDS); | ||||
| } | } | ||||
| else { | else { | ||||
| memset(&automask_data->orig_data, 0, sizeof(automask_data->orig_data)); | memset(&automask_data->orig_data, 0, sizeof(automask_data->orig_data)); | ||||
| } | } | ||||
| } | } | ||||
| void SCULPT_automasking_node_update(SculptSession *UNUSED(ss), | void SCULPT_automasking_node_update(SculptSession * /*ss*/, | ||||
| AutomaskingNodeData *automask_data, | AutomaskingNodeData *automask_data, | ||||
| PBVHVertexIter *vd) | PBVHVertexIter *vd) | ||||
| { | { | ||||
| if (automask_data->have_orig_data) { | if (automask_data->have_orig_data) { | ||||
| SCULPT_orig_vert_data_update(&automask_data->orig_data, vd); | SCULPT_orig_vert_data_update(&automask_data->orig_data, vd); | ||||
| } | } | ||||
| } | } | ||||
| Show All 29 Lines | bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool original) | ||||
| return srd.hit; | return srd.hit; | ||||
| } | } | ||||
| void SCULPT_stroke_id_next(Object *ob) | void SCULPT_stroke_id_next(Object *ob) | ||||
| { | { | ||||
| /* Manually wrap in int32 space to avoid tripping up undefined behavior | /* Manually wrap in int32 space to avoid tripping up undefined behavior | ||||
| * sanitizers. | * sanitizers. | ||||
| */ | */ | ||||
| ob->sculpt->stroke_id = (uchar)(((int)ob->sculpt->stroke_id + 1) & 255); | ob->sculpt->stroke_id = uchar((int(ob->sculpt->stroke_id) + 1) & 255); | ||||
| } | } | ||||
| void SCULPT_stroke_id_ensure(Object *ob) | void SCULPT_stroke_id_ensure(Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| if (!ss->attrs.automasking_stroke_id) { | if (!ss->attrs.automasking_stroke_id) { | ||||
| SculptAttributeParams params = {0}; | SculptAttributeParams params = {0}; | ||||
| Show All 10 Lines | |||||