830 lines
30 KiB
C++
830 lines
30 KiB
C++
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup modifiers
|
|
*/
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLI_array_utils.hh"
|
|
#include "BLI_listbase.h"
|
|
|
|
#include "BLT_translation.hh"
|
|
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_defaults.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "BKE_action.hh" /* BKE_pose_channel_find_name */
|
|
#include "BKE_customdata.hh"
|
|
#include "BKE_deform.hh"
|
|
#include "BKE_lib_query.hh"
|
|
#include "BKE_mesh.hh"
|
|
#include "BKE_modifier.hh"
|
|
|
|
#include "UI_interface.hh"
|
|
#include "UI_resources.hh"
|
|
|
|
#include "RNA_access.hh"
|
|
#include "RNA_prototypes.hh"
|
|
|
|
#include "DEG_depsgraph_build.hh"
|
|
|
|
#include "MOD_ui_common.hh"
|
|
|
|
#include "BLI_array.hh"
|
|
#include "BLI_listbase_wrapper.hh"
|
|
#include "BLI_vector.hh"
|
|
|
|
using blender::Array;
|
|
using blender::float3;
|
|
using blender::IndexRange;
|
|
using blender::int2;
|
|
using blender::ListBaseWrapper;
|
|
using blender::MutableSpan;
|
|
using blender::Span;
|
|
using blender::Vector;
|
|
|
|
static void init_data(ModifierData *md)
|
|
{
|
|
MaskModifierData *mmd = (MaskModifierData *)md;
|
|
|
|
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mmd, modifier));
|
|
|
|
MEMCPY_STRUCT_AFTER(mmd, DNA_struct_default_get(MaskModifierData), modifier);
|
|
}
|
|
|
|
static void required_data_mask(ModifierData * /*md*/, CustomData_MeshMasks *r_cddata_masks)
|
|
{
|
|
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
|
|
}
|
|
|
|
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
|
|
{
|
|
MaskModifierData *mmd = reinterpret_cast<MaskModifierData *>(md);
|
|
walk(user_data, ob, (ID **)&mmd->ob_arm, IDWALK_CB_NOP);
|
|
}
|
|
|
|
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
|
|
{
|
|
MaskModifierData *mmd = reinterpret_cast<MaskModifierData *>(md);
|
|
if (mmd->ob_arm) {
|
|
bArmature *arm = (bArmature *)mmd->ob_arm->data;
|
|
/* Tag relationship in depsgraph, but also on the armature. */
|
|
/* TODO(sergey): Is it a proper relation here? */
|
|
DEG_add_object_relation(ctx->node, mmd->ob_arm, DEG_OB_COMP_TRANSFORM, "Mask Modifier");
|
|
arm->flag |= ARM_HAS_VIZ_DEPS;
|
|
DEG_add_depends_on_transform_relation(ctx->node, "Mask Modifier");
|
|
}
|
|
}
|
|
|
|
/* A vertex will be in the mask if a selected bone influences it more than a certain threshold. */
|
|
static void compute_vertex_mask__armature_mode(const MDeformVert *dvert,
|
|
Mesh *mesh,
|
|
Object *armature_ob,
|
|
float threshold,
|
|
MutableSpan<bool> r_vertex_mask)
|
|
{
|
|
/* Element i is true if there is a selected bone that uses vertex group i. */
|
|
Vector<bool> selected_bone_uses_group;
|
|
|
|
LISTBASE_FOREACH (bDeformGroup *, def, &mesh->vertex_group_names) {
|
|
bPoseChannel *pchan = BKE_pose_channel_find_name(armature_ob->pose, def->name);
|
|
bool bone_for_group_exists = pchan && pchan->bone && (pchan->bone->flag & BONE_SELECTED);
|
|
selected_bone_uses_group.append(bone_for_group_exists);
|
|
}
|
|
const int64_t total_size = selected_bone_uses_group.size();
|
|
|
|
for (int i : r_vertex_mask.index_range()) {
|
|
Span<MDeformWeight> weights(dvert[i].dw, dvert[i].totweight);
|
|
r_vertex_mask[i] = false;
|
|
|
|
/* check the groups that vertex is assigned to, and see if it was any use */
|
|
for (const MDeformWeight &dw : weights) {
|
|
if (dw.def_nr >= total_size) {
|
|
continue;
|
|
}
|
|
if (selected_bone_uses_group[dw.def_nr]) {
|
|
if (dw.weight > threshold) {
|
|
r_vertex_mask[i] = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* A vertex will be in the mask if the vertex group influences it more than a certain threshold. */
|
|
static void compute_vertex_mask__vertex_group_mode(const MDeformVert *dvert,
|
|
int defgrp_index,
|
|
float threshold,
|
|
MutableSpan<bool> r_vertex_mask)
|
|
{
|
|
for (int i : r_vertex_mask.index_range()) {
|
|
const bool found = BKE_defvert_find_weight(&dvert[i], defgrp_index) > threshold;
|
|
r_vertex_mask[i] = found;
|
|
}
|
|
}
|
|
|
|
static void compute_masked_verts(Span<bool> vertex_mask,
|
|
MutableSpan<int> r_vertex_map,
|
|
uint *r_verts_masked_num)
|
|
{
|
|
BLI_assert(vertex_mask.size() == r_vertex_map.size());
|
|
|
|
uint verts_masked_num = 0;
|
|
for (uint i_src : r_vertex_map.index_range()) {
|
|
if (vertex_mask[i_src]) {
|
|
r_vertex_map[i_src] = verts_masked_num;
|
|
verts_masked_num++;
|
|
}
|
|
else {
|
|
r_vertex_map[i_src] = -1;
|
|
}
|
|
}
|
|
|
|
*r_verts_masked_num = verts_masked_num;
|
|
}
|
|
|
|
static void computed_masked_edges(const Mesh *mesh,
|
|
Span<bool> vertex_mask,
|
|
MutableSpan<int> r_edge_map,
|
|
uint *r_edges_masked_num)
|
|
{
|
|
BLI_assert(mesh->edges_num == r_edge_map.size());
|
|
const Span<int2> edges = mesh->edges();
|
|
|
|
uint edges_masked_num = 0;
|
|
for (int i : IndexRange(mesh->edges_num)) {
|
|
const int2 &edge = edges[i];
|
|
|
|
/* only add if both verts will be in new mesh */
|
|
if (vertex_mask[edge[0]] && vertex_mask[edge[1]]) {
|
|
r_edge_map[i] = edges_masked_num;
|
|
edges_masked_num++;
|
|
}
|
|
else {
|
|
r_edge_map[i] = -1;
|
|
}
|
|
}
|
|
|
|
*r_edges_masked_num = edges_masked_num;
|
|
}
|
|
|
|
static void computed_masked_edges_smooth(const Mesh *mesh,
|
|
Span<bool> vertex_mask,
|
|
MutableSpan<int> r_edge_map,
|
|
uint *r_edges_masked_num,
|
|
uint *r_verts_add_num)
|
|
{
|
|
BLI_assert(mesh->edges_num == r_edge_map.size());
|
|
const Span<int2> edges = mesh->edges();
|
|
|
|
uint edges_masked_num = 0;
|
|
uint verts_add_num = 0;
|
|
for (int i : IndexRange(mesh->edges_num)) {
|
|
const int2 &edge = edges[i];
|
|
|
|
/* only add if both verts will be in new mesh */
|
|
bool v1 = vertex_mask[edge[0]];
|
|
bool v2 = vertex_mask[edge[1]];
|
|
if (v1 && v2) {
|
|
r_edge_map[i] = edges_masked_num;
|
|
edges_masked_num++;
|
|
}
|
|
else if (v1 != v2) {
|
|
r_edge_map[i] = -2;
|
|
verts_add_num++;
|
|
}
|
|
else {
|
|
r_edge_map[i] = -1;
|
|
}
|
|
}
|
|
|
|
edges_masked_num += verts_add_num;
|
|
*r_edges_masked_num = edges_masked_num;
|
|
*r_verts_add_num = verts_add_num;
|
|
}
|
|
|
|
static void computed_masked_faces(const Mesh *mesh,
|
|
Span<bool> vertex_mask,
|
|
Vector<int> &r_masked_face_indices,
|
|
Vector<int> &r_loop_starts,
|
|
uint *r_faces_masked_num,
|
|
uint *r_loops_masked_num)
|
|
{
|
|
BLI_assert(mesh->verts_num == vertex_mask.size());
|
|
const blender::OffsetIndices faces = mesh->faces();
|
|
const Span<int> corner_verts = mesh->corner_verts();
|
|
|
|
r_masked_face_indices.reserve(mesh->faces_num);
|
|
r_loop_starts.reserve(mesh->faces_num);
|
|
|
|
uint loops_masked_num = 0;
|
|
for (int i : IndexRange(mesh->faces_num)) {
|
|
const blender::IndexRange face = faces[i];
|
|
|
|
bool all_verts_in_mask = true;
|
|
for (const int vert_i : corner_verts.slice(face)) {
|
|
if (!vertex_mask[vert_i]) {
|
|
all_verts_in_mask = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (all_verts_in_mask) {
|
|
r_masked_face_indices.append_unchecked(i);
|
|
r_loop_starts.append_unchecked(loops_masked_num);
|
|
loops_masked_num += face.size();
|
|
}
|
|
}
|
|
|
|
*r_faces_masked_num = r_masked_face_indices.size();
|
|
*r_loops_masked_num = loops_masked_num;
|
|
}
|
|
|
|
static void compute_interpolated_faces(const Mesh *mesh,
|
|
Span<bool> vertex_mask,
|
|
uint verts_add_num,
|
|
uint loops_masked_num,
|
|
Vector<int> &r_masked_face_indices,
|
|
Vector<int> &r_loop_starts,
|
|
uint *r_edges_add_num,
|
|
uint *r_faces_add_num,
|
|
uint *r_loops_add_num)
|
|
{
|
|
BLI_assert(mesh->verts_num == vertex_mask.size());
|
|
|
|
/* Can't really know ahead of time how much space to use exactly. Estimate limit instead. */
|
|
/* NOTE: this reserve can only lift the capacity if there are ngons, which get split. */
|
|
r_masked_face_indices.reserve(r_masked_face_indices.size() + verts_add_num);
|
|
r_loop_starts.reserve(r_loop_starts.size() + verts_add_num);
|
|
const blender::OffsetIndices faces = mesh->faces();
|
|
const Span<int> corner_verts = mesh->corner_verts();
|
|
|
|
uint edges_add_num = 0;
|
|
uint faces_add_num = 0;
|
|
uint loops_add_num = 0;
|
|
for (int i : IndexRange(mesh->faces_num)) {
|
|
const blender::IndexRange face_src = faces[i];
|
|
|
|
int in_count = 0;
|
|
int start = -1;
|
|
int dst_totloop = -1;
|
|
const Span<int> face_verts_src = corner_verts.slice(face_src);
|
|
for (const int j : face_verts_src.index_range()) {
|
|
const int vert_i = face_verts_src[j];
|
|
if (vertex_mask[vert_i]) {
|
|
in_count++;
|
|
}
|
|
else if (start == -1) {
|
|
start = j;
|
|
}
|
|
}
|
|
if (0 < in_count && in_count < face_src.size()) {
|
|
/* Ring search starting at a vertex which is not included in the mask. */
|
|
int last_corner_vert = face_verts_src[start];
|
|
bool v_loop_in_mask_last = vertex_mask[last_corner_vert];
|
|
for (const int j : face_verts_src.index_range()) {
|
|
const int corner_vert = face_verts_src[(start + 1 + j) % face_src.size()];
|
|
const bool v_loop_in_mask = vertex_mask[corner_vert];
|
|
if (v_loop_in_mask && !v_loop_in_mask_last) {
|
|
dst_totloop = 3;
|
|
}
|
|
else if (!v_loop_in_mask && v_loop_in_mask_last) {
|
|
BLI_assert(dst_totloop > 2);
|
|
r_masked_face_indices.append(i);
|
|
r_loop_starts.append(loops_masked_num + loops_add_num);
|
|
loops_add_num += dst_totloop;
|
|
faces_add_num++;
|
|
edges_add_num++;
|
|
dst_totloop = -1;
|
|
}
|
|
else if (v_loop_in_mask && v_loop_in_mask_last) {
|
|
BLI_assert(dst_totloop > 2);
|
|
dst_totloop++;
|
|
}
|
|
last_corner_vert = corner_vert;
|
|
v_loop_in_mask_last = v_loop_in_mask;
|
|
}
|
|
}
|
|
}
|
|
|
|
*r_edges_add_num = edges_add_num;
|
|
*r_faces_add_num = faces_add_num;
|
|
*r_loops_add_num = loops_add_num;
|
|
}
|
|
|
|
static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh,
|
|
Mesh &dst_mesh,
|
|
Span<int> vertex_map)
|
|
{
|
|
BLI_assert(src_mesh.verts_num == vertex_map.size());
|
|
for (const int i_src : vertex_map.index_range()) {
|
|
const int i_dst = vertex_map[i_src];
|
|
if (i_dst == -1) {
|
|
continue;
|
|
}
|
|
|
|
CustomData_copy_data(&src_mesh.vert_data, &dst_mesh.vert_data, i_src, i_dst, 1);
|
|
}
|
|
}
|
|
|
|
static float get_interp_factor_from_vgroup(
|
|
const MDeformVert *dvert, int defgrp_index, float threshold, uint v1, uint v2)
|
|
{
|
|
/* NOTE: this calculation is done twice for every vertex,
|
|
* instead of storing it the first time and then reusing it. */
|
|
float value1 = BKE_defvert_find_weight(&dvert[v1], defgrp_index);
|
|
float value2 = BKE_defvert_find_weight(&dvert[v2], defgrp_index);
|
|
return (threshold - value1) / (value2 - value1);
|
|
}
|
|
|
|
static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh,
|
|
Mesh &dst_mesh,
|
|
Span<bool> vertex_mask,
|
|
Span<int> vertex_map,
|
|
const MDeformVert *dvert,
|
|
int defgrp_index,
|
|
float threshold,
|
|
uint edges_masked_num,
|
|
uint verts_add_num,
|
|
MutableSpan<int> r_edge_map)
|
|
{
|
|
BLI_assert(src_mesh.verts_num == vertex_mask.size());
|
|
BLI_assert(src_mesh.edges_num == r_edge_map.size());
|
|
const Span<int2> src_edges = src_mesh.edges();
|
|
MutableSpan<int2> dst_edges = dst_mesh.edges_for_write();
|
|
|
|
uint vert_index = dst_mesh.verts_num - verts_add_num;
|
|
uint edge_index = edges_masked_num - verts_add_num;
|
|
for (int i_src : IndexRange(src_mesh.edges_num)) {
|
|
if (r_edge_map[i_src] != -1) {
|
|
int i_dst = r_edge_map[i_src];
|
|
if (i_dst == -2) {
|
|
i_dst = edge_index;
|
|
}
|
|
const int2 &e_src = src_edges[i_src];
|
|
int2 &e_dst = dst_edges[i_dst];
|
|
|
|
CustomData_copy_data(&src_mesh.edge_data, &dst_mesh.edge_data, i_src, i_dst, 1);
|
|
e_dst = e_src;
|
|
e_dst[0] = vertex_map[e_src[0]];
|
|
e_dst[1] = vertex_map[e_src[1]];
|
|
}
|
|
if (r_edge_map[i_src] == -2) {
|
|
const int i_dst = edge_index++;
|
|
r_edge_map[i_src] = i_dst;
|
|
const int2 &e_src = src_edges[i_src];
|
|
/* Cut destination edge and make v1 the new vertex. */
|
|
int2 &e_dst = dst_edges[i_dst];
|
|
if (!vertex_mask[e_src[0]]) {
|
|
e_dst[0] = vert_index;
|
|
}
|
|
else {
|
|
BLI_assert(!vertex_mask[e_src[1]]);
|
|
e_dst[1] = e_dst[0];
|
|
e_dst[0] = vert_index;
|
|
}
|
|
/* Create the new vertex. */
|
|
float fac = get_interp_factor_from_vgroup(
|
|
dvert, defgrp_index, threshold, e_src[0], e_src[1]);
|
|
|
|
float weights[2] = {1.0f - fac, fac};
|
|
CustomData_interp(&src_mesh.vert_data,
|
|
&dst_mesh.vert_data,
|
|
(int *)&e_src[0],
|
|
weights,
|
|
nullptr,
|
|
2,
|
|
vert_index);
|
|
vert_index++;
|
|
}
|
|
}
|
|
BLI_assert(vert_index == dst_mesh.verts_num);
|
|
BLI_assert(edge_index == edges_masked_num);
|
|
}
|
|
|
|
static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
|
|
Mesh &dst_mesh,
|
|
Span<int> vertex_map,
|
|
Span<int> edge_map)
|
|
{
|
|
const Span<int2> src_edges = src_mesh.edges();
|
|
MutableSpan<int2> dst_edges = dst_mesh.edges_for_write();
|
|
|
|
BLI_assert(src_mesh.verts_num == vertex_map.size());
|
|
BLI_assert(src_mesh.edges_num == edge_map.size());
|
|
for (const int i_src : IndexRange(src_mesh.edges_num)) {
|
|
const int i_dst = edge_map[i_src];
|
|
if (ELEM(i_dst, -1, -2)) {
|
|
continue;
|
|
}
|
|
|
|
CustomData_copy_data(&src_mesh.edge_data, &dst_mesh.edge_data, i_src, i_dst, 1);
|
|
dst_edges[i_dst][0] = vertex_map[src_edges[i_src][0]];
|
|
dst_edges[i_dst][1] = vertex_map[src_edges[i_src][1]];
|
|
}
|
|
}
|
|
|
|
static void copy_masked_faces_to_new_mesh(const Mesh &src_mesh,
|
|
Mesh &dst_mesh,
|
|
Span<int> vertex_map,
|
|
Span<int> edge_map,
|
|
Span<int> masked_face_indices,
|
|
Span<int> new_loop_starts,
|
|
int faces_masked_num)
|
|
{
|
|
const blender::OffsetIndices src_faces = src_mesh.faces();
|
|
MutableSpan<int> dst_face_offsets = dst_mesh.face_offsets_for_write();
|
|
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
|
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
|
MutableSpan<int> dst_corner_verts = dst_mesh.corner_verts_for_write();
|
|
MutableSpan<int> dst_corner_edges = dst_mesh.corner_edges_for_write();
|
|
|
|
for (const int i_dst : IndexRange(faces_masked_num)) {
|
|
const int i_src = masked_face_indices[i_dst];
|
|
const blender::IndexRange src_face = src_faces[i_src];
|
|
|
|
dst_face_offsets[i_dst] = new_loop_starts[i_dst];
|
|
|
|
CustomData_copy_data(&src_mesh.face_data, &dst_mesh.face_data, i_src, i_dst, 1);
|
|
CustomData_copy_data(&src_mesh.corner_data,
|
|
&dst_mesh.corner_data,
|
|
src_face.start(),
|
|
dst_face_offsets[i_dst],
|
|
src_face.size());
|
|
|
|
for (int i : IndexRange(src_face.size())) {
|
|
dst_corner_verts[new_loop_starts[i_dst] + i] = vertex_map[src_corner_verts[src_face[i]]];
|
|
dst_corner_edges[new_loop_starts[i_dst] + i] = edge_map[src_corner_edges[src_face[i]]];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void add_interpolated_faces_to_new_mesh(const Mesh &src_mesh,
|
|
Mesh &dst_mesh,
|
|
Span<bool> vertex_mask,
|
|
Span<int> vertex_map,
|
|
Span<int> edge_map,
|
|
const MDeformVert *dvert,
|
|
int defgrp_index,
|
|
float threshold,
|
|
Span<int> masked_face_indices,
|
|
Span<int> new_loop_starts,
|
|
int faces_masked_num,
|
|
int edges_add_num)
|
|
{
|
|
const blender::OffsetIndices src_faces = src_mesh.faces();
|
|
MutableSpan<int> dst_face_offsets = dst_mesh.face_offsets_for_write();
|
|
MutableSpan<int2> dst_edges = dst_mesh.edges_for_write();
|
|
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
|
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
|
MutableSpan<int> dst_corner_verts = dst_mesh.corner_verts_for_write();
|
|
MutableSpan<int> dst_corner_edges = dst_mesh.corner_edges_for_write();
|
|
|
|
int edge_index = dst_mesh.edges_num - edges_add_num;
|
|
int sub_face_index = 0;
|
|
int last_i_src = -1;
|
|
for (const int i_dst :
|
|
IndexRange(faces_masked_num, masked_face_indices.size() - faces_masked_num))
|
|
{
|
|
const int i_src = masked_face_indices[i_dst];
|
|
if (i_src == last_i_src) {
|
|
sub_face_index++;
|
|
}
|
|
else {
|
|
sub_face_index = 0;
|
|
last_i_src = i_src;
|
|
}
|
|
|
|
const blender::IndexRange src_face = src_faces[i_src];
|
|
const int i_ml_src = src_face.start();
|
|
int i_ml_dst = new_loop_starts[i_dst];
|
|
CustomData_copy_data(&src_mesh.face_data, &dst_mesh.face_data, i_src, i_dst, 1);
|
|
|
|
dst_face_offsets[i_dst] = i_ml_dst;
|
|
|
|
/* Ring search starting at a vertex which is not included in the mask. */
|
|
int start = -sub_face_index - 1;
|
|
bool skip = false;
|
|
const Span<int> face_verts_src = src_corner_verts.slice(src_face);
|
|
const Span<int> face_edges_src = src_corner_edges.slice(src_face);
|
|
for (const int j : face_verts_src.index_range()) {
|
|
if (!vertex_mask[face_verts_src[j]]) {
|
|
if (start == -1) {
|
|
start = j;
|
|
break;
|
|
}
|
|
if (!skip) {
|
|
skip = true;
|
|
}
|
|
}
|
|
else if (skip) {
|
|
skip = false;
|
|
start++;
|
|
}
|
|
}
|
|
|
|
BLI_assert(start >= 0);
|
|
BLI_assert(edge_index < dst_mesh.edges_num);
|
|
|
|
int last_index = start;
|
|
bool v_loop_in_mask_last = vertex_mask[face_verts_src[last_index]];
|
|
for (const int j : face_verts_src.index_range()) {
|
|
const int index = (start + 1 + j) % src_face.size();
|
|
const bool v_loop_in_mask = vertex_mask[face_verts_src[index]];
|
|
if (v_loop_in_mask && !v_loop_in_mask_last) {
|
|
/* Start new cut. */
|
|
float fac = get_interp_factor_from_vgroup(
|
|
dvert, defgrp_index, threshold, face_verts_src[last_index], face_verts_src[index]);
|
|
float weights[2] = {1.0f - fac, fac};
|
|
int indices[2] = {i_ml_src + last_index, i_ml_src + index};
|
|
CustomData_interp(
|
|
&src_mesh.corner_data, &dst_mesh.corner_data, indices, weights, nullptr, 2, i_ml_dst);
|
|
dst_corner_edges[i_ml_dst] = edge_map[face_edges_src[last_index]];
|
|
dst_corner_verts[i_ml_dst] = dst_edges[dst_corner_edges[i_ml_dst]][0];
|
|
i_ml_dst++;
|
|
|
|
CustomData_copy_data(
|
|
&src_mesh.corner_data, &dst_mesh.corner_data, i_ml_src + index, i_ml_dst, 1);
|
|
dst_corner_verts[i_ml_dst] = vertex_map[face_verts_src[index]];
|
|
dst_corner_edges[i_ml_dst] = edge_map[face_edges_src[index]];
|
|
i_ml_dst++;
|
|
}
|
|
else if (!v_loop_in_mask && v_loop_in_mask_last) {
|
|
BLI_assert(i_ml_dst != dst_face_offsets[i_dst]);
|
|
/* End active cut. */
|
|
float fac = get_interp_factor_from_vgroup(
|
|
dvert, defgrp_index, threshold, face_verts_src[last_index], face_verts_src[index]);
|
|
float weights[2] = {1.0f - fac, fac};
|
|
int indices[2] = {i_ml_src + last_index, i_ml_src + index};
|
|
CustomData_interp(
|
|
&src_mesh.corner_data, &dst_mesh.corner_data, indices, weights, nullptr, 2, i_ml_dst);
|
|
dst_corner_edges[i_ml_dst] = edge_index;
|
|
dst_corner_verts[i_ml_dst] = dst_edges[edge_map[face_edges_src[last_index]]][0];
|
|
|
|
/* Create closing edge. */
|
|
int2 &cut_edge = dst_edges[edge_index];
|
|
cut_edge[0] = dst_corner_verts[dst_face_offsets[i_dst]];
|
|
cut_edge[1] = dst_corner_verts[i_ml_dst];
|
|
BLI_assert(cut_edge[0] != cut_edge[1]);
|
|
edge_index++;
|
|
i_ml_dst++;
|
|
|
|
/* Only handle one of the cuts per iteration. */
|
|
break;
|
|
}
|
|
else if (v_loop_in_mask && v_loop_in_mask_last) {
|
|
BLI_assert(i_ml_dst != dst_face_offsets[i_dst]);
|
|
/* Extend active face. */
|
|
CustomData_copy_data(
|
|
&src_mesh.corner_data, &dst_mesh.corner_data, i_ml_src + index, i_ml_dst, 1);
|
|
dst_corner_verts[i_ml_dst] = vertex_map[face_verts_src[index]];
|
|
dst_corner_edges[i_ml_dst] = edge_map[face_edges_src[index]];
|
|
i_ml_dst++;
|
|
}
|
|
last_index = index;
|
|
v_loop_in_mask_last = v_loop_in_mask;
|
|
}
|
|
}
|
|
BLI_assert(edge_index == dst_mesh.edges_num);
|
|
}
|
|
|
|
/* Components of the algorithm:
|
|
* 1. Figure out which vertices should be present in the output mesh.
|
|
* 2. Find edges and faces only using those vertices.
|
|
* 3. Create a new mesh that only uses the found vertices, edges and faces.
|
|
*/
|
|
static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext * /*ctx*/, Mesh *mesh)
|
|
{
|
|
MaskModifierData *mmd = reinterpret_cast<MaskModifierData *>(md);
|
|
const bool invert_mask = mmd->flag & MOD_MASK_INV;
|
|
const bool use_interpolation = mmd->mode == MOD_MASK_MODE_VGROUP &&
|
|
(mmd->flag & MOD_MASK_SMOOTH);
|
|
|
|
/* Return empty or input mesh when there are no vertex groups. */
|
|
const Span<MDeformVert> dverts = mesh->deform_verts();
|
|
if (dverts.is_empty()) {
|
|
return invert_mask ? mesh : BKE_mesh_new_nomain_from_template(mesh, 0, 0, 0, 0);
|
|
}
|
|
|
|
/* Quick test to see if we can return early. */
|
|
if (!ELEM(mmd->mode, MOD_MASK_MODE_ARM, MOD_MASK_MODE_VGROUP) || (mesh->verts_num == 0) ||
|
|
BLI_listbase_is_empty(&mesh->vertex_group_names))
|
|
{
|
|
return mesh;
|
|
}
|
|
|
|
int defgrp_index = -1;
|
|
|
|
Array<bool> vertex_mask;
|
|
if (mmd->mode == MOD_MASK_MODE_ARM) {
|
|
Object *armature_ob = mmd->ob_arm;
|
|
|
|
/* Return input mesh if there is no armature with bones. */
|
|
if (ELEM(nullptr, armature_ob, armature_ob->pose)) {
|
|
return mesh;
|
|
}
|
|
|
|
vertex_mask = Array<bool>(mesh->verts_num);
|
|
compute_vertex_mask__armature_mode(
|
|
dverts.data(), mesh, armature_ob, mmd->threshold, vertex_mask);
|
|
}
|
|
else {
|
|
BLI_assert(mmd->mode == MOD_MASK_MODE_VGROUP);
|
|
defgrp_index = BKE_id_defgroup_name_index(&mesh->id, mmd->vgroup);
|
|
|
|
/* Return input mesh if the vertex group does not exist. */
|
|
if (defgrp_index == -1) {
|
|
return mesh;
|
|
}
|
|
|
|
vertex_mask = Array<bool>(mesh->verts_num);
|
|
compute_vertex_mask__vertex_group_mode(
|
|
dverts.data(), defgrp_index, mmd->threshold, vertex_mask);
|
|
}
|
|
|
|
if (invert_mask) {
|
|
blender::array_utils::invert_booleans(vertex_mask);
|
|
}
|
|
|
|
Array<int> vertex_map(mesh->verts_num);
|
|
uint verts_masked_num;
|
|
compute_masked_verts(vertex_mask, vertex_map, &verts_masked_num);
|
|
|
|
Array<int> edge_map(mesh->edges_num);
|
|
uint edges_masked_num;
|
|
uint verts_add_num;
|
|
if (use_interpolation) {
|
|
computed_masked_edges_smooth(mesh, vertex_mask, edge_map, &edges_masked_num, &verts_add_num);
|
|
}
|
|
else {
|
|
computed_masked_edges(mesh, vertex_mask, edge_map, &edges_masked_num);
|
|
verts_add_num = 0;
|
|
}
|
|
|
|
Vector<int> masked_face_indices;
|
|
Vector<int> new_loop_starts;
|
|
uint faces_masked_num;
|
|
uint loops_masked_num;
|
|
computed_masked_faces(mesh,
|
|
vertex_mask,
|
|
masked_face_indices,
|
|
new_loop_starts,
|
|
&faces_masked_num,
|
|
&loops_masked_num);
|
|
|
|
uint edges_add_num = 0;
|
|
uint faces_add_num = 0;
|
|
uint loops_add_num = 0;
|
|
if (use_interpolation) {
|
|
compute_interpolated_faces(mesh,
|
|
vertex_mask,
|
|
verts_add_num,
|
|
loops_masked_num,
|
|
masked_face_indices,
|
|
new_loop_starts,
|
|
&edges_add_num,
|
|
&faces_add_num,
|
|
&loops_add_num);
|
|
}
|
|
|
|
Mesh *result = BKE_mesh_new_nomain_from_template(mesh,
|
|
verts_masked_num + verts_add_num,
|
|
edges_masked_num + edges_add_num,
|
|
faces_masked_num + faces_add_num,
|
|
loops_masked_num + loops_add_num);
|
|
|
|
copy_masked_verts_to_new_mesh(*mesh, *result, vertex_map);
|
|
if (use_interpolation) {
|
|
add_interp_verts_copy_edges_to_new_mesh(*mesh,
|
|
*result,
|
|
vertex_mask,
|
|
vertex_map,
|
|
dverts.data(),
|
|
defgrp_index,
|
|
mmd->threshold,
|
|
edges_masked_num,
|
|
verts_add_num,
|
|
edge_map);
|
|
}
|
|
else {
|
|
copy_masked_edges_to_new_mesh(*mesh, *result, vertex_map, edge_map);
|
|
}
|
|
copy_masked_faces_to_new_mesh(*mesh,
|
|
*result,
|
|
vertex_map,
|
|
edge_map,
|
|
masked_face_indices,
|
|
new_loop_starts,
|
|
faces_masked_num);
|
|
if (use_interpolation) {
|
|
add_interpolated_faces_to_new_mesh(*mesh,
|
|
*result,
|
|
vertex_mask,
|
|
vertex_map,
|
|
edge_map,
|
|
dverts.data(),
|
|
defgrp_index,
|
|
mmd->threshold,
|
|
masked_face_indices,
|
|
new_loop_starts,
|
|
faces_masked_num,
|
|
edges_add_num);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
|
|
{
|
|
MaskModifierData *mmd = reinterpret_cast<MaskModifierData *>(md);
|
|
|
|
/* The object type check is only needed here in case we have a placeholder
|
|
* object assigned (because the library containing the armature is missing).
|
|
*
|
|
* In other cases it should be impossible to have a type mismatch.
|
|
*/
|
|
return mmd->ob_arm && mmd->ob_arm->type != OB_ARMATURE;
|
|
}
|
|
|
|
static void panel_draw(const bContext * /*C*/, Panel *panel)
|
|
{
|
|
uiLayout *sub, *row;
|
|
uiLayout *layout = panel->layout;
|
|
|
|
PointerRNA ob_ptr;
|
|
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
|
|
|
|
int mode = RNA_enum_get(ptr, "mode");
|
|
|
|
layout->prop(ptr, "mode", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
|
|
|
|
uiLayoutSetPropSep(layout, true);
|
|
|
|
if (mode == MOD_MASK_MODE_ARM) {
|
|
row = &layout->row(true);
|
|
row->prop(ptr, "armature", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
|
sub = &row->row(true);
|
|
uiLayoutSetPropDecorate(sub, false);
|
|
sub->prop(ptr, "invert_vertex_group", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
|
|
}
|
|
else if (mode == MOD_MASK_MODE_VGROUP) {
|
|
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", std::nullopt);
|
|
layout->prop(ptr, "use_smooth", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
|
}
|
|
|
|
layout->prop(ptr, "threshold", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
|
|
|
modifier_error_message_draw(layout, ptr);
|
|
}
|
|
|
|
static void panel_register(ARegionType *region_type)
|
|
{
|
|
modifier_panel_register(region_type, eModifierType_Mask, panel_draw);
|
|
}
|
|
|
|
ModifierTypeInfo modifierType_Mask = {
|
|
/*idname*/ "Mask",
|
|
/*name*/ N_("Mask"),
|
|
/*struct_name*/ "MaskModifierData",
|
|
/*struct_size*/ sizeof(MaskModifierData),
|
|
/*srna*/ &RNA_MaskModifier,
|
|
/*type*/ ModifierTypeType::Nonconstructive,
|
|
/*flags*/
|
|
(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping |
|
|
eModifierTypeFlag_SupportsEditmode),
|
|
/*icon*/ ICON_MOD_MASK,
|
|
|
|
/*copy_data*/ BKE_modifier_copydata_generic,
|
|
|
|
/*deform_verts*/ nullptr,
|
|
/*deform_matrices*/ nullptr,
|
|
/*deform_verts_EM*/ nullptr,
|
|
/*deform_matrices_EM*/ nullptr,
|
|
/*modify_mesh*/ modify_mesh,
|
|
/*modify_geometry_set*/ nullptr,
|
|
|
|
/*init_data*/ init_data,
|
|
/*required_data_mask*/ required_data_mask,
|
|
/*free_data*/ nullptr,
|
|
/*is_disabled*/ is_disabled,
|
|
/*update_depsgraph*/ update_depsgraph,
|
|
/*depends_on_time*/ nullptr,
|
|
/*depends_on_normals*/ nullptr,
|
|
/*foreach_ID_link*/ foreach_ID_link,
|
|
/*foreach_tex_link*/ nullptr,
|
|
/*free_runtime_data*/ nullptr,
|
|
/*panel_register*/ panel_register,
|
|
/*blend_write*/ nullptr,
|
|
/*blend_read*/ nullptr,
|
|
/*foreach_cache*/ nullptr,
|
|
};
|