Fix #114346: PLY export crash with multiple objects that have different custom attributes
Custom attribute export (#114320) code did not realize that PLY exports everything as one single "mesh" with the same vertex data for everything. So if exporting several input meshes and they have different vertex attributes, the result needs to have a union of all present attributes, with fake/zero data for the ones that were not actually present. Fixes #114346.
This commit is contained in:
@@ -148,11 +148,33 @@ static void generate_vertex_map(const Mesh *mesh,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_custom_attributes(const Mesh *mesh, Vector<PlyCustomAttribute> &r_attributes)
|
static float *find_or_add_attribute(const StringRef name,
|
||||||
|
int64_t size,
|
||||||
|
uint32_t vertex_offset,
|
||||||
|
Vector<PlyCustomAttribute> &r_attributes)
|
||||||
|
{
|
||||||
|
/* Do we have this attribute from some other object already? */
|
||||||
|
for (PlyCustomAttribute &attr : r_attributes) {
|
||||||
|
if (attr.name == name) {
|
||||||
|
BLI_assert(attr.data.size() == vertex_offset);
|
||||||
|
attr.data.resize(attr.data.size() + size, 0.0f);
|
||||||
|
return attr.data.data() + vertex_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* We don't have it yet, create and fill with zero data for previous objects. */
|
||||||
|
r_attributes.append(PlyCustomAttribute(name, vertex_offset + size));
|
||||||
|
return r_attributes.last().data.data() + vertex_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_custom_attributes(const Mesh *mesh,
|
||||||
|
const Vector<int> &ply_to_vertex,
|
||||||
|
uint32_t vertex_offset,
|
||||||
|
Vector<PlyCustomAttribute> &r_attributes)
|
||||||
{
|
{
|
||||||
const bke::AttributeAccessor attributes = mesh->attributes();
|
const bke::AttributeAccessor attributes = mesh->attributes();
|
||||||
const StringRef color_name = mesh->active_color_attribute;
|
const StringRef color_name = mesh->active_color_attribute;
|
||||||
const StringRef uv_name = CustomData_get_active_layer_name(&mesh->loop_data, CD_PROP_FLOAT2);
|
const StringRef uv_name = CustomData_get_active_layer_name(&mesh->loop_data, CD_PROP_FLOAT2);
|
||||||
|
const int64_t size = ply_to_vertex.size();
|
||||||
|
|
||||||
attributes.for_all([&](const bke::AttributeIDRef &attribute_id,
|
attributes.for_all([&](const bke::AttributeIDRef &attribute_id,
|
||||||
const bke::AttributeMetaData &meta_data) {
|
const bke::AttributeMetaData &meta_data) {
|
||||||
@@ -165,140 +187,143 @@ static void load_custom_attributes(const Mesh *mesh, Vector<PlyCustomAttribute>
|
|||||||
|
|
||||||
const GVArraySpan attribute = *mesh->attributes().lookup(
|
const GVArraySpan attribute = *mesh->attributes().lookup(
|
||||||
attribute_id, meta_data.domain, meta_data.data_type);
|
attribute_id, meta_data.domain, meta_data.data_type);
|
||||||
const int64_t size = attribute.size();
|
if (attribute.is_empty()) {
|
||||||
if (size == 0) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
switch (meta_data.data_type) {
|
switch (meta_data.data_type) {
|
||||||
case CD_PROP_FLOAT: {
|
case CD_PROP_FLOAT: {
|
||||||
PlyCustomAttribute attr(attribute_id.name(), size);
|
float *attr = find_or_add_attribute(
|
||||||
|
attribute_id.name(), size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<float>();
|
auto typed = attribute.typed<float>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
attr.data[i] = typed[i];
|
attr[i] = typed[ply_to_vertex[i]];
|
||||||
}
|
}
|
||||||
r_attributes.append(attr);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_INT8: {
|
case CD_PROP_INT8: {
|
||||||
PlyCustomAttribute attr(attribute_id.name(), size);
|
float *attr = find_or_add_attribute(
|
||||||
|
attribute_id.name(), size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<int8_t>();
|
auto typed = attribute.typed<int8_t>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
attr.data[i] = typed[i];
|
attr[i] = typed[ply_to_vertex[i]];
|
||||||
}
|
}
|
||||||
r_attributes.append(attr);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_INT32: {
|
case CD_PROP_INT32: {
|
||||||
PlyCustomAttribute attr(attribute_id.name(), size);
|
float *attr = find_or_add_attribute(
|
||||||
|
attribute_id.name(), size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<int32_t>();
|
auto typed = attribute.typed<int32_t>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
attr.data[i] = typed[i];
|
attr[i] = typed[ply_to_vertex[i]];
|
||||||
}
|
}
|
||||||
r_attributes.append(attr);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_INT32_2D: {
|
case CD_PROP_INT32_2D: {
|
||||||
PlyCustomAttribute attr_x(attribute_id.name() + "_x", size);
|
float *attr_x = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_y(attribute_id.name() + "_y", size);
|
attribute_id.name() + "_x", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_y = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_y", size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<int2>();
|
auto typed = attribute.typed<int2>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
attr_x.data[i] = typed[i].x;
|
int j = ply_to_vertex[i];
|
||||||
attr_y.data[i] = typed[i].y;
|
attr_x[i] = typed[j].x;
|
||||||
|
attr_y[i] = typed[j].y;
|
||||||
}
|
}
|
||||||
r_attributes.append(attr_x);
|
|
||||||
r_attributes.append(attr_y);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_FLOAT2: {
|
case CD_PROP_FLOAT2: {
|
||||||
PlyCustomAttribute attr_x(attribute_id.name() + "_x", size);
|
float *attr_x = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_y(attribute_id.name() + "_y", size);
|
attribute_id.name() + "_x", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_y = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_y", size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<float2>();
|
auto typed = attribute.typed<float2>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
attr_x.data[i] = typed[i].x;
|
int j = ply_to_vertex[i];
|
||||||
attr_y.data[i] = typed[i].y;
|
attr_x[i] = typed[j].x;
|
||||||
|
attr_y[i] = typed[j].y;
|
||||||
}
|
}
|
||||||
r_attributes.append(attr_x);
|
|
||||||
r_attributes.append(attr_y);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_FLOAT3: {
|
case CD_PROP_FLOAT3: {
|
||||||
PlyCustomAttribute attr_x(attribute_id.name() + "_x", size);
|
float *attr_x = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_y(attribute_id.name() + "_y", size);
|
attribute_id.name() + "_x", size, vertex_offset, r_attributes);
|
||||||
PlyCustomAttribute attr_z(attribute_id.name() + "_z", size);
|
float *attr_y = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_y", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_z = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_z", size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<float3>();
|
auto typed = attribute.typed<float3>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
attr_x.data[i] = typed[i].x;
|
int j = ply_to_vertex[i];
|
||||||
attr_y.data[i] = typed[i].y;
|
attr_x[i] = typed[j].x;
|
||||||
attr_z.data[i] = typed[i].z;
|
attr_y[i] = typed[j].y;
|
||||||
|
attr_z[i] = typed[j].z;
|
||||||
}
|
}
|
||||||
r_attributes.append(attr_x);
|
|
||||||
r_attributes.append(attr_y);
|
|
||||||
r_attributes.append(attr_z);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_BYTE_COLOR: {
|
case CD_PROP_BYTE_COLOR: {
|
||||||
PlyCustomAttribute attr_r(attribute_id.name() + "_r", size);
|
float *attr_r = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_g(attribute_id.name() + "_g", size);
|
attribute_id.name() + "_r", size, vertex_offset, r_attributes);
|
||||||
PlyCustomAttribute attr_b(attribute_id.name() + "_b", size);
|
float *attr_g = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_a(attribute_id.name() + "_a", size);
|
attribute_id.name() + "_g", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_b = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_b", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_a = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_a", size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<ColorGeometry4b>();
|
auto typed = attribute.typed<ColorGeometry4b>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
ColorGeometry4f col = typed[i].decode();
|
ColorGeometry4f col = typed[ply_to_vertex[i]].decode();
|
||||||
attr_r.data[i] = col.r;
|
attr_r[i] = col.r;
|
||||||
attr_g.data[i] = col.g;
|
attr_g[i] = col.g;
|
||||||
attr_b.data[i] = col.b;
|
attr_b[i] = col.b;
|
||||||
attr_a.data[i] = col.a;
|
attr_a[i] = col.a;
|
||||||
}
|
}
|
||||||
r_attributes.append(attr_r);
|
|
||||||
r_attributes.append(attr_g);
|
|
||||||
r_attributes.append(attr_b);
|
|
||||||
r_attributes.append(attr_a);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_COLOR: {
|
case CD_PROP_COLOR: {
|
||||||
PlyCustomAttribute attr_r(attribute_id.name() + "_r", size);
|
float *attr_r = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_g(attribute_id.name() + "_g", size);
|
attribute_id.name() + "_r", size, vertex_offset, r_attributes);
|
||||||
PlyCustomAttribute attr_b(attribute_id.name() + "_b", size);
|
float *attr_g = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_a(attribute_id.name() + "_a", size);
|
attribute_id.name() + "_g", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_b = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_b", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_a = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_a", size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<ColorGeometry4f>();
|
auto typed = attribute.typed<ColorGeometry4f>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
ColorGeometry4f col = typed[i];
|
ColorGeometry4f col = typed[ply_to_vertex[i]];
|
||||||
attr_r.data[i] = col.r;
|
attr_r[i] = col.r;
|
||||||
attr_g.data[i] = col.g;
|
attr_g[i] = col.g;
|
||||||
attr_b.data[i] = col.b;
|
attr_b[i] = col.b;
|
||||||
attr_a.data[i] = col.a;
|
attr_a[i] = col.a;
|
||||||
}
|
}
|
||||||
r_attributes.append(attr_r);
|
|
||||||
r_attributes.append(attr_g);
|
|
||||||
r_attributes.append(attr_b);
|
|
||||||
r_attributes.append(attr_a);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_BOOL: {
|
case CD_PROP_BOOL: {
|
||||||
PlyCustomAttribute attr(attribute_id.name(), size);
|
float *attr = find_or_add_attribute(
|
||||||
|
attribute_id.name(), size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<bool>();
|
auto typed = attribute.typed<bool>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
attr.data[i] = typed[i] ? 1.0f : 0.0f;
|
attr[i] = typed[ply_to_vertex[i]] ? 1.0f : 0.0f;
|
||||||
}
|
}
|
||||||
r_attributes.append(attr);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CD_PROP_QUATERNION: {
|
case CD_PROP_QUATERNION: {
|
||||||
PlyCustomAttribute attr_x(attribute_id.name() + "_x", size);
|
float *attr_x = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_y(attribute_id.name() + "_y", size);
|
attribute_id.name() + "_x", size, vertex_offset, r_attributes);
|
||||||
PlyCustomAttribute attr_z(attribute_id.name() + "_z", size);
|
float *attr_y = find_or_add_attribute(
|
||||||
PlyCustomAttribute attr_w(attribute_id.name() + "_w", size);
|
attribute_id.name() + "_y", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_z = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_z", size, vertex_offset, r_attributes);
|
||||||
|
float *attr_w = find_or_add_attribute(
|
||||||
|
attribute_id.name() + "_w", size, vertex_offset, r_attributes);
|
||||||
auto typed = attribute.typed<math::Quaternion>();
|
auto typed = attribute.typed<math::Quaternion>();
|
||||||
for (const int64_t i : typed.index_range()) {
|
for (const int64_t i : ply_to_vertex.index_range()) {
|
||||||
attr_x.data[i] = typed[i].x;
|
int j = ply_to_vertex[i];
|
||||||
attr_y.data[i] = typed[i].y;
|
attr_x[i] = typed[j].x;
|
||||||
attr_z.data[i] = typed[i].z;
|
attr_y[i] = typed[j].y;
|
||||||
attr_w.data[i] = typed[i].w;
|
attr_z[i] = typed[j].z;
|
||||||
|
attr_w[i] = typed[j].w;
|
||||||
}
|
}
|
||||||
r_attributes.append(attr_x);
|
|
||||||
r_attributes.append(attr_y);
|
|
||||||
r_attributes.append(attr_z);
|
|
||||||
r_attributes.append(attr_w);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -430,7 +455,7 @@ void load_plydata(PlyData &plyData, Depsgraph *depsgraph, const PLYExportParams
|
|||||||
|
|
||||||
/* Custom attributes */
|
/* Custom attributes */
|
||||||
if (export_params.export_attributes) {
|
if (export_params.export_attributes) {
|
||||||
load_custom_attributes(mesh, plyData.vertex_custom_attr);
|
load_custom_attributes(mesh, ply_to_vertex, vertex_offset, plyData.vertex_custom_attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loose edges */
|
/* Loose edges */
|
||||||
@@ -451,6 +476,12 @@ void load_plydata(PlyData &plyData, Depsgraph *depsgraph, const PLYExportParams
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEG_OBJECT_ITER_END;
|
DEG_OBJECT_ITER_END;
|
||||||
|
|
||||||
|
/* Make sure all custom attribute data arrays are encompassing all input objects */
|
||||||
|
for (PlyCustomAttribute &attr : plyData.vertex_custom_attr) {
|
||||||
|
BLI_assert(attr.data.size() <= vertex_offset);
|
||||||
|
attr.data.resize(vertex_offset, 0.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blender::io::ply
|
} // namespace blender::io::ply
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BLI_array.hh"
|
|
||||||
#include "BLI_math_vector_types.hh"
|
#include "BLI_math_vector_types.hh"
|
||||||
#include "BLI_string_ref.hh"
|
#include "BLI_string_ref.hh"
|
||||||
#include "BLI_vector.hh"
|
#include "BLI_vector.hh"
|
||||||
@@ -18,9 +17,9 @@ namespace blender::io::ply {
|
|||||||
enum PlyDataTypes { NONE, CHAR, UCHAR, SHORT, USHORT, INT, UINT, FLOAT, DOUBLE, PLY_TYPE_COUNT };
|
enum PlyDataTypes { NONE, CHAR, UCHAR, SHORT, USHORT, INT, UINT, FLOAT, DOUBLE, PLY_TYPE_COUNT };
|
||||||
|
|
||||||
struct PlyCustomAttribute {
|
struct PlyCustomAttribute {
|
||||||
PlyCustomAttribute(const StringRef name_, int64_t size) : name(name_), data(size) {}
|
PlyCustomAttribute(const StringRef name_, int64_t size) : name(name_), data(size, 0.0f) {}
|
||||||
std::string name;
|
std::string name;
|
||||||
Array<float> data; /* Any custom PLY attributes are converted to floats. */
|
Vector<float> data; /* Any custom PLY attributes are converted to floats. */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PlyData {
|
struct PlyData {
|
||||||
|
|||||||
@@ -525,6 +525,18 @@ TEST_F(ply_exporter_ply_data_test, CubeLooseEdgesLoadPLYDataUV)
|
|||||||
EXPECT_EQ_ARRAY(exp_faces, plyData.face_vertices.data(), ARRAY_SIZE(exp_faces));
|
EXPECT_EQ_ARRAY(exp_faces, plyData.face_vertices.data(), ARRAY_SIZE(exp_faces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ply_exporter_ply_data_test, CubesVertexAttrs)
|
||||||
|
{
|
||||||
|
PLYExportParams params = {};
|
||||||
|
params.export_uv = true;
|
||||||
|
params.export_attributes = true;
|
||||||
|
PlyData plyData = load_ply_data_from_blendfile(
|
||||||
|
"io_tests/blend_geometry/cubes_vertex_attrs.blend", params);
|
||||||
|
EXPECT_EQ(plyData.vertices.size(), 28);
|
||||||
|
EXPECT_EQ(plyData.vertex_custom_attr.size(), 11); /* Float 1 + Color 4 + ByteColor 4 + Int2D 2*/
|
||||||
|
EXPECT_EQ(plyData.vertex_custom_attr[0].data.size(), 28);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ply_exporter_ply_data_test, SuzanneLoadPLYDataUV)
|
TEST_F(ply_exporter_ply_data_test, SuzanneLoadPLYDataUV)
|
||||||
{
|
{
|
||||||
PLYExportParams params = {};
|
PLYExportParams params = {};
|
||||||
|
|||||||
Reference in New Issue
Block a user