Mesh: 5.0 changes for mask, custom normals, UV seam

Complete the versioning and writing changes from these commits:
- f9b627d29c
- f2bcd73bd2
- c5ba8bd7c2

4.5 has the forward compatibility handling to support reading files
written by 5.0. This removes the conversion when writing files
and removes the lookup of ".uv_seam" as a fallback for "uv_seam".

Pull Request: https://projects.blender.org/blender/blender/pulls/139841
This commit is contained in:
Hans Goudey
2025-06-04 17:46:48 +02:00
committed by Hans Goudey
parent 1ee43f7b2b
commit 84bee96757
7 changed files with 69 additions and 175 deletions

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 0
#define BLENDER_FILE_SUBVERSION 1
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -8,8 +8,6 @@
* \ingroup bke
*/
#include "BLI_span.hh"
struct CustomData;
struct Main;
struct Mesh;
@@ -18,10 +16,8 @@ struct CustomDataLayer;
namespace blender::bke {
void mesh_custom_normals_to_legacy(MutableSpan<CustomDataLayer> corner_layers);
void mesh_custom_normals_to_generic(Mesh &mesh);
void mesh_sculpt_mask_to_legacy(MutableSpan<CustomDataLayer> vert_layers);
void mesh_sculpt_mask_to_generic(Mesh &mesh);
} // namespace blender::bke
@@ -129,9 +125,9 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh);
/* NOTE(@sybren): Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.hh,
* but I don't want to force every user of BKE_mesh.h to also include that file. */
BLI_INLINE int BKE_mesh_origindex_mface_mpoly(const int *index_mf_to_mpoly,
const int *index_mp_to_orig,
const int i)
inline int BKE_mesh_origindex_mface_mpoly(const int *index_mf_to_mpoly,
const int *index_mp_to_orig,
const int i)
{
const int j = index_mf_to_mpoly[i];
return (j != -1) ? (index_mp_to_orig ? index_mp_to_orig[j] : j) : -1;

View File

@@ -284,53 +284,6 @@ static void mesh_foreach_path(ID *id, BPathForeachPathData *bpath_data)
}
}
static void rename_seam_layer_to_old_name(const ListBase vertex_groups,
const Span<CustomDataLayer> vert_layers,
MutableSpan<CustomDataLayer> edge_layers,
const Span<CustomDataLayer> face_layers,
const Span<CustomDataLayer> corner_layers)
{
CustomDataLayer *seam_layer = nullptr;
for (CustomDataLayer &layer : edge_layers) {
if (STREQ(layer.name, ".uv_seam")) {
return;
}
if (layer.type == CD_PROP_BOOL && STREQ(layer.name, "uv_seam")) {
seam_layer = &layer;
}
}
if (!seam_layer) {
return;
}
/* Current files are not expected to have a ".uv_seam" attribute (the old name) except in the
* rare case users created it themselves. If that happens, avoid renaming the current UV seam
* attribute so that at least it's not hidden in the old version. */
for (const CustomDataLayer &layer : vert_layers) {
if (STREQ(layer.name, ".uv_seam")) {
return;
}
}
for (const CustomDataLayer &layer : face_layers) {
if (STREQ(layer.name, ".uv_seam")) {
return;
}
}
for (const CustomDataLayer &layer : corner_layers) {
if (STREQ(layer.name, ".uv_seam")) {
return;
}
}
LISTBASE_FOREACH (const bDeformGroup *, vertex_group, &vertex_groups) {
if (STREQ(vertex_group->name, ".uv_seam")) {
return;
}
}
STRNCPY(seam_layer->name, ".uv_seam");
}
static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
using namespace blender;
@@ -382,13 +335,6 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address
mesh->corner_data, AttrDomain::Corner, mesh->corners_num, loop_layers, attribute_data);
mesh->attribute_storage.dna_attributes = attribute_data.attributes.data();
mesh->attribute_storage.dna_attributes_num = attribute_data.attributes.size();
if (!is_undo) {
/* Write forward compatible format. To be removed in 5.0. */
rename_seam_layer_to_old_name(
mesh->vertex_group_names, vert_layers, edge_layers, face_layers, loop_layers);
mesh_sculpt_mask_to_legacy(vert_layers);
mesh_custom_normals_to_legacy(loop_layers);
}
}
const blender::bke::MeshRuntime *mesh_runtime = mesh->runtime;

View File

