PLY: Improve robustness and performance of the new PLY importer #105842

Merged
Aras Pranckevicius merged 10 commits from aras_p/blender:ply-importer into main 2023-03-17 12:20:56 +01:00
6 changed files with 97 additions and 71 deletions
Showing only changes of commit 711a0ea7f2 - Show all commits

View File

@ -42,6 +42,7 @@ void write_faces(FileBuffer &buffer, const PlyData &ply_data)
const uint32_t *indices = ply_data.face_vertices.data();
for (uint32_t face_size : ply_data.face_sizes) {
buffer.write_face(char(face_size), Span<uint32_t>(indices, face_size));
indices += face_size;
}
buffer.write_to_file();
}

View File

@ -184,9 +184,26 @@ void importer_main(Main *bmain,
return;
}
/* Parse actual file data. */
std::unique_ptr<PlyData> data = import_ply_data(file, header);
if (data == nullptr) {
fprintf(stderr, "PLY Importer: failed importing %s, unknown error\n", ob_name);
BKE_report(op->reports, RPT_ERROR, "PLY Importer: failed importing, unknown error.");
return;
}
if (!data->error.empty()) {
fprintf(stderr, "PLY Importer: failed importing %s: %s\n", ob_name, data->error.c_str());
BKE_report(op->reports, RPT_ERROR, "PLY Importer: failed importing, unknown error.");
return;
}
if (data->vertices.is_empty()) {
fprintf(stderr, "PLY Importer: file %s contains no vertices\n", ob_name);
BKE_report(op->reports, RPT_ERROR, "PLY Importer: failed importing, no vertices.");
return;
}
/* Create mesh and do all prep work. */
Mesh *mesh = BKE_mesh_add(bmain, ob_name);
BKE_view_layer_base_deselect_all(scene, view_layer);
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name);
@ -196,19 +213,10 @@ void importer_main(Main *bmain,
Base *base = BKE_view_layer_base_find(view_layer, obj);
BKE_view_layer_base_select_and_set_active(view_layer, base);
/* Parse actual file data. */
try {
std::unique_ptr<PlyData> data = import_ply_data(file, header);
Mesh *temp_val = convert_ply_to_mesh(*data, mesh, import_params);
if (import_params.merge_verts && temp_val != mesh) {
BKE_mesh_nomain_to_mesh(temp_val, mesh, obj);
}
}
catch (std::exception &e) {
fprintf(stderr, "PLY Importer: failed to read file. %s.\n", e.what());
BKE_report(op->reports, RPT_ERROR, "PLY Importer: failed to parse file.");
return;
/* Stuff ply data into the mesh. */
Mesh *temp_val = convert_ply_to_mesh(*data, mesh, import_params);
if (import_params.merge_verts && temp_val != mesh) {
BKE_mesh_nomain_to_mesh(temp_val, mesh, obj);
}
/* Object matrix and finishing up. */

View File

@ -145,7 +145,7 @@ static int2 get_uv_index(const PlyElement &element)
return {get_index(element, "s"), get_index(element, "t")};
}
static void parse_row_ascii(PlyReadBuffer &file, Vector<float> &r_values)
static const char *parse_row_ascii(PlyReadBuffer &file, Vector<float> &r_values)
{
Span<char> line = file.read_line();
@ -158,6 +158,7 @@ static void parse_row_ascii(PlyReadBuffer &file, Vector<float> &r_values)
p = parse_float(p, end, 0.0f, val);
r_values[value_idx++] = val;
}
return nullptr;
}
template<typename T> static T get_binary_value(PlyDataTypes type, const uint8_t *&r_ptr)
@ -199,25 +200,24 @@ template<typename T> static T get_binary_value(PlyDataTypes type, const uint8_t
r_ptr += 8;
break;
default:
throw std::runtime_error("Unknown property type");
BLI_assert_msg(false, "Unknown property type");
}
return val;
}
static void parse_row_binary(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
Vector<uint8_t> &r_scratch,
Vector<float> &r_values)
static const char *parse_row_binary(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
Vector<uint8_t> &r_scratch,
Vector<float> &r_values)
{
if (element.stride == 0) {
throw std::runtime_error(
"Vertex/Edge element contains list properties, this is not supported");
return "Vertex/Edge element contains list properties, this is not supported";
}
BLI_assert(r_scratch.size() == element.stride);
BLI_assert(r_values.size() == element.properties.size());
if (!file.read_bytes(r_scratch.data(), r_scratch.size())) {
throw std::runtime_error("Could not read row of binary property");
return "Could not read row of binary property";
}
const uint8_t *ptr = r_scratch.data();
@ -239,14 +239,15 @@ static void parse_row_binary(PlyReadBuffer &file,
}
}
else {
throw std::runtime_error("Unknown binary ply format for vertex element");
return "Unknown binary ply format for vertex element";
}
return nullptr;
}
static void load_vertex_element(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
PlyData *data)
static const char *load_vertex_element(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
PlyData *data)
{
/* Figure out vertex component indices. */
int3 vertex_index = get_vertex_index(element);
@ -262,7 +263,7 @@ static void load_vertex_element(PlyReadBuffer &file,
bool has_alpha = alpha_index >= 0;
if (!has_vertex) {
throw std::runtime_error("Vertex positions are not present in the file");
return "Vertex positions are not present in the file";
}
data->vertices.reserve(element.count);
@ -294,11 +295,15 @@ static void load_vertex_element(PlyReadBuffer &file,
for (int i = 0; i < element.count; i++) {
const char *error = nullptr;
if (header.type == PlyFormatType::ASCII) {
parse_row_ascii(file, value_vec);
error = parse_row_ascii(file, value_vec);
}
else {
parse_row_binary(file, header, element, scratch, value_vec);
error = parse_row_binary(file, header, element, scratch, value_vec);
}
if (error != nullptr) {
return error;
}
/* Vertex coord */
@ -340,6 +345,7 @@ static void load_vertex_element(PlyReadBuffer &file,
data->uv_coordinates.append(uvmap);
}
}
return nullptr;
}
static uint32_t read_list_count(PlyReadBuffer &file,
@ -372,10 +378,10 @@ static void skip_property(PlyReadBuffer &file,
}
}
static void load_face_element(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
PlyData *data)
static const char *load_face_element(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
PlyData *data)
{
int prop_index = get_index(element, "vertex_indices");
if (prop_index < 0) {
@ -385,11 +391,11 @@ static void load_face_element(PlyReadBuffer &file,
prop_index = 0;
}
if (prop_index < 0) {
throw std::runtime_error("Face element does not contain vertex indices property");
return "Face element does not contain vertex indices property";
}
const PlyProperty &prop = element.properties[prop_index];
if (prop.count_type == PlyDataTypes::NONE) {
throw std::runtime_error("Face element vertex indices property must be a list");
return "Face element vertex indices property must be a list";
}
data->face_vertices.reserve(element.count * 3);
@ -466,22 +472,23 @@ static void load_face_element(PlyReadBuffer &file,
}
}
}
return nullptr;
}
static void load_tristrips_element(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
PlyData *data)
static const char *load_tristrips_element(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
PlyData *data)
{
if (element.count != 1) {
throw std::runtime_error("Tristrips element should contain one row");
return "Tristrips element should contain one row";
}
if (element.properties.size() != 1) {
throw std::runtime_error("Tristrips element should contain one property");
return "Tristrips element should contain one property";
}
const PlyProperty &prop = element.properties[0];
if (prop.count_type == PlyDataTypes::NONE) {
throw std::runtime_error("Tristrips element property must be a list");
return "Tristrips element property must be a list";
}
Vector<int> strip;
@ -543,17 +550,18 @@ static void load_tristrips_element(PlyReadBuffer &file,
}
}
}
return nullptr;
}
static void load_edge_element(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
PlyData *data)
static const char *load_edge_element(PlyReadBuffer &file,
const PlyHeader &header,
const PlyElement &element,
PlyData *data)
{
int prop_vertex1 = get_index(element, "vertex1");
int prop_vertex2 = get_index(element, "vertex2");
if (prop_vertex1 < 0 || prop_vertex2 < 0) {
throw std::runtime_error("Edge element does not contain vertex1 and vertex2 properties");
return "Edge element does not contain vertex1 and vertex2 properties";
}
data->edges.reserve(element.count);
@ -565,16 +573,21 @@ static void load_edge_element(PlyReadBuffer &file,
}
for (int i = 0; i < element.count; i++) {
const char *error = nullptr;
if (header.type == PlyFormatType::ASCII) {
parse_row_ascii(file, value_vec);
error = parse_row_ascii(file, value_vec);
}
else {
parse_row_binary(file, header, element, scratch, value_vec);
error = parse_row_binary(file, header, element, scratch, value_vec);
}
if (error != nullptr) {
return error;
}
int index1 = value_vec[prop_vertex1];
int index2 = value_vec[prop_vertex2];
data->edges.append(std::make_pair(index1, index2));
}
return nullptr;
}
std::unique_ptr<PlyData> import_ply_data(PlyReadBuffer &file, PlyHeader &header)
@ -582,17 +595,22 @@ std::unique_ptr<PlyData> import_ply_data(PlyReadBuffer &file, PlyHeader &header)
std::unique_ptr<PlyData> data = std::make_unique<PlyData>();
for (const PlyElement &element : header.elements) {
const char *error = nullptr;
if (element.name == "vertex") {
load_vertex_element(file, header, element, data.get());
error = load_vertex_element(file, header, element, data.get());
}
else if (element.name == "face") {
load_face_element(file, header, element, data.get());
error = load_face_element(file, header, element, data.get());
}
else if (element.name == "tristrips") {
load_tristrips_element(file, header, element, data.get());
error = load_tristrips_element(file, header, element, data.get());
}
else if (element.name == "edge") {
load_edge_element(file, header, element, data.get());
error = load_edge_element(file, header, element, data.get());
}
if (error != nullptr) {
data->error = error;
return data;
}
}

