Sculpt: Remove duplicate BVH tree positions storage
When there is a deform modifier or shape keys, currently the evaluated position array is copied to a duplicate array on the sculpt PBVH tree. Historically most code has used this array directly, so the code has been a bit confusing, but conceptually this is just supposed to be the evaluated positions. Instead of editing the BVH tree position array while sculpting, we can edit the arrays on the evaluated mesh directly-- or the original mesh when there are no deform modifiers. Removing this array reduces memory usage and plays better with implicit sharing since we don't need to unshare the attribute just to store a mutable copy. The remaining non-ideal part is sculpt mode's use of a specialized crazy-space function `BKE_crazyspace_build_sculpt` for building the evaluated position array rather than using the general system for retrieving deformed mesh data, `BKE_object_get_mesh_deform_eval`. This should be replaced at some point. This change makes clear a few simplifications for sculpt normals recomputation, because we can store the normals for all cases as a shared cache, unifying the code paths for original and deformed normals. Part of #118145. Pull Request: https://projects.blender.org/blender/blender/pulls/126382
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_ordered_edge.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_shared_cache.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
#include "DNA_brush_enums.h"
|
||||
@@ -470,6 +471,13 @@ struct SculptSession : blender::NonCopyable, blender::NonMovable {
|
||||
/* Crazy-space deformation matrices. */
|
||||
blender::Array<blender::float3x3, 0> deform_imats;
|
||||
|
||||
/**
|
||||
* Normals corresponding to the #deform_cos evaluated/deform positions. Stored as a #SharedCache
|
||||
* for consistency with mesh caches in #MeshRuntime::vert_normals_cache.
|
||||
*/
|
||||
blender::SharedCache<blender::Vector<blender::float3>> vert_normals_deform;
|
||||
blender::SharedCache<blender::Vector<blender::float3>> face_normals_deform;
|
||||
|
||||
/* Pool for texture evaluations. */
|
||||
ImagePool *tex_pool = nullptr;
|
||||
|
||||
|
||||
@@ -183,23 +183,9 @@ class Tree {
|
||||
/* Memory backing for Node.prim_indices. */
|
||||
Array<int> prim_indices_;
|
||||
|
||||
/** Local array used when not sculpting base mesh positions directly. */
|
||||
Array<float3> vert_positions_deformed_;
|
||||
/** Local array used when not sculpting base mesh positions directly. */
|
||||
Array<float3> vert_normals_deformed_;
|
||||
/** Local array used when not sculpting base mesh positions directly. */
|
||||
Array<float3> face_normals_deformed_;
|
||||
|
||||
MutableSpan<float3> vert_positions_;
|
||||
Span<float3> vert_normals_;
|
||||
Span<float3> face_normals_;
|
||||
|
||||
/* Grid Data */
|
||||
SubdivCCG *subdiv_ccg_ = nullptr;
|
||||
|
||||
/* flag are verts/faces deformed */
|
||||
bool deformed_ = false;
|
||||
|
||||
float planes_[6][4];
|
||||
int num_planes_;
|
||||
|
||||
@@ -261,7 +247,6 @@ namespace blender::bke::pbvh {
|
||||
* Do a full rebuild with on Mesh data structure.
|
||||
*/
|
||||
std::unique_ptr<Tree> build_mesh(Mesh *mesh);
|
||||
void update_mesh_pointers(Tree &pbvh, Mesh *mesh);
|
||||
/**
|
||||
* Do a full rebuild with on Grids data structure.
|
||||
*/
|
||||
@@ -515,7 +500,6 @@ void BKE_pbvh_subdiv_cgg_set(blender::bke::pbvh::Tree &pbvh, SubdivCCG *subdiv_c
|
||||
|
||||
void BKE_pbvh_vert_coords_apply(blender::bke::pbvh::Tree &pbvh,
|
||||
blender::Span<blender::float3> vert_positions);
|
||||
bool BKE_pbvh_is_deformed(const blender::bke::pbvh::Tree &pbvh);
|
||||
|
||||
void BKE_pbvh_node_get_bm_orco_data(blender::bke::pbvh::Node *node,
|
||||
int (**r_orco_tris)[3],
|
||||
@@ -530,7 +514,7 @@ namespace blender::bke::pbvh {
|
||||
* topology-changing operations. If there are no deform modifiers, this returns the original mesh's
|
||||
* vertex positions.
|
||||
*/
|
||||
Span<float3> vert_positions_eval(const Depsgraph &depsgraph, const Object &object);
|
||||
Span<float3> vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig);
|
||||
Span<float3> vert_positions_eval_from_eval(const Object &object_eval);
|
||||
|
||||
/**
|
||||
@@ -539,13 +523,13 @@ Span<float3> vert_positions_eval_from_eval(const Object &object_eval);
|
||||
* they are used for drawing and we don't run a full dependency graph update whenever they are
|
||||
* changed.
|
||||
*/
|
||||
MutableSpan<float3> vert_positions_eval_for_write(const Depsgraph &depsgraph, Object &object);
|
||||
MutableSpan<float3> vert_positions_eval_for_write(const Depsgraph &depsgraph, Object &object_orig);
|
||||
|
||||
/**
|
||||
* Return the vertex normals corresponding the the positions from #vert_positions_eval. This may be
|
||||
* a reference to the normals cache on the original mesh.
|
||||
*/
|
||||
Span<float3> vert_normals_eval(const Depsgraph &depsgraph, const Object &object);
|
||||
Span<float3> vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig);
|
||||
Span<float3> vert_normals_eval_from_eval(const Object &object_eval);
|
||||
|
||||
} // namespace blender::bke::pbvh
|
||||
|
||||
@@ -2052,16 +2052,12 @@ static void sculpt_update_object(Depsgraph *depsgraph,
|
||||
|
||||
/* if pbvh is deformed, key block is already applied to it */
|
||||
if (ss.shapekey_active) {
|
||||
bool pbvh_deformed = BKE_pbvh_is_deformed(*ss.pbvh);
|
||||
if (!pbvh_deformed || ss.deform_cos.is_empty()) {
|
||||
if (ss.deform_cos.is_empty()) {
|
||||
const Span key_data(static_cast<const float3 *>(ss.shapekey_active->data),
|
||||
mesh_orig->verts_num);
|
||||
|
||||
if (key_data.data() != nullptr) {
|
||||
if (!pbvh_deformed) {
|
||||
/* apply shape keys coordinates to pbvh::Tree */
|
||||
BKE_pbvh_vert_coords_apply(*ss.pbvh, key_data);
|
||||
}
|
||||
BKE_pbvh_vert_coords_apply(*ss.pbvh, key_data);
|
||||
if (ss.deform_cos.is_empty()) {
|
||||
ss.deform_cos = key_data;
|
||||
}
|
||||
@@ -2390,18 +2386,6 @@ blender::bke::pbvh::Tree *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Ob
|
||||
}
|
||||
|
||||
if (ob->sculpt->pbvh) {
|
||||
/* NOTE: It is possible that pointers to grids or other geometry data changed. Need to update
|
||||
* those pointers. */
|
||||
const pbvh::Type pbvh_type = ob->sculpt->pbvh->type();
|
||||
switch (pbvh_type) {
|
||||
case pbvh::Type::Mesh:
|
||||
pbvh::update_mesh_pointers(*ob->sculpt->pbvh, BKE_object_get_original_mesh(ob));
|
||||
break;
|
||||
case pbvh::Type::Grids:
|
||||
case pbvh::Type::BMesh:
|
||||
break;
|
||||
}
|
||||
|
||||
return ob->sculpt->pbvh.get();
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "BKE_ccg.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_mesh_mapping.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_pbvh_api.hh"
|
||||
#include "BKE_subdiv_ccg.hh"
|
||||
@@ -341,23 +342,9 @@ static void build_nodes_recursive_mesh(const Span<int> corner_verts,
|
||||
nodes);
|
||||
}
|
||||
|
||||
void update_mesh_pointers(Tree &pbvh, Mesh *mesh)
|
||||
{
|
||||
BLI_assert(pbvh.type() == Type::Mesh);
|
||||
if (!pbvh.deformed_) {
|
||||
/* Deformed data not matching the original mesh are owned directly by the
|
||||
* Tree, and are set separately by #BKE_pbvh_vert_coords_apply. */
|
||||
pbvh.vert_positions_ = mesh->vert_positions_for_write();
|
||||
pbvh.vert_normals_ = mesh->vert_normals();
|
||||
pbvh.face_normals_ = mesh->face_normals();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Tree> build_mesh(Mesh *mesh)
|
||||
{
|
||||
std::unique_ptr<Tree> pbvh = std::make_unique<Tree>(Type::Mesh);
|
||||
update_mesh_pointers(*pbvh, mesh);
|
||||
|
||||
MutableSpan<float3> vert_positions = mesh->vert_positions_for_write();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
const Span<int3> corner_tris = mesh->corner_tris();
|
||||
@@ -846,6 +833,79 @@ static bool update_search(Node *node, const int flag)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logic used to test whether to use the evaluated mesh for positions.
|
||||
* \todo A deeper test of equality of topology array pointers would be better. This is kept for now
|
||||
* to avoid changing logic during a refactor.
|
||||
*/
|
||||
static bool mesh_topology_count_matches(const Mesh &a, const Mesh &b)
|
||||
{
|
||||
return a.faces_num == b.faces_num && a.corners_num == b.corners_num &&
|
||||
a.verts_num == b.verts_num;
|
||||
}
|
||||
|
||||
static const SharedCache<Vector<float3>> &vert_normals_cache_eval(const Object &object_orig,
|
||||
const Object &object_eval)
|
||||
{
|
||||
const SculptSession &ss = *object_orig.sculpt;
|
||||
const Mesh &mesh_orig = *static_cast<const Mesh *>(object_orig.data);
|
||||
BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh);
|
||||
if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
|
||||
if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) {
|
||||
if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) {
|
||||
return mesh_eval->runtime->vert_normals_cache;
|
||||
}
|
||||
}
|
||||
if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) {
|
||||
return mesh_eval->runtime->vert_normals_cache;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ss.deform_cos.is_empty()) {
|
||||
BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num);
|
||||
return ss.vert_normals_deform;
|
||||
}
|
||||
|
||||
return mesh_orig.runtime->vert_normals_cache;
|
||||
}
|
||||
static SharedCache<Vector<float3>> &vert_normals_cache_eval_for_write(Object &object_orig,
|
||||
Object &object_eval)
|
||||
{
|
||||
return const_cast<SharedCache<Vector<float3>> &>(
|
||||
vert_normals_cache_eval(object_orig, object_eval));
|
||||
}
|
||||
|
||||
static const SharedCache<Vector<float3>> &face_normals_cache_eval(const Object &object_orig,
|
||||
const Object &object_eval)
|
||||
{
|
||||
const SculptSession &ss = *object_orig.sculpt;
|
||||
const Mesh &mesh_orig = *static_cast<const Mesh *>(object_orig.data);
|
||||
BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh);
|
||||
if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
|
||||
if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) {
|
||||
if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) {
|
||||
return mesh_eval->runtime->face_normals_cache;
|
||||
}
|
||||
}
|
||||
if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) {
|
||||
return mesh_eval->runtime->face_normals_cache;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ss.deform_cos.is_empty()) {
|
||||
BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num);
|
||||
return ss.face_normals_deform;
|
||||
}
|
||||
|
||||
return mesh_orig.runtime->face_normals_cache;
|
||||
}
|
||||
static SharedCache<Vector<float3>> &face_normals_cache_eval_for_write(Object &object_orig,
|
||||
Object &object_eval)
|
||||
{
|
||||
return const_cast<SharedCache<Vector<float3>> &>(
|
||||
face_normals_cache_eval(object_orig, object_eval));
|
||||
}
|
||||
|
||||
static void normals_calc_faces(const Span<float3> positions,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
@@ -940,13 +1000,17 @@ static void update_normals_mesh(Object &object_orig, Object &object_eval, Span<N
|
||||
* duplicate work recalculation for the same vertex, and to make parallel storage for vertices
|
||||
* during recalculation thread-safe. */
|
||||
Mesh &mesh = *static_cast<Mesh *>(object_orig.data);
|
||||
Tree &pbvh = *object_orig.sculpt->pbvh;
|
||||
const Span<float3> positions = bke::pbvh::vert_positions_eval_from_eval(object_eval);
|
||||
const OffsetIndices faces = mesh.faces();
|
||||
const Span<int> corner_verts = mesh.corner_verts();
|
||||
const Span<int> tri_faces = mesh.corner_tri_faces();
|
||||
const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
|
||||
|
||||
SharedCache<Vector<float3>> &vert_normals_cache = vert_normals_cache_eval_for_write(object_orig,
|
||||
object_eval);
|
||||
SharedCache<Vector<float3>> &face_normals_cache = face_normals_cache_eval_for_write(object_orig,
|
||||
object_eval);
|
||||
|
||||
VectorSet<int> boundary_faces;
|
||||
for (const Node *node : nodes) {
|
||||
for (const int vert : node->vert_indices_.as_span().drop_front(node->unique_verts_num_)) {
|
||||
@@ -954,35 +1018,21 @@ static void update_normals_mesh(Object &object_orig, Object &object_eval, Span<N
|
||||
}
|
||||
}
|
||||
|
||||
/* In certain cases when undoing strokes on a duplicate object, the cached data may be marked
|
||||
* dirty before this code is run, leaving the relevant vectors empty. We force reinitialize the
|
||||
* vectors to prevent crashes here.
|
||||
* See #125375 for more detail. */
|
||||
if (!pbvh.deformed_) {
|
||||
if (mesh.runtime->face_normals_cache.is_dirty()) {
|
||||
mesh.face_normals();
|
||||
}
|
||||
if (mesh.runtime->vert_normals_cache.is_dirty()) {
|
||||
mesh.vert_normals();
|
||||
}
|
||||
}
|
||||
|
||||
VectorSet<int> boundary_verts;
|
||||
|
||||
threading::parallel_invoke(
|
||||
[&]() {
|
||||
if (pbvh.deformed_) {
|
||||
calc_node_face_normals(
|
||||
positions, faces, corner_verts, tri_faces, nodes, pbvh.face_normals_deformed_);
|
||||
calc_boundary_face_normals(
|
||||
positions, faces, corner_verts, boundary_faces, pbvh.face_normals_deformed_);
|
||||
if (face_normals_cache.is_dirty()) {
|
||||
face_normals_cache.ensure([&](Vector<float3> &r_data) {
|
||||
r_data.resize(faces.size());
|
||||
bke::mesh::normals_calc_faces(positions, faces, corner_verts, r_data);
|
||||
});
|
||||
}
|
||||
else {
|
||||
mesh.runtime->face_normals_cache.update([&](Vector<float3> &r_data) {
|
||||
face_normals_cache.update([&](Vector<float3> &r_data) {
|
||||
calc_node_face_normals(positions, faces, corner_verts, tri_faces, nodes, r_data);
|
||||
calc_boundary_face_normals(positions, faces, corner_verts, boundary_faces, r_data);
|
||||
});
|
||||
/* #SharedCache::update() reallocates cached vectors if they were shared initially. */
|
||||
pbvh.face_normals_ = mesh.runtime->face_normals_cache.data();
|
||||
}
|
||||
},
|
||||
[&]() {
|
||||
@@ -992,19 +1042,20 @@ static void update_normals_mesh(Object &object_orig, Object &object_eval, Span<N
|
||||
boundary_verts.add_multiple(corner_verts.slice(faces[face]));
|
||||
}
|
||||
});
|
||||
const Span<float3> face_normals = face_normals_cache.data();
|
||||
|
||||
if (pbvh.deformed_) {
|
||||
calc_node_vert_normals(
|
||||
vert_to_face_map, pbvh.face_normals_, nodes, pbvh.vert_normals_deformed_);
|
||||
calc_boundary_vert_normals(
|
||||
vert_to_face_map, pbvh.face_normals_, boundary_verts, pbvh.vert_normals_deformed_);
|
||||
if (vert_normals_cache.is_dirty()) {
|
||||
vert_normals_cache.ensure([&](Vector<float3> &r_data) {
|
||||
r_data.resize(positions.size());
|
||||
mesh::normals_calc_verts(
|
||||
positions, faces, corner_verts, vert_to_face_map, face_normals, r_data);
|
||||
});
|
||||
}
|
||||
else {
|
||||
mesh.runtime->vert_normals_cache.update([&](Vector<float3> &r_data) {
|
||||
calc_node_vert_normals(vert_to_face_map, pbvh.face_normals_, nodes, r_data);
|
||||
calc_boundary_vert_normals(vert_to_face_map, pbvh.face_normals_, boundary_verts, r_data);
|
||||
vert_normals_cache.update([&](Vector<float3> &r_data) {
|
||||
calc_node_vert_normals(vert_to_face_map, face_normals, nodes, r_data);
|
||||
calc_boundary_vert_normals(vert_to_face_map, face_normals, boundary_verts, r_data);
|
||||
});
|
||||
pbvh.vert_normals_ = mesh.runtime->vert_normals_cache.data();
|
||||
}
|
||||
|
||||
for (Node *node : nodes) {
|
||||
@@ -2473,7 +2524,8 @@ static blender::draw::pbvh::PBVH_GPU_Args pbvh_draw_args_init(const Object &obje
|
||||
args.corner_edges = mesh_eval.corner_edges();
|
||||
args.corner_tris = mesh_eval.corner_tris();
|
||||
args.vert_normals = blender::bke::pbvh::vert_normals_eval_from_eval(object_eval);
|
||||
args.face_normals = pbvh.face_normals_;
|
||||
args.face_normals =
|
||||
blender::bke::pbvh::face_normals_cache_eval(object_orig, object_eval).data();
|
||||
args.hide_poly = *mesh_orig.attributes().lookup<bool>(".hide_poly",
|
||||
blender::bke::AttrDomain::Face);
|
||||
|
||||
@@ -2668,46 +2720,11 @@ void BKE_pbvh_vert_coords_apply(blender::bke::pbvh::Tree &pbvh,
|
||||
const blender::Span<blender::float3> vert_positions)
|
||||
{
|
||||
using namespace blender::bke::pbvh;
|
||||
|
||||
if (!pbvh.deformed_) {
|
||||
if (!pbvh.vert_positions_.is_empty()) {
|
||||
/* When the Tree is deformed, it creates a separate vertex position array
|
||||
* that it owns directly. Conceptually these copies often aren't and often adds extra
|
||||
* indirection, but:
|
||||
* - Sculpting shape keys, the deformations are flushed back to the keys as a separate step.
|
||||
* - Sculpting on a deformed mesh, deformations are also flushed to original positions
|
||||
* separately.
|
||||
* - The Tree currently always assumes we want to change positions, and
|
||||
* has no way to avoid calculating normals if it's only used for painting, for example. */
|
||||
pbvh.vert_positions_deformed_ = pbvh.vert_positions_.as_span();
|
||||
pbvh.vert_positions_ = pbvh.vert_positions_deformed_;
|
||||
|
||||
pbvh.vert_normals_deformed_ = pbvh.vert_normals_;
|
||||
pbvh.vert_normals_ = pbvh.vert_normals_deformed_;
|
||||
|
||||
pbvh.face_normals_deformed_ = pbvh.face_normals_;
|
||||
pbvh.face_normals_ = pbvh.face_normals_deformed_;
|
||||
|
||||
pbvh.deformed_ = true;
|
||||
}
|
||||
for (Node &node : pbvh.nodes_) {
|
||||
BKE_pbvh_node_mark_positions_update(node);
|
||||
}
|
||||
|
||||
if (!pbvh.vert_positions_.is_empty()) {
|
||||
blender::MutableSpan<blender::float3> positions = pbvh.vert_positions_;
|
||||
positions.copy_from(vert_positions);
|
||||
|
||||
for (Node &node : pbvh.nodes_) {
|
||||
BKE_pbvh_node_mark_positions_update(node);
|
||||
}
|
||||
|
||||
update_bounds_mesh(vert_positions, pbvh);
|
||||
store_bounds_orig(pbvh);
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_pbvh_is_deformed(const blender::bke::pbvh::Tree &pbvh)
|
||||
{
|
||||
return pbvh.deformed_;
|
||||
update_bounds_mesh(vert_positions, pbvh);
|
||||
store_bounds_orig(pbvh);
|
||||
}
|
||||
|
||||
namespace blender::bke::pbvh {
|
||||
@@ -2728,38 +2745,87 @@ void get_frustum_planes(const Tree &pbvh, PBVHFrustumPlanes *planes)
|
||||
}
|
||||
}
|
||||
|
||||
Span<float3> vert_positions_eval(const Depsgraph & /*depsgraph*/, const Object &object)
|
||||
static Span<float3> vert_positions_eval(const Object &object_orig, const Object &object_eval)
|
||||
{
|
||||
BLI_assert(object.sculpt->pbvh->type() == Type::Mesh);
|
||||
return object.sculpt->pbvh->vert_positions_;
|
||||
const SculptSession &ss = *object_orig.sculpt;
|
||||
const Mesh &mesh_orig = *static_cast<const Mesh *>(object_orig.data);
|
||||
BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh);
|
||||
if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
|
||||
if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) {
|
||||
if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) {
|
||||
return mesh_eval->vert_positions();
|
||||
}
|
||||
}
|
||||
if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) {
|
||||
return mesh_eval->vert_positions();
|
||||
}
|
||||
}
|
||||
|
||||
if (!ss.deform_cos.is_empty()) {
|
||||
BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num);
|
||||
return ss.deform_cos;
|
||||
}
|
||||
|
||||
return mesh_orig.vert_positions();
|
||||
}
|
||||
static MutableSpan<float3> vert_positions_eval_for_write(Object &object_orig, Object &object_eval)
|
||||
{
|
||||
SculptSession &ss = *object_orig.sculpt;
|
||||
Mesh &mesh_orig = *static_cast<Mesh *>(object_orig.data);
|
||||
BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh);
|
||||
if (object_orig.mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
|
||||
if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(&object_eval)) {
|
||||
if (mesh_topology_count_matches(*mesh_eval, mesh_orig)) {
|
||||
Mesh *mesh_eval_mut = const_cast<Mesh *>(mesh_eval);
|
||||
return mesh_eval_mut->vert_positions_for_write();
|
||||
}
|
||||
}
|
||||
if (const Mesh *mesh_eval = BKE_object_get_mesh_deform_eval(&object_eval)) {
|
||||
Mesh *mesh_eval_mut = const_cast<Mesh *>(mesh_eval);
|
||||
return mesh_eval_mut->vert_positions_for_write();
|
||||
}
|
||||
}
|
||||
|
||||
if (!ss.deform_cos.is_empty()) {
|
||||
BLI_assert(ss.deform_cos.size() == mesh_orig.verts_num);
|
||||
return ss.deform_cos;
|
||||
}
|
||||
|
||||
return mesh_orig.vert_positions_for_write();
|
||||
}
|
||||
|
||||
Span<float3> vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
|
||||
{
|
||||
const Object &object_eval = *DEG_get_evaluated_object(&depsgraph,
|
||||
&const_cast<Object &>(object_orig));
|
||||
return vert_positions_eval(object_orig, object_eval);
|
||||
}
|
||||
|
||||
Span<float3> vert_positions_eval_from_eval(const Object &object_eval)
|
||||
{
|
||||
BLI_assert(!DEG_is_original_object(&object_eval));
|
||||
Object &object_orig = *DEG_get_original_object(&const_cast<Object &>(object_eval));
|
||||
BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh);
|
||||
return object_orig.sculpt->pbvh->vert_positions_;
|
||||
const Object &object_orig = *DEG_get_original_object(&const_cast<Object &>(object_eval));
|
||||
return vert_positions_eval(object_orig, object_eval);
|
||||
}
|
||||
|
||||
MutableSpan<float3> vert_positions_eval_for_write(const Depsgraph & /*depsgraph*/, Object &object)
|
||||
MutableSpan<float3> vert_positions_eval_for_write(const Depsgraph &depsgraph, Object &object_orig)
|
||||
{
|
||||
BLI_assert(object.sculpt->pbvh->type() == Type::Mesh);
|
||||
return object.sculpt->pbvh->vert_positions_;
|
||||
Object &object_eval = *DEG_get_evaluated_object(&depsgraph, &object_orig);
|
||||
return vert_positions_eval_for_write(object_orig, object_eval);
|
||||
}
|
||||
|
||||
Span<float3> vert_normals_eval(const Depsgraph & /*depsgraph*/, const Object &object)
|
||||
Span<float3> vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
|
||||
{
|
||||
BLI_assert(object.sculpt->pbvh->type() == Type::Mesh);
|
||||
return object.sculpt->pbvh->vert_normals_;
|
||||
const Object &object_eval = *DEG_get_evaluated_object(&depsgraph,
|
||||
&const_cast<Object &>(object_orig));
|
||||
return vert_normals_cache_eval(object_orig, object_eval).data();
|
||||
}
|
||||
|
||||
Span<float3> vert_normals_eval_from_eval(const Object &object_eval)
|
||||
{
|
||||
BLI_assert(!DEG_is_original_object(&object_eval));
|
||||
Object &object_orig = *DEG_get_original_object(&const_cast<Object &>(object_eval));
|
||||
BLI_assert(object_orig.sculpt->pbvh->type() == Type::Mesh);
|
||||
return object_orig.sculpt->pbvh->vert_normals_;
|
||||
return vert_normals_cache_eval(object_orig, object_eval).data();
|
||||
}
|
||||
|
||||
} // namespace blender::bke::pbvh
|
||||
|
||||
@@ -430,17 +430,6 @@ void update_shape_keys(Object &object,
|
||||
Span<float3> translations,
|
||||
Span<float3> positions_orig);
|
||||
|
||||
/**
|
||||
* Currently the pbvh::Tree owns its own copy of deformed positions that needs to be updated to
|
||||
* stay in sync with brush deformations.
|
||||
* \todo This should be removed one the pbvh::Tree no longer stores this copy of deformed
|
||||
* positions.
|
||||
*/
|
||||
void apply_translations_to_pbvh(const Depsgraph &depsgraph,
|
||||
Object &object,
|
||||
Span<int> verts,
|
||||
Span<float3> translations);
|
||||
|
||||
/**
|
||||
* Write the new translated positions to the original mesh, taking into account inverse
|
||||
* deformation from modifiers, axis locking, and clipping. Flush the deformation to shape keys as
|
||||
|
||||
@@ -7209,21 +7209,6 @@ void update_shape_keys(Object &object,
|
||||
}
|
||||
}
|
||||
|
||||
void apply_translations_to_pbvh(const Depsgraph &depsgraph,
|
||||
Object &object,
|
||||
const Span<int> verts,
|
||||
const Span<float3> translations)
|
||||
{
|
||||
if (!BKE_pbvh_is_deformed(*object.sculpt->pbvh)) {
|
||||
return;
|
||||
}
|
||||
MutableSpan<float3> pbvh_positions = bke::pbvh::vert_positions_eval_for_write(depsgraph, object);
|
||||
for (const int i : verts.index_range()) {
|
||||
const int vert = verts[i];
|
||||
pbvh_positions[vert] += translations[i];
|
||||
}
|
||||
}
|
||||
|
||||
void write_translations(const Depsgraph &depsgraph,
|
||||
const Sculpt &sd,
|
||||
Object &object,
|
||||
@@ -7236,7 +7221,11 @@ void write_translations(const Depsgraph &depsgraph,
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions_eval, verts, translations);
|
||||
|
||||
apply_translations_to_pbvh(depsgraph, object, verts, translations);
|
||||
MutableSpan<float3> positions_eval_mut = bke::pbvh::vert_positions_eval_for_write(depsgraph,
|
||||
object);
|
||||
if (positions_eval_mut.data() != positions_orig.data()) {
|
||||
apply_translations(translations, verts, positions_eval_mut);
|
||||
}
|
||||
|
||||
if (!ss.deform_imats.is_empty()) {
|
||||
apply_crazyspace_to_translations(ss.deform_imats, verts, translations);
|
||||
|
||||
Reference in New Issue
Block a user