Files
test/source/blender/blenkernel/intern/customdata.cc
Jacques Lucke 7ed85bfe17 Fix #148032: vertex/weight paint undo broken
The issue was that the data-blocks of two different undo steps were detected to
be identical, even if the attributes changed. That's because even if the
implicitly-shared data was different, they were turned into the same pointer by
cadb3fe5c5 on write.

This patch makes it so that for undo steps, implicitly shared data does not use
the pointer stability feature (in a sense, implicit-sharing itself provides
pointer stability for undo steps already).

The main tricky aspect is that we need to know if a pointer is implicitly shared
in `writestruct_at_address_nr` and oftentimes that's called before the
corresponding shared data is actually written with `BLO_write_shared`. The
solution is to enforce that the blend-write code has to know what pointers are
implicitly-shared before they are written the first time. The simplest way to
ensure that is to call `BLO_write_shared` first. However, that's not always
possible, especially when the pointer is directly embedded in an ID. Therefore,
there is a new `BLO_write_shared_tag` function that can be used in such cases.

The undo performance for the file in #141262 is still fixed with this change.

Pull Request: https://projects.blender.org/blender/blender/pulls/148144
2025-10-16 17:16:05 +02:00

5426 lines
153 KiB
C++

/* SPDX-FileCopyrightText: 2006 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
* Implementation of CustomData.
*
* BKE_customdata.hh contains the function prototypes for this file.
*/
#include <algorithm>
#include "MEM_guardedalloc.h"
/* Since we have versioning code here (CustomData_verify_versions()). */
#define DNA_DEPRECATED_ALLOW
#include "DNA_ID.h"
#include "DNA_customdata_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_enums.h"
#include "DNA_userdef_types.h"
#include "DNA_vec_types.h"
#include "BLI_bit_vector.hh"
#include "BLI_bitmap.h"
#include "BLI_index_range.hh"
#include "BLI_math_color_blend.h"
#include "BLI_math_quaternion_types.hh"
#include "BLI_math_vector.hh"
#include "BLI_memory_counter.hh"
#include "BLI_mempool.h"
#include "BLI_path_utils.hh"
#include "BLI_resource_scope.hh"
#include "BLI_set.hh"
#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_string_ref.hh"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.hh"
#include "BLI_utildefines.h"
#ifndef NDEBUG
# include "BLI_dynstr.h"
#endif
#include "BLT_translation.hh"
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_attribute_legacy_convert.hh"
#include "BKE_attribute_math.hh"
#include "BKE_attribute_storage.hh"
#include "BKE_ccg.hh"
#include "BKE_customdata.hh"
#include "BKE_customdata_file.h"
#include "BKE_deform.hh"
#include "BKE_library.hh"
#include "BKE_main.hh"
#include "BKE_mesh_remap.hh"
#include "BKE_multires.hh"
#include "BLO_read_write.hh"
#include "bmesh.hh"
#include "CLG_log.h"
/* only for customdata_data_transfer_interp_normal_normals */
#include "data_transfer_intern.hh"
using blender::Array;
using blender::BitVector;
using blender::float2;
using blender::ImplicitSharingInfo;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Set;
using blender::Span;
using blender::StringRef;
using blender::Vector;
/* number of layers to add when growing a CustomData object */
#define CUSTOMDATA_GROW 5
/* ensure typemap size is ok */
BLI_STATIC_ASSERT(BOUNDED_ARRAY_TYPE_SIZE<decltype(CustomData::typemap)>() == CD_NUMTYPES,
"size mismatch");
static CLG_LogRef LOG = {"geom.customdata"};
/* -------------------------------------------------------------------- */
/** \name Mesh Mask Utilities
* \{ */
void CustomData_MeshMasks_update(CustomData_MeshMasks *mask_dst,
const CustomData_MeshMasks *mask_src)
{
mask_dst->vmask |= mask_src->vmask;
mask_dst->emask |= mask_src->emask;
mask_dst->fmask |= mask_src->fmask;
mask_dst->pmask |= mask_src->pmask;
mask_dst->lmask |= mask_src->lmask;
}
bool CustomData_MeshMasks_are_matching(const CustomData_MeshMasks *mask_ref,
const CustomData_MeshMasks *mask_required)
{
return (((mask_required->vmask & mask_ref->vmask) == mask_required->vmask) &&
((mask_required->emask & mask_ref->emask) == mask_required->emask) &&
((mask_required->fmask & mask_ref->fmask) == mask_required->fmask) &&
((mask_required->pmask & mask_ref->pmask) == mask_required->pmask) &&
((mask_required->lmask & mask_ref->lmask) == mask_required->lmask));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Layer Type Information Struct
* \{ */
struct LayerTypeInfo {
int size; /* the memory size of one element of this layer's data */
int alignment;
/** name of the struct used, for file writing */
const char *structname;
/** number of structs per element, for file writing */
int structnum;
/**
* default layer name.
*
* \note when null this is a way to ensure there is only ever one item
* see: CustomData_layertype_is_singleton().
*/
const char *defaultname;
/**
* a function to copy count elements of this layer's data
* (deep copy if appropriate)
* if null, memcpy is used
*/
cd_copy copy;
/**
* a function to destruct this layer's data.
*
* \note implementations should make sure that the data pointer itself is not freed.
*/
cd_free free;
/**
* a function to interpolate between count source elements of this
* layer's data and store the result in dest
* if weights == null they should default to 1
*
* weights gives the weight for each element in sources
* count gives the number of elements in sources
*
* \note in some cases `dest` pointer is in `sources` so all functions have to take this
* into account and delay applying changes while reading from sources. See #32395.
*/
cd_interp interp;
/** a function to swap the data in corners of the element */
void (*swap)(void *data, const int *corner_indices);
/**
* Set values to the type's default. If undefined, the default is assumed to be zeroes.
* Memory pointed to by #data is expected to be uninitialized.
*/
cd_set_default_value set_default_value;
/**
* Construct and fill a valid value for the type. Necessary for non-trivial types.
* Memory pointed to by #data is expected to be uninitialized.
*/
void (*construct)(void *data, int count);
/** A function used by mesh validating code, must ensures passed item has valid data. */
cd_validate validate;
/** Functions necessary for geometry collapse. */
bool (*equal)(const void *data1, const void *data2);
void (*multiply)(void *data, float fac);
void (*initminmax)(void *min, void *max);
void (*add)(void *data1, const void *data2);
void (*dominmax)(const void *data1, void *min, void *max);
void (*copyvalue)(const void *source, void *dest, int mixmode, const float mixfactor);
/** A function to read data from a cdf file. */
bool (*read)(CDataFile *cdf, void *data, int count);
/** A function to write data to a cdf file. */
bool (*write)(CDataFile *cdf, const void *data, int count);
/** A function to determine file size. */
size_t (*filesize)(CDataFile *cdf, const void *data, int count);
/**
* A function to determine max allowed number of layers,
* should be null or return -1 if no limit.
*/
int (*layers_max)();
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MDeformVert, #CD_MDEFORMVERT)
* \{ */
static void layerCopy_mdeformvert(const void *source, void *dest, const int count)
{
int i, size = sizeof(MDeformVert);
memcpy(dest, source, count * size);
for (i = 0; i < count; i++) {
MDeformVert *dvert = static_cast<MDeformVert *>(POINTER_OFFSET(dest, i * size));
if (dvert->totweight) {
MDeformWeight *dw = MEM_malloc_arrayN<MDeformWeight>(size_t(dvert->totweight), __func__);
memcpy(dw, dvert->dw, dvert->totweight * sizeof(*dw));
dvert->dw = dw;
}
else {
dvert->dw = nullptr;
}
}
}
static void layerFree_mdeformvert(void *data, const int count)
{
for (MDeformVert &dvert : MutableSpan(static_cast<MDeformVert *>(data), count)) {
if (dvert.dw) {
MEM_freeN(dvert.dw);
dvert.dw = nullptr;
dvert.totweight = 0;
}
}
}
static void layerInterp_mdeformvert(const void **sources,
const float *weights,
const int count,
void *dest)
{
/* A single linked list of #MDeformWeight's.
* use this to avoid double allocations (which #LinkNode would do). */
struct MDeformWeight_Link {
MDeformWeight_Link *next;
MDeformWeight dw;
};
MDeformVert *dvert = static_cast<MDeformVert *>(dest);
MDeformWeight_Link *dest_dwlink = nullptr;
MDeformWeight_Link *node;
/* build a list of unique def_nrs for dest */
int totweight = 0;
for (int i = 0; i < count; i++) {
const MDeformVert *source = static_cast<const MDeformVert *>(sources[i]);
float interp_weight = weights[i];
for (int j = 0; j < source->totweight; j++) {
MDeformWeight *dw = &source->dw[j];
float weight = dw->weight * interp_weight;
if (weight == 0.0f) {
continue;
}
for (node = dest_dwlink; node; node = node->next) {
MDeformWeight *tmp_dw = &node->dw;
if (tmp_dw->def_nr == dw->def_nr) {
tmp_dw->weight += weight;
break;
}
}
/* if this def_nr is not in the list, add it */
if (!node) {
MDeformWeight_Link *tmp_dwlink = static_cast<MDeformWeight_Link *>(
alloca(sizeof(*tmp_dwlink)));
tmp_dwlink->dw.def_nr = dw->def_nr;
tmp_dwlink->dw.weight = weight;
/* Inline linked-list. */
tmp_dwlink->next = dest_dwlink;
dest_dwlink = tmp_dwlink;
totweight++;
}
}
}
/* Delay writing to the destination in case dest is in sources. */
/* now we know how many unique deform weights there are, so realloc */
if (dvert->dw && (dvert->totweight == totweight)) {
/* pass (fast-path if we don't need to realloc). */
}
else {
if (dvert->dw) {
MEM_freeN(dvert->dw);
}
if (totweight) {
dvert->dw = MEM_malloc_arrayN<MDeformWeight>(size_t(totweight), __func__);
}
}
if (totweight) {
dvert->totweight = totweight;
int i = 0;
for (node = dest_dwlink; node; node = node->next, i++) {
node->dw.weight = std::min(node->dw.weight, 1.0f);
dvert->dw[i] = node->dw;
}
}
else {
*dvert = MDeformVert{};
}
}
static void layerConstruct_mdeformvert(void *data, const int count)
{
std::fill_n(static_cast<MDeformVert *>(data), count, MDeformVert{});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#vec3f, #CD_NORMAL)
* \{ */
static void layerInterp_normal(const void **sources,
const float *weights,
const int count,
void *dest)
{
/* NOTE: This is linear interpolation, which is not optimal for vectors.
* Unfortunately, spherical interpolation of more than two values is hairy,
* so for now it will do... */
float no[3] = {0.0f};
for (const int i : IndexRange(count)) {
madd_v3_v3fl(no, (const float *)sources[i], weights[i]);
}
/* Weighted sum of normalized vectors will **not** be normalized, even if weights are. */
normalize_v3_v3((float *)dest, no);
}
static void layerCopyValue_normal(const void *source,
void *dest,
const int mixmode,
const float mixfactor)
{
const float *no_src = (const float *)source;
float *no_dst = (float *)dest;
float no_tmp[3];
if (ELEM(mixmode,
CDT_MIX_NOMIX,
CDT_MIX_REPLACE_ABOVE_THRESHOLD,
CDT_MIX_REPLACE_BELOW_THRESHOLD))
{
/* Above/below threshold modes are not supported here, fallback to nomix (just in case). */
copy_v3_v3(no_dst, no_src);
}
else { /* Modes that support 'real' mix factor. */
/* Since we normalize in the end, MIX and ADD are the same op here. */
if (ELEM(mixmode, CDT_MIX_MIX, CDT_MIX_ADD)) {
add_v3_v3v3(no_tmp, no_dst, no_src);
normalize_v3(no_tmp);
}
else if (mixmode == CDT_MIX_SUB) {
sub_v3_v3v3(no_tmp, no_dst, no_src);
normalize_v3(no_tmp);
}
else if (mixmode == CDT_MIX_MUL) {
mul_v3_v3v3(no_tmp, no_dst, no_src);
normalize_v3(no_tmp);
}
else {
copy_v3_v3(no_tmp, no_src);
}
interp_v3_v3v3_slerp_safe(no_dst, no_dst, no_tmp, mixfactor);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MTFace, #CD_MTFACE)
* \{ */
static void layerCopy_tface(const void *source, void *dest, const int count)
{
const MTFace *source_tf = (const MTFace *)source;
MTFace *dest_tf = (MTFace *)dest;
for (int i = 0; i < count; i++) {
dest_tf[i] = source_tf[i];
}
}
static void layerInterp_tface(const void **sources,
const float *weights,
const int count,
void *dest)
{
MTFace *tf = static_cast<MTFace *>(dest);
float uv[4][2] = {{0.0f}};
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const MTFace *src = static_cast<const MTFace *>(sources[i]);
for (int j = 0; j < 4; j++) {
madd_v2_v2fl(uv[j], src->uv[j], interp_weight);
}
}
/* Delay writing to the destination in case dest is in sources. */
*tf = *(MTFace *)(*sources);
memcpy(tf->uv, uv, sizeof(tf->uv));
}
static void layerSwap_tface(void *data, const int *corner_indices)
{
MTFace *tf = static_cast<MTFace *>(data);
float uv[4][2];
for (int j = 0; j < 4; j++) {
const int source_index = corner_indices[j];
copy_v2_v2(uv[j], tf->uv[source_index]);
}
memcpy(tf->uv, uv, sizeof(tf->uv));
}
static void layerDefault_tface(void *data, const int count)
{
static MTFace default_tf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}};
MTFace *tf = (MTFace *)data;
for (int i = 0; i < count; i++) {
tf[i] = default_tf;
}
}
static int layerMaxNum_tface()
{
return MAX_MTFACE;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MFloatProperty, #CD_PROP_FLOAT)
* \{ */
static void layerCopy_propFloat(const void *source, void *dest, const int count)
{
memcpy(dest, source, sizeof(MFloatProperty) * count);
}
static void layerInterp_propFloat(const void **sources,
const float *weights,
const int count,
void *dest)
{
float result = 0.0f;
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const float src = *(const float *)sources[i];
result += src * interp_weight;
}
*(float *)dest = result;
}
static bool layerValidate_propFloat(void *data, const uint totitems, const bool do_fixes)
{
MFloatProperty *fp = static_cast<MFloatProperty *>(data);
bool has_errors = false;
for (int i = 0; i < totitems; i++, fp++) {
if (!isfinite(fp->f)) {
if (do_fixes) {
fp->f = 0.0f;
}
has_errors = true;
}
}
return has_errors;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MIntProperty, #CD_PROP_INT32)
* \{ */
static void layerInterp_propInt(const void **sources,
const float *weights,
const int count,
void *dest)
{
float result = 0.0f;
for (const int i : IndexRange(count)) {
const float weight = weights[i];
const float src = *static_cast<const int *>(sources[i]);
result += src * weight;
}
const int rounded_result = int(round(result));
*static_cast<int *>(dest) = rounded_result;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MStringProperty, #CD_PROP_STRING)
* \{ */
static void layerCopy_propString(const void *source, void *dest, const int count)
{
memcpy(dest, source, sizeof(MStringProperty) * count);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#OrigSpaceFace, #CD_ORIGSPACE)
* \{ */
static void layerCopy_origspace_face(const void *source, void *dest, const int count)
{
const OrigSpaceFace *source_tf = (const OrigSpaceFace *)source;
OrigSpaceFace *dest_tf = (OrigSpaceFace *)dest;
for (int i = 0; i < count; i++) {
dest_tf[i] = source_tf[i];
}
}
static void layerInterp_origspace_face(const void **sources,
const float *weights,
const int count,
void *dest)
{
OrigSpaceFace *osf = static_cast<OrigSpaceFace *>(dest);
float uv[4][2] = {{0.0f}};
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const OrigSpaceFace *src = static_cast<const OrigSpaceFace *>(sources[i]);
for (int j = 0; j < 4; j++) {
madd_v2_v2fl(uv[j], src->uv[j], interp_weight);
}
}
/* Delay writing to the destination in case dest is in sources. */
memcpy(osf->uv, uv, sizeof(osf->uv));
}
static void layerSwap_origspace_face(void *data, const int *corner_indices)
{
OrigSpaceFace *osf = static_cast<OrigSpaceFace *>(data);
float uv[4][2];
for (int j = 0; j < 4; j++) {
copy_v2_v2(uv[j], osf->uv[corner_indices[j]]);
}
memcpy(osf->uv, uv, sizeof(osf->uv));
}
static void layerDefault_origspace_face(void *data, const int count)
{
static OrigSpaceFace default_osf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}};
OrigSpaceFace *osf = (OrigSpaceFace *)data;
for (int i = 0; i < count; i++) {
osf[i] = default_osf;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MDisps, #CD_MDISPS)
* \{ */
static void layerSwap_mdisps(void *data, const int *ci)
{
MDisps *s = static_cast<MDisps *>(data);
if (s->disps) {
int nverts = (ci[1] == 3) ? 4 : 3; /* silly way to know vertex count of face */
int corners = multires_mdisp_corners(s);
int cornersize = s->totdisp / corners;
if (corners != nverts) {
/* happens when face changed vertex count in edit mode
* if it happened, just forgot displacement */
MEM_freeN(s->disps);
s->totdisp = (s->totdisp / corners) * nverts;
s->disps = MEM_calloc_arrayN<float[3]>(s->totdisp, "mdisp swap");
return;
}
float (*d)[3] = MEM_calloc_arrayN<float[3]>(s->totdisp, "mdisps swap");
for (int S = 0; S < corners; S++) {
memcpy(d + cornersize * S, s->disps + cornersize * ci[S], sizeof(float[3]) * cornersize);
}
MEM_freeN(s->disps);
s->disps = d;
}
}
static void layerCopy_mdisps(const void *source, void *dest, const int count)
{
const MDisps *s = static_cast<const MDisps *>(source);
MDisps *d = static_cast<MDisps *>(dest);
for (int i = 0; i < count; i++) {
if (s[i].disps) {
d[i].disps = static_cast<float (*)[3]>(MEM_dupallocN(s[i].disps));
d[i].hidden = static_cast<uint *>(MEM_dupallocN(s[i].hidden));
}
else {
d[i].disps = nullptr;
d[i].hidden = nullptr;
}
/* still copy even if not in memory, displacement can be external */
d[i].totdisp = s[i].totdisp;
d[i].level = s[i].level;
}
}
static void layerFree_mdisps(void *data, const int count)
{
for (MDisps &d : MutableSpan(static_cast<MDisps *>(data), count)) {
MEM_SAFE_FREE(d.disps);
MEM_SAFE_FREE(d.hidden);
d.totdisp = 0;
d.level = 0;
}
}
static void layerConstruct_mdisps(void *data, const int count)
{
std::fill_n(static_cast<MDisps *>(data), count, MDisps{});
}
static bool layerRead_mdisps(CDataFile *cdf, void *data, const int count)
{
MDisps *d = static_cast<MDisps *>(data);
for (int i = 0; i < count; i++) {
if (!d[i].disps) {
d[i].disps = MEM_calloc_arrayN<float[3]>(d[i].totdisp, "mdisps read");
}
if (!cdf_read_data(cdf, sizeof(float[3]) * d[i].totdisp, d[i].disps)) {
CLOG_ERROR(&LOG, "failed to read multires displacement %d/%d %d", i, count, d[i].totdisp);
return false;
}
}
return true;
}
static bool layerWrite_mdisps(CDataFile *cdf, const void *data, const int count)
{
const MDisps *d = static_cast<const MDisps *>(data);
for (int i = 0; i < count; i++) {
if (!cdf_write_data(cdf, sizeof(float[3]) * d[i].totdisp, d[i].disps)) {
CLOG_ERROR(&LOG, "failed to write multires displacement %d/%d %d", i, count, d[i].totdisp);
return false;
}
}
return true;
}
static size_t layerFilesize_mdisps(CDataFile * /*cdf*/, const void *data, const int count)
{
const MDisps *d = static_cast<const MDisps *>(data);
size_t size = 0;
for (int i = 0; i < count; i++) {
size += sizeof(float[3]) * d[i].totdisp;
}
return size;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#CD_BM_ELEM_PYPTR)
* \{ */
/* copy just zeros in this case */
static void layerCopy_bmesh_elem_py_ptr(const void * /*source*/, void *dest, const int count)
{
const int size = sizeof(void *);
for (int i = 0; i < count; i++) {
void **ptr = (void **)POINTER_OFFSET(dest, i * size);
*ptr = nullptr;
}
}
#ifndef WITH_PYTHON
void bpy_bm_generic_invalidate(struct BPy_BMGeneric * /*self*/)
{
/* dummy */
}
#endif
static void layerFree_bmesh_elem_py_ptr(void *data, const int count)
{
for (int i = 0; i < count; i++) {
void **ptr = (void **)POINTER_OFFSET(data, i * sizeof(void *));
if (*ptr) {
bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(*ptr));
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#GridPaintMask, #CD_GRID_PAINT_MASK)
* \{ */
static void layerCopy_grid_paint_mask(const void *source, void *dest, const int count)
{
const GridPaintMask *s = static_cast<const GridPaintMask *>(source);
GridPaintMask *d = static_cast<GridPaintMask *>(dest);
for (int i = 0; i < count; i++) {
if (s[i].data) {
d[i].data = static_cast<float *>(MEM_dupallocN(s[i].data));
d[i].level = s[i].level;
}
else {
d[i].data = nullptr;
d[i].level = 0;
}
}
}
static void layerFree_grid_paint_mask(void *data, const int count)
{
for (GridPaintMask &gpm : MutableSpan(static_cast<GridPaintMask *>(data), count)) {
MEM_SAFE_FREE(gpm.data);
gpm.level = 0;
}
}
static void layerConstruct_grid_paint_mask(void *data, const int count)
{
std::fill_n(static_cast<GridPaintMask *>(data), count, GridPaintMask{});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MLoopCol, #CD_PROP_BYTE_COLOR)
* \{ */
static void layerCopyValue_mloopcol(const void *source,
void *dest,
const int mixmode,
const float mixfactor)
{
const MLoopCol *m1 = static_cast<const MLoopCol *>(source);
MLoopCol *m2 = static_cast<MLoopCol *>(dest);
uchar tmp_col[4];
if (ELEM(mixmode,
CDT_MIX_NOMIX,
CDT_MIX_REPLACE_ABOVE_THRESHOLD,
CDT_MIX_REPLACE_BELOW_THRESHOLD))
{
/* Modes that do a full copy or nothing. */
if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) {
/* TODO: Check for a real valid way to get 'factor' value of our dest color? */
const float f = (float(m2->r) + float(m2->g) + float(m2->b)) / 3.0f;
if (mixmode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && f < mixfactor) {
return; /* Do Nothing! */
}
if (mixmode == CDT_MIX_REPLACE_BELOW_THRESHOLD && f > mixfactor) {
return; /* Do Nothing! */
}
}
m2->r = m1->r;
m2->g = m1->g;
m2->b = m1->b;
m2->a = m1->a;
}
else { /* Modes that support 'real' mix factor. */
uchar src[4] = {m1->r, m1->g, m1->b, m1->a};
uchar dst[4] = {m2->r, m2->g, m2->b, m2->a};
if (mixmode == CDT_MIX_MIX) {
blend_color_mix_byte(tmp_col, dst, src);
}
else if (mixmode == CDT_MIX_ADD) {
blend_color_add_byte(tmp_col, dst, src);
}
else if (mixmode == CDT_MIX_SUB) {
blend_color_sub_byte(tmp_col, dst, src);
}
else if (mixmode == CDT_MIX_MUL) {
blend_color_mul_byte(tmp_col, dst, src);
}
else {
memcpy(tmp_col, src, sizeof(tmp_col));
}
blend_color_interpolate_byte(dst, dst, tmp_col, mixfactor);
m2->r = char(dst[0]);
m2->g = char(dst[1]);
m2->b = char(dst[2]);
m2->a = char(dst[3]);
}
}
static bool layerEqual_mloopcol(const void *data1, const void *data2)
{
const MLoopCol *m1 = static_cast<const MLoopCol *>(data1);
const MLoopCol *m2 = static_cast<const MLoopCol *>(data2);
float r, g, b, a;
r = m1->r - m2->r;
g = m1->g - m2->g;
b = m1->b - m2->b;
a = m1->a - m2->a;
return r * r + g * g + b * b + a * a < 0.001f;
}
static void layerMultiply_mloopcol(void *data, const float fac)
{
MLoopCol *m = static_cast<MLoopCol *>(data);
m->r = float(m->r) * fac;
m->g = float(m->g) * fac;
m->b = float(m->b) * fac;
m->a = float(m->a) * fac;
}
static void layerAdd_mloopcol(void *data1, const void *data2)
{
MLoopCol *m = static_cast<MLoopCol *>(data1);
const MLoopCol *m2 = static_cast<const MLoopCol *>(data2);
m->r += m2->r;
m->g += m2->g;
m->b += m2->b;
m->a += m2->a;
}
static void layerDoMinMax_mloopcol(const void *data, void *vmin, void *vmax)
{
const MLoopCol *m = static_cast<const MLoopCol *>(data);
MLoopCol *min = static_cast<MLoopCol *>(vmin);
MLoopCol *max = static_cast<MLoopCol *>(vmax);
min->r = std::min(m->r, min->r);
min->g = std::min(m->g, min->g);
min->b = std::min(m->b, min->b);
min->a = std::min(m->a, min->a);
max->r = std::max(m->r, max->r);
max->g = std::max(m->g, max->g);
max->b = std::max(m->b, max->b);
max->a = std::max(m->a, max->a);
}
static void layerInitMinMax_mloopcol(void *vmin, void *vmax)
{
MLoopCol *min = static_cast<MLoopCol *>(vmin);
MLoopCol *max = static_cast<MLoopCol *>(vmax);
min->r = 255;
min->g = 255;
min->b = 255;
min->a = 255;
max->r = 0;
max->g = 0;
max->b = 0;
max->a = 0;
}
static void layerDefault_mloopcol(void *data, const int count)
{
MLoopCol default_mloopcol = {255, 255, 255, 255};
MLoopCol *mlcol = (MLoopCol *)data;
for (int i = 0; i < count; i++) {
mlcol[i] = default_mloopcol;
}
}
static void layerInterp_mloopcol(const void **sources, const float *weights, int count, void *dest)
{
MLoopCol *mc = static_cast<MLoopCol *>(dest);
struct {
float a;
float r;
float g;
float b;
} col = {0};
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const MLoopCol *src = static_cast<const MLoopCol *>(sources[i]);
col.r += src->r * interp_weight;
col.g += src->g * interp_weight;
col.b += src->b * interp_weight;
col.a += src->a * interp_weight;
}
/* Subdivide smooth or fractal can cause problems without clamping
* although weights should also not cause this situation */
/* Also delay writing to the destination in case dest is in sources. */
mc->r = round_fl_to_uchar_clamp(col.r);
mc->g = round_fl_to_uchar_clamp(col.g);
mc->b = round_fl_to_uchar_clamp(col.b);
mc->a = round_fl_to_uchar_clamp(col.a);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for #OrigSpaceLoop
* \{ */
/* origspace is almost exact copy of #MLoopUV, keep in sync. */
static void layerCopyValue_mloop_origspace(const void *source,
void *dest,
const int /*mixmode*/,
const float /*mixfactor*/)
{
const OrigSpaceLoop *luv1 = static_cast<const OrigSpaceLoop *>(source);
OrigSpaceLoop *luv2 = static_cast<OrigSpaceLoop *>(dest);
copy_v2_v2(luv2->uv, luv1->uv);
}
static bool layerEqual_mloop_origspace(const void *data1, const void *data2)
{
const OrigSpaceLoop *luv1 = static_cast<const OrigSpaceLoop *>(data1);
const OrigSpaceLoop *luv2 = static_cast<const OrigSpaceLoop *>(data2);
return len_squared_v2v2(luv1->uv, luv2->uv) < 0.00001f;
}
static void layerMultiply_mloop_origspace(void *data, const float fac)
{
OrigSpaceLoop *luv = static_cast<OrigSpaceLoop *>(data);
mul_v2_fl(luv->uv, fac);
}
static void layerInitMinMax_mloop_origspace(void *vmin, void *vmax)
{
OrigSpaceLoop *min = static_cast<OrigSpaceLoop *>(vmin);
OrigSpaceLoop *max = static_cast<OrigSpaceLoop *>(vmax);
INIT_MINMAX2(min->uv, max->uv);
}
static void layerDoMinMax_mloop_origspace(const void *data, void *vmin, void *vmax)
{
const OrigSpaceLoop *luv = static_cast<const OrigSpaceLoop *>(data);
OrigSpaceLoop *min = static_cast<OrigSpaceLoop *>(vmin);
OrigSpaceLoop *max = static_cast<OrigSpaceLoop *>(vmax);
minmax_v2v2_v2(min->uv, max->uv, luv->uv);
}
static void layerAdd_mloop_origspace(void *data1, const void *data2)
{
OrigSpaceLoop *l1 = static_cast<OrigSpaceLoop *>(data1);
const OrigSpaceLoop *l2 = static_cast<const OrigSpaceLoop *>(data2);
add_v2_v2(l1->uv, l2->uv);
}
static void layerInterp_mloop_origspace(const void **sources,
const float *weights,
int count,
void *dest)
{
float uv[2];
zero_v2(uv);
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const OrigSpaceLoop *src = static_cast<const OrigSpaceLoop *>(sources[i]);
madd_v2_v2fl(uv, src->uv, interp_weight);
}
/* Delay writing to the destination in case dest is in sources. */
copy_v2_v2(((OrigSpaceLoop *)dest)->uv, uv);
}
/* --- end copy */
static void layerInterp_mcol(const void **sources,
const float *weights,
const int count,
void *dest)
{
MCol *mc = static_cast<MCol *>(dest);
struct {
float a;
float r;
float g;
float b;
} col[4] = {{0.0f}};
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
for (int j = 0; j < 4; j++) {
const MCol *src = static_cast<const MCol *>(sources[i]);
col[j].a += src[j].a * interp_weight;
col[j].r += src[j].r * interp_weight;
col[j].g += src[j].g * interp_weight;
col[j].b += src[j].b * interp_weight;
}
}
/* Delay writing to the destination in case dest is in sources. */
for (int j = 0; j < 4; j++) {
/* Subdivide smooth or fractal can cause problems without clamping
* although weights should also not cause this situation */
mc[j].a = round_fl_to_uchar_clamp(col[j].a);
mc[j].r = round_fl_to_uchar_clamp(col[j].r);
mc[j].g = round_fl_to_uchar_clamp(col[j].g);
mc[j].b = round_fl_to_uchar_clamp(col[j].b);
}
}
static void layerSwap_mcol(void *data, const int *corner_indices)
{
MCol *mcol = static_cast<MCol *>(data);
MCol col[4];
for (int j = 0; j < 4; j++) {
col[j] = mcol[corner_indices[j]];
}
memcpy(mcol, col, sizeof(col));
}
static void layerDefault_mcol(void *data, const int count)
{
static MCol default_mcol = {255, 255, 255, 255};
MCol *mcol = (MCol *)data;
for (int i = 0; i < 4 * count; i++) {
mcol[i] = default_mcol;
}
}
static void layerDefault_origindex(void *data, const int count)
{
copy_vn_i((int *)data, count, ORIGINDEX_NONE);
}
static void layerInterp_shapekey(const void **sources, const float *weights, int count, void *dest)
{
float **in = (float **)sources;
if (count <= 0) {
return;
}
float co[3];
zero_v3(co);
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
madd_v3_v3fl(co, in[i], interp_weight);
}
/* Delay writing to the destination in case dest is in sources. */
copy_v3_v3((float *)dest, co);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MVertSkin, #CD_MVERT_SKIN)
* \{ */
static void layerDefault_mvert_skin(void *data, const int count)
{
MVertSkin *vs = static_cast<MVertSkin *>(data);
for (int i = 0; i < count; i++) {
copy_v3_fl(vs[i].radius, 0.25f);
vs[i].flag = 0;
}
}
static void layerCopy_mvert_skin(const void *source, void *dest, const int count)
{
memcpy(dest, source, sizeof(MVertSkin) * count);
}
static void layerInterp_mvert_skin(const void **sources,
const float *weights,
int count,
void *dest)
{
float radius[3];
zero_v3(radius);
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const MVertSkin *vs_src = static_cast<const MVertSkin *>(sources[i]);
madd_v3_v3fl(radius, vs_src->radius, interp_weight);
}
/* Delay writing to the destination in case dest is in sources. */
MVertSkin *vs_dst = static_cast<MVertSkin *>(dest);
copy_v3_v3(vs_dst->radius, radius);
vs_dst->flag &= ~MVERT_SKIN_ROOT;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (`short[4][3]`, #CD_TESSLOOPNORMAL)
* \{ */
static void layerSwap_flnor(void *data, const int *corner_indices)
{
short (*flnors)[4][3] = static_cast<short (*)[4][3]>(data);
short nors[4][3];
int i = 4;
while (i--) {
copy_v3_v3_short(nors[i], (*flnors)[corner_indices[i]]);
}
memcpy(flnors, nors, sizeof(nors));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#MPropCol, #CD_PROP_COLOR)
* \{ */
static void layerCopyValue_propcol(const void *source,
void *dest,
const int mixmode,
const float mixfactor)
{
const MPropCol *m1 = static_cast<const MPropCol *>(source);
MPropCol *m2 = static_cast<MPropCol *>(dest);
float tmp_col[4];
if (ELEM(mixmode,
CDT_MIX_NOMIX,
CDT_MIX_REPLACE_ABOVE_THRESHOLD,
CDT_MIX_REPLACE_BELOW_THRESHOLD))
{
/* Modes that do a full copy or nothing. */
if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) {
/* TODO: Check for a real valid way to get 'factor' value of our dest color? */
const float f = (m2->color[0] + m2->color[1] + m2->color[2]) / 3.0f;
if (mixmode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && f < mixfactor) {
return; /* Do Nothing! */
}
if (mixmode == CDT_MIX_REPLACE_BELOW_THRESHOLD && f > mixfactor) {
return; /* Do Nothing! */
}
}
copy_v4_v4(m2->color, m1->color);
}
else { /* Modes that support 'real' mix factor. */
if (mixmode == CDT_MIX_MIX) {
blend_color_mix_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_ADD) {
blend_color_add_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_SUB) {
blend_color_sub_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_MUL) {
blend_color_mul_float(tmp_col, m2->color, m1->color);
}
else {
memcpy(tmp_col, m1->color, sizeof(tmp_col));
}
blend_color_interpolate_float(m2->color, m2->color, tmp_col, mixfactor);
}
}
static bool layerEqual_propcol(const void *data1, const void *data2)
{
const MPropCol *m1 = static_cast<const MPropCol *>(data1);
const MPropCol *m2 = static_cast<const MPropCol *>(data2);
float tot = 0;
for (int i = 0; i < 4; i++) {
float c = (m1->color[i] - m2->color[i]);
tot += c * c;
}
return tot < 0.001f;
}
static void layerMultiply_propcol(void *data, const float fac)
{
MPropCol *m = static_cast<MPropCol *>(data);
mul_v4_fl(m->color, fac);
}
static void layerAdd_propcol(void *data1, const void *data2)
{
MPropCol *m = static_cast<MPropCol *>(data1);
const MPropCol *m2 = static_cast<const MPropCol *>(data2);
add_v4_v4(m->color, m2->color);
}
static void layerDoMinMax_propcol(const void *data, void *vmin, void *vmax)
{
const MPropCol *m = static_cast<const MPropCol *>(data);
MPropCol *min = static_cast<MPropCol *>(vmin);
MPropCol *max = static_cast<MPropCol *>(vmax);
minmax_v4v4_v4(min->color, max->color, m->color);
}
static void layerInitMinMax_propcol(void *vmin, void *vmax)
{
MPropCol *min = static_cast<MPropCol *>(vmin);
MPropCol *max = static_cast<MPropCol *>(vmax);
copy_v4_fl(min->color, FLT_MAX);
copy_v4_fl(max->color, FLT_MIN);
}
static void layerDefault_propcol(void *data, const int count)
{
/* Default to white, full alpha. */
MPropCol default_propcol = {{1.0f, 1.0f, 1.0f, 1.0f}};
MPropCol *pcol = (MPropCol *)data;
for (int i = 0; i < count; i++) {
copy_v4_v4(pcol[i].color, default_propcol.color);
}
}
static void layerInterp_propcol(const void **sources, const float *weights, int count, void *dest)
{
MPropCol *mc = static_cast<MPropCol *>(dest);
float col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const MPropCol *src = static_cast<const MPropCol *>(sources[i]);
madd_v4_v4fl(col, src->color, interp_weight);
}
copy_v4_v4(mc->color, col);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#vec3f, #CD_PROP_FLOAT3)
* \{ */
static void layerInterp_propfloat3(const void **sources,
const float *weights,
int count,
void *dest)
{
vec3f result = {0.0f, 0.0f, 0.0f};
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const vec3f *src = static_cast<const vec3f *>(sources[i]);
madd_v3_v3fl(&result.x, &src->x, interp_weight);
}
copy_v3_v3((float *)dest, &result.x);
}
static void layerMultiply_propfloat3(void *data, const float fac)
{
vec3f *vec = static_cast<vec3f *>(data);
vec->x *= fac;
vec->y *= fac;
vec->z *= fac;
}
static void layerAdd_propfloat3(void *data1, const void *data2)
{
vec3f *vec1 = static_cast<vec3f *>(data1);
const vec3f *vec2 = static_cast<const vec3f *>(data2);
vec1->x += vec2->x;
vec1->y += vec2->y;
vec1->z += vec2->z;
}
static bool layerValidate_propfloat3(void *data, const uint totitems, const bool do_fixes)
{
float *values = static_cast<float *>(data);
bool has_errors = false;
for (int i = 0; i < totitems * 3; i++) {
if (!isfinite(values[i])) {
if (do_fixes) {
values[i] = 0.0f;
}
has_errors = true;
}
}
return has_errors;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#vec2f, #CD_PROP_FLOAT2)
* \{ */
static void layerInterp_propfloat2(const void **sources,
const float *weights,
int count,
void *dest)
{
vec2f result = {0.0f, 0.0f};
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const vec2f *src = static_cast<const vec2f *>(sources[i]);
madd_v2_v2fl(&result.x, &src->x, interp_weight);
}
copy_v2_v2((float *)dest, &result.x);
}
static void layerMultiply_propfloat2(void *data, const float fac)
{
vec2f *vec = static_cast<vec2f *>(data);
vec->x *= fac;
vec->y *= fac;
}
static void layerAdd_propfloat2(void *data1, const void *data2)
{
vec2f *vec1 = static_cast<vec2f *>(data1);
const vec2f *vec2 = static_cast<const vec2f *>(data2);
vec1->x += vec2->x;
vec1->y += vec2->y;
}
static bool layerValidate_propfloat2(void *data, const uint totitems, const bool do_fixes)
{
float *values = static_cast<float *>(data);
bool has_errors = false;
for (int i = 0; i < totitems * 2; i++) {
if (!isfinite(values[i])) {
if (do_fixes) {
values[i] = 0.0f;
}
has_errors = true;
}
}
return has_errors;
}
static bool layerEqual_propfloat2(const void *data1, const void *data2)
{
const float2 &a = *static_cast<const float2 *>(data1);
const float2 &b = *static_cast<const float2 *>(data2);
return blender::math::distance_squared(a, b) < 0.00001f;
}
static void layerInitMinMax_propfloat2(void *vmin, void *vmax)
{
float2 &min = *static_cast<float2 *>(vmin);
float2 &max = *static_cast<float2 *>(vmax);
INIT_MINMAX2(min, max);
}
static void layerDoMinMax_propfloat2(const void *data, void *vmin, void *vmax)
{
const float2 &value = *static_cast<const float2 *>(data);
float2 &a = *static_cast<float2 *>(vmin);
float2 &b = *static_cast<float2 *>(vmax);
blender::math::min_max(value, a, b);
}
static void layerCopyValue_propfloat2(const void *source,
void *dest,
const int mixmode,
const float mixfactor)
{
const float2 &a = *static_cast<const float2 *>(source);
float2 &b = *static_cast<float2 *>(dest);
/* We only support a limited subset of advanced mixing here-
* namely the mixfactor interpolation. */
if (mixmode == CDT_MIX_NOMIX) {
b = a;
}
else {
b = blender::math::interpolate(b, a, mixfactor);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (`bool`, #CD_PROP_BOOL)
* \{ */
static void layerInterp_propbool(const void **sources, const float *weights, int count, void *dest)
{
bool result = false;
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const bool src = *(const bool *)sources[i];
result |= src && (interp_weight > 0.0f);
}
*(bool *)dest = result;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#math::Quaternion, #CD_PROP_QUATERNION)
* \{ */
static void layerDefault_propquaternion(void *data, const int count)
{
using namespace blender;
MutableSpan(static_cast<math::Quaternion *>(data), count).fill(math::Quaternion::identity());
}
static void layerInterp_propquaternion(const void **sources,
const float *weights,
int count,
void *dest)
{
using blender::math::Quaternion;
Quaternion result;
blender::bke::attribute_math::DefaultMixer<Quaternion> mixer({&result, 1},
Quaternion::identity());
for (int i = 0; i < count; i++) {
const float interp_weight = weights[i];
const Quaternion *src = static_cast<const Quaternion *>(sources[i]);
mixer.mix_in(0, *src, interp_weight);
}
mixer.finalize();
*static_cast<Quaternion *>(dest) = result;
}
/* -------------------------------------------------------------------- */
/** \name Callbacks for (#math::Quaternion, #CD_PROP_FLOAT4X4)
* \{ */
static void layerDefault_propfloat4x4(void *data, const int count)
{
using namespace blender;
MutableSpan(static_cast<float4x4 *>(data), count).fill(float4x4::identity());
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Layer Type Information (#LAYERTYPEINFO)
* \{ */
static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 0: CD_MVERT */ /* DEPRECATED */
{sizeof(MVert),
alignof(MVert),
"MVert",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 1: CD_MSTICKY */ /* DEPRECATED */
{sizeof(float[2]),
alignof(float2),
"",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 2: CD_MDEFORMVERT */
{sizeof(MDeformVert),
alignof(MDeformVert),
"MDeformVert",
1,
nullptr,
layerCopy_mdeformvert,
layerFree_mdeformvert,
layerInterp_mdeformvert,
nullptr,
nullptr,
layerConstruct_mdeformvert},
/* 3: CD_MEDGE */ /* DEPRECATED */
{sizeof(MEdge),
alignof(MEdge),
"MEdge",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 4: CD_MFACE */
{sizeof(MFace),
alignof(MFace),
"MFace",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 5: CD_MTFACE */
{sizeof(MTFace),
alignof(MTFace),
"MTFace",
1,
N_("UVMap"),
layerCopy_tface,
nullptr,
layerInterp_tface,
layerSwap_tface,
nullptr,
layerDefault_tface,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
layerMaxNum_tface},
/* 6: CD_MCOL */
/* 4 MCol structs per face */
{sizeof(MCol[4]),
alignof(MCol[4]),
"MCol",
4,
N_("Col"),
nullptr,
nullptr,
layerInterp_mcol,
layerSwap_mcol,
layerDefault_mcol},
/* 7: CD_ORIGINDEX */
{sizeof(int),
alignof(int),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
layerDefault_origindex},
/* 8: CD_NORMAL */
/* 3 floats per normal vector */
{sizeof(float[3]),
alignof(blender::float3),
"vec3f",
1,
nullptr,
nullptr,
nullptr,
layerInterp_normal,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
layerCopyValue_normal},
/* 9: CD_FACEMAP */ /* DEPRECATED */
{sizeof(int), alignof(int), ""},
/* 10: CD_PROP_FLOAT */
{sizeof(MFloatProperty),
alignof(float),
"MFloatProperty",
1,
N_("Float"),
layerCopy_propFloat,
nullptr,
layerInterp_propFloat,
nullptr,
nullptr,
nullptr,
layerValidate_propFloat},
/* 11: CD_PROP_INT32 */
{sizeof(MIntProperty),
alignof(int),
"MIntProperty",
1,
N_("Int"),
nullptr,
nullptr,
layerInterp_propInt,
nullptr},
/* 12: CD_PROP_STRING */
{sizeof(MStringProperty),
alignof(MStringProperty),
"MStringProperty",
1,
N_("String"),
layerCopy_propString,
nullptr,
nullptr,
nullptr},
/* 13: CD_ORIGSPACE */
{sizeof(OrigSpaceFace),
alignof(OrigSpaceFace),
"OrigSpaceFace",
1,
N_("UVMap"),
layerCopy_origspace_face,
nullptr,
layerInterp_origspace_face,
layerSwap_origspace_face,
layerDefault_origspace_face},
/* 14: CD_ORCO */
{sizeof(float[3]),
alignof(blender::float3),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 15: CD_MTEXPOLY */ /* DEPRECATED */
/* NOTE: when we expose the UV Map / TexFace split to the user,
* change this back to face Texture. */
{sizeof(int), alignof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 16: CD_MLOOPUV */ /* DEPRECATED */
{sizeof(MLoopUV), alignof(MLoopUV), "MLoopUV", 1, N_("UVMap")},
/* 17: CD_PROP_BYTE_COLOR */
{sizeof(MLoopCol),
alignof(MLoopCol),
"MLoopCol",
1,
N_("Col"),
nullptr,
nullptr,
layerInterp_mloopcol,
nullptr,
layerDefault_mloopcol,
nullptr,
nullptr,
layerEqual_mloopcol,
layerMultiply_mloopcol,
layerInitMinMax_mloopcol,
layerAdd_mloopcol,
layerDoMinMax_mloopcol,
layerCopyValue_mloopcol,
nullptr,
nullptr,
nullptr,
nullptr},
/* 18: CD_TANGENT */ /* DEPRECATED */
{sizeof(float[4]), alignof(float[4]), "", 0, N_("Tangent")},
/* 19: CD_MDISPS */
{sizeof(MDisps),
alignof(MDisps),
"MDisps",
1,
nullptr,
layerCopy_mdisps,
layerFree_mdisps,
nullptr,
layerSwap_mdisps,
nullptr,
layerConstruct_mdisps,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
layerRead_mdisps,
layerWrite_mdisps,
layerFilesize_mdisps},
/* 20: CD_PREVIEW_MCOL */
{sizeof(blender::float4x4),
alignof(blender::float4x4),
"mat4x4f",
1,
N_("4 by 4 Float Matrix"),
nullptr,
nullptr,
nullptr,
nullptr,
layerDefault_propfloat4x4},
/* 21: CD_ID_MCOL */ /* DEPRECATED */
{sizeof(MCol[4]),
alignof(MCol[4]),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 22: CD_PROP_INT16_2D */
{sizeof(blender::short2),
alignof(blender::short2),
"vec2s",
1,
N_("2D 16-Bit Integer"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 23: CD_CLOTH_ORCO */
{sizeof(float[3]),
alignof(float[3]),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 24: CD_RECAST */
{sizeof(MRecast),
alignof(MRecast),
"MRecast",
1,
N_("Recast"),
nullptr,
nullptr,
nullptr,
nullptr},
/* 25: CD_MPOLY */ /* DEPRECATED */
{sizeof(MPoly),
alignof(MPoly),
"MPoly",
1,
N_("NGon Face"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 26: CD_MLOOP */ /* DEPRECATED */
{sizeof(MLoop),
alignof(MLoop),
"MLoop",
1,
N_("NGon Face-Vertex"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 27: CD_SHAPE_KEYINDEX */
{sizeof(int), alignof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 28: CD_SHAPEKEY */
{sizeof(float[3]),
alignof(float[3]),
"",
0,
N_("ShapeKey"),
nullptr,
nullptr,
layerInterp_shapekey},
/* 29: CD_BWEIGHT */ /* DEPRECATED */
{sizeof(MFloatProperty), alignof(MFloatProperty), "MFloatProperty", 1},
/* 30: CD_CREASE */ /* DEPRECATED */
{sizeof(float), alignof(float), ""},
/* 31: CD_ORIGSPACE_MLOOP */
{sizeof(OrigSpaceLoop),
alignof(OrigSpaceLoop),
"OrigSpaceLoop",
1,
N_("OS Loop"),
nullptr,
nullptr,
layerInterp_mloop_origspace,
nullptr,
nullptr,
nullptr,
nullptr,
layerEqual_mloop_origspace,
layerMultiply_mloop_origspace,
layerInitMinMax_mloop_origspace,
layerAdd_mloop_origspace,
layerDoMinMax_mloop_origspace,
layerCopyValue_mloop_origspace},
/* 32: CD_PREVIEW_MLOOPCOL */ /* DEPRECATED */ /* UNUSED */
{},
/* 33: CD_BM_ELEM_PYPTR */
{sizeof(void *),
alignof(void *),
"",
1,
nullptr,
layerCopy_bmesh_elem_py_ptr,
layerFree_bmesh_elem_py_ptr,
nullptr,
nullptr,
nullptr},
/* 34: CD_PAINT_MASK */ /* DEPRECATED */
{sizeof(float), alignof(float), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 35: CD_GRID_PAINT_MASK */
{sizeof(GridPaintMask),
alignof(GridPaintMask),
"GridPaintMask",
1,
nullptr,
layerCopy_grid_paint_mask,
layerFree_grid_paint_mask,
nullptr,
nullptr,
nullptr,
layerConstruct_grid_paint_mask},
/* 36: CD_MVERT_SKIN */
{sizeof(MVertSkin),
alignof(MVertSkin),
"MVertSkin",
1,
nullptr,
layerCopy_mvert_skin,
nullptr,
layerInterp_mvert_skin,
nullptr,
layerDefault_mvert_skin},
/* 37: CD_FREESTYLE_EDGE */ /* DEPRECATED */
{sizeof(FreestyleEdge), alignof(FreestyleEdge), "FreestyleEdge", 1},
/* 38: CD_FREESTYLE_FACE */ /* DEPRECATED */
{sizeof(FreestyleFace), alignof(FreestyleFace), "FreestyleFace", 1},
/* 39: CD_MLOOPTANGENT */
{sizeof(float[4]),
alignof(float[4]),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 40: CD_TESSLOOPNORMAL */
{sizeof(short[4][3]),
alignof(short[4][3]),
"",
0,
nullptr,
nullptr,
nullptr,
nullptr,
layerSwap_flnor,
nullptr},
/* 41: CD_CUSTOMLOOPNORMAL */ /* DEPRECATED */
{sizeof(short[2]), alignof(short[2]), "vec2s", 1},
/* 42: CD_SCULPT_FACE_SETS */ /* DEPRECATED */
{sizeof(int), alignof(int), ""},
/* 43: CD_LOCATION */
{sizeof(float[3]),
alignof(float[3]),
"vec3f",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 44: CD_RADIUS */
{sizeof(float),
alignof(float),
"MFloatProperty",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 45: CD_PROP_INT8 */
{sizeof(int8_t),
alignof(int8_t),
"MInt8Property",
1,
N_("Int8"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 46: CD_PROP_INT32_2D */
{sizeof(blender::int2),
alignof(blender::int2),
"vec2i",
1,
N_("Int 2D"),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 47: CD_PROP_COLOR */
{sizeof(MPropCol),
alignof(MPropCol),
"MPropCol",
1,
N_("Color"),
nullptr,
nullptr,
layerInterp_propcol,
nullptr,
layerDefault_propcol,
nullptr,
nullptr,
layerEqual_propcol,
layerMultiply_propcol,
layerInitMinMax_propcol,
layerAdd_propcol,
layerDoMinMax_propcol,
layerCopyValue_propcol,
nullptr,
nullptr,
nullptr,
nullptr},
/* 48: CD_PROP_FLOAT3 */
{sizeof(float[3]),
alignof(blender::float3),
"vec3f",
1,
N_("Float3"),
nullptr,
nullptr,
layerInterp_propfloat3,
nullptr,
nullptr,
nullptr,
layerValidate_propfloat3,
nullptr,
layerMultiply_propfloat3,
nullptr,
layerAdd_propfloat3},
/* 49: CD_PROP_FLOAT2 */
{sizeof(float[2]),
alignof(float2),
"vec2f",
1,
N_("Float2"),
nullptr,
nullptr,
layerInterp_propfloat2,
nullptr,
nullptr,
nullptr,
layerValidate_propfloat2,
layerEqual_propfloat2,
layerMultiply_propfloat2,
layerInitMinMax_propfloat2,
layerAdd_propfloat2,
layerDoMinMax_propfloat2,
layerCopyValue_propfloat2},
/* 50: CD_PROP_BOOL */
{sizeof(bool),
alignof(bool),
"bool",
1,
N_("Boolean"),
nullptr,
nullptr,
layerInterp_propbool,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 51: CD_HAIRLENGTH */ /* DEPRECATED */ /* UNUSED */
{sizeof(float),
alignof(float),
"float",
1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr},
/* 52: CD_PROP_QUATERNION */
{sizeof(float[4]),
alignof(blender::float4),
"vec4f",
1,
N_("Quaternion"),
nullptr,
nullptr,
layerInterp_propquaternion,
nullptr,
layerDefault_propquaternion},
};
static_assert(sizeof(mat4x4f) == sizeof(blender::float4x4));
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
/* 0-4 */ "CDMVert",
"CDMSticky",
"CDMDeformVert",
"CDMEdge",
"CDMFace",
/* 5-9 */ "CDMTFace",
"CDMCol",
"CDOrigIndex",
"CDNormal",
"CDFaceMap",
/* 10-14 */ "CDMFloatProperty",
"CDMIntProperty",
"CDMStringProperty",
"CDOrigSpace",
"CDOrco",
/* 15-19 */ "CDMTexPoly",
"CDMLoopUV",
"CDMloopCol",
"CDTangent",
"CDMDisps",
/* 20-24 */ "CDPreviewMCol",
"CDIDMCol",
"CDTextureMCol",
"CDClothOrco",
"CDMRecast",
/* BMESH ONLY */
/* 25-29 */ "CDMPoly",
"CDMLoop",
"CDShapeKeyIndex",
"CDShapeKey",
"CDBevelWeight",
/* 30-34 */ "CDSubSurfCrease",
"CDOrigSpaceLoop",
"CDPreviewLoopCol",
"CDBMElemPyPtr",
"CDPaintMask",
/* 35-36 */ "CDGridPaintMask",
"CDMVertSkin",
/* 37-38 */ "CDFreestyleEdge",
"CDFreestyleFace",
/* 39-42 */ "CDMLoopTangent",
"CDTessLoopNormal",
"CDCustomLoopNormal",
"CDSculptFaceGroups",
/* 43-46 */ "CDHairPoint",
"CDPropInt8",
"CDHairMapping",
"CDPoint",
"CDPropCol",
"CDPropFloat3",
"CDPropFloat2",
"CDPropBoolean",
"CDHairLength",
"CDPropQuaternion",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
/*vmask*/ CD_MASK_PROP_FLOAT3,
/*emask*/ CD_MASK_PROP_INT32_2D,
/*fmask*/ 0,
/*pmask*/ 0,
/*lmask*/ CD_MASK_PROP_INT32,
};
const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = {
/*vmask*/ CD_MASK_PROP_FLOAT3 | CD_MASK_ORIGINDEX,
/*emask*/ CD_MASK_PROP_INT32_2D | CD_MASK_ORIGINDEX,
/*fmask*/ 0,
/*pmask*/ CD_MASK_ORIGINDEX,
/*lmask*/ CD_MASK_PROP_INT32,
};
const CustomData_MeshMasks CD_MASK_MESH = {
/*vmask*/ (CD_MASK_PROP_FLOAT3 | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PROP_ALL),
/*emask*/
CD_MASK_PROP_ALL,
/*fmask*/ 0,
/*pmask*/
CD_MASK_PROP_ALL,
/*lmask*/
(CD_MASK_MDISPS | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
};
const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
/*vmask*/ (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN |
CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL),
/*emask*/
(CD_MASK_ORIGINDEX | CD_MASK_PROP_ALL),
/*fmask*/ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE),
/*pmask*/
(CD_MASK_ORIGINDEX | CD_MASK_PROP_ALL),
/*lmask*/
(CD_MASK_ORIGSPACE_MLOOP | CD_MASK_PROP_ALL), /* XXX: MISSING #CD_MASK_MLOOPTANGENT ? */
};
const CustomData_MeshMasks CD_MASK_BMESH = {
/*vmask*/ (CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PROP_ALL),
/*emask*/ CD_MASK_PROP_ALL,
/*fmask*/ 0,
/*pmask*/
CD_MASK_PROP_ALL,
/*lmask*/
(CD_MASK_MDISPS | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
};
const CustomData_MeshMasks CD_MASK_EVERYTHING = {
/*vmask*/ (CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT |
CD_MASK_MVERT_SKIN | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY |
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PROP_ALL),
/*emask*/
(CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_PROP_ALL),
/*fmask*/
(CD_MASK_MFACE | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_MTFACE | CD_MASK_MCOL |
CD_MASK_ORIGSPACE | CD_MASK_TESSLOOPNORMAL | CD_MASK_PROP_ALL),
/*pmask*/
(CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_PROP_ALL),
/*lmask*/
(CD_MASK_BM_ELEM_PYPTR | CD_MASK_MDISPS | CD_MASK_NORMAL | CD_MASK_MLOOPTANGENT |
CD_MASK_ORIGSPACE_MLOOP | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
};
static const LayerTypeInfo *layerType_getInfo(const eCustomDataType type)
{
if (type < 0 || type >= CD_NUMTYPES) {
return nullptr;
}
return &LAYERTYPEINFO[type];
}
static const char *layerType_getName(const eCustomDataType type)
{
if (type < 0 || type >= CD_NUMTYPES) {
return nullptr;
}
return LAYERTYPENAMES[type];
}
void customData_mask_layers__print(const CustomData_MeshMasks *mask)
{
printf("verts mask=0x%" PRIx64 ":\n", mask->vmask);
for (int i = 0; i < CD_NUMTYPES; i++) {
if (mask->vmask & CD_TYPE_AS_MASK(eCustomDataType(i))) {
printf(" %s\n", layerType_getName(eCustomDataType(i)));
}
}
printf("edges mask=0x%" PRIx64 ":\n", mask->emask);
for (int i = 0; i < CD_NUMTYPES; i++) {
if (mask->emask & CD_TYPE_AS_MASK(eCustomDataType(i))) {
printf(" %s\n", layerType_getName(eCustomDataType(i)));
}
}
printf("faces mask=0x%" PRIx64 ":\n", mask->fmask);
for (int i = 0; i < CD_NUMTYPES; i++) {
if (mask->fmask & CD_TYPE_AS_MASK(eCustomDataType(i))) {
printf(" %s\n", layerType_getName(eCustomDataType(i)));
}
}
printf("loops mask=0x%" PRIx64 ":\n", mask->lmask);
for (int i = 0; i < CD_NUMTYPES; i++) {
if (mask->lmask & CD_TYPE_AS_MASK(eCustomDataType(i))) {
printf(" %s\n", layerType_getName(eCustomDataType(i)));
}
}
printf("polys mask=0x%" PRIx64 ":\n", mask->pmask);
for (int i = 0; i < CD_NUMTYPES; i++) {
if (mask->pmask & CD_TYPE_AS_MASK(eCustomDataType(i))) {
printf(" %s\n", layerType_getName(eCustomDataType(i)));
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name CustomData Functions
* \{ */
static void customData_update_offsets(CustomData *data);
static CustomDataLayer *customData_add_layer__internal(
CustomData *data,
eCustomDataType type,
std::optional<eCDAllocType> alloctype,
void *layer_data_to_assign,
const ImplicitSharingInfo *sharing_info_to_assign,
int totelem,
const StringRef name);
void CustomData_update_typemap(CustomData *data)
{
int lasttype = -1;
for (int i = 0; i < CD_NUMTYPES; i++) {
data->typemap[i] = -1;
}
for (int i = 0; i < data->totlayer; i++) {
const eCustomDataType type = eCustomDataType(data->layers[i].type);
if (type != lasttype) {
data->typemap[type] = i;
lasttype = type;
}
}
}
/* currently only used in BLI_assert */
#ifndef NDEBUG
static bool customdata_typemap_is_valid(const CustomData *data)
{
CustomData data_copy = *data;
CustomData_update_typemap(&data_copy);
return (memcmp(data->typemap, data_copy.typemap, sizeof(data->typemap)) == 0);
}
#endif
static void *copy_layer_data(const eCustomDataType type, const void *data, const int totelem)
{
const LayerTypeInfo &type_info = *layerType_getInfo(type);
const int64_t size_in_bytes = int64_t(totelem) * type_info.size;
void *new_data = MEM_mallocN_aligned(size_in_bytes, type_info.alignment, __func__);
if (type_info.copy) {
type_info.copy(data, new_data, totelem);
}
else {
memcpy(new_data, data, size_in_bytes);
}
return new_data;
}
static void free_layer_data(const eCustomDataType type, const void *data, const int totelem)
{
const LayerTypeInfo &type_info = *layerType_getInfo(type);
if (type_info.free) {
type_info.free(const_cast<void *>(data), totelem);
}
MEM_freeN(const_cast<void *>(data));
}
static bool customdata_merge_internal(const CustomData *source,
CustomData *dest,
const eCustomDataMask mask,
const std::optional<eCDAllocType> alloctype,
const int totelem)
{
bool changed = false;
int last_type = -1;
int last_active = 0;
int last_render = 0;
int last_clone = 0;
int last_mask = 0;
int current_type_layer_count = 0;
int max_current_type_layer_count = -1;
for (int i = 0; i < source->totlayer; i++) {
const CustomDataLayer &src_layer = source->layers[i];
const eCustomDataType type = eCustomDataType(src_layer.type);
const int src_layer_flag = src_layer.flag;
if (type != last_type) {
/* Don't exceed layer count on destination. */
const int layernum_dst = CustomData_number_of_layers(dest, type);
current_type_layer_count = layernum_dst;
max_current_type_layer_count = CustomData_layertype_layers_max(type);
last_active = src_layer.active;
last_render = src_layer.active_rnd;
last_clone = src_layer.active_clone;
last_mask = src_layer.active_mask;
last_type = type;
}
else {
current_type_layer_count++;
}
if (src_layer_flag & CD_FLAG_NOCOPY) {
/* Don't merge this layer because it's not supposed to leave the source data. */
continue;
}
if (!(mask & CD_TYPE_AS_MASK(type))) {
/* Don't merge this layer because it does not match the type mask. */
continue;
}
if ((max_current_type_layer_count != -1) &&
(current_type_layer_count >= max_current_type_layer_count))
{
/* Don't merge this layer because the maximum amount of layers of this type is reached. */
continue;
}
if (CustomData_get_named_layer_index(dest, type, src_layer.name) != -1) {
/* Don't merge this layer because it exists in the destination already. */
continue;
}
void *layer_data_to_assign = nullptr;
const ImplicitSharingInfo *sharing_info_to_assign = nullptr;
if (!alloctype.has_value()) {
if (src_layer.data != nullptr) {
if (src_layer.sharing_info == nullptr) {
/* Can't share the layer, duplicate it instead. */
layer_data_to_assign = copy_layer_data(type, src_layer.data, totelem);
}
else {
/* Share the layer. */
layer_data_to_assign = src_layer.data;
sharing_info_to_assign = src_layer.sharing_info;
}
}
}
CustomDataLayer *new_layer = customData_add_layer__internal(dest,
type,
alloctype,
layer_data_to_assign,
sharing_info_to_assign,
totelem,
src_layer.name);
new_layer->uid = src_layer.uid;
new_layer->flag |= src_layer_flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
new_layer->active = last_active;
new_layer->active_rnd = last_render;
new_layer->active_clone = last_clone;
new_layer->active_mask = last_mask;
changed = true;
}
CustomData_update_typemap(dest);
return changed;
}
bool CustomData_merge(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
int totelem)
{
return customdata_merge_internal(source, dest, mask, std::nullopt, totelem);
}
bool CustomData_merge_layout(const CustomData *source,
CustomData *dest,
const eCustomDataMask mask,
const eCDAllocType alloctype,
const int totelem)
{
return customdata_merge_internal(source, dest, mask, alloctype, totelem);
}
CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData *src,
const eCustomDataMask mask)
{
Vector<CustomDataLayer> dst_layers;
for (const CustomDataLayer &layer : Span<CustomDataLayer>{src->layers, src->totlayer}) {
if (BM_attribute_stored_in_bmesh_builtin(layer.name)) {
continue;
}
if (!(mask & CD_TYPE_AS_MASK(eCustomDataType(layer.type)))) {
continue;
}
dst_layers.append(layer);
}
CustomData dst = *src;
dst.layers = MEM_calloc_arrayN<CustomDataLayer>(dst_layers.size(), __func__);
dst.maxlayer = dst.totlayer = dst_layers.size();
memcpy(dst.layers, dst_layers.data(), dst_layers.as_span().size_in_bytes());
CustomData_update_typemap(&dst);
return dst;
}
/**
* An #ImplicitSharingInfo that knows how to free the entire referenced custom data layer
* (including potentially separately allocated chunks like for vertex groups).
*/
class CustomDataLayerImplicitSharing : public ImplicitSharingInfo {
private:
const void *data_;
int totelem_;
const eCustomDataType type_;
public:
CustomDataLayerImplicitSharing(const void *data, const int totelem, const eCustomDataType type)
: ImplicitSharingInfo(), data_(data), totelem_(totelem), type_(type)
{
}
private:
void delete_self_with_data() override
{
if (data_ != nullptr) {
free_layer_data(type_, data_, totelem_);
}
MEM_delete(this);
}
void delete_data_only() override
{
free_layer_data(type_, data_, totelem_);
data_ = nullptr;
totelem_ = 0;
}
};
/** Create a #ImplicitSharingInfo that takes ownership of the data. */
static const ImplicitSharingInfo *make_implicit_sharing_info_for_layer(const eCustomDataType type,
const void *data,
const int totelem)
{
return MEM_new<CustomDataLayerImplicitSharing>(__func__, data, totelem, type);
}
/**
* If the layer data is currently shared (hence it is immutable), create a copy that can be edited.
*/
static void ensure_layer_data_is_mutable(CustomDataLayer &layer, const int totelem)
{
if (layer.data == nullptr) {
return;
}
BLI_assert(layer.sharing_info != nullptr);
if (layer.sharing_info->is_mutable()) {
layer.sharing_info->tag_ensured_mutable();
}
else {
const eCustomDataType type = eCustomDataType(layer.type);
const void *old_data = layer.data;
/* Copy the layer before removing the user because otherwise the data might be freed while
* we're still copying from it here. */
layer.data = copy_layer_data(type, old_data, totelem);
layer.sharing_info->remove_user_and_delete_if_last();
layer.sharing_info = make_implicit_sharing_info_for_layer(type, layer.data, totelem);
}
}
[[maybe_unused]] static bool layer_is_mutable(CustomDataLayer &layer)
{
if (!layer.data) {
return true;
}
return layer.sharing_info->is_mutable();
}
void CustomData_ensure_data_is_mutable(CustomDataLayer *layer, const int totelem)
{
ensure_layer_data_is_mutable(*layer, totelem);
}
void CustomData_ensure_layers_are_mutable(CustomData *data, int totelem)
{
for (const int i : IndexRange(data->totlayer)) {
ensure_layer_data_is_mutable(data->layers[i], totelem);
}
}
void CustomData_realloc(CustomData *data,
const int old_size,
const int new_size,
const eCDAllocType alloctype)
{
BLI_assert(new_size >= 0);
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
const int64_t old_size_in_bytes = int64_t(old_size) * typeInfo->size;
const int64_t new_size_in_bytes = int64_t(new_size) * typeInfo->size;
void *new_layer_data = (new_size > 0) ? MEM_mallocN_aligned(
new_size_in_bytes, typeInfo->alignment, __func__) :
nullptr;
if (old_size_in_bytes > 0) {
if (new_layer_data != nullptr) {
/* Copy data to new array. */
if (typeInfo->copy) {
typeInfo->copy(layer->data, new_layer_data, std::min(old_size, new_size));
}
else {
BLI_assert(layer->data != nullptr);
memcpy(new_layer_data, layer->data, std::min(old_size_in_bytes, new_size_in_bytes));
}
}
BLI_assert(layer->sharing_info != nullptr);
layer->sharing_info->remove_user_and_delete_if_last();
layer->sharing_info = nullptr;
}
/* Take ownership of new array. */
layer->data = new_layer_data;
if (layer->data) {
layer->sharing_info = make_implicit_sharing_info_for_layer(
eCustomDataType(layer->type), layer->data, new_size);
}
if (new_size > old_size) {
const int new_elements_num = new_size - old_size;
void *new_elements_begin = POINTER_OFFSET(layer->data, old_size_in_bytes);
switch (alloctype) {
case CD_CONSTRUCT: {
/* Initialize new values for non-trivial types. */
if (typeInfo->construct) {
typeInfo->construct(new_elements_begin, new_elements_num);
}
break;
}
case CD_SET_DEFAULT: {
if (typeInfo->set_default_value) {
typeInfo->set_default_value(new_elements_begin, new_elements_num);
}
else {
memset(new_elements_begin, 0, typeInfo->size * new_elements_num);
}
break;
}
}
}
}
}
void CustomData_init_from(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
int totelem)
{
CustomData_reset(dest);
if (source->external) {
dest->external = static_cast<CustomDataExternal *>(MEM_dupallocN(source->external));
}
CustomData_merge(source, dest, mask, totelem);
}
void CustomData_init_layout_from(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
int totelem)
{
CustomData_reset(dest);
if (source->external) {
dest->external = static_cast<CustomDataExternal *>(MEM_dupallocN(source->external));
}
CustomData_merge_layout(source, dest, mask, alloctype, totelem);
}
static void customData_free_layer__internal(CustomDataLayer *layer)
{
if (!layer->sharing_info) {
BLI_assert(!layer->data);
return;
}
layer->sharing_info->remove_user_and_delete_if_last();
layer->sharing_info = nullptr;
}
static void CustomData_external_free(CustomData *data)
{
if (data->external) {
MEM_freeN(data->external);
data->external = nullptr;
}
}
void CustomData_reset(CustomData *data)
{
*data = CustomData{};
copy_vn_i(data->typemap, CD_NUMTYPES, -1);
}
void CustomData_free(CustomData *data)
{
for (int i = 0; i < data->totlayer; i++) {
customData_free_layer__internal(&data->layers[i]);
}
if (data->layers) {
MEM_freeN(data->layers);
}
CustomData_external_free(data);
CustomData_reset(data);
}
static void customData_update_offsets(CustomData *data)
{
const LayerTypeInfo *typeInfo;
int offset = 0;
for (int i = 0; i < data->totlayer; i++) {
typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type));
data->layers[i].offset = offset;
offset += typeInfo->size;
}
data->totsize = offset;
CustomData_update_typemap(data);
}
/* to use when we're in the middle of modifying layers */
static int CustomData_get_layer_index__notypemap(const CustomData *data,
const eCustomDataType type)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
return i;
}
}
return -1;
}
/* -------------------------------------------------------------------- */
/* index values to access the layers (offset from the layer start) */
int CustomData_get_layer_index(const CustomData *data, const eCustomDataType type)
{
BLI_assert(customdata_typemap_is_valid(data));
return data->typemap[type];
}
int CustomData_get_layer_index_n(const CustomData *data, const eCustomDataType type, const int n)
{
BLI_assert(n >= 0);
int i = CustomData_get_layer_index(data, type);
if (i != -1) {
/* If the value of n goes past the block of layers of the correct type, return -1. */
i = (i + n < data->totlayer && data->layers[i + n].type == type) ? (i + n) : (-1);
}
return i;
}
int CustomData_get_named_layer_index(const CustomData *data,
const eCustomDataType type,
const StringRef name)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
if (data->layers[i].name == name) {
return i;
}
}
}
return -1;
}
int CustomData_get_named_layer_index_notype(const CustomData *data, const StringRef name)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].name == name) {
return i;
}
}
return -1;
}
int CustomData_get_active_layer_index(const CustomData *data, const eCustomDataType type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? layer_index + data->layers[layer_index].active : -1;
}
int CustomData_get_render_layer_index(const CustomData *data, const eCustomDataType type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? layer_index + data->layers[layer_index].active_rnd : -1;
}
int CustomData_get_clone_layer_index(const CustomData *data, const eCustomDataType type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? layer_index + data->layers[layer_index].active_clone : -1;
}
int CustomData_get_stencil_layer_index(const CustomData *data, const eCustomDataType type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? layer_index + data->layers[layer_index].active_mask : -1;
}
/* -------------------------------------------------------------------- */
/* index values per layer type */
int CustomData_get_named_layer(const CustomData *data,
const eCustomDataType type,
const StringRef name)
{
const int named_index = CustomData_get_named_layer_index(data, type, name);
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (named_index != -1) ? named_index - layer_index : -1;
}
int CustomData_get_active_layer(const CustomData *data, const eCustomDataType type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? data->layers[layer_index].active : -1;
}
int CustomData_get_render_layer(const CustomData *data, const eCustomDataType type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? data->layers[layer_index].active_rnd : -1;
}
int CustomData_get_clone_layer(const CustomData *data, const eCustomDataType type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? data->layers[layer_index].active_clone : -1;
}
int CustomData_get_stencil_layer(const CustomData *data, const eCustomDataType type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? data->layers[layer_index].active_mask : -1;
}
const char *CustomData_get_active_layer_name(const CustomData *data, const eCustomDataType type)
{
/* Get the layer index of the active layer of this type. */
const int layer_index = CustomData_get_active_layer_index(data, type);
return layer_index < 0 ? nullptr : data->layers[layer_index].name;
}
const char *CustomData_get_render_layer_name(const CustomData *data, const eCustomDataType type)
{
const int layer_index = CustomData_get_render_layer_index(data, type);
return layer_index < 0 ? nullptr : data->layers[layer_index].name;
}
void CustomData_set_layer_active(CustomData *data, const eCustomDataType type, const int n)
{
#ifndef NDEBUG
const int layer_num = CustomData_number_of_layers(data, type);
#endif
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
BLI_assert(uint(n) < uint(layer_num));
data->layers[i].active = n;
}
}
}
void CustomData_set_layer_render(CustomData *data, const eCustomDataType type, const int n)
{
#ifndef NDEBUG
const int layer_num = CustomData_number_of_layers(data, type);
#endif
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
BLI_assert(uint(n) < uint(layer_num));
data->layers[i].active_rnd = n;
}
}
}
void CustomData_set_layer_clone(CustomData *data, const eCustomDataType type, const int n)
{
#ifndef NDEBUG
const int layer_num = CustomData_number_of_layers(data, type);
#endif
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
BLI_assert(uint(n) < uint(layer_num));
data->layers[i].active_clone = n;
}
}
}
void CustomData_set_layer_stencil(CustomData *data, const eCustomDataType type, const int n)
{
#ifndef NDEBUG
const int layer_num = CustomData_number_of_layers(data, type);
#endif
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
BLI_assert(uint(n) < uint(layer_num));
data->layers[i].active_mask = n;
}
}
}
void CustomData_set_layer_active_index(CustomData *data, const eCustomDataType type, const int n)
{
#ifndef NDEBUG
const int layer_num = CustomData_number_of_layers(data, type);
#endif
const int layer_index = n - data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
BLI_assert(uint(layer_index) < uint(layer_num));
data->layers[i].active = layer_index;
}
}
}
void CustomData_set_layer_render_index(CustomData *data, const eCustomDataType type, const int n)
{
#ifndef NDEBUG
const int layer_num = CustomData_number_of_layers(data, type);
#endif
const int layer_index = n - data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
BLI_assert(uint(layer_index) < uint(layer_num));
data->layers[i].active_rnd = layer_index;
}
}
}
void CustomData_set_layer_clone_index(CustomData *data, const eCustomDataType type, const int n)
{
#ifndef NDEBUG
const int layer_num = CustomData_number_of_layers(data, type);
#endif
const int layer_index = n - data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
BLI_assert(uint(layer_index) < uint(layer_num));
data->layers[i].active_clone = layer_index;
}
}
}
void CustomData_set_layer_flag(CustomData *data, const eCustomDataType type, const int flag)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
data->layers[i].flag |= flag;
}
}
}
bool CustomData_layer_is_anonymous(const CustomData *data, eCustomDataType type, int n)
{
const int layer_index = CustomData_get_layer_index_n(data, type, n);
BLI_assert(layer_index >= 0);
return blender::bke::attribute_name_is_anonymous(data->layers[layer_index].name);
}
static void customData_resize(CustomData *data, const int grow_amount)
{
data->layers = static_cast<CustomDataLayer *>(
MEM_reallocN(data->layers, (data->maxlayer + grow_amount) * sizeof(CustomDataLayer)));
data->maxlayer += grow_amount;
}
static CustomDataLayer *customData_add_layer__internal(
CustomData *data,
const eCustomDataType type,
const std::optional<eCDAllocType> alloctype,
void *layer_data_to_assign,
const ImplicitSharingInfo *sharing_info_to_assign,
const int totelem,
StringRef name)
{
const LayerTypeInfo &type_info = *layerType_getInfo(type);
int flag = 0;
/* Some layer types only support a single layer. */
if (!type_info.defaultname && CustomData_has_layer(data, type)) {
/* This function doesn't support dealing with existing layer data for these layer types when
* the layer already exists. */
BLI_assert(layer_data_to_assign == nullptr);
return &data->layers[CustomData_get_layer_index(data, type)];
}
int index = data->totlayer;
if (index >= data->maxlayer) {
customData_resize(data, CUSTOMDATA_GROW);
}
data->totlayer++;
/* Keep layers ordered by type. */
for (; index > 0 && data->layers[index - 1].type > type; index--) {
data->layers[index] = data->layers[index - 1];
}
CustomDataLayer &new_layer = data->layers[index];
/* Clear remaining data on the layer. The original data on the layer has been moved to another
* index. Without this, it can happen that information from the previous layer at that index
* leaks into the new layer. */
new_layer = CustomDataLayer{};
const int64_t size_in_bytes = int64_t(totelem) * type_info.size;
const char *alloc_name = layerType_getName(type);
if (alloctype.has_value()) {
switch (*alloctype) {
case CD_SET_DEFAULT: {
if (totelem > 0) {
new_layer.data = MEM_mallocN_aligned(size_in_bytes, type_info.alignment, alloc_name);
if (type_info.set_default_value) {
type_info.set_default_value(new_layer.data, totelem);
}
else {
/* Alternatively, #MEM_calloc_arrayN is faster, but has no aligned version. */
memset(new_layer.data, 0, size_in_bytes);
}
}
break;
}
case CD_CONSTRUCT: {
if (totelem > 0) {
new_layer.data = MEM_mallocN_aligned(size_in_bytes, type_info.alignment, alloc_name);
if (type_info.construct) {
type_info.construct(new_layer.data, totelem);
}
}
break;
}
}
}
else {
if (totelem == 0 && sharing_info_to_assign == nullptr) {
MEM_SAFE_FREE(layer_data_to_assign);
}
else {
new_layer.data = layer_data_to_assign;
new_layer.sharing_info = sharing_info_to_assign;
if (new_layer.sharing_info) {
new_layer.sharing_info->add_user();
}
}
}
if (new_layer.data != nullptr && new_layer.sharing_info == nullptr) {
/* Make layer data shareable. */
new_layer.sharing_info = make_implicit_sharing_info_for_layer(type, new_layer.data, totelem);
}
new_layer.type = type;
new_layer.flag = flag;
/* Set default name if none exists. Note we only call DATA_() once
* we know there is a default name, to avoid overhead of locale lookups
* in the depsgraph. */
if (name.is_empty() && type_info.defaultname) {
name = DATA_(type_info.defaultname);
}
if (!name.is_empty()) {
name.copy_utf8_truncated(new_layer.name);
CustomData_set_layer_unique_name(data, index);
}
else {
new_layer.name[0] = '\0';
}
if (index > 0 && data->layers[index - 1].type == type) {
new_layer.active = data->layers[index - 1].active;
new_layer.active_rnd = data->layers[index - 1].active_rnd;
new_layer.active_clone = data->layers[index - 1].active_clone;
new_layer.active_mask = data->layers[index - 1].active_mask;
}
else {
new_layer.active = 0;
new_layer.active_rnd = 0;
new_layer.active_clone = 0;
new_layer.active_mask = 0;
}
customData_update_offsets(data);
return &data->layers[index];
}
void *CustomData_add_layer(CustomData *data,
const eCustomDataType type,
eCDAllocType alloctype,
const int totelem)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, nullptr, nullptr, totelem, typeInfo->defaultname);
CustomData_update_typemap(data);
if (layer) {
return layer->data;
}
return nullptr;
}
const void *CustomData_add_layer_with_data(CustomData *data,
const eCustomDataType type,
void *layer_data,
const int totelem,
const ImplicitSharingInfo *sharing_info)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
CustomDataLayer *layer = customData_add_layer__internal(
data, type, std::nullopt, layer_data, sharing_info, totelem, typeInfo->defaultname);
CustomData_update_typemap(data);
if (layer) {
return layer->data;
}
return nullptr;
}
void *CustomData_add_layer_named(CustomData *data,
const eCustomDataType type,
const eCDAllocType alloctype,
const int totelem,
const StringRef name)
{
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, nullptr, nullptr, totelem, name);
CustomData_update_typemap(data);
if (layer) {
return layer->data;
}
return nullptr;
}
const void *CustomData_add_layer_named_with_data(CustomData *data,
eCustomDataType type,
void *layer_data,
int totelem,
const StringRef name,
const ImplicitSharingInfo *sharing_info)
{
CustomDataLayer *layer = customData_add_layer__internal(
data, type, std::nullopt, layer_data, sharing_info, totelem, name);
CustomData_update_typemap(data);
if (layer) {
return layer->data;
}
return nullptr;
}
bool CustomData_free_layer(CustomData *data, const eCustomDataType type, const int index)
{
const int index_first = CustomData_get_layer_index(data, type);
const int n = index - index_first;
BLI_assert(index >= index_first);
if ((index_first == -1) || (n < 0)) {
return false;
}
BLI_assert(data->layers[index].type == type);
customData_free_layer__internal(&data->layers[index]);
for (int i = index + 1; i < data->totlayer; i++) {
data->layers[i - 1] = data->layers[i];
}
data->totlayer--;
/* if layer was last of type in array, set new active layer */
int i = CustomData_get_layer_index__notypemap(data, type);
if (i != -1) {
/* don't decrement zero index */
const int index_nonzero = n ? n : 1;
CustomDataLayer *layer;
for (layer = &data->layers[i]; i < data->totlayer && layer->type == type; i++, layer++) {
if (layer->active >= index_nonzero) {
layer->active--;
}
if (layer->active_rnd >= index_nonzero) {
layer->active_rnd--;
}
if (layer->active_clone >= index_nonzero) {
layer->active_clone--;
}
if (layer->active_mask >= index_nonzero) {
layer->active_mask--;
}
}
}
if (data->totlayer <= data->maxlayer - CUSTOMDATA_GROW) {
customData_resize(data, -CUSTOMDATA_GROW);
}
customData_update_offsets(data);
return true;
}
bool CustomData_free_layer_named(CustomData *data, const StringRef name)
{
for (const int i : IndexRange(data->totlayer)) {
const CustomDataLayer &layer = data->layers[i];
if (StringRef(layer.name) == name) {
CustomData_free_layer(data, eCustomDataType(layer.type), i);
return true;
}
}
return false;
}
bool CustomData_free_layer_active(CustomData *data, const eCustomDataType type)
{
const int index = CustomData_get_active_layer_index(data, type);
if (index == -1) {
return false;
}
return CustomData_free_layer(data, type, index);
}
void CustomData_free_layers(CustomData *data, const eCustomDataType type)
{
const int index = CustomData_get_layer_index(data, type);
while (CustomData_free_layer(data, type, index)) {
/* pass */
}
}
bool CustomData_has_layer_named(const CustomData *data,
const eCustomDataType type,
const StringRef name)
{
return CustomData_get_named_layer_index(data, type, name) != -1;
}
bool CustomData_has_layer(const CustomData *data, const eCustomDataType type)
{
return (CustomData_get_layer_index(data, type) != -1);
}
int CustomData_number_of_layers(const CustomData *data, const eCustomDataType type)
{
int number = 0;
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
number++;
}
}
return number;
}
int CustomData_number_of_anonymous_layers(const CustomData *data, const eCustomDataType type)
{
int number = 0;
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type &&
blender::bke::attribute_name_is_anonymous(data->layers[i].name))
{
number++;
}
}
return number;
}
int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDataMask mask)
{
int number = 0;
for (int i = 0; i < data->totlayer; i++) {
if (mask & CD_TYPE_AS_MASK(eCustomDataType(data->layers[i].type))) {
number++;
}
}
return number;
}
void CustomData_set_only_copy(const CustomData *data, const eCustomDataMask mask)
{
for (int i = 0; i < data->totlayer; i++) {
if (!(mask & CD_TYPE_AS_MASK(eCustomDataType(data->layers[i].type)))) {
data->layers[i].flag |= CD_FLAG_NOCOPY;
}
}
}
void CustomData_copy_elements(const eCustomDataType type,
const void *src_data,
void *dst_data,
const int count)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (typeInfo->copy) {
typeInfo->copy(src_data, dst_data, count);
}
else {
memcpy(dst_data, src_data, size_t(count) * typeInfo->size);
}
}
void CustomData_copy_data_layer(const CustomData *source,
CustomData *dest,
const int src_layer_index,
const int dst_layer_index,
const int src_index,
const int dst_index,
const int count)
{
const LayerTypeInfo *typeInfo;
BLI_assert(layer_is_mutable(dest->layers[dst_layer_index]));
const void *src_data = source->layers[src_layer_index].data;
void *dst_data = dest->layers[dst_layer_index].data;
typeInfo = layerType_getInfo(eCustomDataType(source->layers[src_layer_index].type));
const size_t src_offset = size_t(src_index) * typeInfo->size;
const size_t dst_offset = size_t(dst_index) * typeInfo->size;
if (!count || !src_data || !dst_data) {
if (count && !(src_data == nullptr && dst_data == nullptr)) {
CLOG_WARN(&LOG,
"null data for %s type (%p --> %p), skipping",
layerType_getName(eCustomDataType(source->layers[src_layer_index].type)),
(void *)src_data,
(void *)dst_data);
}
return;
}
if (typeInfo->copy) {
typeInfo->copy(
POINTER_OFFSET(src_data, src_offset), POINTER_OFFSET(dst_data, dst_offset), count);
}
else {
memcpy(POINTER_OFFSET(dst_data, dst_offset),
POINTER_OFFSET(src_data, src_offset),
size_t(count) * typeInfo->size);
}
}
void CustomData_copy_data(const CustomData *source,
CustomData *dest,
const int source_index,
const int dest_index,
const int count)
{
/* copies a layer at a time */
int dest_i = 0;
for (int src_i = 0; src_i < source->totlayer; src_i++) {
/* find the first dest layer with type >= the source type
* (this should work because layers are ordered by type)
*/
while (dest_i < dest->totlayer && dest->layers[dest_i].type < source->layers[src_i].type) {
dest_i++;
}
/* if there are no more dest layers, we're done */
if (dest_i >= dest->totlayer) {
return;
}
/* if we found a matching layer, copy the data */
if (dest->layers[dest_i].type == source->layers[src_i].type) {
CustomData_copy_data_layer(source, dest, src_i, dest_i, source_index, dest_index, count);
/* if there are multiple source & dest layers of the same type,
* we don't want to copy all source layers to the same dest, so
* increment dest_i
*/
dest_i++;
}
}
}
void CustomData_copy_layer_type_data(const CustomData *source,
CustomData *destination,
const eCustomDataType type,
int source_index,
int destination_index,
int count)
{
const int source_layer_index = CustomData_get_layer_index(source, type);
if (source_layer_index == -1) {
return;
}
const int destinaiton_layer_index = CustomData_get_layer_index(destination, type);
if (destinaiton_layer_index == -1) {
return;
}
CustomData_copy_data_layer(source,
destination,
source_layer_index,
destinaiton_layer_index,
source_index,
destination_index,
count);
}
void CustomData_free_elem(CustomData *data, const int index, const int count)
{
for (int i = 0; i < data->totlayer; i++) {
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type));
if (typeInfo->free) {
size_t offset = size_t(index) * typeInfo->size;
BLI_assert(layer_is_mutable(data->layers[i]));
typeInfo->free(POINTER_OFFSET(data->layers[i].data, offset), count);
}
}
}
#define SOURCE_BUF_SIZE 100
void CustomData_interp(const CustomData *source,
CustomData *dest,
const int *src_indices,
const float *weights,
int count,
int dest_index)
{
if (count <= 0) {
return;
}
const void *source_buf[SOURCE_BUF_SIZE];
const void **sources = source_buf;
/* Slow fallback in case we're interpolating a ridiculous number of elements. */
if (count > SOURCE_BUF_SIZE) {
sources = MEM_malloc_arrayN<const void *>(size_t(count), __func__);
}
/* If no weights are given, generate default ones to produce an average result. */
float default_weights_buf[SOURCE_BUF_SIZE];
float *default_weights = nullptr;
if (weights == nullptr) {
default_weights = (count > SOURCE_BUF_SIZE) ?
MEM_malloc_arrayN<float>(size_t(count), __func__) :
default_weights_buf;
copy_vn_fl(default_weights, count, 1.0f / count);
weights = default_weights;
}
/* interpolates a layer at a time */
int dest_i = 0;
for (int src_i = 0; src_i < source->totlayer; src_i++) {
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(source->layers[src_i].type));
if (!typeInfo->interp) {
continue;
}
/* find the first dest layer with type >= the source type
* (this should work because layers are ordered by type)
*/
while (dest_i < dest->totlayer && dest->layers[dest_i].type < source->layers[src_i].type) {
dest_i++;
}
/* if there are no more dest layers, we're done */
if (dest_i >= dest->totlayer) {
break;
}
/* if we found a matching layer, copy the data */
if (dest->layers[dest_i].type == source->layers[src_i].type) {
void *src_data = source->layers[src_i].data;
for (int j = 0; j < count; j++) {
sources[j] = POINTER_OFFSET(src_data, size_t(src_indices[j]) * typeInfo->size);
}
typeInfo->interp(
sources,
weights,
count,
POINTER_OFFSET(dest->layers[dest_i].data, size_t(dest_index) * typeInfo->size));
/* if there are multiple source & dest layers of the same type,
* we don't want to copy all source layers to the same dest, so
* increment dest_i
*/
dest_i++;
}
}
if (count > SOURCE_BUF_SIZE) {
MEM_freeN(sources);
}
if (!ELEM(default_weights, nullptr, default_weights_buf)) {
MEM_freeN(default_weights);
}
}
void CustomData_swap_corners(CustomData *data, const int index, const int *corner_indices)
{
for (int i = 0; i < data->totlayer; i++) {
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type));
if (typeInfo->swap) {
const size_t offset = size_t(index) * typeInfo->size;
typeInfo->swap(POINTER_OFFSET(data->layers[i].data, offset), corner_indices);
}
}
}
void *CustomData_get_for_write(CustomData *data,
const int index,
const eCustomDataType type,
int totelem)
{
BLI_assert(index >= 0);
void *layer_data = CustomData_get_layer_for_write(data, type, totelem);
if (!layer_data) {
return nullptr;
}
return POINTER_OFFSET(layer_data, size_t(index) * layerType_getInfo(type)->size);
}
void *CustomData_get_n_for_write(
CustomData *data, const eCustomDataType type, const int index, const int n, int totelem)
{
BLI_assert(index >= 0);
void *layer_data = CustomData_get_layer_n_for_write(data, type, n, totelem);
if (!layer_data) {
return nullptr;
}
return POINTER_OFFSET(layer_data, size_t(index) * layerType_getInfo(type)->size);
}
const void *CustomData_get_layer(const CustomData *data, const eCustomDataType type)
{
int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
return nullptr;
}
return data->layers[layer_index].data;
}
void *CustomData_get_layer_for_write(CustomData *data,
const eCustomDataType type,
const int totelem)
{
const int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
return layer.data;
}
const void *CustomData_get_layer_n(const CustomData *data, const eCustomDataType type, const int n)
{
int layer_index = CustomData_get_layer_index_n(data, type, n);
if (layer_index == -1) {
return nullptr;
}
return data->layers[layer_index].data;
}
void *CustomData_get_layer_n_for_write(CustomData *data,
const eCustomDataType type,
const int n,
const int totelem)
{
const int layer_index = CustomData_get_layer_index_n(data, type, n);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
return layer.data;
}
const void *CustomData_get_layer_named(const CustomData *data,
const eCustomDataType type,
const StringRef name)
{
int layer_index = CustomData_get_named_layer_index(data, type, name);
if (layer_index == -1) {
return nullptr;
}
return data->layers[layer_index].data;
}
void *CustomData_get_layer_named_for_write(CustomData *data,
const eCustomDataType type,
const StringRef name,
const int totelem)
{
const int layer_index = CustomData_get_named_layer_index(data, type, name);
if (layer_index == -1) {
return nullptr;
}
CustomDataLayer &layer = data->layers[layer_index];
ensure_layer_data_is_mutable(layer, totelem);
return layer.data;
}
int CustomData_get_offset(const CustomData *data, const eCustomDataType type)
{
int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
return -1;
}
return data->layers[layer_index].offset;
}
int CustomData_get_n_offset(const CustomData *data, const eCustomDataType type, const int n)
{
int layer_index = CustomData_get_layer_index_n(data, type, n);
if (layer_index == -1) {
return -1;
}
return data->layers[layer_index].offset;
}
int CustomData_get_offset_named(const CustomData *data,
const eCustomDataType type,
const StringRef name)
{
int layer_index = CustomData_get_named_layer_index(data, type, name);
if (layer_index == -1) {
return -1;
}
return data->layers[layer_index].offset;
}
bool CustomData_set_layer_name(CustomData *data,
const eCustomDataType type,
const int n,
const StringRef name)
{
const int layer_index = CustomData_get_layer_index_n(data, type, n);
if (layer_index == -1) {
return false;
}
name.copy_utf8_truncated(data->layers[layer_index].name);
return true;
}
const char *CustomData_get_layer_name(const CustomData *data,
const eCustomDataType type,
const int n)
{
const int layer_index = CustomData_get_layer_index_n(data, type, n);
return (layer_index == -1) ? nullptr : data->layers[layer_index].name;
}
/* BMesh functions */
void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char htype)
{
int chunksize;
/* Dispose old pools before calling here to avoid leaks */
BLI_assert(data->pool == nullptr);
switch (htype) {
case BM_VERT:
chunksize = bm_mesh_chunksize_default.totvert;
break;
case BM_EDGE:
chunksize = bm_mesh_chunksize_default.totedge;
break;
case BM_LOOP:
chunksize = bm_mesh_chunksize_default.totloop;
break;
case BM_FACE:
chunksize = bm_mesh_chunksize_default.totface;
break;
default:
BLI_assert_unreachable();
chunksize = 512;
break;
}
/* If there are no layers, no pool is needed just yet */
if (data->totlayer) {
data->pool = BLI_mempool_create(data->totsize, totelem, chunksize, BLI_MEMPOOL_NOP);
}
}
bool CustomData_bmesh_merge_layout(const CustomData *source,
CustomData *dest,
eCustomDataMask mask,
eCDAllocType alloctype,
BMesh *bm,
const char htype)
{
if (CustomData_number_of_layers_typemask(source, mask) == 0) {
return false;
}
/* copy old layer description so that old data can be copied into
* the new allocation */
CustomData destold = *dest;
if (destold.layers) {
destold.layers = static_cast<CustomDataLayer *>(MEM_dupallocN(destold.layers));
}
if (CustomData_merge_layout(source, dest, mask, alloctype, 0) == false) {
if (destold.layers) {
MEM_freeN(destold.layers);
}
return false;
}
const BMCustomDataCopyMap map = CustomData_bmesh_copy_map_calc(destold, *dest);
int iter_type;
int totelem;
switch (htype) {
case BM_VERT:
iter_type = BM_VERTS_OF_MESH;
totelem = bm->totvert;
break;
case BM_EDGE:
iter_type = BM_EDGES_OF_MESH;
totelem = bm->totedge;
break;
case BM_LOOP:
iter_type = BM_LOOPS_OF_FACE;
totelem = bm->totloop;
break;
case BM_FACE:
iter_type = BM_FACES_OF_MESH;
totelem = bm->totface;
break;
default: /* should never happen */
BLI_assert_msg(0, "invalid type given");
iter_type = BM_VERTS_OF_MESH;
totelem = bm->totvert;
break;
}
dest->pool = nullptr;
CustomData_bmesh_init_pool(dest, totelem, htype);
if (iter_type != BM_LOOPS_OF_FACE) {
BMHeader *h;
BMIter iter;
/* Ensure all current elements follow new customdata layout. */
BM_ITER_MESH (h, &iter, bm, iter_type) {
void *tmp = nullptr;
CustomData_bmesh_copy_block(*dest, map, h->data, &tmp);
CustomData_bmesh_free_block(&destold, &h->data);
h->data = tmp;
}
}
else {
BMFace *f;
BMLoop *l;
BMIter iter;
BMIter liter;
/* Ensure all current elements follow new customdata layout. */
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
void *tmp = nullptr;
CustomData_bmesh_copy_block(*dest, map, l->head.data, &tmp);
CustomData_bmesh_free_block(&destold, &l->head.data);
l->head.data = tmp;
}
}
}
if (destold.pool) {
BLI_mempool_destroy(destold.pool);
}
if (destold.layers) {
MEM_freeN(destold.layers);
}
return true;
}
void CustomData_bmesh_free_block(CustomData *data, void **block)
{
if (*block == nullptr) {
return;
}
for (int i = 0; i < data->totlayer; i++) {
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type));
if (typeInfo->free) {
int offset = data->layers[i].offset;
typeInfo->free(POINTER_OFFSET(*block, offset), 1);
}
}
if (data->totsize) {
BLI_mempool_free(data->pool, *block);
}
*block = nullptr;
}
void CustomData_bmesh_free_block_data(CustomData *data, void *block)
{
if (block == nullptr) {
return;
}
for (int i = 0; i < data->totlayer; i++) {
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type));
if (typeInfo->free) {
const size_t offset = data->layers[i].offset;
typeInfo->free(POINTER_OFFSET(block, offset), 1);
}
}
if (data->totsize) {
memset(block, 0, data->totsize);
}
}
void CustomData_bmesh_alloc_block(CustomData *data, void **block)
{
if (*block) {
CustomData_bmesh_free_block(data, block);
}
if (data->totsize > 0) {
*block = BLI_mempool_alloc(data->pool);
}
else {
*block = nullptr;
}
}
void CustomData_data_set_default_value(const eCustomDataType type, void *elem)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (typeInfo->set_default_value) {
typeInfo->set_default_value(elem, 1);
}
else {
memset(elem, 0, typeInfo->size);
}
}
static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const int n)
{
const int offset = data->layers[n].offset;
CustomData_data_set_default_value(eCustomDataType(data->layers[n].type),
POINTER_OFFSET(*block, offset));
}
void CustomData_bmesh_set_default(CustomData *data, void **block)
{
if (*block == nullptr) {
CustomData_bmesh_alloc_block(data, block);
}
for (int i = 0; i < data->totlayer; i++) {
CustomData_bmesh_set_default_n(data, block, i);
}
}
BMCustomDataCopyMap CustomData_bmesh_copy_map_calc(const CustomData &src,
const CustomData &dst,
const eCustomDataMask mask_exclude)
{
BMCustomDataCopyMap map;
for (const CustomDataLayer &layer_dst : Span(dst.layers, dst.totlayer)) {
const int dst_offset = layer_dst.offset;
const eCustomDataType dst_type = eCustomDataType(layer_dst.type);
const LayerTypeInfo &type_info = *layerType_getInfo(dst_type);
const int src_offset = CustomData_get_offset_named(&src, dst_type, layer_dst.name);
if (src_offset == -1 || CD_TYPE_AS_MASK(dst_type) & mask_exclude) {
if (type_info.set_default_value) {
map.defaults.append({type_info.set_default_value, dst_offset});
}
else {
map.trivial_defaults.append({type_info.size, dst_offset});
}
}
else {
if (type_info.copy) {
map.copies.append({type_info.copy, src_offset, dst_offset});
}
else {
/* NOTE: A way to improve performance of copies (by reducing the number of `memcpy`
* calls) would be combining contiguous chunks in the source and result format. */
map.trivial_copies.append({type_info.size, src_offset, dst_offset});
}
}
if (type_info.free) {
map.free.append({type_info.free, dst_offset});
}
}
return map;
}
void CustomData_bmesh_copy_block(CustomData &dst_data,
const BMCustomDataCopyMap &copy_map,
const void *src_block,
void **dst_block)
{
if (*dst_block) {
for (const BMCustomDataCopyMap::Free &info : copy_map.free) {
info.fn(POINTER_OFFSET(*dst_block, info.dst_offset), 1);
}
}
else {
if (dst_data.totsize == 0) {
return;
}
*dst_block = BLI_mempool_alloc(dst_data.pool);
}
for (const BMCustomDataCopyMap::TrivialCopy &info : copy_map.trivial_copies) {
memcpy(POINTER_OFFSET(*dst_block, info.dst_offset),
POINTER_OFFSET(src_block, info.src_offset),
info.size);
}
for (const BMCustomDataCopyMap::Copy &info : copy_map.copies) {
info.fn(POINTER_OFFSET(src_block, info.src_offset),
POINTER_OFFSET(*dst_block, info.dst_offset),
1);
}
for (const BMCustomDataCopyMap::TrivialDefault &info : copy_map.trivial_defaults) {
memset(POINTER_OFFSET(*dst_block, info.dst_offset), 0, info.size);
}
for (const BMCustomDataCopyMap::Default &info : copy_map.defaults) {
info.fn(POINTER_OFFSET(*dst_block, info.dst_offset), 1);
}
}
void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_block)
{
if (*dst_block) {
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
const LayerTypeInfo &info = *layerType_getInfo(eCustomDataType(layer.type));
if (info.free) {
info.free(POINTER_OFFSET(*dst_block, layer.offset), 1);
}
}
}
else {
if (data.totsize == 0) {
return;
}
*dst_block = BLI_mempool_alloc(data.pool);
}
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
const int offset = layer.offset;
const LayerTypeInfo &info = *layerType_getInfo(eCustomDataType(layer.type));
if (info.copy) {
info.copy(POINTER_OFFSET(src_block, offset), POINTER_OFFSET(*dst_block, offset), 1);
}
else {
memcpy(POINTER_OFFSET(*dst_block, offset), POINTER_OFFSET(src_block, offset), info.size);
}
}
}
void *CustomData_bmesh_get(const CustomData *data, void *block, const eCustomDataType type)
{
int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
return nullptr;
}
return POINTER_OFFSET(block, data->layers[layer_index].offset);
}
void *CustomData_bmesh_get_n(const CustomData *data,
void *block,
const eCustomDataType type,
const int n)
{
int layer_index = CustomData_get_layer_index(data, type);
if (layer_index == -1) {
return nullptr;
}
return POINTER_OFFSET(block, data->layers[layer_index + n].offset);
}
void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, const int n)
{
if (n < 0 || n >= data->totlayer) {
return nullptr;
}
return POINTER_OFFSET(block, data->layers[n].offset);
}
bool CustomData_layer_has_math(const CustomData *data, const int layer_n)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[layer_n].type));
if (typeInfo->equal && typeInfo->add && typeInfo->multiply && typeInfo->initminmax &&
typeInfo->dominmax)
{
return true;
}
return false;
}
bool CustomData_layer_has_interp(const CustomData *data, const int layer_n)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[layer_n].type));
if (typeInfo->interp) {
return true;
}
return false;
}
bool CustomData_has_math(const CustomData *data)
{
/* interpolates a layer at a time */
for (int i = 0; i < data->totlayer; i++) {
if (CustomData_layer_has_math(data, i)) {
return true;
}
}
return false;
}
bool CustomData_bmesh_has_free(const CustomData *data)
{
for (int i = 0; i < data->totlayer; i++) {
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type));
if (typeInfo->free) {
return true;
}
}
return false;
}
bool CustomData_has_interp(const CustomData *data)
{
/* interpolates a layer at a time */
for (int i = 0; i < data->totlayer; i++) {
if (CustomData_layer_has_interp(data, i)) {
return true;
}
}
return false;
}
void CustomData_data_copy_value(const eCustomDataType type, const void *source, void *dest)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (!dest) {
return;
}
if (typeInfo->copy) {
typeInfo->copy(source, dest, 1);
}
else {
memcpy(dest, source, typeInfo->size);
}
}
void CustomData_data_mix_value(const eCustomDataType type,
const void *source,
void *dest,
const int mixmode,
const float mixfactor)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (!dest) {
return;
}
if (typeInfo->copyvalue) {
typeInfo->copyvalue(source, dest, mixmode, mixfactor);
}
else {
/* Mere copy if no advanced interpolation is supported. */
memcpy(dest, source, typeInfo->size);
}
}
bool CustomData_data_equals(const eCustomDataType type, const void *data1, const void *data2)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (typeInfo->equal) {
return typeInfo->equal(data1, data2);
}
return !memcmp(data1, data2, typeInfo->size);
}
void CustomData_data_initminmax(const eCustomDataType type, void *min, void *max)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (typeInfo->initminmax) {
typeInfo->initminmax(min, max);
}
}
void CustomData_data_dominmax(const eCustomDataType type, const void *data, void *min, void *max)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (typeInfo->dominmax) {
typeInfo->dominmax(data, min, max);
}
}
void CustomData_data_multiply(const eCustomDataType type, void *data, const float fac)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (typeInfo->multiply) {
typeInfo->multiply(data, fac);
}
}
void CustomData_data_add(const eCustomDataType type, void *data1, const void *data2)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (typeInfo->add) {
typeInfo->add(data1, data2);
}
}
void CustomData_bmesh_set_n(
CustomData *data, void *block, const eCustomDataType type, const int n, const void *source)
{
void *dest = CustomData_bmesh_get_n(data, block, type, n);
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
if (!dest) {
return;
}
if (typeInfo->copy) {
typeInfo->copy(source, dest, 1);
}
else {
memcpy(dest, source, typeInfo->size);
}
}
void CustomData_bmesh_interp_n(CustomData *data,
const void **src_blocks_ofs,
const float *weights,
int count,
void *dst_block_ofs,
int n)
{
BLI_assert(weights != nullptr);
BLI_assert(count > 0);
CustomDataLayer *layer = &data->layers[n];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
typeInfo->interp(src_blocks_ofs, weights, count, dst_block_ofs);
}
void CustomData_bmesh_interp(
CustomData *data, const void **src_blocks, const float *weights, int count, void *dst_block)
{
if (count <= 0) {
return;
}
void *source_buf[SOURCE_BUF_SIZE];
const void **sources = (const void **)source_buf;
/* Slow fallback in case we're interpolating a ridiculous number of elements. */
if (count > SOURCE_BUF_SIZE) {
sources = MEM_malloc_arrayN<const void *>(size_t(count), __func__);
}
/* If no weights are given, generate default ones to produce an average result. */
float default_weights_buf[SOURCE_BUF_SIZE];
float *default_weights = nullptr;
if (weights == nullptr) {
default_weights = (count > SOURCE_BUF_SIZE) ?
MEM_malloc_arrayN<float>(size_t(count), __func__) :
default_weights_buf;
copy_vn_fl(default_weights, count, 1.0f / count);
weights = default_weights;
}
/* interpolates a layer at a time */
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if (typeInfo->interp) {
for (int j = 0; j < count; j++) {
sources[j] = POINTER_OFFSET(src_blocks[j], layer->offset);
}
CustomData_bmesh_interp_n(
data, sources, weights, count, POINTER_OFFSET(dst_block, layer->offset), i);
}
}
if (count > SOURCE_BUF_SIZE) {
MEM_freeN(sources);
}
if (!ELEM(default_weights, nullptr, default_weights_buf)) {
MEM_freeN(default_weights);
}
}
int CustomData_sizeof(const eCustomDataType type)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
return typeInfo->size;
}
const char *CustomData_layertype_name(const eCustomDataType type)
{
return layerType_getName(type);
}
bool CustomData_layertype_is_singleton(const eCustomDataType type)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
return typeInfo->defaultname == nullptr;
}
bool CustomData_layertype_is_dynamic(const eCustomDataType type)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
return (typeInfo->free != nullptr);
}
int CustomData_layertype_layers_max(const eCustomDataType type)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
/* Same test as for singleton above. */
if (typeInfo->defaultname == nullptr) {
return 1;
}
if (typeInfo->layers_max == nullptr) {
return -1;
}
return typeInfo->layers_max();
}
static bool cd_layer_find_dupe(CustomData *data,
const StringRef name,
const eCustomDataType type,
const int index)
{
/* see if there is a duplicate */
for (int i = 0; i < data->totlayer; i++) {
if (i != index) {
CustomDataLayer *layer = &data->layers[i];
if (CD_TYPE_AS_MASK(type) & CD_MASK_PROP_ALL) {
if ((CD_TYPE_AS_MASK(eCustomDataType(layer->type)) & CD_MASK_PROP_ALL) &&
layer->name == name)
{
return true;
}
}
else {
if (i != index && layer->type == type && layer->name == name) {
return true;
}
}
}
}
return false;
}
int CustomData_name_maxncpy_calc(const blender::StringRef name)
{
if (name.startswith(".")) {
return MAX_CUSTOMDATA_LAYER_NAME_NO_PREFIX;
}
for (const blender::StringRef prefix : {UV_PINNED_NAME "."}) {
if (name.startswith(prefix)) {
return MAX_CUSTOMDATA_LAYER_NAME;
}
}
return MAX_CUSTOMDATA_LAYER_NAME_NO_PREFIX;
}
void CustomData_set_layer_unique_name(CustomData *data, const int index)
{
CustomDataLayer *nlayer = &data->layers[index];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(nlayer->type));
if (!typeInfo->defaultname) {
return;
}
const int name_maxncpy = CustomData_name_maxncpy_calc(nlayer->name);
/* Set default name if none specified. Note we only call DATA_() when
* needed to avoid overhead of locale lookups in the depsgraph. */
if (nlayer->name[0] == '\0') {
STRNCPY_UTF8(nlayer->name, DATA_(typeInfo->defaultname));
}
const char *defname = ""; /* Dummy argument, never used as `name` is never zero length. */
BLI_uniquename_cb(
[&](const StringRef name) {
return cd_layer_find_dupe(data, name, eCustomDataType(nlayer->type), index);
},
defname,
'.',
nlayer->name,
name_maxncpy);
}
void CustomData_validate_layer_name(const CustomData *data,
const eCustomDataType type,
const StringRef name,
char *outname)
{
int index = -1;
/* if a layer name was given, try to find that layer */
if (!name.is_empty()) {
index = CustomData_get_named_layer_index(data, type, name);
}
if (index == -1) {
/* either no layer was specified, or the layer we want has been
* deleted, so assign the active layer to name
*/
index = CustomData_get_active_layer_index(data, type);
BLI_strncpy_utf8(outname, data->layers[index].name, MAX_CUSTOMDATA_LAYER_NAME);
}
else {
name.copy_utf8_truncated(outname, MAX_CUSTOMDATA_LAYER_NAME);
}
}
bool CustomData_verify_versions(CustomData *data, const int index)
{
const LayerTypeInfo *typeInfo;
CustomDataLayer *layer = &data->layers[index];
bool keeplayer = true;
if (layer->type >= CD_NUMTYPES) {
keeplayer = false; /* unknown layer type from future version */
}
else {
typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if (!typeInfo->defaultname && (index > 0) && data->layers[index - 1].type == layer->type) {
keeplayer = false; /* multiple layers of which we only support one */
}
/* This is a preemptive fix for cases that should not happen
* (layers that should not be written in .blend files),
* but can happen due to bugs (see e.g. #62318).
* Also for forward compatibility, in future,
* we may put into `.blend` file some currently un-written data types,
* this should cover that case as well.
* Better to be safe here, and fix issue on the fly rather than crash... */
/* 0 structnum is used in writing code to tag layer types that should not be written. */
else if (typeInfo->structnum == 0 &&
/* XXX Not sure why those three are exception, maybe that should be fixed? */
!ELEM(layer->type,
CD_PAINT_MASK,
CD_FACEMAP,
CD_MTEXPOLY,
CD_SCULPT_FACE_SETS,
CD_CREASE))
{
keeplayer = false;
CLOG_WARN(&LOG, ".blend file read: removing a data layer that should not have been written");
}
}
if (!keeplayer) {
for (int i = index + 1; i < data->totlayer; i++) {
data->layers[i - 1] = data->layers[i];
}
data->totlayer--;
}
return keeplayer;
}
static bool CustomData_layer_ensure_data_exists(CustomDataLayer *layer, size_t count)
{
BLI_assert(layer);
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
BLI_assert(typeInfo);
if (layer->data || count == 0) {
return false;
}
switch (layer->type) {
/* When more instances of corrupt files are found, add them here. */
case CD_PROP_BOOL: /* See #84935. */
case CD_MLOOPUV: /* See #90620. */
case CD_PROP_FLOAT2: /* See #90620. */
layer->data = MEM_calloc_arrayN(
count, typeInfo->size, layerType_getName(eCustomDataType(layer->type)));
BLI_assert(layer->data);
if (typeInfo->set_default_value) {
typeInfo->set_default_value(layer->data, count);
}
return true;
break;
case CD_MTEXPOLY:
/* TODO: Investigate multiple test failures on cycles, e.g. cycles_shadow_catcher_cpu. */
break;
default:
/* Log an error so we can collect instances of bad files. */
CLOG_WARN(&LOG, "CustomDataLayer->data is null for type %d.", layer->type);
break;
}
return false;
}
bool CustomData_layer_validate(CustomDataLayer *layer, const uint totitems, const bool do_fixes)
{
BLI_assert(layer);
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
BLI_assert(typeInfo);
if (do_fixes) {
CustomData_layer_ensure_data_exists(layer, totitems);
}
BLI_assert((totitems == 0) || layer->data);
if (typeInfo->validate != nullptr) {
return typeInfo->validate(layer->data, totitems, do_fixes);
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name External Files
* \{ */
static void customdata_external_filename(char filepath[FILE_MAX],
ID *id,
CustomDataExternal *external)
{
BLI_strncpy(filepath, external->filepath, FILE_MAX);
BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(id));
}
void CustomData_external_reload(CustomData *data, ID * /*id*/, eCustomDataMask mask, int totelem)
{
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if (!(mask & CD_TYPE_AS_MASK(eCustomDataType(layer->type)))) {
/* pass */
}
else if ((layer->flag & CD_FLAG_EXTERNAL) && (layer->flag & CD_FLAG_IN_MEMORY)) {
if (typeInfo->free) {
typeInfo->free(layer->data, totelem);
}
layer->flag &= ~CD_FLAG_IN_MEMORY;
}
}
}
void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, const int totelem)
{
CustomDataExternal *external = data->external;
CustomDataLayer *layer;
char filepath[FILE_MAX];
int update = 0;
if (!external) {
return;
}
for (int i = 0; i < data->totlayer; i++) {
layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if (!(mask & CD_TYPE_AS_MASK(eCustomDataType(layer->type)))) {
/* pass */
}
else if (layer->flag & CD_FLAG_IN_MEMORY) {
/* pass */
}
else if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->read) {
update = 1;
}
}
if (!update) {
return;
}
customdata_external_filename(filepath, id, external);
CDataFile *cdf = cdf_create(CDF_TYPE_MESH);
if (!cdf_read_open(cdf, filepath)) {
cdf_free(cdf);
CLOG_ERROR(&LOG,
"Failed to read %s layer from %s.",
layerType_getName(eCustomDataType(layer->type)),
filepath);
return;
}
for (int i = 0; i < data->totlayer; i++) {
layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if (!(mask & CD_TYPE_AS_MASK(eCustomDataType(layer->type)))) {
/* pass */
}
else if (layer->flag & CD_FLAG_IN_MEMORY) {
/* pass */
}
else if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->read) {
const CDataFileLayer *blay = cdf_layer_find(cdf, layer->type, layer->name);
if (blay) {
if (cdf_read_layer(cdf, blay)) {
if (typeInfo->read(cdf, layer->data, totelem)) {
/* pass */
}
else {
break;
}
layer->flag |= CD_FLAG_IN_MEMORY;
}
else {
break;
}
}
}
}
cdf_read_close(cdf);
cdf_free(cdf);
}
void CustomData_external_write(
CustomData *data, ID *id, eCustomDataMask mask, const int totelem, const int free)
{
CustomDataExternal *external = data->external;
int update = 0;
char filepath[FILE_MAX];
if (!external) {
return;
}
/* test if there is anything to write */
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if (!(mask & CD_TYPE_AS_MASK(eCustomDataType(layer->type)))) {
/* pass */
}
else if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->write) {
update = 1;
}
}
if (!update) {
return;
}
/* make sure data is read before we try to write */
CustomData_external_read(data, id, mask, totelem);
customdata_external_filename(filepath, id, external);
CDataFile *cdf = cdf_create(CDF_TYPE_MESH);
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->filesize) {
if (layer->flag & CD_FLAG_IN_MEMORY) {
cdf_layer_add(
cdf, layer->type, layer->name, typeInfo->filesize(cdf, layer->data, totelem));
}
else {
cdf_free(cdf);
return; /* read failed for a layer! */
}
}
}
if (!cdf_write_open(cdf, filepath)) {
CLOG_ERROR(&LOG, "Failed to open %s for writing.", filepath);
cdf_free(cdf);
return;
}
int i;
for (i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->write) {
CDataFileLayer *blay = cdf_layer_find(cdf, layer->type, layer->name);
if (cdf_write_layer(cdf, blay)) {
if (typeInfo->write(cdf, layer->data, totelem)) {
/* pass */
}
else {
break;
}
}
else {
break;
}
}
}
if (i != data->totlayer) {
CLOG_ERROR(&LOG, "Failed to write data to %s.", filepath);
cdf_write_close(cdf);
cdf_free(cdf);
return;
}
for (i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type));
if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->write) {
if (free) {
if (typeInfo->free) {
typeInfo->free(layer->data, totelem);
}
layer->flag &= ~CD_FLAG_IN_MEMORY;
}
}
}
cdf_write_close(cdf);
cdf_free(cdf);
}
void CustomData_external_add(CustomData *data,
ID * /*id*/,
const eCustomDataType type,
const int /*totelem*/,
const char *filepath)
{
CustomDataExternal *external = data->external;
int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
return;
}
CustomDataLayer *layer = &data->layers[layer_index];
if (layer->flag & CD_FLAG_EXTERNAL) {
return;
}
if (!external) {
external = MEM_callocN<CustomDataExternal>(__func__);
data->external = external;
}
STRNCPY(external->filepath, filepath);
layer->flag |= CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY;
}
void CustomData_external_remove(CustomData *data,
ID *id,
const eCustomDataType type,
const int totelem)
{
CustomDataExternal *external = data->external;
int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
return;
}
CustomDataLayer *layer = &data->layers[layer_index];
if (!external) {
return;
}
if (layer->flag & CD_FLAG_EXTERNAL) {
if (!(layer->flag & CD_FLAG_IN_MEMORY)) {
CustomData_external_read(data, id, CD_TYPE_AS_MASK(eCustomDataType(layer->type)), totelem);
}
layer->flag &= ~CD_FLAG_EXTERNAL;
}
}
bool CustomData_external_test(CustomData *data, const eCustomDataType type)
{
int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
return false;
}
CustomDataLayer *layer = &data->layers[layer_index];
return (layer->flag & CD_FLAG_EXTERNAL) != 0;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh-to-Mesh Data Transfer
* \{ */
static void copy_bit_flag(void *dst, const void *src, const size_t data_size, const uint64_t flag)
{
#define COPY_BIT_FLAG(_type, _dst, _src, _f) \
{ \
const _type _val = *((_type *)(_src)) & (_type)(_f); \
*((_type *)(_dst)) &= ~(_type)(_f); \
*((_type *)(_dst)) |= _val; \
} \
(void)0
switch (data_size) {
case 1:
COPY_BIT_FLAG(uint8_t, dst, src, flag);
break;
case 2:
COPY_BIT_FLAG(uint16_t, dst, src, flag);
break;
case 4:
COPY_BIT_FLAG(uint32_t, dst, src, flag);
break;
case 8:
COPY_BIT_FLAG(uint64_t, dst, src, flag);
break;
default:
// CLOG_ERROR(&LOG, "Unknown flags-container size (%zu)", datasize);
break;
}
#undef COPY_BIT_FLAG
}
static bool check_bit_flag(const void *data, const size_t data_size, const uint64_t flag)
{
switch (data_size) {
case 1:
return ((*((uint8_t *)data) & uint8_t(flag)) != 0);
case 2:
return ((*((uint16_t *)data) & uint16_t(flag)) != 0);
case 4:
return ((*((uint32_t *)data) & uint32_t(flag)) != 0);
case 8:
return ((*((uint64_t *)data) & uint64_t(flag)) != 0);
default:
// CLOG_ERROR(&LOG, "Unknown flags-container size (%zu)", datasize);
return false;
}
}
static void customdata_data_transfer_interp_generic(const CustomDataTransferLayerMap *laymap,
void *data_dst,
const void **sources,
const float *weights,
const int count,
const float mix_factor)
{
BLI_assert(weights != nullptr);
BLI_assert(count > 0);
/* Fake interpolation, we actually copy highest weighted source to dest.
* Note we also handle bitflags here,
* in which case we rather choose to transfer value of elements totaling
* more than 0.5 of weight. */
int best_src_idx = 0;
const int data_type = laymap->data_type;
const int mix_mode = laymap->mix_mode;
size_t data_size;
const uint64_t data_flag = laymap->data_flag;
cd_interp interp_cd = nullptr;
cd_copy copy_cd = nullptr;
if (!sources) {
/* Not supported here, abort. */
return;
}
if (int(data_type) & CD_FAKE) {
data_size = laymap->data_size;
}
else {
const LayerTypeInfo *type_info = layerType_getInfo(eCustomDataType(data_type));
data_size = size_t(type_info->size);
interp_cd = type_info->interp;
copy_cd = type_info->copy;
}
void *tmp_dst = MEM_mallocN(data_size, __func__);
if (count > 1 && !interp_cd) {
if (data_flag) {
/* Boolean case, we can 'interpolate' in two groups,
* and choose value from highest weighted group. */
float tot_weight_true = 0.0f;
int item_true_idx = -1, item_false_idx = -1;
for (int i = 0; i < count; i++) {
if (check_bit_flag(sources[i], data_size, data_flag)) {
tot_weight_true += weights[i];
item_true_idx = i;
}
else {
item_false_idx = i;
}
}
best_src_idx = (tot_weight_true >= 0.5f) ? item_true_idx : item_false_idx;
}
else {
/* We just choose highest weighted source. */
float max_weight = 0.0f;
for (int i = 0; i < count; i++) {
if (weights[i] > max_weight) {
max_weight = weights[i];
best_src_idx = i;
}
}
}
}
BLI_assert(best_src_idx >= 0);
if (interp_cd) {
interp_cd(sources, weights, count, tmp_dst);
}
else if (data_flag) {
copy_bit_flag(tmp_dst, sources[best_src_idx], data_size, data_flag);
}
/* No interpolation, just copy highest weight source element's data. */
else if (copy_cd) {
copy_cd(sources[best_src_idx], tmp_dst, 1);
}
else {
memcpy(tmp_dst, sources[best_src_idx], data_size);
}
if (data_flag) {
/* Bool flags, only copy if dest data is set (resp. unset) -
* only 'advanced' modes we can support here! */
if (mix_factor >= 0.5f && ((mix_mode == CDT_MIX_TRANSFER) ||
(mix_mode == CDT_MIX_REPLACE_ABOVE_THRESHOLD &&
check_bit_flag(data_dst, data_size, data_flag)) ||
(mix_mode == CDT_MIX_REPLACE_BELOW_THRESHOLD &&
!check_bit_flag(data_dst, data_size, data_flag))))
{
copy_bit_flag(data_dst, tmp_dst, data_size, data_flag);
}
}
else if (!(int(data_type) & CD_FAKE)) {
CustomData_data_mix_value(eCustomDataType(data_type), tmp_dst, data_dst, mix_mode, mix_factor);
}
/* Else we can do nothing by default, needs custom interp func!
* Note this is here only for sake of consistency, not expected to be used much actually? */
else {
if (mix_factor >= 0.5f) {
memcpy(data_dst, tmp_dst, data_size);
}
}
MEM_freeN(tmp_dst);
}
void customdata_data_transfer_interp_normal_normals(const CustomDataTransferLayerMap *laymap,
void *data_dst,
const void **sources,
const float *weights,
const int count,
const float mix_factor)
{
BLI_assert(weights != nullptr);
BLI_assert(count > 0);
const eCustomDataType data_type = eCustomDataType(laymap->data_type);
BLI_assert(data_type == CD_NORMAL);
const int mix_mode = laymap->mix_mode;
SpaceTransform *space_transform = static_cast<SpaceTransform *>(laymap->interp_data);
const LayerTypeInfo *type_info = layerType_getInfo(data_type);
cd_interp interp_cd = type_info->interp;
float tmp_dst[3];
if (!sources) {
/* Not supported here, abort. */
return;
}
interp_cd(sources, weights, count, tmp_dst);
if (space_transform) {
/* tmp_dst is in source space so far, bring it back in destination space. */
BLI_space_transform_invert_normal(space_transform, tmp_dst);
}
CustomData_data_mix_value(data_type, tmp_dst, data_dst, mix_mode, mix_factor);
}
void CustomData_data_transfer(const MeshPairRemap *me_remap,
const CustomDataTransferLayerMap *laymap)
{
MeshPairRemapItem *mapit = me_remap->items;
const int totelem = me_remap->items_num;
const int data_type = laymap->data_type;
const void *data_src = laymap->data_src;
void *data_dst = laymap->data_dst;
size_t data_step;
size_t data_size;
size_t data_offset;
cd_datatransfer_interp interp = nullptr;
size_t tmp_buff_size = 32;
const void **tmp_data_src = nullptr;
/* NOTE: null data_src may happen and be valid (see vgroups...). */
if (!data_dst) {
return;
}
if (data_src) {
tmp_data_src = MEM_malloc_arrayN<const void *>(tmp_buff_size, __func__);
}
if (int(data_type) & CD_FAKE) {
data_step = laymap->elem_size;
data_size = laymap->data_size;
data_offset = laymap->data_offset;
}
else {
const LayerTypeInfo *type_info = layerType_getInfo(eCustomDataType(data_type));
/* NOTE: we can use 'fake' CDLayers for crease :/. */
data_size = size_t(type_info->size);
data_step = laymap->elem_size ? laymap->elem_size : data_size;
data_offset = laymap->data_offset;
}
interp = laymap->interp ? laymap->interp : customdata_data_transfer_interp_generic;
for (int i = 0; i < totelem; i++, data_dst = POINTER_OFFSET(data_dst, data_step), mapit++) {
const int sources_num = mapit->sources_num;
const float mix_factor = laymap->mix_factor *
(laymap->mix_weights ? laymap->mix_weights[i] : 1.0f);
if (!sources_num) {
/* No sources for this element, skip it. */
continue;
}
if (tmp_data_src) {
if (UNLIKELY(sources_num > tmp_buff_size)) {
tmp_buff_size = size_t(sources_num);
tmp_data_src = (const void **)MEM_reallocN((void *)tmp_data_src,
sizeof(*tmp_data_src) * tmp_buff_size);
}
for (int j = 0; j < sources_num; j++) {
const size_t src_idx = size_t(mapit->indices_src[j]);
tmp_data_src[j] = POINTER_OFFSET(data_src, (data_step * src_idx) + data_offset);
}
}
interp(laymap,
POINTER_OFFSET(data_dst, data_offset),
tmp_data_src,
mapit->weights_src,
sources_num,
mix_factor);
}
MEM_SAFE_FREE(tmp_data_src);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Custom Data IO
* \{ */
static void get_type_file_write_info(const eCustomDataType type,
const char **r_struct_name,
int *r_struct_num)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
*r_struct_name = typeInfo->structname;
*r_struct_num = typeInfo->structnum;
}
void CustomData_blend_write_prepare(CustomData &data,
const blender::bke::AttrDomain domain,
const int domain_size,
Vector<CustomDataLayer, 16> &layers_to_write,
blender::bke::AttributeStorage::BlendWriteData &write_data)
{
using namespace blender::bke;
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.flag & CD_FLAG_NOCOPY) {
continue;
}
const StringRef name = layer.name;
if (attribute_name_is_anonymous(name)) {
continue;
}
/* We always write the data in the new #AttributeStorage format, even though it's not yet used
* at runtime. This block should be removed when the new format is used at runtime. */
const eCustomDataType data_type = eCustomDataType(layer.type);
if (const std::optional<AttrType> type = custom_data_type_to_attr_type(data_type)) {
::Attribute attribute_dna{};
attribute_dna.name = layer.name;
attribute_dna.data_type = int16_t(*type);
attribute_dna.domain = int8_t(domain);
attribute_dna.storage_type = int8_t(AttrStorageType::Array);
/* Do not increase the user count; #::AttributeArray does not act as an owner of the
* attribute data, since it's only used temporarily for writing files. Changing the user
* count would be okay too, but it's unnecessary because none of this data should be
* modified while it's being written anyway. */
auto &array_dna = write_data.scope.construct<::AttributeArray>();
array_dna.data = layer.data;
array_dna.sharing_info = layer.sharing_info;
array_dna.size = domain_size;
attribute_dna.data = &array_dna;
write_data.attributes.append(attribute_dna);
continue;
}
layers_to_write.append(layer);
}
data.totlayer = layers_to_write.size();
data.maxlayer = data.totlayer;
std::fill_n(data.typemap, CD_NUMTYPES, 0);
data.totsize = 0;
if (layers_to_write.is_empty()) {
data.layers = nullptr;
}
/* NOTE: `data->layers` may be null, this happens when adding
* a legacy #MPoly struct to a mesh with no other face attributes.
* This leaves us with no unique ID for DNA to identify the old
* data with when loading the file. */
if (!data.layers && layers_to_write.size() > 0) {
/* We just need an address that's unique. */
data.layers = reinterpret_cast<CustomDataLayer *>(&data.layers);
}
}
static void write_mdisps(BlendWriter *writer,
const int count,
const MDisps *mdlist,
const int external)
{
if (mdlist) {
BLO_write_struct_array(writer, MDisps, count, mdlist);
for (int i = 0; i < count; i++) {
const MDisps *md = &mdlist[i];
if (md->disps) {
if (!external) {
BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]);
}
}
if (md->hidden) {
BLO_write_int8_array(writer,
BLI_BITMAP_SIZE(md->totdisp) * sizeof(BLI_bitmap),
reinterpret_cast<const int8_t *>(md->hidden));
}
}
}
}
static void write_grid_paint_mask(BlendWriter *writer,
int count,
const GridPaintMask *grid_paint_mask)
{
if (grid_paint_mask) {
BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask);
for (int i = 0; i < count; i++) {
const GridPaintMask *gpm = &grid_paint_mask[i];
if (gpm->data) {
const uint32_t gridsize = uint32_t(CCG_grid_size(gpm->level));
BLO_write_float_array(writer, gridsize * gridsize, gpm->data);
}
}
}
}
static void blend_write_layer_data(BlendWriter *writer,
const CustomDataLayer &layer,
const int count)
{
switch (layer.type) {
case CD_MDEFORMVERT:
BKE_defvert_blend_write(writer, count, static_cast<const MDeformVert *>(layer.data));
break;
case CD_MDISPS:
write_mdisps(
writer, count, static_cast<const MDisps *>(layer.data), layer.flag & CD_FLAG_EXTERNAL);
break;
case CD_PAINT_MASK:
BLO_write_float_array(writer, count, static_cast<const float *>(layer.data));
break;
case CD_GRID_PAINT_MASK:
write_grid_paint_mask(writer, count, static_cast<const GridPaintMask *>(layer.data));
break;
case CD_PROP_BOOL:
BLI_STATIC_ASSERT(sizeof(bool) == sizeof(uint8_t),
"bool type is expected to have the same size as uint8_t")
BLO_write_uint8_array(writer, count, static_cast<const uint8_t *>(layer.data));
break;
default: {
const char *structname;
int structnum;
get_type_file_write_info(eCustomDataType(layer.type), &structname, &structnum);
if (structnum > 0) {
int datasize = structnum * count;
BLO_write_struct_array_by_name(writer, structname, datasize, layer.data);
}
else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */
printf("%s error: layer '%s':%d - can't be written to file\n",
__func__,
structname,
layer.type);
}
}
}
}
void CustomData_blend_write(BlendWriter *writer,
CustomData *data,
Span<CustomDataLayer> layers_to_write,
int count,
eCustomDataMask cddata_mask,
ID *id)
{
/* write external customdata (not for undo) */
if (data->external && !BLO_write_is_undo(writer)) {
CustomData_external_write(data, id, cddata_mask, count, 0);
}
for (const CustomDataLayer &layer : layers_to_write) {
const size_t size_in_bytes = CustomData_sizeof(eCustomDataType(layer.type)) * count;
BLO_write_shared(writer, layer.data, size_in_bytes, layer.sharing_info, [&]() {
blend_write_layer_data(writer, layer, count);
});
}
BLO_write_struct_array_at_address(
writer, CustomDataLayer, data->totlayer, data->layers, layers_to_write.data());
if (data->external) {
BLO_write_struct(writer, CustomDataExternal, data->external);
}
}
static void blend_read_mdisps(BlendDataReader *reader,
const int count,
MDisps *mdisps,
const int external)
{
if (mdisps) {
for (int i = 0; i < count; i++) {
MDisps &md = mdisps[i];
BLO_read_float3_array(reader, md.totdisp, reinterpret_cast<float **>(&md.disps));
BLO_read_int8_array(reader,
BLI_BITMAP_SIZE(md.totdisp) * sizeof(BLI_bitmap),
reinterpret_cast<int8_t **>(&md.hidden));
if (md.totdisp && !md.level) {
/* this calculation is only correct for loop mdisps;
* if loading pre-BMesh face mdisps this will be
* overwritten with the correct value in
* #bm_corners_to_loops() */
float gridsize = sqrtf(md.totdisp);
md.level = int(logf(gridsize - 1.0f) / float(M_LN2)) + 1;
}
if (!external && !md.disps) {
md.totdisp = 0;
}
}
}
}
static void blend_read_paint_mask(BlendDataReader *reader,
int count,
GridPaintMask *grid_paint_mask)
{
if (grid_paint_mask) {
for (int i = 0; i < count; i++) {
GridPaintMask *gpm = &grid_paint_mask[i];
if (gpm->data) {
const int gridsize = CCG_grid_size(gpm->level);
BLO_read_float_array(reader, gridsize * gridsize, &gpm->data);
}
}
}
}
static void blend_read_layer_data(BlendDataReader *reader, CustomDataLayer &layer, const int count)
{
switch (layer.type) {
case CD_MDEFORMVERT:
BLO_read_struct_array(reader, MDeformVert, count, &layer.data);
BKE_defvert_blend_read(reader, count, static_cast<MDeformVert *>(layer.data));
break;
case CD_MDISPS:
BLO_read_struct_array(reader, MDisps, count, &layer.data);
blend_read_mdisps(
reader, count, static_cast<MDisps *>(layer.data), layer.flag & CD_FLAG_EXTERNAL);
break;
case CD_PAINT_MASK:
BLO_read_float_array(reader, count, reinterpret_cast<float **>(&layer.data));
break;
case CD_GRID_PAINT_MASK:
BLO_read_struct_array(reader, GridPaintMask, count, &layer.data);
blend_read_paint_mask(reader, count, static_cast<GridPaintMask *>(layer.data));
break;
case CD_PROP_BOOL:
BLI_STATIC_ASSERT(sizeof(bool) == sizeof(uint8_t),
"bool type is expected to have the same size as uint8_t")
BLO_read_uint8_array(reader, count, reinterpret_cast<uint8_t **>(&layer.data));
break;
default: {
const char *structname;
int structnum;
get_type_file_write_info(eCustomDataType(layer.type), &structname, &structnum);
if (structnum > 0) {
const int data_num = structnum * count;
layer.data = BLO_read_struct_by_name_array(reader, structname, data_num, layer.data);
}
else {
/* Can happen with deprecated types of customdata. */
const size_t elem_size = CustomData_sizeof(eCustomDataType(layer.type));
BLO_read_struct_array(reader, char, elem_size *count, &layer.data);
}
}
}
if (CustomData_layer_ensure_data_exists(&layer, count)) {
/* Under normal operations, this shouldn't happen, but...
* For a CD_PROP_BOOL example, see #84935.
* For a CD_MLOOPUV example, see #90620. */
CLOG_WARN(&LOG,
"Allocated custom data layer that was not saved correctly for layer.type = %d.",
layer.type);
}
}
void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int count)
{
BLO_read_struct_array(reader, CustomDataLayer, data->totlayer, &data->layers);
/* Annoying workaround for bug #31079 loading legacy files with
* no polygons _but_ have stale custom-data. */
if (UNLIKELY(count == 0 && data->layers == nullptr && data->totlayer != 0)) {
CustomData_reset(data);
return;
}
/* There was a short time (Blender 500 sub 33) where the custom data struct was saved in an
* invalid state (see @11d2f48882). This check is unfortunate, but avoids crashing when trying to
* load the invalid data (see e.g. #143720). */
if (UNLIKELY(data->layers == nullptr && data->totlayer != 0)) {
CustomData_reset(data);
return;
}
BLO_read_struct(reader, CustomDataExternal, &data->external);
int i = 0;
while (i < data->totlayer) {
CustomDataLayer *layer = &data->layers[i];
if (layer->flag & CD_FLAG_EXTERNAL) {
layer->flag &= ~CD_FLAG_IN_MEMORY;
}
layer->sharing_info = nullptr;
if (CustomData_verify_versions(data, i)) {
layer->sharing_info = BLO_read_shared(
reader, &layer->data, [&]() -> const ImplicitSharingInfo * {
blend_read_layer_data(reader, *layer, count);
if (layer->data == nullptr) {
return nullptr;
}
return make_implicit_sharing_info_for_layer(
eCustomDataType(layer->type), layer->data, count);
});
i++;
}
}
/* Ensure allocated size is set to the size of the read array. While this should always be the
* case (see #CustomData_blend_write_prepare), there can be some corruption in rare cases (e.g.
* files saved between ff3d535bc2a63092 and 945f32e66d6ada2a). */
data->maxlayer = data->totlayer;
CustomData_update_typemap(data);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Custom Data Debugging
* \{ */
#ifndef NDEBUG
void CustomData_debug_info_from_layers(const CustomData *data, const char *indent, DynStr *dynstr)
{
for (eCustomDataType type = eCustomDataType(0); type < CD_NUMTYPES;
type = eCustomDataType(type + 1))
{
if (CustomData_has_layer(data, type)) {
/* NOTE: doesn't account for multiple layers. */
const char *name = CustomData_layertype_name(type);
const int size = CustomData_sizeof(type);
const void *pt = CustomData_get_layer(data, type);
const int pt_size = pt ? int(MEM_allocN_len(pt) / size) : 0;
const char *structname;
int structnum;
get_type_file_write_info(type, &structname, &structnum);
BLI_dynstr_appendf(
dynstr,
"%sdict(name='%s', struct='%s', type=%d, ptr='%p', elem=%d, length=%d),\n",
indent,
name,
structname,
type,
pt,
size,
pt_size);
}
}
}
#endif /* !NDEBUG */
/** \} */
namespace blender::bke {
/* -------------------------------------------------------------------- */
/** \name Custom Data C++ API
* \{ */
std::optional<VolumeGridType> custom_data_type_to_volume_grid_type(const eCustomDataType type)
{
switch (type) {
case CD_PROP_FLOAT:
return VOLUME_GRID_FLOAT;
case CD_PROP_FLOAT3:
return VOLUME_GRID_VECTOR_FLOAT;
case CD_PROP_INT32:
return VOLUME_GRID_INT;
case CD_PROP_BOOL:
return VOLUME_GRID_BOOLEAN;
default:
return std::nullopt;
}
}
std::optional<eCustomDataType> volume_grid_type_to_custom_data_type(const VolumeGridType type)
{
switch (type) {
case VOLUME_GRID_FLOAT:
return CD_PROP_FLOAT;
case VOLUME_GRID_VECTOR_FLOAT:
return CD_PROP_FLOAT3;
case VOLUME_GRID_INT:
return CD_PROP_INT32;
case VOLUME_GRID_BOOLEAN:
return CD_PROP_BOOL;
default:
return std::nullopt;
}
}
/** \} */
} // namespace blender::bke
size_t CustomData_get_elem_size(const CustomDataLayer *layer)
{
return LAYERTYPEINFO[layer->type].size;
}
void CustomData_count_memory(const CustomData &data,
const int totelem,
blender::MemoryCounter &memory)
{
for (const CustomDataLayer &layer : Span{data.layers, data.totlayer}) {
memory.add_shared(layer.sharing_info, [&](blender::MemoryCounter &shared_memory) {
/* Not quite correct for all types, but this is only a rough approximation anyway. */
const int64_t elem_size = CustomData_get_elem_size(&layer);
shared_memory.add(totelem * elem_size);
});
}
}