Blender V5.0
ply_import.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_context.hh"
10#include "BKE_layer.hh"
11#include "BKE_mesh.hh"
12#include "BKE_object.hh"
13#include "BKE_report.hh"
14
16#include "DNA_object_types.h"
17#include "DNA_scene_types.h"
18
19#include "BLI_math_matrix.h"
20#include "BLI_math_rotation.h"
21#include "BLI_span.hh"
22#include "BLI_string.h"
23
24#include "DEG_depsgraph.hh"
26
27#include "ply_data.hh"
28#include "ply_import.hh"
29#include "ply_import_buffer.hh"
30#include "ply_import_data.hh"
31#include "ply_import_mesh.hh"
32
33#include "CLG_log.h"
34static CLG_LogRef LOG = {"io.ply"};
35
36namespace blender::io::ply {
37
38/* If line starts with keyword, returns true and drops it from the line. */
39static bool parse_keyword(Span<char> &str, StringRef keyword)
40{
41 const size_t keyword_len = keyword.size();
42 if (str.size() < keyword_len) {
43 return false;
44 }
45 if (memcmp(str.data(), keyword.data(), keyword_len) != 0) {
46 return false;
47 }
48 str = str.drop_front(keyword_len);
49 return true;
50}
51
53{
54 size_t len = 0;
55 while (len < str.size() && str[len] > ' ') {
56 ++len;
57 }
58 Span<char> word(str.begin(), len);
59 str = str.drop_front(len);
60 return word;
61}
62
64{
65 while (!str.is_empty() && str[0] <= ' ') {
66 str = str.drop_front(1);
67 }
68}
69
71{
72 StringRef input(word.data(), word.size());
73 if (ELEM(input, "uchar", "uint8")) {
75 }
76 if (ELEM(input, "char", "int8")) {
77 return PlyDataTypes::CHAR;
78 }
79 if (ELEM(input, "ushort", "uint16")) {
81 }
82 if (ELEM(input, "short", "int16")) {
84 }
85 if (ELEM(input, "uint", "uint32")) {
86 return PlyDataTypes::UINT;
87 }
88 if (ELEM(input, "int", "int32")) {
89 return PlyDataTypes::INT;
90 }
91 if (ELEM(input, "float", "float32")) {
93 }
94 if (ELEM(input, "double", "float64")) {
96 }
97 return PlyDataTypes::NONE;
98}
99
100const char *read_header(PlyReadBuffer &file, PlyHeader &r_header)
101{
102 Span<char> word, line;
103 line = file.read_line();
104 if (StringRef(line.data(), line.size()) != "ply") {
105 return "Invalid PLY header.";
106 }
107
108 while (true) { /* We break when end_header is encountered. */
109 line = file.read_line();
110
111 if (parse_keyword(line, "format")) {
112 skip_space(line);
113 if (parse_keyword(line, "ascii")) {
114 r_header.type = PlyFormatType::ASCII;
115 }
116 else if (parse_keyword(line, "binary_big_endian")) {
118 }
119 else if (parse_keyword(line, "binary_little_endian")) {
121 }
122 }
123 else if (parse_keyword(line, "element")) {
125
126 skip_space(line);
127 word = parse_word(line);
128 element.name = std::string(word.data(), word.size());
129 skip_space(line);
130 word = parse_word(line);
131 element.count = std::stoi(std::string(word.data(), word.size()));
132 r_header.elements.append(element);
133 }
134 else if (parse_keyword(line, "property")) {
135 PlyProperty property;
136 skip_space(line);
137 if (parse_keyword(line, "list")) {
138 skip_space(line);
139 property.count_type = type_from_string(parse_word(line));
140 }
141 skip_space(line);
142 property.type = type_from_string(parse_word(line));
143 skip_space(line);
144 word = parse_word(line);
145 property.name = std::string(word.data(), word.size());
146 r_header.elements.last().properties.append(property);
147 }
148 else if (parse_keyword(line, "end_header")) {
149 break;
150 }
151 else if (line.is_empty() || (line.first() >= '0' && line.first() <= '9') ||
152 line.first() == '-')
153 {
154 /* A value was found before we broke out of the loop. No end_header. */
155 return "No end_header.";
156 }
157 }
158
159 file.after_header(r_header.type != PlyFormatType::ASCII);
160 for (PlyElement &el : r_header.elements) {
161 el.calc_stride();
162 }
163 return nullptr;
164}
165
166static Mesh *read_ply_to_mesh(const PLYImportParams &import_params, const char *ob_name)
167{
168 /* Parse header. */
169 PlyReadBuffer file(import_params.filepath, 64 * 1024);
170
171 PlyHeader header;
172 const char *err = read_header(file, header);
173 if (err != nullptr) {
174 CLOG_ERROR(&LOG, "PLY Importer: %s: %s", ob_name, err);
175 BKE_reportf(import_params.reports, RPT_ERROR, "PLY Importer: %s: %s", ob_name, err);
176 return nullptr;
177 }
178
179 /* Parse actual file data. */
180 std::unique_ptr<PlyData> data = import_ply_data(file, header);
181 if (data == nullptr) {
182 CLOG_ERROR(&LOG, "PLY Importer: failed importing %s, unknown error", ob_name);
183 BKE_report(import_params.reports, RPT_ERROR, "PLY Importer: failed importing, unknown error");
184 return nullptr;
185 }
186 if (!data->error.empty()) {
187 CLOG_ERROR(&LOG, "PLY Importer: failed importing %s: %s", ob_name, data->error.c_str());
188 BKE_report(import_params.reports, RPT_ERROR, "PLY Importer: failed importing, unknown error");
189 return nullptr;
190 }
191 if (data->vertices.is_empty()) {
192 CLOG_ERROR(&LOG, "PLY Importer: file %s contains no vertices", ob_name);
193 BKE_report(import_params.reports, RPT_ERROR, "PLY Importer: failed importing, no vertices");
194 return nullptr;
195 }
196
197 return convert_ply_to_mesh(*data, import_params);
198}
199
200Mesh *import_mesh(const PLYImportParams &import_params)
201{
202 /* File base name used for both mesh and object. */
203 char ob_name[FILE_MAX];
204 STRNCPY(ob_name, BLI_path_basename(import_params.filepath));
206
207 /* Stuff ply data into the mesh. */
208 return read_ply_to_mesh(import_params, ob_name);
209}
210
211void importer_main(bContext *C, const PLYImportParams &import_params)
212{
213 Main *bmain = CTX_data_main(C);
214 Scene *scene = CTX_data_scene(C);
215 ViewLayer *view_layer = CTX_data_view_layer(C);
216 importer_main(bmain, scene, view_layer, import_params);
217}
218
219void importer_main(Main *bmain,
220 Scene *scene,
221 ViewLayer *view_layer,
222 const PLYImportParams &import_params)
223{
224 /* File base name used for both mesh and object. */
225 char ob_name[FILE_MAX];
226 STRNCPY(ob_name, BLI_path_basename(import_params.filepath));
228
229 /* Stuff ply data into the mesh. */
230 Mesh *mesh = read_ply_to_mesh(import_params, ob_name);
231
232 if (mesh == nullptr) {
233 return;
234 }
235
236 /* Create mesh and do all prep work. */
237 Mesh *mesh_in_main = BKE_mesh_add(bmain, ob_name);
238 BKE_view_layer_base_deselect_all(scene, view_layer);
240 Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name);
241 obj->data = mesh_in_main;
243 BKE_view_layer_synced_ensure(scene, view_layer);
244 Base *base = BKE_view_layer_base_find(view_layer, obj);
246
247 BKE_mesh_nomain_to_mesh(mesh, mesh_in_main, obj);
248
249 /* Object matrix and finishing up. */
250 float global_scale = import_params.global_scale;
251 if ((scene->unit.system != USER_UNIT_NONE) && import_params.use_scene_unit) {
252 global_scale /= scene->unit.scale_length;
253 }
254 float scale_vec[3] = {global_scale, global_scale, global_scale};
255 float obmat3x3[3][3];
256 unit_m3(obmat3x3);
257 float obmat4x4[4][4];
258 unit_m4(obmat4x4);
259 /* +Y-forward and +Z-up are the Blender's default axis settings. */
261 IO_AXIS_Y, IO_AXIS_Z, import_params.forward_axis, import_params.up_axis, obmat3x3);
262 copy_m4_m3(obmat4x4, obmat3x3);
263 rescale_m4(obmat4x4, scale_vec);
264 BKE_object_apply_mat4(obj, obmat4x4, true, false);
265
269 DEG_id_tag_update_ex(bmain, &obj->id, flags);
272}
273} // namespace blender::io::ply
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
LayerCollection * BKE_layer_collection_get_active(ViewLayer *view_layer)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
void BKE_view_layer_base_deselect_all(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
void BKE_view_layer_base_select_and_set_active(ViewLayer *view_layer, Base *selbase)
Mesh * BKE_mesh_add(Main *bmain, const char *name)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob, bool process_shape_keys=true)
General operations, lookup, etc. for blender objects.
void BKE_object_apply_mat4(Object *ob, const float mat[4][4], bool use_compat, bool use_parent)
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void unit_m3(float m[3][3])
void copy_m4_m3(float m1[4][4], const float m2[3][3])
void rescale_m4(float mat[4][4], const float scale[3])
void unit_m4(float m[4][4])
bool mat3_from_axis_conversion(int src_forward, int src_up, int dst_forward, int dst_up, float r_mat[3][3])
bool bool BLI_path_extension_strip(char *path) ATTR_NONNULL(1)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAX
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define ELEM(...)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1104
Object groups, one object can be in many groups at once.
Object is a sort of wrapper for general info.
@ OB_MESH
@ USER_UNIT_NONE
@ IO_AXIS_Y
@ IO_AXIS_Z
#define C
Definition RandGen.cpp:29
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const void * element
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr int64_t size() const
constexpr const char * data() const
#define str(s)
#define input
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
#define LOG(level)
Definition log.h:97
static PlyDataTypes type_from_string(Span< char > word)
Definition ply_import.cc:70
static void skip_space(Span< char > &str)
Definition ply_import.cc:63
void importer_main(bContext *C, const PLYImportParams &import_params)
std::unique_ptr< PlyData > import_ply_data(PlyReadBuffer &file, PlyHeader &header)
static Mesh * read_ply_to_mesh(const PLYImportParams &import_params, const char *ob_name)
static Span< char > parse_word(Span< char > &str)
Definition ply_import.cc:52
static bool parse_keyword(Span< char > &str, StringRef keyword)
Definition ply_import.cc:39
Mesh * convert_ply_to_mesh(PlyData &data, const PLYImportParams &params)
const char * read_header(PlyReadBuffer &file, PlyHeader &r_header)
Mesh * import_mesh(const PLYImportParams &import_params)
struct Collection * collection
eIOAxis up_axis
Definition IO_ply.hh:61
bool use_scene_unit
Definition IO_ply.hh:62
ReportList * reports
Definition IO_ply.hh:68
char filepath[FILE_MAX]
Definition IO_ply.hh:59
float global_scale
Definition IO_ply.hh:63
eIOAxis forward_axis
Definition IO_ply.hh:60
struct UnitSettings unit
Vector< PlyElement > elements
Definition ply_data.hh:58
uint len