@@ -2462,27 +2462,6 @@ void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
namespace blender::bke {
void mesh_sculpt_mask_to_legacy(MutableSpan<CustomDataLayer> vert_layers)
{
bool changed = false;
for (CustomDataLayer &layer : vert_layers) {
if (StringRef(layer.name) == ".sculpt_mask") {
layer.type = CD_PAINT_MASK;
layer.name[0] = '\0';
changed = true;
break;
}
}
if (!changed) {
return;
}
/* #CustomData expects the layers to be sorted in increasing order based on type. */
std::stable_sort(
vert_layers.begin(),
vert_layers.end(),
[](const CustomDataLayer &a, const CustomDataLayer &b) { return a.type < b.type; });
}
void mesh_sculpt_mask_to_generic(Mesh &mesh)
{
if (mesh.attributes().contains(".sculpt_mask")) {
@@ -2510,27 +2489,6 @@ void mesh_sculpt_mask_to_generic(Mesh &mesh)
}
}
void mesh_custom_normals_to_legacy(MutableSpan<CustomDataLayer> corner_layers)
{
bool changed = false;
for (CustomDataLayer &layer : corner_layers) {
if (StringRef(layer.name) == "custom_normal" && layer.type == CD_PROP_INT16_2D) {
layer.type = CD_CUSTOMLOOPNORMAL;
layer.name[0] = '\0';
changed = true;
break;
}
}
if (!changed) {
return;
}
/* #CustomData expects the layers to be sorted in increasing order based on type. */
std::stable_sort(
corner_layers.begin(),
corner_layers.end(),
[](const CustomDataLayer &a, const CustomDataLayer &b) { return a.type < b.type; });
}
void mesh_custom_normals_to_generic(Mesh &mesh)
{
if (mesh.attributes().contains("custom_normal")) {

View File

@@ -4957,55 +4957,6 @@ void do_versions_after_linking_450(FileData * /*fd*/, Main *bmain)
*/
}
static CustomDataLayer *find_old_seam_layer(CustomData &custom_data, const blender::StringRef name)
{
for (CustomDataLayer &layer : blender::MutableSpan(custom_data.layers, custom_data.totlayer)) {
if (layer.name == name) {
return &layer;
}
}
return nullptr;
}
static void rename_mesh_uv_seam_attribute(Mesh &mesh)
{
using namespace blender;
CustomDataLayer *old_seam_layer = find_old_seam_layer(mesh.edge_data, ".uv_seam");
if (!old_seam_layer) {
return;
}
Set<StringRef> names;
for (const CustomDataLayer &layer : Span(mesh.vert_data.layers, mesh.vert_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.edge_data.layers, mesh.edge_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.face_data.layers, mesh.face_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.corner_data.layers, mesh.corner_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
LISTBASE_FOREACH (const bDeformGroup *, vertex_group, &mesh.vertex_group_names) {
names.add(vertex_group->name);
}
/* If the new UV name is already taken, still rename the attribute so it becomes visible in the
* list. Then the user can deal with the name conflict themselves. */
const std::string new_name = BLI_uniquename_cb(
[&](const StringRef name) { return names.contains(name); }, '.', "uv_seam");
STRNCPY(old_seam_layer->name, new_name.c_str());
}
static void do_version_node_curve_to_mesh_scale_input(bNodeTree *tree)
{
using namespace blender;
@@ -6184,15 +6135,6 @@ void blo_do_versions_450(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
/* Always run this versioning (keep at the bottom of the function). Meshes are written with the
* legacy format which always needs to be converted to the new format on file load. To be moved
* to a subversion check in 5.0. */
LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
blender::bke::mesh_sculpt_mask_to_generic(*mesh);
blender::bke::mesh_custom_normals_to_generic(*mesh);
rename_mesh_uv_seam_attribute(*mesh);
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@@ -9,10 +9,15 @@
#define DNA_DEPRECATED_ALLOW
#include "DNA_ID.h"
#include "DNA_mesh_types.h"
#include "BLI_sys_types.h"
#include "BLI_listbase.h"
#include "BLI_set.hh"
#include "BLI_string.h"
#include "BLI_string_utils.hh"
#include "BKE_main.hh"
#include "BKE_mesh_legacy_convert.hh"
#include "readfile.hh"
@@ -21,6 +26,55 @@
// #include "CLG_log.h"
// static CLG_LogRef LOG = {"blo.readfile.doversion"};
static CustomDataLayer *find_old_seam_layer(CustomData &custom_data, const blender::StringRef name)
{
for (CustomDataLayer &layer : blender::MutableSpan(custom_data.layers, custom_data.totlayer)) {
if (layer.name == name) {
return &layer;
}
}
return nullptr;
}
static void rename_mesh_uv_seam_attribute(Mesh &mesh)
{
using namespace blender;
CustomDataLayer *old_seam_layer = find_old_seam_layer(mesh.edge_data, ".uv_seam");
if (!old_seam_layer) {
return;
}
Set<StringRef> names;
for (const CustomDataLayer &layer : Span(mesh.vert_data.layers, mesh.vert_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.edge_data.layers, mesh.edge_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.face_data.layers, mesh.face_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.corner_data.layers, mesh.corner_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
LISTBASE_FOREACH (const bDeformGroup *, vertex_group, &mesh.vertex_group_names) {
names.add(vertex_group->name);
}
/* If the new UV name is already taken, still rename the attribute so it becomes visible in the
* list. Then the user can deal with the name conflict themselves. */
const std::string new_name = BLI_uniquename_cb(
[&](const StringRef name) { return names.contains(name); }, '.', "uv_seam");
STRNCPY(old_seam_layer->name, new_name.c_str());
}
void do_versions_after_linking_500(FileData * /*fd*/, Main * /*bmain*/)
{
/**
@@ -31,8 +85,17 @@ void do_versions_after_linking_500(FileData * /*fd*/, Main * /*bmain*/)
*/
}
void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main * /*bmain*/)
void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
{
using namespace blender;
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 1)) {
LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
bke::mesh_sculpt_mask_to_generic(*mesh);
bke::mesh_custom_normals_to_generic(*mesh);
rename_mesh_uv_seam_attribute(*mesh);
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@@ -653,17 +653,6 @@ bool rna_AttributeGroup_lookup_string(PointerRNA *ptr, const char *key, PointerR
return true;
}
/* Support retrieving UV seam name convention with older name. To be removed as part of 5.0
* breaking changes. */
if (STREQ(key, ".uv_seam")) {
if (CustomDataLayer *layer = BKE_attribute_search_for_write(
owner, "uv_seam", CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL))
{
rna_pointer_create_with_ancestors(*ptr, &RNA_Attribute, layer, *r_ptr);
return true;
}
}
*r_ptr = PointerRNA_NULL;
return false;
}