Mesh: Replace older flip faces utilities
Replace `BKE_mesh_face_flip_ex()` with `bke::mesh_flip_faces()`.
This gives better performance since it avoids custom data name
lookups for every face index, and processes data in a more cache
friendly way. Also remove the `CustomData_swap` API function
as mentioned in 09b6eac5c4.
Flipping 16 million faces, I observed a 3.25x speedup, from 760 to 230 ms.
Pull Request: https://projects.blender.org/blender/blender/pulls/110702
This commit is contained in:
@@ -429,11 +429,6 @@ void CustomData_bmesh_interp(struct CustomData *data,
|
||||
*/
|
||||
void CustomData_swap_corners(struct CustomData *data, int index, const int *corner_indices);
|
||||
|
||||
/**
|
||||
* Swap two items of given custom data, in all available layers.
|
||||
*/
|
||||
void CustomData_swap(struct CustomData *data, int index_a, int index_b, const int totelem);
|
||||
|
||||
/**
|
||||
* Retrieve a pointer to an element of the active layer of the given \a type, chosen by the
|
||||
* \a index, if it exists.
|
||||
|
||||
@@ -537,42 +537,6 @@ void BKE_mesh_calc_volume(const float (*vert_positions)[3],
|
||||
*/
|
||||
void BKE_mesh_mdisp_flip(struct MDisps *md, bool use_loop_mdisp_flip);
|
||||
|
||||
/**
|
||||
* Flip (invert winding of) the given \a face, i.e. reverse order of its loops
|
||||
* (keeping the same vertex as 'start point').
|
||||
*
|
||||
* \param face: the face to flip.
|
||||
* \param mloop: the full loops array.
|
||||
* \param loop_data: the loops custom data.
|
||||
*/
|
||||
void BKE_mesh_face_flip_ex(int face_offset,
|
||||
int face_size,
|
||||
int *corner_verts,
|
||||
int *corner_edges,
|
||||
struct CustomData *loop_data,
|
||||
int tot_loop,
|
||||
float (*lnors)[3],
|
||||
struct MDisps *mdisp,
|
||||
bool use_loop_mdisp_flip);
|
||||
void BKE_mesh_face_flip(int face_offset,
|
||||
int face_size,
|
||||
int *corner_verts,
|
||||
int *corner_edges,
|
||||
struct CustomData *loop_data,
|
||||
int totloop);
|
||||
|
||||
/**
|
||||
* Flip (invert winding of) all faces (used to inverse their normals).
|
||||
*
|
||||
* \note Invalidates tessellation, caller must handle that.
|
||||
*/
|
||||
void BKE_mesh_faces_flip(const int *face_offsets,
|
||||
int *corner_verts,
|
||||
int *corner_edges,
|
||||
struct CustomData *loop_data,
|
||||
int loops_num,
|
||||
int faces_num);
|
||||
|
||||
/**
|
||||
* Account for custom-data such as UVs becoming detached because of imprecision
|
||||
* in custom-data interpolation.
|
||||
|
||||
@@ -3505,32 +3505,6 @@ void CustomData_swap_corners(CustomData *data, const int index, const int *corne
|
||||
}
|
||||
}
|
||||
|
||||
void CustomData_swap(CustomData *data, const int index_a, const int index_b, const int totelem)
|
||||
{
|
||||
char buff_static[256];
|
||||
|
||||
if (index_a == index_b) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < data->totlayer; i++) {
|
||||
CustomDataLayer &layer = data->layers[i];
|
||||
ensure_layer_data_is_mutable(layer, totelem);
|
||||
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer.type));
|
||||
const size_t size = typeInfo->size;
|
||||
const size_t offset_a = size * index_a;
|
||||
const size_t offset_b = size * index_b;
|
||||
|
||||
void *buff = size <= sizeof(buff_static) ? buff_static : MEM_mallocN(size, __func__);
|
||||
memcpy(buff, POINTER_OFFSET(layer.data, offset_a), size);
|
||||
memcpy(POINTER_OFFSET(layer.data, offset_a), POINTER_OFFSET(layer.data, offset_b), size);
|
||||
memcpy(POINTER_OFFSET(layer.data, offset_b), buff, size);
|
||||
|
||||
if (buff != buff_static) {
|
||||
MEM_freeN(buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *CustomData_get_for_write(CustomData *data,
|
||||
const int index,
|
||||
|
||||
@@ -501,99 +501,6 @@ void BKE_mesh_mdisp_flip(MDisps *md, const bool use_loop_mdisp_flip)
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_mesh_face_flip_ex(const int face_offset,
|
||||
const int face_size,
|
||||
int *corner_verts,
|
||||
int *corner_edges,
|
||||
CustomData *loop_data,
|
||||
int tot_loop,
|
||||
float (*lnors)[3],
|
||||
MDisps *mdisp,
|
||||
const bool use_loop_mdisp_flip)
|
||||
{
|
||||
int loopstart = face_offset;
|
||||
int loopend = loopstart + face_size - 1;
|
||||
const bool corner_verts_in_data =
|
||||
(CustomData_get_layer_named(loop_data, CD_PROP_INT32, ".corner_vert") == corner_verts);
|
||||
const bool corner_edges_in_data =
|
||||
(CustomData_get_layer_named(loop_data, CD_PROP_INT32, ".corner_edge") == corner_edges);
|
||||
|
||||
if (mdisp) {
|
||||
for (int i = loopstart; i <= loopend; i++) {
|
||||
BKE_mesh_mdisp_flip(&mdisp[i], use_loop_mdisp_flip);
|
||||
}
|
||||
}
|
||||
|
||||
/* Note that we keep same start vertex for flipped face. */
|
||||
|
||||
/* We also have to update loops edge
|
||||
* (they will get their original 'other edge', that is,
|
||||
* the original edge of their original previous loop)... */
|
||||
int prev_edge_index = corner_edges[loopstart];
|
||||
corner_edges[loopstart] = corner_edges[loopend];
|
||||
|
||||
for (loopstart++; loopend > loopstart; loopstart++, loopend--) {
|
||||
corner_edges[loopend] = corner_edges[loopend - 1];
|
||||
std::swap(corner_edges[loopstart], prev_edge_index);
|
||||
|
||||
if (!corner_verts_in_data) {
|
||||
std::swap(corner_verts[loopstart], corner_verts[loopend]);
|
||||
}
|
||||
if (!corner_edges_in_data) {
|
||||
std::swap(corner_edges[loopstart], corner_edges[loopend]);
|
||||
}
|
||||
if (lnors) {
|
||||
swap_v3_v3(lnors[loopstart], lnors[loopend]);
|
||||
}
|
||||
CustomData_swap(loop_data, loopstart, loopend, tot_loop);
|
||||
}
|
||||
/* Even if we did not swap the other 'pivot' loop, we need to set its swapped edge. */
|
||||
if (loopstart == loopend) {
|
||||
corner_edges[loopstart] = prev_edge_index;
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_mesh_face_flip(const int face_offset,
|
||||
const int face_size,
|
||||
int *corner_verts,
|
||||
int *corner_edges,
|
||||
CustomData *loop_data,
|
||||
const int totloop)
|
||||
{
|
||||
MDisps *mdisp = (MDisps *)CustomData_get_layer_for_write(loop_data, CD_MDISPS, totloop);
|
||||
BKE_mesh_face_flip_ex(face_offset,
|
||||
face_size,
|
||||
corner_verts,
|
||||
corner_edges,
|
||||
loop_data,
|
||||
totloop,
|
||||
nullptr,
|
||||
mdisp,
|
||||
true);
|
||||
}
|
||||
|
||||
void BKE_mesh_faces_flip(const int *face_offsets,
|
||||
int *corner_verts,
|
||||
int *corner_edges,
|
||||
CustomData *loop_data,
|
||||
int loops_num,
|
||||
int faces_num)
|
||||
{
|
||||
const blender::OffsetIndices faces(blender::Span(face_offsets, faces_num + 1));
|
||||
MDisps *mdisp = (MDisps *)CustomData_get_layer_for_write(loop_data, CD_MDISPS, faces_num);
|
||||
for (const int i : faces.index_range()) {
|
||||
BKE_mesh_face_flip_ex(faces[i].start(),
|
||||
faces[i].size(),
|
||||
corner_verts,
|
||||
corner_edges,
|
||||
loop_data,
|
||||
loops_num,
|
||||
nullptr,
|
||||
mdisp,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -12,6 +12,29 @@
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
template<typename T>
|
||||
static void flip_corner_data(const OffsetIndices<int> faces,
|
||||
const IndexMask &face_selection,
|
||||
MutableSpan<T> data)
|
||||
{
|
||||
face_selection.foreach_index(GrainSize(1024),
|
||||
[&](const int i) { data.slice(faces[i].drop_front(1)).reverse(); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void flip_custom_data_type(const OffsetIndices<int> faces,
|
||||
CustomData &loop_data,
|
||||
const IndexMask &face_selection,
|
||||
const eCustomDataType data_type)
|
||||
{
|
||||
BLI_assert(sizeof(T) == CustomData_sizeof(data_type));
|
||||
for (const int i : IndexRange(CustomData_number_of_layers(&loop_data, data_type))) {
|
||||
T *data = static_cast<T *>(
|
||||
CustomData_get_layer_n_for_write(&loop_data, data_type, i, faces.total_size()));
|
||||
flip_corner_data(faces, face_selection, MutableSpan(data, faces.total_size()));
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_flip_faces(Mesh &mesh, const IndexMask &selection)
|
||||
{
|
||||
if (mesh.faces_num == 0 || selection.is_empty()) {
|
||||
@@ -32,6 +55,23 @@ void mesh_flip_faces(Mesh &mesh, const IndexMask &selection)
|
||||
}
|
||||
});
|
||||
|
||||
flip_custom_data_type<float4x4>(faces, mesh.loop_data, selection, CD_TANGENT);
|
||||
flip_custom_data_type<float4>(faces, mesh.loop_data, selection, CD_MLOOPTANGENT);
|
||||
flip_custom_data_type<short2>(faces, mesh.loop_data, selection, CD_CUSTOMLOOPNORMAL);
|
||||
flip_custom_data_type<float>(faces, mesh.loop_data, selection, CD_PAINT_MASK);
|
||||
flip_custom_data_type<GridPaintMask>(faces, mesh.loop_data, selection, CD_GRID_PAINT_MASK);
|
||||
flip_custom_data_type<OrigSpaceLoop>(faces, mesh.loop_data, selection, CD_ORIGSPACE_MLOOP);
|
||||
flip_custom_data_type<MDisps>(faces, mesh.loop_data, selection, CD_MDISPS);
|
||||
if (MDisps *mdisp = static_cast<MDisps *>(
|
||||
CustomData_get_layer_for_write(&mesh.loop_data, CD_MDISPS, mesh.totloop)))
|
||||
{
|
||||
selection.foreach_index(GrainSize(512), [&](const int i) {
|
||||
for (const int corner : faces[i]) {
|
||||
BKE_mesh_mdisp_flip(&mdisp[corner], true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
||||
attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data) {
|
||||
@@ -47,10 +87,7 @@ void mesh_flip_faces(Mesh &mesh, const IndexMask &selection)
|
||||
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(attribute_id);
|
||||
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> dst_span = attribute.span.typed<T>();
|
||||
selection.foreach_index(GrainSize(1024), [&](const int i) {
|
||||
dst_span.slice(faces[i].drop_front(1)).reverse();
|
||||
});
|
||||
flip_corner_data(faces, selection, attribute.span.typed<T>());
|
||||
});
|
||||
attribute.finish();
|
||||
return true;
|
||||
|
||||
@@ -671,13 +671,10 @@ static float rna_MeshPolygon_area_get(PointerRNA *ptr)
|
||||
|
||||
static void rna_MeshPolygon_flip(ID *id, MIntProperty *poly_offset_p)
|
||||
{
|
||||
using namespace blender;
|
||||
Mesh *me = (Mesh *)id;
|
||||
const int poly_start = *((const int *)poly_offset_p);
|
||||
const int poly_size = *(((const int *)poly_offset_p) + 1) - poly_start;
|
||||
int *corner_verts = me->corner_verts_for_write().data();
|
||||
int *corner_edges = me->corner_edges_for_write().data();
|
||||
BKE_mesh_face_flip(
|
||||
poly_start, poly_size, corner_verts, corner_edges, &me->loop_data, me->totloop);
|
||||
const int index = reinterpret_cast<int *>(poly_offset_p) - me->faces().data();
|
||||
bke::mesh_flip_faces(*me, IndexMask(IndexRange(index, 1)));
|
||||
BKE_mesh_tessface_clear(me);
|
||||
BKE_mesh_runtime_clear_geometry(me);
|
||||
}
|
||||
|
||||
@@ -160,15 +160,10 @@ static void rna_Mesh_transform(Mesh *mesh, const float mat[16], bool shape_keys)
|
||||
|
||||
static void rna_Mesh_flip_normals(Mesh *mesh)
|
||||
{
|
||||
BKE_mesh_faces_flip(BKE_mesh_face_offsets(mesh),
|
||||
mesh->corner_verts_for_write().data(),
|
||||
mesh->corner_edges_for_write().data(),
|
||||
&mesh->loop_data,
|
||||
mesh->totloop,
|
||||
mesh->faces_num);
|
||||
using namespace blender;
|
||||
bke::mesh_flip_faces(*mesh, IndexMask(mesh->faces_num));
|
||||
BKE_mesh_tessface_clear(mesh);
|
||||
BKE_mesh_runtime_clear_geometry(mesh);
|
||||
|
||||
DEG_id_tag_update(&mesh->id, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -182,44 +182,34 @@ static void mix_normals(const float mix_factor,
|
||||
|
||||
/* Check face normals and new loop normals are compatible, otherwise flip faces
|
||||
* (and invert matching face normals). */
|
||||
static bool faces_check_flip(blender::MutableSpan<int> corner_verts,
|
||||
blender::MutableSpan<int> corner_edges,
|
||||
blender::float3 *nos,
|
||||
CustomData *ldata,
|
||||
const blender::OffsetIndices<int> faces,
|
||||
static void faces_check_flip(Mesh &mesh,
|
||||
blender::MutableSpan<blender::float3> nos,
|
||||
const blender::Span<blender::float3> face_normals)
|
||||
{
|
||||
MDisps *mdisp = static_cast<MDisps *>(
|
||||
CustomData_get_layer_for_write(ldata, CD_MDISPS, corner_verts.size()));
|
||||
bool flipped = false;
|
||||
using namespace blender;
|
||||
const OffsetIndices faces = mesh.faces();
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask faces_to_flip = IndexMask::from_predicate(
|
||||
faces.index_range(), GrainSize(1024), memory, [&](const int i) {
|
||||
const blender::IndexRange face = faces[i];
|
||||
float norsum[3] = {0.0f};
|
||||
|
||||
for (const int i : faces.index_range()) {
|
||||
const blender::IndexRange face = faces[i];
|
||||
float norsum[3] = {0.0f};
|
||||
for (const int64_t j : face) {
|
||||
add_v3_v3(norsum, nos[j]);
|
||||
}
|
||||
if (!normalize_v3(norsum)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const int64_t j : face) {
|
||||
add_v3_v3(norsum, nos[j]);
|
||||
}
|
||||
if (!normalize_v3(norsum)) {
|
||||
continue;
|
||||
}
|
||||
/* If average of new loop normals is opposed to face normal, flip face. */
|
||||
if (dot_v3v3(face_normals[i], norsum) < 0.0f) {
|
||||
nos.slice(faces[i].drop_front(1)).reverse();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
/* If average of new loop normals is opposed to face normal, flip face. */
|
||||
if (dot_v3v3(face_normals[i], norsum) < 0.0f) {
|
||||
BKE_mesh_face_flip_ex(face.start(),
|
||||
face.size(),
|
||||
corner_verts.data(),
|
||||
corner_edges.data(),
|
||||
ldata,
|
||||
corner_edges.size(),
|
||||
reinterpret_cast<float(*)[3]>(nos),
|
||||
mdisp,
|
||||
true);
|
||||
flipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
return flipped;
|
||||
bke::mesh_flip_faces(mesh, faces_to_flip);
|
||||
}
|
||||
|
||||
static void normalEditModifier_do_radial(NormalEditModifierData *enmd,
|
||||
@@ -327,11 +317,8 @@ static void normalEditModifier_do_radial(NormalEditModifierData *enmd,
|
||||
nos.data());
|
||||
}
|
||||
|
||||
if (do_facenors_fix &&
|
||||
faces_check_flip(
|
||||
corner_verts, corner_edges, nos.data(), &mesh->loop_data, faces, mesh->face_normals()))
|
||||
{
|
||||
BKE_mesh_tag_face_winding_changed(mesh);
|
||||
if (do_facenors_fix) {
|
||||
faces_check_flip(*mesh, nos, mesh->face_normals());
|
||||
}
|
||||
const bool *sharp_faces = static_cast<const bool *>(
|
||||
CustomData_get_layer_named(&mesh->face_data, CD_PROP_BOOL, "sharp_face"));
|
||||
@@ -435,11 +422,8 @@ static void normalEditModifier_do_directional(NormalEditModifierData *enmd,
|
||||
nos.data());
|
||||
}
|
||||
|
||||
if (do_facenors_fix &&
|
||||
faces_check_flip(
|
||||
corner_verts, corner_edges, nos.data(), &mesh->loop_data, faces, mesh->face_normals()))
|
||||
{
|
||||
BKE_mesh_tag_face_winding_changed(mesh);
|
||||
if (do_facenors_fix) {
|
||||
faces_check_flip(*mesh, nos, mesh->face_normals());
|
||||
}
|
||||
const bool *sharp_faces = static_cast<const bool *>(
|
||||
CustomData_get_layer_named(&mesh->face_data, CD_PROP_BOOL, "sharp_face"));
|
||||
|
||||
Reference in New Issue
Block a user