View File

@ -22,6 +22,7 @@ struct PlyData {
Vector<uint32_t> face_vertices;
Vector<uint32_t> face_sizes;
Vector<float2> uv_coordinates;
std::string error;
};
enum PlyFormatType { ASCII, BINARY_LE, BINARY_BE };

View File

@ -21,7 +21,7 @@
namespace blender::io::ply {
class PlyExportTest : public BlendfileLoadingBaseTest {
class ply_export_test : public BlendfileLoadingBaseTest {
public:
bool load_file_and_depsgraph(const std::string &filepath,
const eEvaluationMode eval_mode = DAG_EVAL_VIEWPORT)
@ -127,7 +127,7 @@ static std::vector<char> read_temp_file_in_vectorchar(const std::string &file_pa
return res;
}
TEST_F(PlyExportTest, WriteHeaderAscii)
TEST_F(ply_export_test, WriteHeaderAscii)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params = {};
@ -165,7 +165,7 @@ TEST_F(PlyExportTest, WriteHeaderAscii)
ASSERT_STREQ(result.c_str(), expected.c_str());
}
TEST_F(PlyExportTest, WriteHeaderBinary)
TEST_F(ply_export_test, WriteHeaderBinary)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params = {};
@ -203,7 +203,7 @@ TEST_F(PlyExportTest, WriteHeaderBinary)
ASSERT_STREQ(result.c_str(), expected.c_str());
}
TEST_F(PlyExportTest, WriteVerticesAscii)
TEST_F(ply_export_test, WriteVerticesAscii)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params = {};
@ -235,7 +235,7 @@ TEST_F(PlyExportTest, WriteVerticesAscii)
ASSERT_STREQ(result.c_str(), expected.c_str());
}
TEST_F(PlyExportTest, WriteVerticesBinary)
TEST_F(ply_export_test, WriteVerticesBinary)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params = {};
@ -271,7 +271,7 @@ TEST_F(PlyExportTest, WriteVerticesBinary)
}
}
TEST_F(PlyExportTest, WriteFacesAscii)
TEST_F(ply_export_test, WriteFacesAscii)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params = {};
@ -301,7 +301,7 @@ TEST_F(PlyExportTest, WriteFacesAscii)
ASSERT_STREQ(result.c_str(), expected.c_str());
}
TEST_F(PlyExportTest, WriteFacesBinary)
TEST_F(ply_export_test, WriteFacesBinary)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params = {};
@ -337,7 +337,7 @@ TEST_F(PlyExportTest, WriteFacesBinary)
}
}
TEST_F(PlyExportTest, WriteVertexNormalsAscii)
TEST_F(ply_export_test, WriteVertexNormalsAscii)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params = {};
@ -369,7 +369,7 @@ TEST_F(PlyExportTest, WriteVertexNormalsAscii)
ASSERT_STREQ(result.c_str(), expected.c_str());
}
TEST_F(PlyExportTest, WriteVertexNormalsBinary)
TEST_F(ply_export_test, WriteVertexNormalsBinary)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params = {};
@ -411,7 +411,7 @@ TEST_F(PlyExportTest, WriteVertexNormalsBinary)
}
}
class ply_exporter_ply_data_test : public PlyExportTest {
class ply_exporter_ply_data_test : public ply_export_test {
public:
PlyData load_ply_data_from_blendfile(const std::string &blendfile, PLYExportParams &params)
{

View File

@ -34,11 +34,9 @@ class ply_import_test : public testing::Test {
ADD_FAILURE();
return;
}
std::unique_ptr<PlyData> data;
try {
data = import_ply_data(infile, header);
}
catch (std::exception &e) {
std::unique_ptr<PlyData> data = import_ply_data(infile, header);
if (!data->error.empty()) {
fprintf(stderr, "%s\n", data->error.c_str());
ASSERT_EQ(0, exp.totvert);
ASSERT_EQ(0, exp.totpoly);
return;