Sculpt: Data oriented refactor for cavity baking
Part of #118145. Remove the iterator macro and the "sculpt mask write" abstraction. A few mask utilities have been added/moved to the common mask code. They'll be useful in a couple more places including the mask filter. TODO: Still needs to be tested Pull Request: https://projects.blender.org/blender/blender/pulls/124131
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
#include "BLI_task.h"
|
||||
|
||||
#include "editors/sculpt_paint/mesh_brush_common.hh"
|
||||
#include "editors/sculpt_paint/paint_intern.hh"
|
||||
#include "editors/sculpt_paint/sculpt_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
@@ -81,25 +82,6 @@ static void calc_smooth_masks_faces(const OffsetIndices<int> faces,
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_mask(const Span<float> mask_averages,
|
||||
const Span<float> factors,
|
||||
const MutableSpan<float> masks)
|
||||
{
|
||||
BLI_assert(mask_averages.size() == factors.size());
|
||||
BLI_assert(mask_averages.size() == masks.size());
|
||||
|
||||
for (const int i : masks.index_range()) {
|
||||
masks[i] += (mask_averages[i] - masks[i]) * factors[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void clamp_mask(const MutableSpan<float> masks)
|
||||
{
|
||||
for (float &mask : masks) {
|
||||
mask = std::clamp(mask, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_masks_faces(const Brush &brush,
|
||||
const Span<float3> positions_eval,
|
||||
const Span<float3> vert_normals,
|
||||
@@ -144,8 +126,8 @@ static void apply_masks_faces(const Brush &brush,
|
||||
const MutableSpan<float> new_masks = tls.masks;
|
||||
array_utils::gather(mask.as_span(), verts, new_masks);
|
||||
|
||||
calc_mask(mask_averages, factors, new_masks);
|
||||
clamp_mask(new_masks);
|
||||
mask::mix_new_masks(mask_averages, factors, new_masks);
|
||||
mask::clamp_mask(new_masks);
|
||||
|
||||
array_utils::scatter(new_masks.as_span(), verts, mask);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
@@ -23,6 +24,8 @@ enum class PaintMode : int8_t;
|
||||
|
||||
struct ARegion;
|
||||
struct bContext;
|
||||
struct BMesh;
|
||||
struct BMVert;
|
||||
struct Brush;
|
||||
struct ColorManagedDisplay;
|
||||
struct ColorSpace;
|
||||
@@ -42,6 +45,7 @@ struct ReportList;
|
||||
struct Scene;
|
||||
struct SculptSession;
|
||||
struct SpaceImage;
|
||||
struct SubdivCCG;
|
||||
struct ToolSettings;
|
||||
struct VertProjHandle;
|
||||
struct ViewContext;
|
||||
@@ -470,6 +474,14 @@ void PAINT_OT_visibility_filter(wmOperatorType *ot);
|
||||
namespace blender::ed::sculpt_paint::mask {
|
||||
|
||||
Array<float> duplicate_mask(const Object &object);
|
||||
void mix_new_masks(Span<float> new_masks, Span<float> factors, MutableSpan<float> masks);
|
||||
void clamp_mask(MutableSpan<float> masks);
|
||||
|
||||
void gather_mask_grids(const SubdivCCG &subdiv_ccg, Span<int> grids, MutableSpan<float> r_mask);
|
||||
void gather_mask_bmesh(const BMesh &bm, const Set<BMVert *, 0> &verts, MutableSpan<float> r_mask);
|
||||
|
||||
void scatter_mask_grids(Span<float> mask, SubdivCCG &subdiv_ccg, Span<int> grids);
|
||||
void scatter_mask_bmesh(Span<float> mask, const BMesh &bm, const Set<BMVert *, 0> &verts);
|
||||
|
||||
/** Write to the mask attribute for each node, storing undo data. */
|
||||
void write_mask_mesh(Object &object,
|
||||
|
||||
@@ -101,6 +101,90 @@ Array<float> duplicate_mask(const Object &object)
|
||||
return {};
|
||||
}
|
||||
|
||||
void mix_new_masks(const Span<float> new_masks,
|
||||
const Span<float> factors,
|
||||
const MutableSpan<float> masks)
|
||||
{
|
||||
BLI_assert(new_masks.size() == factors.size());
|
||||
BLI_assert(new_masks.size() == masks.size());
|
||||
|
||||
for (const int i : masks.index_range()) {
|
||||
masks[i] += (new_masks[i] - masks[i]) * factors[i];
|
||||
}
|
||||
}
|
||||
|
||||
void clamp_mask(const MutableSpan<float> masks)
|
||||
{
|
||||
for (float &mask : masks) {
|
||||
mask = std::clamp(mask, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void gather_mask_bmesh(const BMesh &bm,
|
||||
const Set<BMVert *, 0> &verts,
|
||||
const MutableSpan<float> r_mask)
|
||||
{
|
||||
BLI_assert(verts.size() == r_mask.size());
|
||||
|
||||
/* TODO: Avoid overhead of accessing attributes for every PBVH node. */
|
||||
const int mask_offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
int i = 0;
|
||||
for (const BMVert *vert : verts) {
|
||||
r_mask[i] = (mask_offset == -1) ? 0.0f : BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void gather_mask_grids(const SubdivCCG &subdiv_ccg,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float> r_mask)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(grids.size() * key.grid_area == r_mask.size());
|
||||
|
||||
if (key.has_mask) {
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
r_mask[start + offset] = CCG_elem_offset_mask(key, elem, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
r_mask.fill(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void scatter_mask_grids(const Span<float> mask, SubdivCCG &subdiv_ccg, const Span<int> grids)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(key.has_mask);
|
||||
BLI_assert(mask.size() == grids.size() * key.grid_area);
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
CCG_elem_offset_mask(key, elem, offset) = mask[start + offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scatter_mask_bmesh(const Span<float> mask, const BMesh &bm, const Set<BMVert *, 0> &verts)
|
||||
{
|
||||
BLI_assert(verts.size() == mask.size());
|
||||
|
||||
const int mask_offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
BLI_assert(mask_offset != -1);
|
||||
int i = 0;
|
||||
for (BMVert *vert : verts) {
|
||||
BM_ELEM_CD_SET_FLOAT(vert, mask_offset, mask[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void update_mask_mesh(Object &object,
|
||||
const Span<PBVHNode *> nodes,
|
||||
FunctionRef<void(MutableSpan<float>, Span<int>)> update_fn)
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
@@ -38,6 +40,7 @@
|
||||
#include "BKE_pbvh_api.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_subdiv_ccg.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
@@ -946,59 +949,140 @@ enum CavityBakeSettingsSource {
|
||||
AUTOMASK_SETTINGS_BRUSH
|
||||
};
|
||||
|
||||
static void sculpt_bake_cavity_exec_task(Object &ob,
|
||||
blender::ed::sculpt_paint::auto_mask::Cache &automasking,
|
||||
const CavityBakeMixMode mode,
|
||||
const float factor,
|
||||
const SculptMaskWriteInfo mask_write,
|
||||
PBVHNode *node)
|
||||
struct LocalData {
|
||||
Vector<float> mask;
|
||||
Vector<float> factors;
|
||||
Vector<float> new_mask;
|
||||
};
|
||||
|
||||
static void calc_new_masks(const CavityBakeMixMode mode,
|
||||
const Span<float> node_mask,
|
||||
const MutableSpan<float> new_mask)
|
||||
{
|
||||
SculptSession &ss = *ob.sculpt;
|
||||
PBVHVertexIter vd;
|
||||
|
||||
undo::push_node(ob, node, undo::Type::Mask);
|
||||
|
||||
auto_mask::NodeData automask_data = auto_mask::node_begin(ob, &automasking, *node);
|
||||
|
||||
BKE_pbvh_vertex_iter_begin (*ss.pbvh, node, vd, PBVH_ITER_UNIQUE) {
|
||||
auto_mask::node_update(automask_data, vd);
|
||||
|
||||
float automask = auto_mask::factor_get(&automasking, ss, vd.vertex, &automask_data);
|
||||
float mask;
|
||||
|
||||
switch (mode) {
|
||||
case AUTOMASK_BAKE_MIX:
|
||||
mask = automask;
|
||||
break;
|
||||
case AUTOMASK_BAKE_MULTIPLY:
|
||||
mask = vd.mask * automask;
|
||||
break;
|
||||
break;
|
||||
case AUTOMASK_BAKE_DIVIDE:
|
||||
mask = automask > 0.00001f ? vd.mask / automask : 0.0f;
|
||||
break;
|
||||
break;
|
||||
case AUTOMASK_BAKE_ADD:
|
||||
mask = vd.mask + automask;
|
||||
break;
|
||||
case AUTOMASK_BAKE_SUBTRACT:
|
||||
mask = vd.mask - automask;
|
||||
break;
|
||||
}
|
||||
|
||||
mask = vd.mask + (mask - vd.mask) * factor;
|
||||
CLAMP(mask, 0.0f, 1.0f);
|
||||
|
||||
SCULPT_mask_vert_set(BKE_pbvh_type(*ss.pbvh), mask_write, mask, vd);
|
||||
switch (mode) {
|
||||
case AUTOMASK_BAKE_MIX:
|
||||
break;
|
||||
case AUTOMASK_BAKE_MULTIPLY:
|
||||
for (const int i : node_mask.index_range()) {
|
||||
new_mask[i] = node_mask[i] * new_mask[i];
|
||||
}
|
||||
break;
|
||||
case AUTOMASK_BAKE_DIVIDE:
|
||||
for (const int i : node_mask.index_range()) {
|
||||
new_mask[i] = new_mask[i] > 0.00001f ? node_mask[i] / new_mask[i] : 0.0f;
|
||||
}
|
||||
break;
|
||||
case AUTOMASK_BAKE_ADD:
|
||||
for (const int i : node_mask.index_range()) {
|
||||
new_mask[i] = node_mask[i] + new_mask[i];
|
||||
}
|
||||
break;
|
||||
case AUTOMASK_BAKE_SUBTRACT:
|
||||
for (const int i : node_mask.index_range()) {
|
||||
new_mask[i] = node_mask[i] - new_mask[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
mask::clamp_mask(new_mask);
|
||||
}
|
||||
|
||||
BKE_pbvh_node_mark_update_mask(node);
|
||||
static void bake_mask_mesh(const Object &object,
|
||||
const auto_mask::Cache &automasking,
|
||||
const CavityBakeMixMode mode,
|
||||
const float factor,
|
||||
const PBVHNode &node,
|
||||
LocalData &tls,
|
||||
const MutableSpan<float> mask)
|
||||
{
|
||||
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
||||
const Span<int> verts = bke::pbvh::node_unique_verts(node);
|
||||
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide(mesh, verts, factors);
|
||||
scale_factors(factors, factor);
|
||||
|
||||
tls.new_mask.reinitialize(verts.size());
|
||||
const MutableSpan<float> new_mask = tls.new_mask;
|
||||
new_mask.fill(1.0f);
|
||||
auto_mask::calc_vert_factors(object, automasking, node, verts, new_mask);
|
||||
|
||||
tls.mask.reinitialize(verts.size());
|
||||
const MutableSpan<float> node_mask = tls.mask;
|
||||
array_utils::gather(mask.as_span(), verts, node_mask);
|
||||
|
||||
calc_new_masks(mode, node_mask, new_mask);
|
||||
mix_new_masks(new_mask, factors, node_mask);
|
||||
|
||||
array_utils::scatter(node_mask.as_span(), verts, mask);
|
||||
}
|
||||
|
||||
static void bake_mask_grids(Object &object,
|
||||
const auto_mask::Cache &automasking,
|
||||
const CavityBakeMixMode mode,
|
||||
const float factor,
|
||||
const PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
const Span<int> grids = bke::pbvh::node_grid_indices(node);
|
||||
const int grid_verts_num = grids.size() * key.grid_area;
|
||||
|
||||
tls.factors.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide(subdiv_ccg, grids, factors);
|
||||
scale_factors(factors, factor);
|
||||
|
||||
tls.new_mask.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float> new_mask = tls.new_mask;
|
||||
new_mask.fill(1.0f);
|
||||
auto_mask::calc_grids_factors(object, automasking, node, grids, new_mask);
|
||||
|
||||
tls.mask.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float> node_mask = tls.mask;
|
||||
gather_mask_grids(subdiv_ccg, grids, node_mask);
|
||||
|
||||
calc_new_masks(mode, node_mask, new_mask);
|
||||
mix_new_masks(new_mask, factors, node_mask);
|
||||
|
||||
scatter_mask_grids(node_mask.as_span(), subdiv_ccg, grids);
|
||||
}
|
||||
|
||||
static void bake_mask_bmesh(Object &object,
|
||||
const auto_mask::Cache &automasking,
|
||||
const CavityBakeMixMode mode,
|
||||
const float factor,
|
||||
PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(&node);
|
||||
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide(verts, factors);
|
||||
scale_factors(factors, factor);
|
||||
|
||||
tls.new_mask.reinitialize(verts.size());
|
||||
const MutableSpan<float> new_mask = tls.new_mask;
|
||||
new_mask.fill(1.0f);
|
||||
auto_mask::calc_vert_factors(object, automasking, node, verts, new_mask);
|
||||
|
||||
tls.mask.reinitialize(verts.size());
|
||||
const MutableSpan<float> node_mask = tls.mask;
|
||||
gather_mask_bmesh(*ss.bm, verts, node_mask);
|
||||
|
||||
calc_new_masks(mode, node_mask, new_mask);
|
||||
mix_new_masks(new_mask, factors, node_mask);
|
||||
|
||||
scatter_mask_bmesh(node_mask.as_span(), *ss.bm, verts);
|
||||
}
|
||||
|
||||
static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
Object &ob = *CTX_data_active_object(C);
|
||||
SculptSession &ss = *ob.sculpt;
|
||||
@@ -1085,15 +1169,55 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
||||
SCULPT_stroke_id_next(ob);
|
||||
|
||||
std::unique_ptr<auto_mask::Cache> automasking = auto_mask::cache_init(sd2, &brush2, ob);
|
||||
const SculptMaskWriteInfo mask_write = SCULPT_mask_get_for_write(ss);
|
||||
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
sculpt_bake_cavity_exec_task(ob, *automasking, mode, factor, mask_write, nodes[i]);
|
||||
undo::push_nodes(ob, nodes, undo::Type::Mask);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
switch (BKE_pbvh_type(*ss.pbvh)) {
|
||||
case PBVH_FACES: {
|
||||
Mesh &mesh = *static_cast<Mesh *>(ob.data);
|
||||
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
||||
bke::SpanAttributeWriter mask = attributes.lookup_or_add_for_write_span<float>(
|
||||
".sculpt_mask", bke::AttrDomain::Point);
|
||||
if (!mask) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
LocalData &tls = all_tls.local();
|
||||
threading::isolate_task([&]() {
|
||||
for (const int i : range) {
|
||||
bake_mask_mesh(ob, *automasking, mode, factor, *nodes[i], tls, mask.span);
|
||||
BKE_pbvh_node_mark_update_mask(nodes[i]);
|
||||
bke::pbvh::node_update_mask_mesh(mask.span, *nodes[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
case PBVH_GRIDS: {
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
LocalData &tls = all_tls.local();
|
||||
for (const int i : range) {
|
||||
bake_mask_grids(ob, *automasking, mode, factor, *nodes[i], tls);
|
||||
BKE_pbvh_node_mark_update_mask(nodes[i]);
|
||||
}
|
||||
});
|
||||
bke::pbvh::update_mask(*ss.pbvh);
|
||||
break;
|
||||
}
|
||||
case PBVH_BMESH: {
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
LocalData &tls = all_tls.local();
|
||||
for (const int i : range) {
|
||||
bake_mask_bmesh(ob, *automasking, mode, factor, *nodes[i], tls);
|
||||
BKE_pbvh_node_mark_update_mask(nodes[i]);
|
||||
}
|
||||
});
|
||||
bke::pbvh::update_mask(*ss.pbvh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bke::pbvh::update_mask(*ss.pbvh);
|
||||
undo::push_end(ob);
|
||||
|
||||
flush_update_done(C, ob, UpdateType::Mask);
|
||||
|
||||
Reference in New Issue
Block a user