This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/editors/mesh/mesh_data.cc
Hans Goudey 1dc57a89e9 Mesh: Move functions to C++ header
Refactoring mesh code, it has become clear that local cleanups and
simplifications are limited by the need to keep a C public API for
mesh functions. This change makes code more obvious and makes further
refactoring much easier.

- Add a new `BKE_mesh.hh` header for a C++ only mesh API
- Introduce a new `blender::bke::mesh` namespace, documented here:
  https://wiki.blender.org/wiki/Source/Objects/Mesh#Namespaces
- Move some functions to the new namespace, cleaning up their arguments
- Move code to `Array` and `float3` where necessary to use the new API
- Define existing inline mesh data access functions to the new header
- Keep some C API functions where necessary because of RNA
- Move all C++ files to use the new header, which includes the old one

In the future it may make sense to split up `BKE_mesh.hh` more, but for
now keeping the same name as the existing header keeps things simple.

Pull Request: blender/blender#105416
2023-03-12 22:29:15 +01:00

1499 lines
41 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2009 Blender Foundation. All rights reserved. */
/** \file
* \ingroup edmesh
*/
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_view3d_types.h"
#include "BLI_array.hh"
#include "BLI_index_mask_ops.hh"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BKE_attribute.h"
#include "BKE_attribute.hh"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_mesh.hh"
#include "BKE_mesh_runtime.h"
#include "BKE_report.h"
#include "DEG_depsgraph.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_prototypes.h"
#include "WM_api.h"
#include "WM_types.h"
#include "BLT_translation.h"
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_paint.h"
#include "ED_screen.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
#include "GEO_mesh_split_edges.hh"
#include "mesh_intern.h" /* own include */
using blender::Array;
using blender::float2;
using blender::float3;
using blender::MutableSpan;
using blender::Span;
static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_tot)
{
CustomData *data;
BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : nullptr;
int tot;
switch (htype) {
case BM_VERT:
if (bm) {
data = &bm->vdata;
tot = bm->totvert;
}
else {
data = &me->vdata;
tot = me->totvert;
}
break;
case BM_EDGE:
if (bm) {
data = &bm->edata;
tot = bm->totedge;
}
else {
data = &me->edata;
tot = me->totedge;
}
break;
case BM_LOOP:
if (bm) {
data = &bm->ldata;
tot = bm->totloop;
}
else {
data = &me->ldata;
tot = me->totloop;
}
break;
case BM_FACE:
if (bm) {
data = &bm->pdata;
tot = bm->totface;
}
else {
data = &me->pdata;
tot = me->totpoly;
}
break;
default:
BLI_assert(0);
tot = 0;
data = nullptr;
break;
}
*r_tot = tot;
return data;
}
#define GET_CD_DATA(me, data) ((me)->edit_mesh ? &(me)->edit_mesh->bm->data : &(me)->data)
static void mesh_uv_reset_array(float **fuv, const int len)
{
if (len == 3) {
fuv[0][0] = 0.0;
fuv[0][1] = 0.0;
fuv[1][0] = 1.0;
fuv[1][1] = 0.0;
fuv[2][0] = 1.0;
fuv[2][1] = 1.0;
}
else if (len == 4) {
fuv[0][0] = 0.0;
fuv[0][1] = 0.0;
fuv[1][0] = 1.0;
fuv[1][1] = 0.0;
fuv[2][0] = 1.0;
fuv[2][1] = 1.0;
fuv[3][0] = 0.0;
fuv[3][1] = 1.0;
/* Make sure we ignore 2-sided faces. */
}
else if (len > 2) {
float fac = 0.0f, dfac = 1.0f / float(len);
dfac *= float(M_PI) * 2.0f;
for (int i = 0; i < len; i++) {
fuv[i][0] = 0.5f * sinf(fac) + 0.5f;
fuv[i][1] = 0.5f * cosf(fac) + 0.5f;
fac += dfac;
}
}
}
static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset)
{
Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(f->len);
BMIter liter;
BMLoop *l;
int i;
BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, i) {
fuv[i] = ((float *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset));
}
mesh_uv_reset_array(fuv.data(), f->len);
}
static void mesh_uv_reset_mface(const MPoly *poly, float2 *mloopuv)
{
Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(poly->totloop);
for (int i = 0; i < poly->totloop; i++) {
fuv[i] = mloopuv[poly->loopstart + i];
}
mesh_uv_reset_array(fuv.data(), poly->totloop);
}
void ED_mesh_uv_loop_reset_ex(Mesh *me, const int layernum)
{
BMEditMesh *em = me->edit_mesh;
if (em) {
/* Collect BMesh UVs */
const int cd_loop_uv_offset = CustomData_get_n_offset(
&em->bm->ldata, CD_PROP_FLOAT2, layernum);
BMFace *efa;
BMIter iter;
BLI_assert(cd_loop_uv_offset >= 0);
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
continue;
}
mesh_uv_reset_bmface(efa, cd_loop_uv_offset);
}
}
else {
/* Collect Mesh UVs */
BLI_assert(CustomData_has_layer(&me->ldata, CD_PROP_FLOAT2));
float2 *mloopuv = static_cast<float2 *>(
CustomData_get_layer_n_for_write(&me->ldata, CD_PROP_FLOAT2, layernum, me->totloop));
const blender::Span<MPoly> polys = me->polys();
for (const int i : polys.index_range()) {
mesh_uv_reset_mface(&polys[i], mloopuv);
}
}
DEG_id_tag_update(&me->id, 0);
}
void ED_mesh_uv_loop_reset(bContext *C, Mesh *me)
{
/* could be ldata or pdata */
CustomData *ldata = GET_CD_DATA(me, ldata);
const int layernum = CustomData_get_active_layer(ldata, CD_PROP_FLOAT2);
ED_mesh_uv_loop_reset_ex(me, layernum);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
}
int ED_mesh_uv_add(
Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports)
{
/* NOTE: keep in sync with #ED_mesh_color_add. */
BMEditMesh *em;
int layernum_dst;
if (!name) {
name = DATA_("UVMap");
}
char unique_name[MAX_CUSTOMDATA_LAYER_NAME];
BKE_id_attribute_calc_unique_name(&me->id, name, unique_name);
bool is_init = false;
if (me->edit_mesh) {
em = me->edit_mesh;
layernum_dst = CustomData_number_of_layers(&em->bm->ldata, CD_PROP_FLOAT2);
if (layernum_dst >= MAX_MTFACE) {
BKE_reportf(reports, RPT_WARNING, "Cannot add more than %i UV maps", MAX_MTFACE);
return -1;
}
BM_data_layer_add_named(em->bm, &em->bm->ldata, CD_PROP_FLOAT2, unique_name);
BM_uv_map_ensure_select_and_pin_attrs(em->bm);
/* copy data from active UV */
if (layernum_dst && do_init) {
const int layernum_src = CustomData_get_active_layer(&em->bm->ldata, CD_PROP_FLOAT2);
BM_data_layer_copy(em->bm, &em->bm->ldata, CD_PROP_FLOAT2, layernum_src, layernum_dst);
is_init = true;
}
if (active_set || layernum_dst == 0) {
CustomData_set_layer_active(&em->bm->ldata, CD_PROP_FLOAT2, layernum_dst);
}
}
else {
layernum_dst = CustomData_number_of_layers(&me->ldata, CD_PROP_FLOAT2);
if (layernum_dst >= MAX_MTFACE) {
BKE_reportf(reports, RPT_WARNING, "Cannot add more than %i UV maps", MAX_MTFACE);
return -1;
}
if (CustomData_has_layer(&me->ldata, CD_PROP_FLOAT2) && do_init) {
CustomData_add_layer_named(&me->ldata,
CD_PROP_FLOAT2,
CD_DUPLICATE,
const_cast<float2 *>(static_cast<const float2 *>(
CustomData_get_layer(&me->ldata, CD_PROP_FLOAT2))),
me->totloop,
unique_name);
is_init = true;
}
else {
CustomData_add_layer_named(
&me->ldata, CD_PROP_FLOAT2, CD_SET_DEFAULT, nullptr, me->totloop, unique_name);
}
if (active_set || layernum_dst == 0) {
CustomData_set_layer_active(&me->ldata, CD_PROP_FLOAT2, layernum_dst);
}
}
/* don't overwrite our copied coords */
if (!is_init && do_init) {
ED_mesh_uv_loop_reset_ex(me, layernum_dst);
}
DEG_id_tag_update(&me->id, 0);
WM_main_add_notifier(NC_GEOM | ND_DATA, me);
return layernum_dst;
}
static const bool *mesh_loop_boolean_custom_data_get_by_name(const Mesh &mesh, const char *name)
{
return static_cast<const bool *>(CustomData_get_layer_named(&mesh.ldata, CD_PROP_BOOL, name));
}
const bool *ED_mesh_uv_map_vert_select_layer_get(const Mesh *mesh, const int uv_index)
{
using namespace blender::bke;
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const char *uv_name = CustomData_get_layer_name(&mesh->ldata, CD_PROP_FLOAT2, uv_index);
return mesh_loop_boolean_custom_data_get_by_name(
*mesh, BKE_uv_map_vert_select_name_get(uv_name, buffer));
}
/* UV map edge selections are stored on face corners (loops) and not on edges
* because we need selections per face edge, even when the edge is split in UV space. */
const bool *ED_mesh_uv_map_edge_select_layer_get(const Mesh *mesh, const int uv_index)
{
using namespace blender::bke;
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const char *uv_name = CustomData_get_layer_name(&mesh->ldata, CD_PROP_FLOAT2, uv_index);
return mesh_loop_boolean_custom_data_get_by_name(
*mesh, BKE_uv_map_edge_select_name_get(uv_name, buffer));
}
const bool *ED_mesh_uv_map_pin_layer_get(const Mesh *mesh, const int uv_index)
{
using namespace blender::bke;
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const char *uv_name = CustomData_get_layer_name(&mesh->ldata, CD_PROP_FLOAT2, uv_index);
return mesh_loop_boolean_custom_data_get_by_name(*mesh,
BKE_uv_map_pin_name_get(uv_name, buffer));
}
static bool *ensure_corner_boolean_attribute(Mesh &mesh, const blender::StringRefNull name)
{
bool *data = static_cast<bool *>(
CustomData_get_layer_named_for_write(&mesh.ldata, CD_PROP_BOOL, name.c_str(), mesh.totloop));
if (!data) {
data = static_cast<bool *>(CustomData_add_layer_named(
&mesh.ldata, CD_PROP_BOOL, CD_SET_DEFAULT, nullptr, mesh.totpoly, name.c_str()));
}
return data;
}
bool *ED_mesh_uv_map_vert_select_layer_ensure(Mesh *mesh, const int uv_index)
{
using namespace blender::bke;
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const char *uv_name = CustomData_get_layer_name(&mesh->ldata, CD_PROP_FLOAT2, uv_index);
return ensure_corner_boolean_attribute(*mesh, BKE_uv_map_vert_select_name_get(uv_name, buffer));
}
bool *ED_mesh_uv_map_edge_select_layer_ensure(Mesh *mesh, const int uv_index)
{
using namespace blender::bke;
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const char *uv_name = CustomData_get_layer_name(&mesh->ldata, CD_PROP_FLOAT2, uv_index);
return ensure_corner_boolean_attribute(*mesh, BKE_uv_map_edge_select_name_get(uv_name, buffer));
}
bool *ED_mesh_uv_map_pin_layer_ensure(Mesh *mesh, const int uv_index)
{
using namespace blender::bke;
char buffer[MAX_CUSTOMDATA_LAYER_NAME];
const char *uv_name = CustomData_get_layer_name(&mesh->ldata, CD_PROP_FLOAT2, uv_index);
return ensure_corner_boolean_attribute(*mesh, BKE_uv_map_pin_name_get(uv_name, buffer));
}
void ED_mesh_uv_ensure(Mesh *me, const char *name)
{
BMEditMesh *em;
int layernum_dst;
if (me->edit_mesh) {
em = me->edit_mesh;
layernum_dst = CustomData_number_of_layers(&em->bm->ldata, CD_PROP_FLOAT2);
if (layernum_dst == 0) {
ED_mesh_uv_add(me, name, true, true, nullptr);
}
}
else {
layernum_dst = CustomData_number_of_layers(&me->ldata, CD_PROP_FLOAT2);
if (layernum_dst == 0) {
ED_mesh_uv_add(me, name, true, true, nullptr);
}
}
}
int ED_mesh_color_add(
Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports)
{
/* If no name is supplied, provide a backwards compatible default. */
if (!name) {
name = "Col";
}
CustomDataLayer *layer = BKE_id_attribute_new(
&me->id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_CORNER, reports);
if (do_init) {
const char *active_name = me->active_color_attribute;
if (const CustomDataLayer *active_layer = BKE_id_attributes_color_find(&me->id, active_name)) {
if (const BMEditMesh *em = me->edit_mesh) {
BMesh &bm = *em->bm;
const int src_i = CustomData_get_named_layer(&bm.ldata, CD_PROP_BYTE_COLOR, active_name);
const int dst_i = CustomData_get_named_layer(&bm.ldata, CD_PROP_BYTE_COLOR, layer->name);
BM_data_layer_copy(&bm, &bm.ldata, CD_PROP_BYTE_COLOR, src_i, dst_i);
}
else {
memcpy(layer->data, active_layer->data, CustomData_get_elem_size(layer) * me->totloop);
}
}
}
if (active_set) {
BKE_id_attributes_active_color_set(&me->id, layer->name);
}
DEG_id_tag_update(&me->id, 0);
WM_main_add_notifier(NC_GEOM | ND_DATA, me);
int dummy;
const CustomData *data = mesh_customdata_get_type(me, BM_LOOP, &dummy);
return CustomData_get_named_layer(data, CD_PROP_BYTE_COLOR, layer->name);
}
bool ED_mesh_color_ensure(Mesh *me, const char *name)
{
using namespace blender;
BLI_assert(me->edit_mesh == nullptr);
if (me->attributes().contains(me->active_color_attribute)) {
return true;
}
char unique_name[MAX_CUSTOMDATA_LAYER_NAME];
BKE_id_attribute_calc_unique_name(&me->id, name, unique_name);
if (!me->attributes_for_write().add(
unique_name, ATTR_DOMAIN_CORNER, CD_PROP_BYTE_COLOR, bke::AttributeInitDefaultValue())) {
return false;
}
BKE_id_attributes_active_color_set(&me->id, unique_name);
BKE_id_attributes_default_color_set(&me->id, unique_name);
BKE_mesh_tessface_clear(me);
DEG_id_tag_update(&me->id, 0);
return true;
}
/*********************** General poll ************************/
static bool layers_poll(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr;
return (ob && !ID_IS_LINKED(ob) && !ID_IS_OVERRIDE_LIBRARY(ob) && ob->type == OB_MESH && data &&
!ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
}
/*********************** Sculpt Vertex colors operators ************************/
int ED_mesh_sculpt_color_add(Mesh *me, const char *name, const bool do_init, ReportList *reports)
{
/* If no name is supplied, provide a backwards compatible default. */
if (!name) {
name = "Color";
}
if (const CustomDataLayer *layer = BKE_id_attribute_find(
&me->id, me->active_color_attribute, CD_PROP_COLOR, ATTR_DOMAIN_POINT)) {
int dummy;
const CustomData *data = mesh_customdata_get_type(me, BM_LOOP, &dummy);
return CustomData_get_named_layer(data, CD_PROP_BYTE_COLOR, layer->name);
}
CustomDataLayer *layer = BKE_id_attribute_new(
&me->id, name, CD_PROP_COLOR, ATTR_DOMAIN_POINT, reports);
if (do_init) {
const char *active_name = me->active_color_attribute;
if (const CustomDataLayer *active_layer = BKE_id_attributes_color_find(&me->id, active_name)) {
if (const BMEditMesh *em = me->edit_mesh) {
BMesh &bm = *em->bm;
const int src_i = CustomData_get_named_layer(&bm.vdata, CD_PROP_COLOR, active_name);
const int dst_i = CustomData_get_named_layer(&bm.vdata, CD_PROP_COLOR, layer->name);
BM_data_layer_copy(&bm, &bm.vdata, CD_PROP_COLOR, src_i, dst_i);
}
else {
memcpy(layer->data, active_layer->data, CustomData_get_elem_size(layer) * me->totloop);
}
}
}
DEG_id_tag_update(&me->id, 0);
WM_main_add_notifier(NC_GEOM | ND_DATA, me);
int dummy;
const CustomData *data = mesh_customdata_get_type(me, BM_VERT, &dummy);
return CustomData_get_named_layer(data, CD_PROP_COLOR, layer->name);
}
/*********************** UV texture operators ************************/
static bool uv_texture_remove_poll(bContext *C)
{
if (!layers_poll(C)) {
return false;
}
Object *ob = ED_object_context(C);
Mesh *me = static_cast<Mesh *>(ob->data);
CustomData *ldata = GET_CD_DATA(me, ldata);
const int active = CustomData_get_active_layer(ldata, CD_PROP_FLOAT2);
if (active != -1) {
return true;
}
return false;
}
static int mesh_uv_texture_add_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
Mesh *me = static_cast<Mesh *>(ob->data);
if (ED_mesh_uv_add(me, nullptr, true, true, op->reports) == -1) {
return OPERATOR_CANCELLED;
}
if (ob->mode & OB_MODE_TEXTURE_PAINT) {
Scene *scene = CTX_data_scene(C);
ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr);
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr);
}
return OPERATOR_FINISHED;
}
void MESH_OT_uv_texture_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add UV Map";
ot->description = "Add UV map";
ot->idname = "MESH_OT_uv_texture_add";
/* api callbacks */
ot->poll = layers_poll;
ot->exec = mesh_uv_texture_add_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
Mesh *me = static_cast<Mesh *>(ob->data);
CustomData *ldata = GET_CD_DATA(me, ldata);
const char *name = CustomData_get_active_layer_name(ldata, CD_PROP_FLOAT2);
if (!BKE_id_attribute_remove(&me->id, name, op->reports)) {
return OPERATOR_CANCELLED;
}
if (ob->mode & OB_MODE_TEXTURE_PAINT) {
Scene *scene = CTX_data_scene(C);
ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr);
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr);
}
DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, me);
return OPERATOR_FINISHED;
}
void MESH_OT_uv_texture_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove UV Map";
ot->description = "Remove UV map";
ot->idname = "MESH_OT_uv_texture_remove";
/* api callbacks */
ot->poll = uv_texture_remove_poll;
ot->exec = mesh_uv_texture_remove_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* *** CustomData clear functions, we need an operator for each *** */
static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int type)
{
Mesh *me = ED_mesh_context(C);
int tot;
CustomData *data = mesh_customdata_get_type(me, htype, &tot);
BLI_assert(CustomData_layertype_is_singleton(type) == true);
if (CustomData_has_layer(data, type)) {
if (me->edit_mesh) {
BM_data_layer_free(me->edit_mesh->bm, data, type);
}
else {
CustomData_free_layers(data, type, tot);
}
DEG_id_tag_update(&me->id, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static int mesh_customdata_add_exec__internal(bContext *C, char htype, int type)
{
Mesh *mesh = ED_mesh_context(C);
int tot;
CustomData *data = mesh_customdata_get_type(mesh, htype, &tot);
BLI_assert(CustomData_layertype_is_singleton(type) == true);
if (mesh->edit_mesh) {
BM_data_layer_add(mesh->edit_mesh->bm, data, type);
}
else {
CustomData_add_layer(data, type, CD_SET_DEFAULT, nullptr, tot);
}
DEG_id_tag_update(&mesh->id, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh);
return CustomData_has_layer(data, type) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
/* Clear Mask */
static bool mesh_customdata_mask_clear_poll(bContext *C)
{
Object *ob = ED_object_context(C);
if (ob && ob->type == OB_MESH) {
Mesh *me = static_cast<Mesh *>(ob->data);
/* special case - can't run this if we're in sculpt mode */
if (ob->mode & OB_MODE_SCULPT) {
return false;
}
if (!ID_IS_LINKED(me) && !ID_IS_OVERRIDE_LIBRARY(me)) {
CustomData *data = GET_CD_DATA(me, vdata);
if (CustomData_has_layer(data, CD_PAINT_MASK)) {
return true;
}
data = GET_CD_DATA(me, ldata);
if (CustomData_has_layer(data, CD_GRID_PAINT_MASK)) {
return true;
}
}
}
return false;
}
static int mesh_customdata_mask_clear_exec(bContext *C, wmOperator * /*op*/)
{
int ret_a = mesh_customdata_clear_exec__internal(C, BM_VERT, CD_PAINT_MASK);
int ret_b = mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_GRID_PAINT_MASK);
if (ret_a == OPERATOR_FINISHED || ret_b == OPERATOR_FINISHED) {
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MESH_OT_customdata_mask_clear(wmOperatorType *ot)
{
/* NOTE: no create_mask yet */
/* identifiers */
ot->name = "Clear Sculpt Mask Data";
ot->idname = "MESH_OT_customdata_mask_clear";
ot->description = "Clear vertex sculpt masking data from the mesh";
/* api callbacks */
ot->exec = mesh_customdata_mask_clear_exec;
ot->poll = mesh_customdata_mask_clear_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/**
* Clear Skin
* \return -1 invalid state, 0 no skin, 1 has skin.
*/
static int mesh_customdata_skin_state(bContext *C)
{
Object *ob = ED_object_context(C);
if (ob && ob->type == OB_MESH) {
Mesh *me = static_cast<Mesh *>(ob->data);
if (!ID_IS_LINKED(me) && !ID_IS_OVERRIDE_LIBRARY(me)) {
CustomData *data = GET_CD_DATA(me, vdata);
return CustomData_has_layer(data, CD_MVERT_SKIN);
}
}
return -1;
}
static bool mesh_customdata_skin_add_poll(bContext *C)
{
return (mesh_customdata_skin_state(C) == 0);
}
static int mesh_customdata_skin_add_exec(bContext *C, wmOperator * /*op*/)
{
Object *ob = ED_object_context(C);
Mesh *me = static_cast<Mesh *>(ob->data);
BKE_mesh_ensure_skin_customdata(me);
DEG_id_tag_update(&me->id, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
return OPERATOR_FINISHED;
}
void MESH_OT_customdata_skin_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Skin Data";
ot->idname = "MESH_OT_customdata_skin_add";
ot->description = "Add a vertex skin layer";
/* api callbacks */
ot->exec = mesh_customdata_skin_add_exec;
ot->poll = mesh_customdata_skin_add_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static bool mesh_customdata_skin_clear_poll(bContext *C)
{
return (mesh_customdata_skin_state(C) == 1);
}
static int mesh_customdata_skin_clear_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_clear_exec__internal(C, BM_VERT, CD_MVERT_SKIN);
}
void MESH_OT_customdata_skin_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Skin Data";
ot->idname = "MESH_OT_customdata_skin_clear";
ot->description = "Clear vertex skin layer";
/* api callbacks */
ot->exec = mesh_customdata_skin_clear_exec;
ot->poll = mesh_customdata_skin_clear_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* Clear custom loop normals */
static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator * /*op*/)
{
using namespace blender;
Mesh *me = ED_mesh_context(C);
if (BKE_mesh_has_custom_loop_normals(me)) {
return OPERATOR_CANCELLED;
}
if (me->edit_mesh) {
BMesh &bm = *me->edit_mesh->bm;
/* Tag edges as sharp according to smooth threshold if needed,
* to preserve auto-smooth shading. */
if (me->flag & ME_AUTOSMOOTH) {
BM_edges_sharp_from_angle_set(&bm, me->smoothresh);
}
BM_data_layer_add(&bm, &bm.ldata, CD_CUSTOMLOOPNORMAL);
}
else {
/* Tag edges as sharp according to smooth threshold if needed,
* to preserve auto-smooth shading. */
if (me->flag & ME_AUTOSMOOTH) {
bke::MutableAttributeAccessor attributes = me->attributes_for_write();
bke::SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>(
"sharp_edge", ATTR_DOMAIN_EDGE);
const bool *sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, "sharp_face"));
bke::mesh::edges_sharp_from_angle_set(me->polys(),
me->loops(),
me->poly_normals(),
sharp_faces,
me->smoothresh,
sharp_edges.span);
sharp_edges.finish();
}
CustomData_add_layer(&me->ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, nullptr, me->totloop);
}
DEG_id_tag_update(&me->id, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
return OPERATOR_FINISHED;
}
void MESH_OT_customdata_custom_splitnormals_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Custom Split Normals Data";
ot->idname = "MESH_OT_customdata_custom_splitnormals_add";
ot->description = "Add a custom split normals layer, if none exists yet";
/* api callbacks */
ot->exec = mesh_customdata_custom_splitnormals_add_exec;
ot->poll = ED_operator_editable_mesh;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperator * /*op*/)
{
Mesh *me = ED_mesh_context(C);
if (BKE_mesh_has_custom_loop_normals(me)) {
BMEditMesh *em = me->edit_mesh;
if (em != nullptr && em->bm->lnor_spacearr != nullptr) {
BKE_lnor_spacearr_clear(em->bm->lnor_spacearr);
}
return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL);
}
return OPERATOR_CANCELLED;
}
void MESH_OT_customdata_custom_splitnormals_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Custom Split Normals Data";
ot->idname = "MESH_OT_customdata_custom_splitnormals_clear";
ot->description = "Remove the custom split normals layer, if it exists";
/* api callbacks */
ot->exec = mesh_customdata_custom_splitnormals_clear_exec;
ot->poll = ED_operator_editable_mesh;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* Vertex bevel weight. */
static int mesh_customdata_bevel_weight_vertex_state(bContext *C)
{
const Object *object = ED_object_context(C);
if (object && object->type == OB_MESH) {
const Mesh *mesh = static_cast<Mesh *>(object->data);
if (!ID_IS_LINKED(mesh)) {
const CustomData *data = GET_CD_DATA(mesh, vdata);
return CustomData_has_layer(data, CD_BWEIGHT);
}
}
return -1;
}
static bool mesh_customdata_bevel_weight_vertex_add_poll(bContext *C)
{
return mesh_customdata_bevel_weight_vertex_state(C) == 0;
}
static int mesh_customdata_bevel_weight_vertex_add_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_add_exec__internal(C, BM_VERT, CD_BWEIGHT);
}
void MESH_OT_customdata_bevel_weight_vertex_add(wmOperatorType *ot)
{
ot->name = "Add Vertex Bevel Weight";
ot->idname = "MESH_OT_customdata_bevel_weight_vertex_add";
ot->description = "Add a vertex bevel weight layer";
ot->exec = mesh_customdata_bevel_weight_vertex_add_exec;
ot->poll = mesh_customdata_bevel_weight_vertex_add_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static bool mesh_customdata_bevel_weight_vertex_clear_poll(bContext *C)
{
return (mesh_customdata_bevel_weight_vertex_state(C) == 1);
}
static int mesh_customdata_bevel_weight_vertex_clear_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_clear_exec__internal(C, BM_VERT, CD_BWEIGHT);
}
void MESH_OT_customdata_bevel_weight_vertex_clear(wmOperatorType *ot)
{
ot->name = "Clear Vertex Bevel Weight";
ot->idname = "MESH_OT_customdata_bevel_weight_vertex_clear";
ot->description = "Clear the vertex bevel weight layer";
ot->exec = mesh_customdata_bevel_weight_vertex_clear_exec;
ot->poll = mesh_customdata_bevel_weight_vertex_clear_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* Edge bevel weight. */
static int mesh_customdata_bevel_weight_edge_state(bContext *C)
{
const Object *ob = ED_object_context(C);
if (ob && ob->type == OB_MESH) {
const Mesh *mesh = static_cast<Mesh *>(ob->data);
if (!ID_IS_LINKED(mesh)) {
const CustomData *data = GET_CD_DATA(mesh, edata);
return CustomData_has_layer(data, CD_BWEIGHT);
}
}
return -1;
}
static bool mesh_customdata_bevel_weight_edge_add_poll(bContext *C)
{
return mesh_customdata_bevel_weight_edge_state(C) == 0;
}
static int mesh_customdata_bevel_weight_edge_add_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_add_exec__internal(C, BM_EDGE, CD_BWEIGHT);
}
void MESH_OT_customdata_bevel_weight_edge_add(wmOperatorType *ot)
{
ot->name = "Add Edge Bevel Weight";
ot->idname = "MESH_OT_customdata_bevel_weight_edge_add";
ot->description = "Add an edge bevel weight layer";
ot->exec = mesh_customdata_bevel_weight_edge_add_exec;
ot->poll = mesh_customdata_bevel_weight_edge_add_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static bool mesh_customdata_bevel_weight_edge_clear_poll(bContext *C)
{
return mesh_customdata_bevel_weight_edge_state(C) == 1;
}
static int mesh_customdata_bevel_weight_edge_clear_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_clear_exec__internal(C, BM_EDGE, CD_BWEIGHT);
}
void MESH_OT_customdata_bevel_weight_edge_clear(wmOperatorType *ot)
{
ot->name = "Clear Edge Bevel Weight";
ot->idname = "MESH_OT_customdata_bevel_weight_edge_clear";
ot->description = "Clear the edge bevel weight layer";
ot->exec = mesh_customdata_bevel_weight_edge_clear_exec;
ot->poll = mesh_customdata_bevel_weight_edge_clear_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* Edge crease. */
static int mesh_customdata_crease_edge_state(bContext *C)
{
const Object *ob = ED_object_context(C);
if (ob && ob->type == OB_MESH) {
const Mesh *mesh = static_cast<Mesh *>(ob->data);
if (!ID_IS_LINKED(mesh)) {
const CustomData *data = GET_CD_DATA(mesh, edata);
return CustomData_has_layer(data, CD_CREASE);
}
}
return -1;
}
static bool mesh_customdata_crease_edge_add_poll(bContext *C)
{
return mesh_customdata_crease_edge_state(C) == 0;
}
static int mesh_customdata_crease_edge_add_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_add_exec__internal(C, BM_EDGE, CD_CREASE);
}
void MESH_OT_customdata_crease_edge_add(wmOperatorType *ot)
{
ot->name = "Add Edge Crease";
ot->idname = "MESH_OT_customdata_crease_edge_add";
ot->description = "Add an edge crease layer";
ot->exec = mesh_customdata_crease_edge_add_exec;
ot->poll = mesh_customdata_crease_edge_add_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static bool mesh_customdata_crease_edge_clear_poll(bContext *C)
{
return mesh_customdata_crease_edge_state(C) == 1;
}
static int mesh_customdata_crease_edge_clear_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_clear_exec__internal(C, BM_EDGE, CD_CREASE);
}
void MESH_OT_customdata_crease_edge_clear(wmOperatorType *ot)
{
ot->name = "Clear Edge Crease";
ot->idname = "MESH_OT_customdata_crease_edge_clear";
ot->description = "Clear the edge crease layer";
ot->exec = mesh_customdata_crease_edge_clear_exec;
ot->poll = mesh_customdata_crease_edge_clear_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* Vertex crease. */
static int mesh_customdata_crease_vertex_state(bContext *C)
{
const Object *object = ED_object_context(C);
if (object && object->type == OB_MESH) {
const Mesh *mesh = static_cast<Mesh *>(object->data);
if (!ID_IS_LINKED(mesh)) {
const CustomData *data = GET_CD_DATA(mesh, vdata);
return CustomData_has_layer(data, CD_CREASE);
}
}
return -1;
}
static bool mesh_customdata_crease_vertex_add_poll(bContext *C)
{
return mesh_customdata_crease_vertex_state(C) == 0;
}
static int mesh_customdata_crease_vertex_add_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_add_exec__internal(C, BM_VERT, CD_CREASE);
}
void MESH_OT_customdata_crease_vertex_add(wmOperatorType *ot)
{
ot->name = "Add Vertex Crease";
ot->idname = "MESH_OT_customdata_crease_vertex_add";
ot->description = "Add a vertex crease layer";
ot->exec = mesh_customdata_crease_vertex_add_exec;
ot->poll = mesh_customdata_crease_vertex_add_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static bool mesh_customdata_crease_vertex_clear_poll(bContext *C)
{
return (mesh_customdata_crease_vertex_state(C) == 1);
}
static int mesh_customdata_crease_vertex_clear_exec(bContext *C, wmOperator * /*op*/)
{
return mesh_customdata_clear_exec__internal(C, BM_VERT, CD_CREASE);
}
void MESH_OT_customdata_crease_vertex_clear(wmOperatorType *ot)
{
ot->name = "Clear Vertex Crease";
ot->idname = "MESH_OT_customdata_crease_vertex_clear";
ot->description = "Clear the vertex crease layer";
ot->exec = mesh_customdata_crease_vertex_clear_exec;
ot->poll = mesh_customdata_crease_vertex_clear_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/************************** Add Geometry Layers *************************/
void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_loose)
{
if (calc_edges || ((mesh->totpoly || mesh->totface) && mesh->totedge == 0)) {
BKE_mesh_calc_edges(mesh, calc_edges, true);
}
if (calc_edges_loose) {
mesh->runtime->loose_edges_cache.tag_dirty();
}
/* Default state is not to have tessface's so make sure this is the case. */
BKE_mesh_tessface_clear(mesh);
/* Tag lazily calculated data as dirty. */
BKE_mesh_normals_tag_dirty(mesh);
DEG_id_tag_update(&mesh->id, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh);
}
bool ED_mesh_edge_is_loose(const Mesh *mesh, const int index)
{
using namespace blender;
const bke::LooseEdgeCache &loose_edges = mesh->loose_edges();
return loose_edges.count > 0 && loose_edges.is_loose_bits[index];
}
static void mesh_add_verts(Mesh *mesh, int len)
{
using namespace blender;
if (len == 0) {
return;
}
int totvert = mesh->totvert + len;
CustomData vdata;
CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, totvert);
CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
if (!CustomData_get_layer_named(&vdata, CD_PROP_FLOAT3, "position")) {
CustomData_add_layer_named(
&vdata, CD_PROP_FLOAT3, CD_SET_DEFAULT, nullptr, totvert, "position");
}
CustomData_free(&mesh->vdata, mesh->totvert);
mesh->vdata = vdata;
BKE_mesh_runtime_clear_cache(mesh);
mesh->totvert = totvert;
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
".select_vert", ATTR_DOMAIN_POINT);
select_vert.span.take_back(len).fill(true);
select_vert.finish();
}
static void mesh_add_edges(Mesh *mesh, int len)
{
using namespace blender;
CustomData edata;
int totedge;
if (len == 0) {
return;
}
totedge = mesh->totedge + len;
/* Update custom-data. */
CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
if (!CustomData_has_layer(&edata, CD_MEDGE)) {
CustomData_add_layer(&edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, totedge);
}
CustomData_free(&mesh->edata, mesh->totedge);
mesh->edata = edata;
BKE_mesh_runtime_clear_cache(mesh);
mesh->totedge = totedge;
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
bke::SpanAttributeWriter<bool> select_edge = attributes.lookup_or_add_for_write_span<bool>(
".select_edge", ATTR_DOMAIN_EDGE);
select_edge.span.take_back(len).fill(true);
select_edge.finish();
}
static void mesh_add_loops(Mesh *mesh, int len)
{
CustomData ldata;
int totloop;
if (len == 0) {
return;
}
totloop = mesh->totloop + len; /* new face count */
/* update customdata */
CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, totloop);
CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
if (!CustomData_has_layer(&ldata, CD_MLOOP)) {
CustomData_add_layer(&ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, totloop);
}
BKE_mesh_runtime_clear_cache(mesh);
CustomData_free(&mesh->ldata, mesh->totloop);
mesh->ldata = ldata;
mesh->totloop = totloop;
}
static void mesh_add_polys(Mesh *mesh, int len)
{
using namespace blender;
CustomData pdata;
int totpoly;
if (len == 0) {
return;
}
totpoly = mesh->totpoly + len; /* new face count */
/* update customdata */
CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, totpoly);
CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
if (!CustomData_has_layer(&pdata, CD_MPOLY)) {
CustomData_add_layer(&pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, totpoly);
}
CustomData_free(&mesh->pdata, mesh->totpoly);
mesh->pdata = pdata;
BKE_mesh_runtime_clear_cache(mesh);
mesh->totpoly = totpoly;
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
".select_poly", ATTR_DOMAIN_FACE);
select_poly.span.take_back(len).fill(true);
select_poly.finish();
}
/* -------------------------------------------------------------------- */
/** \name Add Geometry
* \{ */
void ED_mesh_verts_add(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
BKE_report(reports, RPT_ERROR, "Cannot add vertices in edit mode");
return;
}
mesh_add_verts(mesh, count);
}
void ED_mesh_edges_add(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
BKE_report(reports, RPT_ERROR, "Cannot add edges in edit mode");
return;
}
mesh_add_edges(mesh, count);
}
void ED_mesh_loops_add(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
BKE_report(reports, RPT_ERROR, "Cannot add loops in edit mode");
return;
}
mesh_add_loops(mesh, count);
}
void ED_mesh_polys_add(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
BKE_report(reports, RPT_ERROR, "Cannot add polygons in edit mode");
return;
}
mesh_add_polys(mesh, count);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Remove Geometry
* \{ */
static void mesh_remove_verts(Mesh *mesh, int len)
{
if (len == 0) {
return;
}
const int totvert = mesh->totvert - len;
CustomData_free_elem(&mesh->vdata, totvert, len);
mesh->totvert = totvert;
}
static void mesh_remove_edges(Mesh *mesh, int len)
{
if (len == 0) {
return;
}
const int totedge = mesh->totedge - len;
CustomData_free_elem(&mesh->edata, totedge, len);
mesh->totedge = totedge;
}
static void mesh_remove_loops(Mesh *mesh, int len)
{
if (len == 0) {
return;
}
const int totloop = mesh->totloop - len;
CustomData_free_elem(&mesh->ldata, totloop, len);
mesh->totloop = totloop;
}
static void mesh_remove_polys(Mesh *mesh, int len)
{
if (len == 0) {
return;
}
const int totpoly = mesh->totpoly - len;
CustomData_free_elem(&mesh->pdata, totpoly, len);
mesh->totpoly = totpoly;
}
void ED_mesh_verts_remove(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
BKE_report(reports, RPT_ERROR, "Cannot remove vertices in edit mode");
return;
}
if (count > mesh->totvert) {
BKE_report(reports, RPT_ERROR, "Cannot remove more vertices than the mesh contains");
return;
}
mesh_remove_verts(mesh, count);
}
void ED_mesh_edges_remove(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
BKE_report(reports, RPT_ERROR, "Cannot remove edges in edit mode");
return;
}
if (count > mesh->totedge) {
BKE_report(reports, RPT_ERROR, "Cannot remove more edges than the mesh contains");
return;
}
mesh_remove_edges(mesh, count);
}
void ED_mesh_loops_remove(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
BKE_report(reports, RPT_ERROR, "Cannot remove loops in edit mode");
return;
}
if (count > mesh->totloop) {
BKE_report(reports, RPT_ERROR, "Cannot remove more loops than the mesh contains");
return;
}
mesh_remove_loops(mesh, count);
}
void ED_mesh_polys_remove(Mesh *mesh, ReportList *reports, int count)
{
if (mesh->edit_mesh) {
BKE_report(reports, RPT_ERROR, "Cannot remove polys in edit mode");
return;
}
if (count > mesh->totpoly) {
BKE_report(reports, RPT_ERROR, "Cannot remove more polys than the mesh contains");
return;
}
mesh_remove_polys(mesh, count);
}
void ED_mesh_geometry_clear(Mesh *mesh)
{
mesh_remove_verts(mesh, mesh->totvert);
mesh_remove_edges(mesh, mesh->totedge);
mesh_remove_loops(mesh, mesh->totloop);
mesh_remove_polys(mesh, mesh->totpoly);
}
/** \} */
void ED_mesh_report_mirror_ex(wmOperator *op, int totmirr, int totfail, char selectmode)
{
const char *elem_type;
if (selectmode & SCE_SELECT_VERTEX) {
elem_type = "vertices";
}
else if (selectmode & SCE_SELECT_EDGE) {
elem_type = "edges";
}
else {
elem_type = "faces";
}
if (totfail) {
BKE_reportf(
op->reports, RPT_WARNING, "%d %s mirrored, %d failed", totmirr, elem_type, totfail);
}
else {
BKE_reportf(op->reports, RPT_INFO, "%d %s mirrored", totmirr, elem_type);
}
}
void ED_mesh_report_mirror(wmOperator *op, int totmirr, int totfail)
{
ED_mesh_report_mirror_ex(op, totmirr, totfail, SCE_SELECT_VERTEX);
}
Mesh *ED_mesh_context(bContext *C)
{
Mesh *mesh = static_cast<Mesh *>(CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data);
if (mesh != nullptr) {
return mesh;
}
Object *ob = ED_object_active_context(C);
if (ob == nullptr) {
return nullptr;
}
ID *data = (ID *)ob->data;
if (data == nullptr || GS(data->name) != ID_ME) {
return nullptr;
}
return (Mesh *)data;
}
void ED_mesh_split_faces(Mesh *mesh)
{
using namespace blender;
const Span<MPoly> polys = mesh->polys();
const Span<MLoop> loops = mesh->loops();
const float split_angle = (mesh->flag & ME_AUTOSMOOTH) != 0 ? mesh->smoothresh : float(M_PI);
const bke::AttributeAccessor attributes = mesh->attributes();
const VArray<bool> mesh_sharp_edges = attributes.lookup_or_default<bool>(
"sharp_edge", ATTR_DOMAIN_EDGE, false);
const bool *sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, "sharp_face"));
Array<bool> sharp_edges(mesh->totedge);
mesh_sharp_edges.materialize(sharp_edges);
bke::mesh::edges_sharp_from_angle_set(
polys, loops, mesh->poly_normals(), sharp_faces, split_angle, sharp_edges);
threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
for (const int poly_i : range) {
const MPoly &poly = polys[poly_i];
if (sharp_faces && sharp_faces[poly_i]) {
for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
sharp_edges[loop.e] = true;
}
}
}
});
Vector<int64_t> split_indices;
const IndexMask split_mask = index_mask_ops::find_indices_from_virtual_array(
sharp_edges.index_range(), VArray<bool>::ForSpan(sharp_edges), 4096, split_indices);
if (split_mask.is_empty()) {
return;
}
const bke::AnonymousAttributePropagationInfo propagation_info;
geometry::split_edges(*mesh, split_mask, propagation_info);
}