Files
test2/source/blender/blenkernel/intern/attribute_legacy_convert.cc
2025-10-10 19:40:26 +02:00

319 lines
10 KiB
C++

/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#define DNA_DEPRECATED_ALLOW
#include <optional>
#include "DNA_grease_pencil_types.h"
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_attribute.hh"
#include "BKE_curves.hh"
#include "BKE_customdata.hh"
#include "BKE_attribute_legacy_convert.hh"
namespace blender::bke {
std::optional<AttrType> custom_data_type_to_attr_type(const eCustomDataType data_type)
{
switch (data_type) {
/* These types are not used for actual #CustomData layers. */
case CD_NUMTYPES:
case CD_AUTO_FROM_NAME:
case CD_TANGENT:
BLI_assert_unreachable();
return std::nullopt;
/* These types are only used for versioning old files. */
case CD_MVERT:
case CD_MSTICKY:
case CD_MEDGE:
case CD_FACEMAP:
case CD_MTEXPOLY:
case CD_MLOOPUV:
case CD_MPOLY:
case CD_MLOOP:
case CD_BWEIGHT:
case CD_CREASE:
case CD_PAINT_MASK:
case CD_CUSTOMLOOPNORMAL:
case CD_SCULPT_FACE_SETS:
case CD_MTFACE:
case CD_TESSLOOPNORMAL:
case CD_FREESTYLE_EDGE:
case CD_FREESTYLE_FACE:
return std::nullopt;
/* These types are only used for #BMesh. */
case CD_SHAPEKEY:
case CD_SHAPE_KEYINDEX:
case CD_BM_ELEM_PYPTR:
return std::nullopt;
/* Only used for legacy #MFace data. */
case CD_MFACE:
case CD_ORIGSPACE:
case CD_MCOL:
return std::nullopt;
/* Custom data on vertices. */
case CD_MDEFORMVERT:
case CD_MVERT_SKIN:
case CD_ORCO:
case CD_CLOTH_ORCO:
return std::nullopt;
/* Custom data on face corners. */
case CD_NORMAL:
case CD_MDISPS:
case CD_ORIGSPACE_MLOOP:
case CD_GRID_PAINT_MASK:
return std::nullopt;
/* Use for editing/selecting original data from evaluated mesh (vertices, edges, faces). */
case CD_ORIGINDEX:
return std::nullopt;
/* Used as a cache of tangents for current RNA API (face corners). */
case CD_MLOOPTANGENT:
return std::nullopt;
/* Attribute types. */
case CD_PROP_FLOAT:
return AttrType::Float;
case CD_PROP_INT32:
return AttrType::Int32;
case CD_PROP_BYTE_COLOR:
return AttrType::ColorByte;
case CD_PROP_FLOAT4X4:
return AttrType::Float4x4;
case CD_PROP_INT16_2D:
return AttrType::Int16_2D;
case CD_PROP_INT8:
return AttrType::Int8;
case CD_PROP_INT32_2D:
return AttrType::Int32_2D;
case CD_PROP_COLOR:
return AttrType::ColorFloat;
case CD_PROP_FLOAT3:
return AttrType::Float3;
case CD_PROP_FLOAT2:
return AttrType::Float2;
case CD_PROP_BOOL:
return AttrType::Bool;
case CD_PROP_STRING:
return AttrType::String;
case CD_PROP_QUATERNION:
return AttrType::Quaternion;
}
return std::nullopt;
}
struct CustomDataAndSize {
CustomData &data;
int size;
};
/**
* Move generic attributes from #CustomData to #AttributeStorage. All other non-generic layers are
* left in #CustomData.
*/
static void attribute_legacy_convert_customdata_to_storage(
const Map<AttrDomain, CustomDataAndSize> &domains, AttributeStorage &storage)
{
struct AttributeToAdd {
StringRef name;
AttrDomain domain;
AttrType type;
void *array_data;
int array_size;
const ImplicitSharingInfo *sharing_info;
};
Map<AttrDomain, Vector<CustomDataLayer>> layers_to_keep;
Vector<AttributeToAdd> attributes_to_add;
for (const auto &item : domains.items()) {
const AttrDomain domain = item.key;
const CustomData &custom_data = item.value.data;
const int domain_size = item.value.size;
for (const CustomDataLayer &layer : MutableSpan(custom_data.layers, custom_data.totlayer)) {
if (const std::optional<AttrType> attr_type = custom_data_type_to_attr_type(
eCustomDataType(layer.type)))
{
/* Skip adding a user. This #CustomDataLayer is just freed below. */
attributes_to_add.append(
{layer.name, domain, *attr_type, layer.data, domain_size, layer.sharing_info});
}
else {
layers_to_keep.lookup_or_add_default(domain).append(layer);
}
}
}
for (AttributeToAdd &attribute : attributes_to_add) {
bke::Attribute::ArrayData array_data;
array_data.data = attribute.array_data;
array_data.size = attribute.array_size;
array_data.sharing_info = ImplicitSharingPtr<>(attribute.sharing_info);
storage.add(storage.unique_name_calc(attribute.name),
attribute.domain,
attribute.type,
std::move(array_data));
}
for (const auto &[domain, custom_data] : domains.items()) {
Vector layers_vector = layers_to_keep.pop_default(domain, {});
MEM_SAFE_FREE(custom_data.data.layers);
custom_data.data.totlayer = 0;
custom_data.data.maxlayer = 0;
if (layers_vector.is_empty()) {
CustomData_update_typemap(&custom_data.data);
continue;
}
VectorData data = layers_vector.release();
custom_data.data.layers = data.data;
custom_data.data.totlayer = data.size;
custom_data.data.maxlayer = data.capacity;
CustomData_update_typemap(&custom_data.data);
}
}
std::optional<eCustomDataType> attr_type_to_custom_data_type(const AttrType attr_type)
{
switch (attr_type) {
case AttrType::Bool:
return CD_PROP_BOOL;
case AttrType::Int8:
return CD_PROP_INT8;
case AttrType::Int16_2D:
return CD_PROP_INT16_2D;
case AttrType::Int32:
return CD_PROP_INT32;
case AttrType::Int32_2D:
return CD_PROP_INT32_2D;
case AttrType::Float:
return CD_PROP_FLOAT;
case AttrType::Float2:
return CD_PROP_FLOAT2;
case AttrType::Float3:
return CD_PROP_FLOAT3;
case AttrType::Float4x4:
return CD_PROP_FLOAT4X4;
case AttrType::ColorByte:
return CD_PROP_BYTE_COLOR;
case AttrType::ColorFloat:
return CD_PROP_COLOR;
case AttrType::Quaternion:
return CD_PROP_QUATERNION;
case AttrType::String:
return CD_PROP_STRING;
}
return std::nullopt;
}
struct CustomDataAndSizeMutable {
CustomData &data;
int size;
};
static void convert_storage_to_customdata(
AttributeStorage &storage,
const Map<AttrDomain, CustomDataAndSizeMutable> &custom_data_domains)
{
/* Name uniqueness is handled by the #CustomData API. */
storage.foreach([&](const Attribute &attribute) {
const std::optional<eCustomDataType> data_type = attr_type_to_custom_data_type(
attribute.data_type());
if (!data_type) {
return;
}
CustomData &custom_data = custom_data_domains.lookup(attribute.domain()).data;
const int domain_size = custom_data_domains.lookup(attribute.domain()).size;
if (const auto *array_data = std::get_if<Attribute::ArrayData>(&attribute.data())) {
BLI_assert(array_data->size == domain_size);
CustomData_add_layer_named_with_data(&custom_data,
*data_type,
array_data->data,
array_data->size,
attribute.name(),
array_data->sharing_info.get());
}
else if (const auto *single_data = std::get_if<Attribute::SingleData>(&attribute.data())) {
const CPPType &cpp_type = *custom_data_type_to_cpp_type(*data_type);
auto *value = new ImplicitSharedValue<GArray<>>(cpp_type, domain_size);
cpp_type.fill_construct_n(single_data->value, value->data.data(), domain_size);
CustomData_add_layer_named_with_data(
&custom_data, *data_type, value->data.data(), domain_size, attribute.name(), value);
}
});
storage = {};
}
void mesh_convert_storage_to_customdata(Mesh &mesh)
{
convert_storage_to_customdata(mesh.attribute_storage.wrap(),
{{AttrDomain::Point, {mesh.vert_data, mesh.verts_num}},
{AttrDomain::Edge, {mesh.edge_data, mesh.edges_num}},
{AttrDomain::Face, {mesh.face_data, mesh.faces_num}},
{AttrDomain::Corner, {mesh.corner_data, mesh.corners_num}}});
if (const char *name = mesh.active_uv_map_attribute) {
const int layer_n = CustomData_get_named_layer(&mesh.corner_data, CD_PROP_FLOAT2, name);
if (layer_n != -1) {
CustomData_set_layer_active(&mesh.corner_data, CD_PROP_FLOAT2, layer_n);
}
MEM_freeN(mesh.active_uv_map_attribute);
mesh.active_uv_map_attribute = nullptr;
}
if (const char *name = mesh.default_uv_map_attribute) {
const int layer_n = CustomData_get_named_layer(&mesh.corner_data, CD_PROP_FLOAT2, name);
if (layer_n != -1) {
CustomData_set_layer_render(&mesh.corner_data, CD_PROP_FLOAT2, layer_n);
}
MEM_freeN(mesh.default_uv_map_attribute);
mesh.default_uv_map_attribute = nullptr;
}
}
void mesh_convert_customdata_to_storage(Mesh &mesh)
{
bke::attribute_legacy_convert_customdata_to_storage(
{{AttrDomain::Point, {mesh.vert_data, mesh.verts_num}},
{AttrDomain::Edge, {mesh.edge_data, mesh.edges_num}},
{AttrDomain::Face, {mesh.face_data, mesh.faces_num}},
{AttrDomain::Corner, {mesh.corner_data, mesh.corners_num}}},
mesh.attribute_storage.wrap());
}
void curves_convert_customdata_to_storage(CurvesGeometry &curves)
{
attribute_legacy_convert_customdata_to_storage(
{{AttrDomain::Point, {curves.point_data, curves.points_num()}},
{AttrDomain::Curve, {curves.curve_data_legacy, curves.curves_num()}}},
curves.attribute_storage.wrap());
CustomData_reset(&curves.curve_data_legacy);
/* Update the curve type count again (the first time was done on file-read, where
* #AttributeStorage data doesn't exist yet for older files). */
curves.update_curve_types();
}
void pointcloud_convert_customdata_to_storage(PointCloud &pointcloud)
{
attribute_legacy_convert_customdata_to_storage(
{{AttrDomain::Point, {pointcloud.pdata_legacy, pointcloud.totpoint}}},
pointcloud.attribute_storage.wrap());
CustomData_reset(&pointcloud.pdata_legacy);
}
void grease_pencil_convert_customdata_to_storage(GreasePencil &grease_pencil)
{
attribute_legacy_convert_customdata_to_storage(
{{AttrDomain::Layer,
{grease_pencil.layers_data_legacy, int(grease_pencil.layers().size())}}},
grease_pencil.attribute_storage.wrap());
CustomData_reset(&grease_pencil.layers_data_legacy);
}
} // namespace blender::bke