Blender V5.0
ply_import_data.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 "ply_import_data.hh"
10#include "ply_data.hh"
11#include "ply_import_buffer.hh"
12
13#include "BLI_endian_switch.h"
14#include "BLI_string_ref.hh"
15
16#include "fast_float.h"
17
18#include <charconv>
19
20#include "CLG_log.h"
21static CLG_LogRef LOG = {"io.ply"};
22
23static bool is_whitespace(char c)
24{
25 return c <= ' ';
26}
27
28static const char *drop_whitespace(const char *p, const char *end)
29{
30 while (p < end && is_whitespace(*p)) {
31 ++p;
32 }
33 return p;
34}
35
36static const char *drop_non_whitespace(const char *p, const char *end)
37{
38 while (p < end && !is_whitespace(*p)) {
39 ++p;
40 }
41 return p;
42}
43
44static const char *drop_plus(const char *p, const char *end)
45{
46 if (p < end && *p == '+') {
47 ++p;
48 }
49 return p;
50}
51
52static const char *parse_float(const char *p, const char *end, float fallback, float &dst)
53{
54 p = drop_whitespace(p, end);
55 p = drop_plus(p, end);
56 fast_float::from_chars_result res = fast_float::from_chars(p, end, dst);
57 if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range)) {
58 dst = fallback;
59 }
60 return res.ptr;
61}
62
63static const char *parse_int(const char *p, const char *end, int fallback, int &dst)
64{
65 p = drop_whitespace(p, end);
66 p = drop_plus(p, end);
67 std::from_chars_result res = std::from_chars(p, end, dst);
68 if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range)) {
69 dst = fallback;
70 }
71 return res.ptr;
72}
73
74static void endian_switch(uint8_t *ptr, int type_size)
75{
76 if (type_size == 2) {
77 BLI_endian_switch_uint16((uint16_t *)ptr);
78 }
79 else if (type_size == 4) {
80 BLI_endian_switch_uint32((uint32_t *)ptr);
81 }
82 else if (type_size == 8) {
84 }
85}
86
87static void endian_switch_array(uint8_t *ptr, int type_size, int size)
88{
89 if (type_size == 2) {
91 }
92 else if (type_size == 4) {
94 }
95 else if (type_size == 8) {
97 }
98}
99
100namespace blender::io::ply {
101
102static const int data_type_size[] = {0, 1, 1, 2, 2, 4, 4, 4, 8};
103static_assert(std::size(data_type_size) == PLY_TYPE_COUNT, "PLY data type size table mismatch");
104
105static const float data_type_normalizer[] = {
106 1.0f, 127.0f, 255.0f, 32767.0f, 65535.0f, float(INT_MAX), float(UINT_MAX), 1.0f, 1.0f};
107static_assert(std::size(data_type_normalizer) == PLY_TYPE_COUNT,
108 "PLY data type normalization factor table mismatch");
109
111{
112 stride = 0;
113 for (PlyProperty &p : properties) {
114 if (p.count_type != PlyDataTypes::NONE) {
115 stride = 0;
116 return;
117 }
118 stride += data_type_size[p.type];
119 }
120}
121
122static int get_index(const PlyElement &element, StringRef property)
123{
124 for (int i = 0, n = int(element.properties.size()); i != n; i++) {
125 const PlyProperty &prop = element.properties[i];
126 if (prop.name == property) {
127 return i;
128 }
129 }
130 return -1;
131}
132
133static const char *parse_row_ascii(PlyReadBuffer &file, Vector<float> &r_values)
134{
135 Span<char> line = file.read_line();
136 if (line.is_empty()) {
137 return "Could not read row of ASCII property";
138 }
139
140 /* Parse whole line as floats. */
141 const char *p = line.data();
142 const char *end = p + line.size();
143 int value_idx = 0;
144 while (p < end && value_idx < r_values.size()) {
145 float val;
146 p = parse_float(p, end, 0.0f, val);
147 r_values[value_idx++] = val;
148 }
149 return nullptr;
150}
151
152template<typename T> static T get_binary_value(PlyDataTypes type, const uint8_t *&r_ptr)
153{
154 T val = 0;
155 switch (type) {
156 case NONE:
157 break;
158 case CHAR:
159 val = *(int8_t *)r_ptr;
160 r_ptr += 1;
161 break;
162 case UCHAR:
163 val = *(uint8_t *)r_ptr;
164 r_ptr += 1;
165 break;
166 case SHORT:
167 val = *(int16_t *)r_ptr;
168 r_ptr += 2;
169 break;
170 case USHORT:
171 val = *(uint16_t *)r_ptr;
172 r_ptr += 2;
173 break;
174 case INT:
175 val = *(int32_t *)r_ptr;
176 r_ptr += 4;
177 break;
178 case UINT:
179 val = *(int32_t *)r_ptr;
180 r_ptr += 4;
181 break;
182 case FLOAT:
183 val = *(float *)r_ptr;
184 r_ptr += 4;
185 break;
186 case DOUBLE:
187 val = *(double *)r_ptr;
188 r_ptr += 8;
189 break;
190 default:
191 BLI_assert_msg(false, "Unknown property type");
192 }
193 return val;
194}
195
196static const char *parse_row_binary(PlyReadBuffer &file,
197 const PlyHeader &header,
198 const PlyElement &element,
199 Vector<uint8_t> &r_scratch,
200 Vector<float> &r_values)
201{
202 if (element.stride == 0) {
203 return "Vertex/Edge element contains list properties, this is not supported";
204 }
205 BLI_assert(r_scratch.size() == element.stride);
206 BLI_assert(r_values.size() == element.properties.size());
207 if (!file.read_bytes(r_scratch.data(), r_scratch.size())) {
208 return "Could not read row of binary property";
209 }
210
211 const uint8_t *ptr = r_scratch.data();
212 if (header.type == PlyFormatType::BINARY_LE) {
213 /* Little endian: just read/convert the values. */
214 for (int i = 0, n = int(element.properties.size()); i != n; i++) {
215 const PlyProperty &prop = element.properties[i];
216 float val = get_binary_value<float>(prop.type, ptr);
217 r_values[i] = val;
218 }
219 }
220 else if (header.type == PlyFormatType::BINARY_BE) {
221 /* Big endian: read, switch endian, convert the values. */
222 for (int i = 0, n = int(element.properties.size()); i != n; i++) {
223 const PlyProperty &prop = element.properties[i];
224 endian_switch((uint8_t *)ptr, data_type_size[prop.type]);
225 float val = get_binary_value<float>(prop.type, ptr);
226 r_values[i] = val;
227 }
228 }
229 else {
230 return "Unknown binary ply format for vertex element";
231 }
232 return nullptr;
233}
234
235static const char *load_vertex_element(PlyReadBuffer &file,
236 const PlyHeader &header,
237 const PlyElement &element,
238 PlyData *data)
239{
240 /* Figure out vertex component indices. */
241 int3 vertex_index = {get_index(element, "x"), get_index(element, "y"), get_index(element, "z")};
242 int3 color_index = {
243 get_index(element, "red"), get_index(element, "green"), get_index(element, "blue")};
244 int3 normal_index = {
245 get_index(element, "nx"), get_index(element, "ny"), get_index(element, "nz")};
246 int2 uv_index = {get_index(element, "s"), get_index(element, "t")};
247 int alpha_index = get_index(element, "alpha");
248
249 bool has_vertex = vertex_index.x >= 0 && vertex_index.y >= 0 && vertex_index.z >= 0;
250 bool has_color = color_index.x >= 0 && color_index.y >= 0 && color_index.z >= 0;
251 bool has_normal = normal_index.x >= 0 && normal_index.y >= 0 && normal_index.z >= 0;
252 bool has_uv = uv_index.x >= 0 && uv_index.y >= 0;
253 bool has_alpha = alpha_index >= 0;
254
255 if (!has_vertex) {
256 return "Vertex positions are not present in the file";
257 }
258
259 Vector<int64_t> custom_attr_indices;
260 for (const int64_t prop_idx : element.properties.index_range()) {
261 const PlyProperty &prop = element.properties[prop_idx];
262 bool is_standard = ELEM(
263 prop.name, "x", "y", "z", "nx", "ny", "nz", "red", "green", "blue", "alpha", "s", "t");
264 if (is_standard) {
265 continue;
266 }
267
268 custom_attr_indices.append(prop_idx);
269 PlyCustomAttribute attr(prop.name, element.count);
270 data->vertex_custom_attr.append(attr);
271 }
272
273 data->vertices.reserve(element.count);
274 if (has_color) {
275 data->vertex_colors.reserve(element.count);
276 }
277 if (has_normal) {
278 data->vertex_normals.reserve(element.count);
279 }
280 if (has_uv) {
281 data->uv_coordinates.reserve(element.count);
282 }
283
284 float4 color_norm = {1, 1, 1, 1};
285 if (has_color) {
286 color_norm.x = data_type_normalizer[element.properties[color_index.x].type];
287 color_norm.y = data_type_normalizer[element.properties[color_index.y].type];
288 color_norm.z = data_type_normalizer[element.properties[color_index.z].type];
289 }
290 if (has_alpha) {
291 color_norm.w = data_type_normalizer[element.properties[alpha_index].type];
292 }
293
294 Vector<float> value_vec(element.properties.size());
295 Vector<uint8_t> scratch;
296 if (header.type != PlyFormatType::ASCII) {
297 scratch.resize(element.stride);
298 }
299
300 for (int i = 0; i < element.count; i++) {
301
302 const char *error = nullptr;
303 if (header.type == PlyFormatType::ASCII) {
304 error = parse_row_ascii(file, value_vec);
305 }
306 else {
307 error = parse_row_binary(file, header, element, scratch, value_vec);
308 }
309 if (error != nullptr) {
310 return error;
311 }
312
313 /* Vertex coord */
314 float3 vertex3;
315 vertex3.x = value_vec[vertex_index.x];
316 vertex3.y = value_vec[vertex_index.y];
317 vertex3.z = value_vec[vertex_index.z];
318 data->vertices.append(vertex3);
319
320 /* Vertex color */
321 if (has_color) {
322 float4 colors4;
323 colors4.x = value_vec[color_index.x] / color_norm.x;
324 colors4.y = value_vec[color_index.y] / color_norm.y;
325 colors4.z = value_vec[color_index.z] / color_norm.z;
326 if (has_alpha) {
327 colors4.w = value_vec[alpha_index] / color_norm.w;
328 }
329 else {
330 colors4.w = 1.0f;
331 }
332 data->vertex_colors.append(colors4);
333 }
334
335 /* If normals */
336 if (has_normal) {
337 float3 normals3;
338 normals3.x = value_vec[normal_index.x];
339 normals3.y = value_vec[normal_index.y];
340 normals3.z = value_vec[normal_index.z];
341 data->vertex_normals.append(normals3);
342 }
343
344 /* If uv */
345 if (has_uv) {
346 float2 uvmap;
347 uvmap.x = value_vec[uv_index.x];
348 uvmap.y = value_vec[uv_index.y];
349 data->uv_coordinates.append(uvmap);
350 }
351
352 /* Custom attributes */
353 for (const int64_t ci : custom_attr_indices.index_range()) {
354 float value = value_vec[custom_attr_indices[ci]];
355 data->vertex_custom_attr[ci].data[i] = value;
356 }
357 }
358 return nullptr;
359}
360
361static uint32_t read_list_count(PlyReadBuffer &file,
362 const PlyProperty &prop,
363 Vector<uint8_t> &scratch,
364 bool big_endian)
365{
366 scratch.resize(8);
367 file.read_bytes(scratch.data(), data_type_size[prop.count_type]);
368 const uint8_t *ptr = scratch.data();
369 if (big_endian) {
370 endian_switch((uint8_t *)ptr, data_type_size[prop.count_type]);
371 }
373 return count;
374}
375
376static void skip_property(PlyReadBuffer &file,
377 const PlyProperty &prop,
378 Vector<uint8_t> &scratch,
379 bool big_endian)
380{
381 if (prop.count_type == PlyDataTypes::NONE) {
382 scratch.resize(8);
383 file.read_bytes(scratch.data(), data_type_size[prop.type]);
384 }
385 else {
386 uint32_t count = read_list_count(file, prop, scratch, big_endian);
387 scratch.resize(count * data_type_size[prop.type]);
388 file.read_bytes(scratch.data(), scratch.size());
389 }
390}
391
392static const char *load_face_element(PlyReadBuffer &file,
393 const PlyHeader &header,
394 const PlyElement &element,
395 PlyData *data)
396{
397 int prop_index = get_index(element, "vertex_indices");
398 if (prop_index < 0) {
399 prop_index = get_index(element, "vertex_index");
400 }
401 if (prop_index < 0 && element.properties.size() == 1) {
402 prop_index = 0;
403 }
404 if (prop_index < 0) {
405 return "Face element does not contain vertex indices property";
406 }
407 const PlyProperty &prop = element.properties[prop_index];
408 if (prop.count_type == PlyDataTypes::NONE) {
409 return "Face element vertex indices property must be a list";
410 }
411
412 data->face_vertices.reserve(element.count * 3);
413 data->face_sizes.reserve(element.count);
414
415 if (header.type == PlyFormatType::ASCII) {
416 for (int i = 0; i < element.count; i++) {
417 /* Read line */
418 Span<char> line = file.read_line();
419
420 const char *p = line.data();
421 const char *end = p + line.size();
422 int count = 0;
423
424 /* Skip any properties before vertex indices. */
425 for (int j = 0; j < prop_index; j++) {
426 p = drop_whitespace(p, end);
427 if (element.properties[j].count_type == PlyDataTypes::NONE) {
428 p = drop_non_whitespace(p, end);
429 }
430 else {
431 p = parse_int(p, end, 0, count);
432 for (int k = 0; k < count; ++k) {
433 p = drop_whitespace(p, end);
434 p = drop_non_whitespace(p, end);
435 }
436 }
437 }
438
439 /* Parse vertex indices list. */
440 p = parse_int(p, end, 0, count);
441 if (count < 1 || count > 255) {
442 return "Invalid face size, must be between 1 and 255";
443 }
444 /* Previous python based importer was accepting faces with fewer
445 * than 3 vertices, and silently dropping them. */
446 if (count < 3) {
447 CLOG_WARN(&LOG, "PLY Importer: ignoring face %i (%i vertices)", i, count);
448 continue;
449 }
450
451 for (int j = 0; j < count; j++) {
452 int index;
453 p = parse_int(p, end, 0, index);
454 data->face_vertices.append(index);
455 }
456 data->face_sizes.append(count);
457 }
458 }
459 else {
460 Vector<uint8_t> scratch(64);
461
462 for (int i = 0; i < element.count; i++) {
463 const uint8_t *ptr;
464
465 /* Skip any properties before vertex indices. */
466 for (int j = 0; j < prop_index; j++) {
468 file, element.properties[j], scratch, header.type == PlyFormatType::BINARY_BE);
469 }
470
471 /* Read vertex indices list. */
472 uint32_t count = read_list_count(
473 file, prop, scratch, header.type == PlyFormatType::BINARY_BE);
474 if (count < 1 || count > 255) {
475 return "Invalid face size, must be between 1 and 255";
476 }
477
478 scratch.resize(count * data_type_size[prop.type]);
479 file.read_bytes(scratch.data(), scratch.size());
480 /* Previous python based importer was accepting faces with fewer
481 * than 3 vertices, and silently dropping them. */
482 if (count < 3) {
483 CLOG_WARN(&LOG, "PLY Importer: ignoring face %i (%u vertices)", i, count);
484 }
485 else {
486 ptr = scratch.data();
487 if (header.type == PlyFormatType::BINARY_BE) {
488 endian_switch_array((uint8_t *)ptr, data_type_size[prop.type], count);
489 }
490 for (int j = 0; j < count; ++j) {
491 uint32_t index = get_binary_value<uint32_t>(prop.type, ptr);
492 data->face_vertices.append(index);
493 }
494 data->face_sizes.append(count);
495 }
496
497 /* Skip any properties after vertex indices. */
498 for (int j = prop_index + 1; j < element.properties.size(); j++) {
500 file, element.properties[j], scratch, header.type == PlyFormatType::BINARY_BE);
501 }
502 }
503 }
504 return nullptr;
505}
506
507static const char *load_tristrips_element(PlyReadBuffer &file,
508 const PlyHeader &header,
509 const PlyElement &element,
510 PlyData *data)
511{
512 if (element.count != 1) {
513 return "Tristrips element should contain one row";
514 }
515 if (element.properties.size() != 1) {
516 return "Tristrips element should contain one property";
517 }
518 const PlyProperty &prop = element.properties[0];
519 if (prop.count_type == PlyDataTypes::NONE) {
520 return "Tristrips element property must be a list";
521 }
522
523 Vector<int> strip;
524
525 if (header.type == PlyFormatType::ASCII) {
526 Span<char> line = file.read_line();
527
528 const char *p = line.data();
529 const char *end = p + line.size();
530 int count = 0;
531 p = parse_int(p, end, 0, count);
532
533 strip.resize(count);
534 for (int j = 0; j < count; j++) {
535 int index;
536 p = parse_int(p, end, 0, index);
537 strip[j] = index;
538 }
539 }
540 else {
541 Vector<uint8_t> scratch(64);
542
543 const uint8_t *ptr;
544
545 uint32_t count = read_list_count(file, prop, scratch, header.type == PlyFormatType::BINARY_BE);
546
547 strip.resize(count);
548 scratch.resize(count * data_type_size[prop.type]);
549 file.read_bytes(scratch.data(), scratch.size());
550 ptr = scratch.data();
551 if (header.type == PlyFormatType::BINARY_BE) {
552 endian_switch_array((uint8_t *)ptr, data_type_size[prop.type], count);
553 }
554 for (int j = 0; j < count; ++j) {
555 int index = get_binary_value<int>(prop.type, ptr);
556 strip[j] = index;
557 }
558 }
559
560 /* Decode triangle strip (with possible -1 restart indices) into faces. */
561 size_t start = 0;
562
563 for (size_t i = 0; i < strip.size(); i++) {
564 if (strip[i] == -1) {
565 /* Restart strip. */
566 start = i + 1;
567 }
568 else if (i - start >= 2) {
569 int a = strip[i - 2], b = strip[i - 1], c = strip[i];
570 /* Flip odd triangles. */
571 if ((i - start) & 1) {
572 std::swap(a, b);
573 }
574 /* Add triangle if it's not degenerate. */
575 if (a != b && a != c && b != c) {
576 data->face_vertices.append(a);
577 data->face_vertices.append(b);
578 data->face_vertices.append(c);
579 data->face_sizes.append(3);
580 }
581 }
582 }
583 return nullptr;
584}
585
586static const char *load_edge_element(PlyReadBuffer &file,
587 const PlyHeader &header,
588 const PlyElement &element,
589 PlyData *data)
590{
591 int prop_vertex1 = get_index(element, "vertex1");
592 int prop_vertex2 = get_index(element, "vertex2");
593 if (prop_vertex1 < 0 || prop_vertex2 < 0) {
594 return "Edge element does not contain vertex1 and vertex2 properties";
595 }
596
597 data->edges.reserve(element.count);
598
599 Vector<float> value_vec(element.properties.size());
600 Vector<uint8_t> scratch;
601 if (header.type != PlyFormatType::ASCII) {
602 scratch.resize(element.stride);
603 }
604
605 for (int i = 0; i < element.count; i++) {
606 const char *error = nullptr;
607 if (header.type == PlyFormatType::ASCII) {
608 error = parse_row_ascii(file, value_vec);
609 }
610 else {
611 error = parse_row_binary(file, header, element, scratch, value_vec);
612 }
613 if (error != nullptr) {
614 return error;
615 }
616 int index1 = value_vec[prop_vertex1];
617 int index2 = value_vec[prop_vertex2];
618 data->edges.append(std::make_pair(index1, index2));
619 }
620 return nullptr;
621}
622
623static const char *skip_element(PlyReadBuffer &file,
624 const PlyHeader &header,
625 const PlyElement &element)
626{
627 if (header.type == PlyFormatType::ASCII) {
628 for (int i = 0; i < element.count; i++) {
629 Span<char> line = file.read_line();
630 (void)line;
631 }
632 }
633 else {
634 Vector<uint8_t> scratch(64);
635 for (int i = 0; i < element.count; i++) {
636 for (const PlyProperty &prop : element.properties) {
637 skip_property(file, prop, scratch, header.type == PlyFormatType::BINARY_BE);
638 }
639 }
640 }
641 return nullptr;
642}
643
644std::unique_ptr<PlyData> import_ply_data(PlyReadBuffer &file, PlyHeader &header)
645{
646 std::unique_ptr<PlyData> data = std::make_unique<PlyData>();
647
648 bool got_vertex = false, got_face = false, got_tristrips = false, got_edge = false;
649 for (const PlyElement &element : header.elements) {
650 const char *error = nullptr;
651 if (element.name == "vertex") {
652 error = load_vertex_element(file, header, element, data.get());
653 got_vertex = true;
654 }
655 else if (element.name == "face") {
656 error = load_face_element(file, header, element, data.get());
657 got_face = true;
658 }
659 else if (element.name == "tristrips") {
660 error = load_tristrips_element(file, header, element, data.get());
661 got_tristrips = true;
662 }
663 else if (element.name == "edge") {
664 error = load_edge_element(file, header, element, data.get());
665 got_edge = true;
666 }
667 else {
668 error = skip_element(file, header, element);
669 }
670 if (error != nullptr) {
671 data->error = error;
672 return data;
673 }
674 if (got_vertex && got_face && got_tristrips && got_edge) {
675 /* We have parsed all the elements we'd need, skip the rest. */
676 break;
677 }
678 }
679
680 return data;
681}
682
683} // namespace blender::io::ply
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
BLI_INLINE void BLI_endian_switch_uint64(uint64_t *val) ATTR_NONNULL(1)
BLI_INLINE void BLI_endian_switch_uint16(unsigned short *val) ATTR_NONNULL(1)
void BLI_endian_switch_uint16_array(unsigned short *val, int size) ATTR_NONNULL(1)
BLI_INLINE void BLI_endian_switch_uint32(unsigned int *val) ATTR_NONNULL(1)
void BLI_endian_switch_uint64_array(uint64_t *val, int size) ATTR_NONNULL(1)
void BLI_endian_switch_uint32_array(unsigned int *val, int size) ATTR_NONNULL(1)
#define ELEM(...)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const void * element
long long int int64_t
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
IndexRange index_range() const
void resize(const int64_t new_size)
bool read_bytes(void *dst, size_t size)
nullptr float
#define UINT_MAX
Definition hash_md5.cc:44
int count
#define LOG(level)
Definition log.h:97
#define T
static void error(const char *str)
static const char * load_face_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)
static const char * parse_row_ascii(PlyReadBuffer &file, 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)
static const char * load_vertex_element(PlyReadBuffer &file, const PlyHeader &header, const PlyElement &element, PlyData *data)
static uint32_t read_list_count(PlyReadBuffer &file, const PlyProperty &prop, Vector< uint8_t > &scratch, bool big_endian)
static const char * load_tristrips_element(PlyReadBuffer &file, const PlyHeader &header, const PlyElement &element, PlyData *data)
static int get_index(const PlyElement &element, StringRef property)
std::unique_ptr< PlyData > import_ply_data(PlyReadBuffer &file, PlyHeader &header)
static const int data_type_size[]
static const float data_type_normalizer[]
static void skip_property(PlyReadBuffer &file, const PlyProperty &prop, Vector< uint8_t > &scratch, bool big_endian)
static T get_binary_value(PlyDataTypes type, const uint8_t *&r_ptr)
static const char * skip_element(PlyReadBuffer &file, const PlyHeader &header, const PlyElement &element)
const char * parse_float(const char *p, const char *end, float fallback, float &dst, bool skip_space, bool require_trailing_space)
const char * drop_non_whitespace(const char *p, const char *end)
const char * parse_int(const char *p, const char *end, int fallback, int &dst, bool skip_space)
const char * drop_whitespace(const char *p, const char *end)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
static void endian_switch_array(uint8_t *ptr, int type_size, int size)
static void endian_switch(uint8_t *ptr, int type_size)
static const char * drop_plus(const char *p, const char *end)
static const char * parse_int(const char *p, const char *end, int fallback, int &dst)
static const char * drop_whitespace(const char *p, const char *end)
static bool is_whitespace(char c)
static const char * parse_float(const char *p, const char *end, float fallback, float &dst)
static const char * drop_non_whitespace(const char *p, const char *end)
Vector< PlyProperty > properties
Definition ply_data.hh:51
Vector< PlyElement > elements
Definition ply_data.hh:58
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238