When allocating new `CustomData` layers, often we do redundant initialization of arrays. For example, it's common that values are allocated, set to their default value, and then set to some other value. This is wasteful, and it negates the benefits of optimizations to the allocator like D15082. There are two reasons for this. The first is array-of-structs storage that makes it annoying to initialize values manually, and the second is confusing options in the Custom Data API. This patch addresses the latter. The `CustomData` "alloc type" options are rearranged. Now, besides the options that use existing layers, there are two remaining: * `CD_SET_DEFAULT` sets the default value. * Usually zeroes, but for colors this is white (how it was before). * Should be used when you add the layer but don't set all values. * `CD_CONSTRUCT` refers to the "default construct" C++ term. * Only necessary or defined for non-trivial types like vertex groups. * Doesn't do anything for trivial types like `int` or `float3`. * Should be used every other time, when all values will be set. The attribute API's `AttributeInit` types are updated as well. To update code, replace `CD_CALLOC` with `CD_SET_DEFAULT` and `CD_DEFAULT` with `CD_CONSTRUCT`. This doesn't cause any functional changes yet. Follow-up commits will change to avoid initializing new layers where the correctness is clear. Differential Revision: https://developer.blender.org/D15617
493 lines
12 KiB
C
493 lines
12 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2008 Blender Foundation. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup edobj
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_workspace_types.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_customdata.h"
|
|
#include "BKE_editmesh.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_object_deform.h"
|
|
#include "BKE_object_facemap.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_define.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "ED_mesh.h"
|
|
#include "ED_object.h"
|
|
|
|
#include "object_intern.h"
|
|
|
|
void ED_object_facemap_face_add(Object *ob, bFaceMap *fmap, int facenum)
|
|
{
|
|
int fmap_nr;
|
|
if (GS(((ID *)ob->data)->name) != ID_ME) {
|
|
return;
|
|
}
|
|
|
|
/* get the face map number, exit if it can't be found */
|
|
fmap_nr = BLI_findindex(&ob->fmaps, fmap);
|
|
|
|
if (fmap_nr != -1) {
|
|
int *facemap;
|
|
Mesh *me = ob->data;
|
|
|
|
/* if there's is no facemap layer then create one */
|
|
if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL) {
|
|
facemap = CustomData_add_layer(&me->pdata, CD_FACEMAP, CD_SET_DEFAULT, NULL, me->totpoly);
|
|
}
|
|
|
|
facemap[facenum] = fmap_nr;
|
|
}
|
|
}
|
|
|
|
void ED_object_facemap_face_remove(Object *ob, bFaceMap *fmap, int facenum)
|
|
{
|
|
int fmap_nr;
|
|
if (GS(((ID *)ob->data)->name) != ID_ME) {
|
|
return;
|
|
}
|
|
|
|
/* get the face map number, exit if it can't be found */
|
|
fmap_nr = BLI_findindex(&ob->fmaps, fmap);
|
|
|
|
if (fmap_nr != -1) {
|
|
int *facemap;
|
|
Mesh *me = ob->data;
|
|
|
|
if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL) {
|
|
return;
|
|
}
|
|
|
|
facemap[facenum] = -1;
|
|
}
|
|
}
|
|
|
|
static void object_fmap_remap_edit_mode(Object *ob, const int *remap)
|
|
{
|
|
if (ob->type != OB_MESH) {
|
|
return;
|
|
}
|
|
|
|
Mesh *me = ob->data;
|
|
if (me->edit_mesh) {
|
|
BMEditMesh *em = me->edit_mesh;
|
|
const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
|
|
|
|
if (cd_fmap_offset != -1) {
|
|
BMFace *efa;
|
|
BMIter iter;
|
|
int *map;
|
|
|
|
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
|
map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
|
|
|
|
if (map && *map != -1) {
|
|
*map = remap[*map];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void object_fmap_remap_object_mode(Object *ob, const int *remap)
|
|
{
|
|
if (ob->type != OB_MESH) {
|
|
return;
|
|
}
|
|
|
|
Mesh *me = ob->data;
|
|
if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) {
|
|
int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP);
|
|
if (map) {
|
|
for (int i = 0; i < me->totpoly; i++) {
|
|
if (map[i] != -1) {
|
|
map[i] = remap[map[i]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void object_facemap_remap(Object *ob, const int *remap)
|
|
{
|
|
if (BKE_object_is_in_editmode(ob)) {
|
|
object_fmap_remap_edit_mode(ob, remap);
|
|
}
|
|
else {
|
|
object_fmap_remap_object_mode(ob, remap);
|
|
}
|
|
}
|
|
|
|
static bool face_map_supported_poll(bContext *C)
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
ID *data = (ob) ? ob->data : NULL;
|
|
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));
|
|
}
|
|
|
|
static bool face_map_supported_edit_mode_poll(bContext *C)
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
|
|
if (face_map_supported_poll(C)) {
|
|
if (ob->mode == OB_MODE_EDIT) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool face_map_supported_remove_poll(bContext *C)
|
|
{
|
|
if (!face_map_supported_poll(C)) {
|
|
return false;
|
|
}
|
|
|
|
Object *ob = ED_object_context(C);
|
|
bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
|
|
if (fmap) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int face_map_add_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
|
|
BKE_object_facemap_add(ob);
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void OBJECT_OT_face_map_add(struct wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Add Face Map";
|
|
ot->idname = "OBJECT_OT_face_map_add";
|
|
ot->description = "Add a new face map to the active object";
|
|
|
|
/* api callbacks */
|
|
ot->poll = face_map_supported_poll;
|
|
ot->exec = face_map_add_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
static int face_map_remove_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
|
|
|
|
if (fmap) {
|
|
BKE_object_facemap_remove(ob, fmap);
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void OBJECT_OT_face_map_remove(struct wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Remove Face Map";
|
|
ot->idname = "OBJECT_OT_face_map_remove";
|
|
ot->description = "Remove a face map from the active object";
|
|
|
|
/* api callbacks */
|
|
ot->poll = face_map_supported_remove_poll;
|
|
ot->exec = face_map_remove_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
static int face_map_assign_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
|
|
|
|
if (fmap) {
|
|
Mesh *me = ob->data;
|
|
BMEditMesh *em = me->edit_mesh;
|
|
BMFace *efa;
|
|
BMIter iter;
|
|
int *map;
|
|
int cd_fmap_offset;
|
|
|
|
if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP)) {
|
|
BM_data_layer_add(em->bm, &em->bm->pdata, CD_FACEMAP);
|
|
}
|
|
|
|
cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
|
|
|
|
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
|
map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
|
|
|
|
if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
|
*map = ob->actfmap - 1;
|
|
}
|
|
}
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void OBJECT_OT_face_map_assign(struct wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Assign Face Map";
|
|
ot->idname = "OBJECT_OT_face_map_assign";
|
|
ot->description = "Assign faces to a face map";
|
|
|
|
/* api callbacks */
|
|
ot->poll = face_map_supported_edit_mode_poll;
|
|
ot->exec = face_map_assign_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
static int face_map_remove_from_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
|
|
|
|
if (fmap) {
|
|
Mesh *me = ob->data;
|
|
BMEditMesh *em = me->edit_mesh;
|
|
BMFace *efa;
|
|
BMIter iter;
|
|
int *map;
|
|
int cd_fmap_offset;
|
|
int mapindex = ob->actfmap - 1;
|
|
|
|
if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP)) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
|
|
|
|
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
|
map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
|
|
|
|
if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && *map == mapindex) {
|
|
*map = -1;
|
|
}
|
|
}
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void OBJECT_OT_face_map_remove_from(struct wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Remove from Face Map";
|
|
ot->idname = "OBJECT_OT_face_map_remove_from";
|
|
ot->description = "Remove faces from a face map";
|
|
|
|
/* api callbacks */
|
|
ot->poll = face_map_supported_edit_mode_poll;
|
|
ot->exec = face_map_remove_from_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
static void fmap_select(Object *ob, bool select)
|
|
{
|
|
Mesh *me = ob->data;
|
|
BMEditMesh *em = me->edit_mesh;
|
|
BMFace *efa;
|
|
BMIter iter;
|
|
int *map;
|
|
int cd_fmap_offset;
|
|
int mapindex = ob->actfmap - 1;
|
|
|
|
if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP)) {
|
|
BM_data_layer_add(em->bm, &em->bm->pdata, CD_FACEMAP);
|
|
}
|
|
|
|
cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
|
|
|
|
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
|
map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
|
|
|
|
if (*map == mapindex) {
|
|
BM_face_select_set(em->bm, efa, select);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int face_map_select_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
|
|
|
|
if (fmap) {
|
|
fmap_select(ob, true);
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void OBJECT_OT_face_map_select(struct wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Select Face Map Faces";
|
|
ot->idname = "OBJECT_OT_face_map_select";
|
|
ot->description = "Select faces belonging to a face map";
|
|
|
|
/* api callbacks */
|
|
ot->poll = face_map_supported_edit_mode_poll;
|
|
ot->exec = face_map_select_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
static int face_map_deselect_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
|
|
|
|
if (fmap) {
|
|
fmap_select(ob, false);
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void OBJECT_OT_face_map_deselect(struct wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Deselect Face Map Faces";
|
|
ot->idname = "OBJECT_OT_face_map_deselect";
|
|
ot->description = "Deselect faces belonging to a face map";
|
|
|
|
/* api callbacks */
|
|
ot->poll = face_map_supported_edit_mode_poll;
|
|
ot->exec = face_map_deselect_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
static int face_map_move_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Object *ob = ED_object_context(C);
|
|
bFaceMap *fmap;
|
|
int dir = RNA_enum_get(op->ptr, "direction");
|
|
|
|
fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
|
|
if (!fmap) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
if (!fmap->prev && !fmap->next) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
int pos1 = BLI_findindex(&ob->fmaps, fmap);
|
|
int pos2 = pos1 - dir;
|
|
int len = BLI_listbase_count(&ob->fmaps);
|
|
int *map = MEM_mallocN(len * sizeof(*map), __func__);
|
|
|
|
if (!IN_RANGE(pos2, -1, len)) {
|
|
const int offset = len - dir;
|
|
for (int i = 0; i < len; i++) {
|
|
map[i] = (i + offset) % len;
|
|
}
|
|
pos2 = map[pos1];
|
|
}
|
|
else {
|
|
range_vn_i(map, len, 0);
|
|
SWAP(int, map[pos1], map[pos2]);
|
|
}
|
|
|
|
void *prev = fmap->prev;
|
|
void *next = fmap->next;
|
|
BLI_remlink(&ob->fmaps, fmap);
|
|
if (dir == 1) { /*up*/
|
|
BLI_insertlinkbefore(&ob->fmaps, prev, fmap);
|
|
}
|
|
else { /*down*/
|
|
BLI_insertlinkafter(&ob->fmaps, next, fmap);
|
|
}
|
|
|
|
/* Iterate through mesh and substitute the indices as necessary. */
|
|
object_facemap_remap(ob, map);
|
|
MEM_freeN(map);
|
|
|
|
ob->actfmap = pos2 + 1;
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void OBJECT_OT_face_map_move(wmOperatorType *ot)
|
|
{
|
|
static EnumPropertyItem fmap_slot_move[] = {
|
|
{1, "UP", 0, "Up", ""},
|
|
{-1, "DOWN", 0, "Down", ""},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
/* identifiers */
|
|
ot->name = "Move Face Map";
|
|
ot->idname = "OBJECT_OT_face_map_move";
|
|
ot->description = "Move the active face map up/down in the list";
|
|
|
|
/* api callbacks */
|
|
ot->poll = face_map_supported_poll;
|
|
ot->exec = face_map_move_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
RNA_def_enum(
|
|
ot->srna, "direction", fmap_slot_move, 0, "Direction", "Direction to move, up or down");
|
|
}
|