-
Notifications
You must be signed in to change notification settings - Fork 4
/
minipbrt.cpp
8263 lines (6941 loc) · 261 KB
/
minipbrt.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
MIT License
Copyright (c) 2019 Vilya Harvey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "minipbrt.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <unordered_map>
#ifndef _WIN32
#include <errno.h>
#endif
#define MINIPBRT_STATIC_ARRAY_LENGTH(arr) static_cast<uint32_t>(sizeof(arr) / sizeof((arr)[0]))
/// miniply - A simple and fast parser for PLY files
/// ================================================
///
/// For details about the PLY format see:
/// * http://paulbourke.net/dataformats/ply/
/// * https://en.wikipedia.org/wiki/PLY_(file_format)
namespace miniply {
//
// Constants
//
static constexpr uint32_t kInvalidIndex = 0xFFFFFFFFu;
// Standard PLY element names
extern const char* kPLYVertexElement; // "vertex"
extern const char* kPLYFaceElement; // "face"
//
// PLY Parsing types
//
enum class PLYFileType {
ASCII,
Binary,
BinaryBigEndian,
};
enum class PLYPropertyType {
Char,
UChar,
Short,
UShort,
Int,
UInt,
Float,
Double,
None, //!< Special value used in Element::listCountType to indicate a non-list property.
};
struct PLYProperty {
std::string name;
PLYPropertyType type = PLYPropertyType::None; //!< Type of the data. Must be set to a value other than None.
PLYPropertyType countType = PLYPropertyType::None; //!< None indicates this is not a list type, otherwise it's the type for the list count.
uint32_t offset = 0; //!< Byte offset from the start of the row.
uint32_t stride = 0;
std::vector<uint8_t> listData;
std::vector<uint32_t> rowCount; // Entry `i` is the number of items (*not* the number of bytes) in row `i`.
};
struct PLYElement {
std::string name; //!< Name of this element.
std::vector<PLYProperty> properties;
uint32_t count = 0; //!< The number of items in this element (e.g. the number of vertices if this is the vertex element).
bool fixedSize = true; //!< `true` if there are only fixed-size properties in this element, i.e. no list properties.
uint32_t rowStride = 0; //!< The number of bytes from the start of one row to the start of the next, for this element.
void calculate_offsets();
/// Returns the index for the named property in this element, or `kInvalidIndex`
/// if it can't be found.
uint32_t find_property(const char* propName) const;
/// Return the indices for several properties in one go. Use it like this:
/// ```
/// uint32_t indexes[3];
/// if (elem.find_properties(indexes, 3, "foo", "bar", "baz")) { ... }
/// ```
/// `propIdxs` is where the property indexes will be stored. `numIdxs` is
/// the number of properties we will look up. There must be exactly
/// `numIdxs` parameters after `numIdxs`; each of the is a c-style string
/// giving the name of a property.
///
/// The return value will be true if all properties were found. If it was
/// not true, you should not use any values from propIdxs.
bool find_properties(uint32_t propIdxs[], uint32_t numIdxs, ...) const;
/// Same as `find_properties`, for when you already have a `va_list`. This
/// is called internally by both `PLYElement::find_properties` and
/// `PLYReader::find_properties`.
bool find_properties_va(uint32_t propIdxs[], uint32_t numIdxs, va_list names) const;
/// Call this on the element at some point before you load its data, when
/// you know that every row's list will have the same length. It will
/// replace the single variable-size property with a set of new fixed-size
/// properties: one for the list count, followed by one for each of the
/// list values. This will allow miniply to load and extract the property
/// data a lot more efficiently, giving a big performance increase.
///
/// After you've called this, you must use PLYReader's `extract_columns`
/// method to get the data, rather than `extract_list_column`.
///
/// The `newPropIdxs` parameter must be an array with at least `listSize`
/// entries. If the function returns true, this will have been populated
/// with the indices of the new properties that represent the list values
/// (i.e. not including the list count property, which will have the same
/// index as the old list property).
///
/// The function returns false if the property index is invalid, or the
/// property it refers to is not a list property. In these cases it will
/// not modify anything. Otherwise it will return true.
bool convert_list_to_fixed_size(uint32_t listPropIdx, uint32_t listSize, uint32_t newPropIdxs[]);
};
class PLYReader {
public:
PLYReader(const char* filename);
~PLYReader();
bool valid() const;
bool has_element() const;
const PLYElement* element() const;
bool load_element();
void next_element();
PLYFileType file_type() const;
int version_major() const;
int version_minor() const;
uint32_t num_elements() const;
uint32_t find_element(const char* name) const;
PLYElement* get_element(uint32_t idx);
/// Check whether the current element has the given name.
bool element_is(const char* name) const;
/// Number of rows in the current element.
uint32_t num_rows() const;
/// Returns the index for the named property in the current element, or
/// `kInvalidIndex` if it can't be found.
uint32_t find_property(const char* name) const;
/// Equivalent to calling `find_properties` on the current element.
bool find_properties(uint32_t propIdxs[], uint32_t numIdxs, ...) const;
/// Copy the data for the specified properties into `dest`, which must be
/// an array with at least enough space to hold all of the extracted column
/// data. `propIdxs` is an array containing the indexes of the properties
/// to copy; it has `numProps` elements.
///
/// `destType` specifies the data type for values stored in `dest`. All
/// property values will be converted to this type if necessary.
///
/// This function does some checks up front to pick the most efficient code
/// path for extracting the data. It considers:
/// (a) whether any data conversion is required.
/// (b) whether all property values to be extracted are in contiguous
/// memory locations for any given item.
/// (c) whether the data for all rows is contiguous in memory.
/// In the best case it reduces to a single memcpy call. In the worst case
/// we must iterate over all values to be copied, applying type conversions
/// as we go.
///
/// Note that this function does not handle list-valued properties. Use
/// `extract_list_column()` for those instead.
bool extract_properties(const uint32_t propIdxs[], uint32_t numProps, PLYPropertyType destType, void* dest) const;
/// Get the array of item counts for a list property. Entry `i` in this
/// array is the number of items in the `i`th list.
const uint32_t* get_list_counts(uint32_t propIdx) const;
const uint8_t* get_list_data(uint32_t propIdx) const;
bool extract_list_property(uint32_t propIdx, PLYPropertyType destType, void* dest) const;
uint32_t num_triangles(uint32_t propIdx) const;
bool requires_triangulation(uint32_t propIdx) const;
bool extract_triangles(uint32_t propIdx, const float pos[], uint32_t numVerts, PLYPropertyType destType, void* dest) const;
bool find_pos(uint32_t propIdxs[3]) const;
bool find_normal(uint32_t propIdxs[3]) const;
bool find_texcoord(uint32_t propIdxs[2]) const;
bool find_color(uint32_t propIdxs[3]) const;
bool find_indices(uint32_t propIdxs[1]) const;
private:
bool refill_buffer();
bool rewind_to_safe_char();
bool accept();
bool advance();
bool next_line();
bool match(const char* str);
bool which(const char* values[], uint32_t* index);
bool which_property_type(PLYPropertyType* type);
bool keyword(const char* kw);
bool identifier(char* dest, size_t destLen);
template <class T> // T must be a type compatible with uint32_t.
bool typed_which(const char* values[], T* index) {
return which(values, reinterpret_cast<uint32_t*>(index));
}
bool int_literal(int* value);
bool float_literal(float* value);
bool double_literal(double* value);
bool parse_elements();
bool parse_element();
bool parse_property(std::vector<PLYProperty>& properties);
bool load_fixed_size_element(PLYElement& elem);
bool load_variable_size_element(PLYElement& elem);
bool load_ascii_scalar_property(PLYProperty& prop, size_t& destIndex);
bool load_ascii_list_property(PLYProperty& prop);
bool load_binary_scalar_property(PLYProperty& prop, size_t& destIndex);
bool load_binary_list_property(PLYProperty& prop);
bool load_binary_scalar_property_big_endian(PLYProperty& prop, size_t& destIndex);
bool load_binary_list_property_big_endian(PLYProperty& prop);
bool ascii_value(PLYPropertyType propType, uint8_t value[8]);
private:
FILE* m_f = nullptr;
char* m_buf = nullptr;
const char* m_bufEnd = nullptr;
const char* m_pos = nullptr;
const char* m_end = nullptr;
bool m_inDataSection = false;
bool m_atEOF = false;
int64_t m_bufOffset = 0;
bool m_valid = false;
PLYFileType m_fileType = PLYFileType::ASCII; //!< Whether the file was ascii, binary little-endian, or binary big-endian.
int m_majorVersion = 0;
int m_minorVersion = 0;
std::vector<PLYElement> m_elements; //!< Element descriptors for this file.
size_t m_currentElement = 0;
bool m_elementLoaded = false;
std::vector<uint8_t> m_elementData;
char* m_tmpBuf = nullptr;
};
/// Given a polygon with `n` vertices, where `n` > 3, triangulate it and
/// store the indices for the resulting triangles in `dst`. The `pos`
/// parameter is the array of all vertex positions for the mesh; `indices` is
/// the list of `n` indices for the polygon we're triangulating; and `dst` is
/// where we write the new indices to.
///
/// The triangulation will always produce `n - 2` triangles, so `dst` must
/// have enough space for `3 * (n - 2)` indices.
///
/// If `n == 3`, we simply copy the input indices to `dst`. If `n < 3`,
/// nothing gets written to dst.
///
/// The return value is the number of triangles.
uint32_t triangulate_polygon(uint32_t n, const float pos[], uint32_t numVerts, const int indices[], int dst[]);
} // namespace miniply
namespace miniply {
//
// Public constants
//
// Standard PLY element names
const char* kPLYVertexElement = "vertex";
const char* kPLYFaceElement = "face";
//
// PLY constants
//
static constexpr uint32_t kPLYReadBufferSize = 128 * 1024;
static constexpr uint32_t kPLYTempBufferSize = kPLYReadBufferSize;
static const char* kPLYFileTypes[] = { "ascii", "binary_little_endian", "binary_big_endian", nullptr };
static const uint32_t kPLYPropertySize[]= { 1, 1, 2, 2, 4, 4, 4, 8 };
struct PLYTypeAlias {
const char* name;
PLYPropertyType type;
};
static const PLYTypeAlias kTypeAliases[] = {
{ "char", PLYPropertyType::Char },
{ "uchar", PLYPropertyType::UChar },
{ "short", PLYPropertyType::Short },
{ "ushort", PLYPropertyType::UShort },
{ "int", PLYPropertyType::Int },
{ "uint", PLYPropertyType::UInt },
{ "float", PLYPropertyType::Float },
{ "double", PLYPropertyType::Double },
{ "uint8", PLYPropertyType::UChar },
{ "uint16", PLYPropertyType::UShort },
{ "uint32", PLYPropertyType::UInt },
{ "int8", PLYPropertyType::Char },
{ "int16", PLYPropertyType::Short },
{ "int32", PLYPropertyType::Int },
{ nullptr, PLYPropertyType::None }
};
//
// Constants
//
static constexpr double kDoubleDigits[10] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
static constexpr float kPi = 3.14159265358979323846f;
//
// Vec2 type
//
struct Vec2 {
float x, y;
};
static inline Vec2 operator - (Vec2 lhs, Vec2 rhs) { return Vec2{ lhs.x - rhs.x, lhs.y - rhs.y }; }
static inline float dot(Vec2 lhs, Vec2 rhs) { return lhs.x * rhs.x + lhs.y * rhs.y; }
static inline float length(Vec2 v) { return std::sqrt(dot(v, v)); }
static inline Vec2 normalize(Vec2 v) { float len = length(v); return Vec2{ v.x / len, v.y / len }; }
//
// Vec3 type
//
struct Vec3 {
float x, y, z;
};
static inline Vec3 operator - (Vec3 lhs, Vec3 rhs) { return Vec3{ lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z }; }
static inline float dot(Vec3 lhs, Vec3 rhs) { return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; }
static inline float length(Vec3 v) { return std::sqrt(dot(v, v)); }
static inline Vec3 normalize(Vec3 v) { float len = length(v); return Vec3{ v.x / len, v.y / len, v.z / len }; }
static inline Vec3 cross(Vec3 lhs, Vec3 rhs) { return Vec3{ lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x }; }
//
// Internal-only functions
//
static inline bool is_whitespace(char ch)
{
return ch == ' ' || ch == '\t' || ch == '\r';
}
static inline bool is_digit(char ch)
{
return ch >= '0' && ch <= '9';
}
static inline bool is_letter(char ch)
{
ch |= 32; // upper and lower case letters differ only at this bit.
return ch >= 'a' && ch <= 'z';
}
static inline bool is_alnum(char ch)
{
return is_digit(ch) || is_letter(ch);
}
static inline bool is_keyword_start(char ch)
{
return is_letter(ch) || ch == '_';
}
static inline bool is_keyword_part(char ch)
{
return is_alnum(ch) || ch == '_';
}
static inline bool is_safe_buffer_end(char ch)
{
return (ch > 0 && ch <= 32) || (ch >= 127);
}
static int file_open(FILE** f, const char* filename, const char* mode)
{
#ifdef _WIN32
return fopen_s(f, filename, mode);
#else
*f = fopen(filename, mode);
return (*f != nullptr) ? 0 : errno;
#endif
}
static inline int file_seek(FILE* file, int64_t offset, int origin)
{
#ifdef _WIN32
return _fseeki64(file, offset, origin);
#else
static_assert(sizeof(off_t) == sizeof(int64_t), "off_t is not 64 bits.");
return fseeko(file, offset, origin);
#endif
}
static bool int_literal(const char* start, char const** end, int* val)
{
const char* pos = start;
bool negative = false;
if (*pos == '-') {
negative = true;
++pos;
}
else if (*pos == '+') {
++pos;
}
bool hasLeadingZeroes = *pos == '0';
if (hasLeadingZeroes) {
do {
++pos;
} while (*pos == '0');
}
int numDigits = 0;
int localVal = 0;
while (is_digit(*pos)) {
// FIXME: this will overflow if we get too many digits.
localVal = localVal * 10 + static_cast<int>(*pos - '0');
++numDigits;
++pos;
}
if (numDigits == 0 && hasLeadingZeroes) {
numDigits = 1;
}
if (numDigits == 0 || is_letter(*pos) || *pos == '_') {
return false;
}
else if (numDigits > 10) {
// Overflow, literal value is larger than an int can hold.
// FIXME: this won't catch *all* cases of overflow, make it exact.
return false;
}
if (val != nullptr) {
*val = negative ? -localVal : localVal;
}
if (end != nullptr) {
*end = pos;
}
return true;
}
static bool double_literal(const char* start, char const** end, double* val)
{
const char* pos = start;
bool negative = false;
if (*pos == '-') {
negative = true;
++pos;
}
else if (*pos == '+') {
++pos;
}
double localVal = 0.0;
bool hasIntDigits = is_digit(*pos);
if (hasIntDigits) {
do {
localVal = localVal * 10.0 + kDoubleDigits[*pos - '0'];
++pos;
} while (is_digit(*pos));
}
else if (*pos != '.') {
// set_error("Not a floating point number");
return false;
}
bool hasFracDigits = false;
if (*pos == '.') {
++pos;
hasFracDigits = is_digit(*pos);
if (hasFracDigits) {
double scale = 0.1;
do {
localVal += scale * kDoubleDigits[*pos - '0'];
scale *= 0.1;
++pos;
} while (is_digit(*pos));
}
else if (!hasIntDigits) {
// set_error("Floating point number has no digits before or after the decimal point");
return false;
}
}
bool hasExponent = *pos == 'e' || *pos == 'E';
if (hasExponent) {
++pos;
bool negativeExponent = false;
if (*pos == '-') {
negativeExponent = true;
++pos;
}
else if (*pos == '+') {
++pos;
}
if (!is_digit(*pos)) {
// set_error("Floating point exponent has no digits");
return false; // error: exponent part has no digits.
}
double exponent = 0.0;
do {
exponent = exponent * 10.0 + kDoubleDigits[*pos - '0'];
++pos;
} while (is_digit(*pos));
if (val != nullptr) {
if (negativeExponent) {
exponent = -exponent;
}
localVal *= std::pow(10.0, exponent);
}
}
if (*pos == '.' || *pos == '_' || is_alnum(*pos)) {
// set_error("Floating point number has trailing chars");
return false;
}
if (negative) {
localVal = -localVal;
}
if (val != nullptr) {
*val = localVal;
}
if (end != nullptr) {
*end = pos;
}
return true;
}
static bool float_literal(const char* start, char const** end, float* val)
{
double tmp = 0.0;
bool ok = double_literal(start, end, &tmp);
if (ok && val != nullptr) {
*val = static_cast<float>(tmp);
}
return ok;
}
static inline void endian_swap_2(uint8_t* data)
{
uint16_t tmp = *reinterpret_cast<uint16_t*>(data);
tmp = static_cast<uint16_t>((tmp >> 8) | (tmp << 8));
*reinterpret_cast<uint16_t*>(data) = tmp;
}
static inline void endian_swap_4(uint8_t* data)
{
uint32_t tmp = *reinterpret_cast<uint32_t*>(data);
tmp = (tmp >> 16) | (tmp << 16);
tmp = ((tmp & 0xFF00FF00) >> 8) | ((tmp & 0x00FF00FF) << 8);
*reinterpret_cast<uint32_t*>(data) = tmp;
}
static inline void endian_swap_8(uint8_t* data)
{
uint64_t tmp = *reinterpret_cast<uint64_t*>(data);
tmp = (tmp >> 32) | (tmp << 32);
tmp = ((tmp & 0xFFFF0000FFFF0000) >> 16) | ((tmp & 0x0000FFFF0000FFFF) << 16);
tmp = ((tmp & 0xFF00FF00FF00FF00) >> 8) | ((tmp & 0x00FF00FF00FF00FF) << 8);
*reinterpret_cast<uint64_t*>(data) = tmp;
}
static inline void endian_swap(uint8_t* data, PLYPropertyType type)
{
switch (kPLYPropertySize[uint32_t(type)]) {
case 2: endian_swap_2(data); break;
case 4: endian_swap_4(data); break;
case 8: endian_swap_8(data); break;
default: break;
}
}
static inline void endian_swap_array(uint8_t* data, PLYPropertyType type, int n)
{
switch (kPLYPropertySize[uint32_t(type)]) {
case 2:
for (const uint8_t* end = data + 2 * n; data < end; data += 2) {
endian_swap_2(data);
}
break;
case 4:
for (const uint8_t* end = data + 4 * n; data < end; data += 4) {
endian_swap_4(data);
}
break;
case 8:
for (const uint8_t* end = data + 8 * n; data < end; data += 8) {
endian_swap_8(data);
}
break;
default:
break;
}
}
template <class T>
static void copy_and_convert_to(T* dest, const uint8_t* src, PLYPropertyType srcType)
{
switch (srcType) {
case PLYPropertyType::Char: *dest = static_cast<T>(*reinterpret_cast<const int8_t*>(src)); break;
case PLYPropertyType::UChar: *dest = static_cast<T>(*reinterpret_cast<const uint8_t*>(src)); break;
case PLYPropertyType::Short: *dest = static_cast<T>(*reinterpret_cast<const int16_t*>(src)); break;
case PLYPropertyType::UShort: *dest = static_cast<T>(*reinterpret_cast<const uint16_t*>(src)); break;
case PLYPropertyType::Int: *dest = static_cast<T>(*reinterpret_cast<const int*>(src)); break;
case PLYPropertyType::UInt: *dest = static_cast<T>(*reinterpret_cast<const uint32_t*>(src)); break;
case PLYPropertyType::Float: *dest = static_cast<T>(*reinterpret_cast<const float*>(src)); break;
case PLYPropertyType::Double: *dest = static_cast<T>(*reinterpret_cast<const double*>(src)); break;
case PLYPropertyType::None: break;
}
}
static void copy_and_convert(uint8_t* dest, PLYPropertyType destType, const uint8_t* src, PLYPropertyType srcType)
{
switch (destType) {
case PLYPropertyType::Char: copy_and_convert_to(reinterpret_cast<int8_t*> (dest), src, srcType); break;
case PLYPropertyType::UChar: copy_and_convert_to(reinterpret_cast<uint8_t*> (dest), src, srcType); break;
case PLYPropertyType::Short: copy_and_convert_to(reinterpret_cast<int16_t*> (dest), src, srcType); break;
case PLYPropertyType::UShort: copy_and_convert_to(reinterpret_cast<uint16_t*>(dest), src, srcType); break;
case PLYPropertyType::Int: copy_and_convert_to(reinterpret_cast<int32_t*> (dest), src, srcType); break;
case PLYPropertyType::UInt: copy_and_convert_to(reinterpret_cast<uint32_t*>(dest), src, srcType); break;
case PLYPropertyType::Float: copy_and_convert_to(reinterpret_cast<float*> (dest), src, srcType); break;
case PLYPropertyType::Double: copy_and_convert_to(reinterpret_cast<double*> (dest), src, srcType); break;
case PLYPropertyType::None: break;
}
}
static inline bool compatible_types(PLYPropertyType srcType, PLYPropertyType destType)
{
return (srcType == destType) ||
(srcType < PLYPropertyType::Float && (uint32_t(srcType) ^ 0x1) == uint32_t(destType));
}
//
// PLYElement methods
//
void PLYElement::calculate_offsets()
{
fixedSize = true;
for (PLYProperty& prop : properties) {
if (prop.countType != PLYPropertyType::None) {
fixedSize = false;
break;
}
}
// Note that each list property gets its own separate storage. Only fixed
// size properties go into the common data block. The `rowStride` is the
// size of a row in the common data block.
rowStride = 0;
for (PLYProperty& prop : properties) {
if (prop.countType != PLYPropertyType::None) {
continue;
}
prop.offset = rowStride;
rowStride += kPLYPropertySize[uint32_t(prop.type)];
}
}
uint32_t PLYElement::find_property(const char *propName) const
{
for (uint32_t i = 0, endI = uint32_t(properties.size()); i < endI; i++) {
if (strcmp(propName, properties.at(i).name.c_str()) == 0) {
return i;
}
}
return kInvalidIndex;
}
bool PLYElement::find_properties(uint32_t propIdxs[], uint32_t numIdxs, ...) const
{
va_list args;
va_start(args, numIdxs);
bool foundAll = find_properties_va(propIdxs, numIdxs, args);
va_end(args);
return foundAll;
}
bool PLYElement::find_properties_va(uint32_t propIdxs[], uint32_t numIdxs, va_list names) const
{
for (uint32_t i = 0; i < numIdxs; i++) {
propIdxs[i] = find_property(va_arg(names, const char*));
if (propIdxs[i] == kInvalidIndex) {
return false;
}
}
return true;
}
bool PLYElement::convert_list_to_fixed_size(uint32_t listPropIdx, uint32_t listSize, uint32_t newPropIdxs[])
{
if (fixedSize || listPropIdx >= properties.size() || properties[listPropIdx].countType == PLYPropertyType::None) {
return false;
}
PLYProperty oldListProp = properties[listPropIdx];
// If the generated names are less than 256 chars, we will use an array on
// the stack as temporary storage. In the rare case that they're longer,
// we'll allocate an array of sufficient size on the heap and use that
// instead. This means we'll avoid allocating in all but the most extreme
// cases.
char inlineBuf[256];
size_t nameBufSize = oldListProp.name.size() + 12; // the +12 allows space for an '_', a number up to 10 digits long and the terminating null.
char* nameBuf = inlineBuf;
if (nameBufSize > sizeof(inlineBuf)) {
nameBuf = new char[nameBufSize];
}
// Set up a property for the list count column.
PLYProperty& countProp = properties[listPropIdx];
snprintf(nameBuf, nameBufSize, "%s_count", oldListProp.name.c_str());
countProp.name = nameBuf;
countProp.type = oldListProp.countType;
countProp.countType = PLYPropertyType::None;
countProp.stride = kPLYPropertySize[uint32_t(oldListProp.countType)];
if (listSize > 0) {
// Set up additional properties for the list entries, 1 per entry.
if (listPropIdx + 1 == properties.size()) {
properties.resize(properties.size() + listSize);
}
else {
properties.insert(properties.begin() + listPropIdx + 1, listSize, PLYProperty());
}
for (uint32_t i = 0; i < listSize; i++) {
uint32_t propIdx = listPropIdx + 1 + i;
PLYProperty& itemProp = properties[propIdx];
snprintf(nameBuf, nameBufSize, "%s_%u", oldListProp.name.c_str(), i);
itemProp.name = nameBuf;
itemProp.type = oldListProp.type;
itemProp.countType = PLYPropertyType::None;
itemProp.stride = kPLYPropertySize[uint32_t(oldListProp.type)];
newPropIdxs[i] = propIdx;
}
}
if (nameBuf != inlineBuf) {
delete[] nameBuf;
}
calculate_offsets();
return true;
}
//
// PLYReader methods
//
PLYReader::PLYReader(const char* filename)
{
m_buf = new char[kPLYReadBufferSize + 1];
m_buf[kPLYReadBufferSize] = '\0';
m_tmpBuf = new char[kPLYTempBufferSize + 1];
m_tmpBuf[kPLYTempBufferSize] = '\0';
m_bufEnd = m_buf + kPLYReadBufferSize;
m_pos = m_bufEnd;
m_end = m_bufEnd;
if (file_open(&m_f, filename, "rb") != 0) {
m_f = nullptr;
m_valid = false;
return;
}
m_valid = true;
refill_buffer();
m_valid = keyword("ply") && next_line() &&
keyword("format") && advance() &&
typed_which(kPLYFileTypes, &m_fileType) && advance() &&
int_literal(&m_majorVersion) && advance() &&
match(".") && advance() &&
int_literal(&m_minorVersion) && next_line() &&
parse_elements() &&
keyword("end_header") && advance() && match("\n") && accept();
if (!m_valid) {
return;
}
m_inDataSection = true;
if (m_fileType == PLYFileType::ASCII) {
advance();
}
for (PLYElement& elem : m_elements) {
elem.calculate_offsets();
}
}
PLYReader::~PLYReader()
{
if (m_f != nullptr) {
fclose(m_f);
}
delete[] m_buf;
delete[] m_tmpBuf;
}
bool PLYReader::valid() const
{
return m_valid;
}
bool PLYReader::has_element() const
{
return m_valid && m_currentElement < m_elements.size();
}
const PLYElement* PLYReader::element() const
{
assert(has_element());
return &m_elements[m_currentElement];
}
bool PLYReader::load_element()
{
assert(has_element());
if (m_elementLoaded) {
return true;
}
PLYElement& elem = m_elements[m_currentElement];
return elem.fixedSize ? load_fixed_size_element(elem) : load_variable_size_element(elem);
}
void PLYReader::next_element()
{
if (!has_element()) {
return;
}
// If the element was loaded, the read buffer should already be positioned at
// the start of the next element.
PLYElement& elem = m_elements[m_currentElement];
m_currentElement++;
if (m_elementLoaded) {
// Clear any temporary storage used for list properties in the current element.
for (PLYProperty& prop : elem.properties) {
if (prop.countType == PLYPropertyType::None) {
continue;
}
prop.listData.clear();
prop.listData.shrink_to_fit();
prop.rowCount.clear();
prop.rowCount.shrink_to_fit();
}
// Clear temporary storage for the non-list properties in the current element.
m_elementData.clear();
m_elementLoaded = false;
return;
}
// If the element wasn't loaded, we have to move the file pointer past its
// contents. How we do that depends on whether this is an ASCII or binary
// file and, if it's a binary, whether the element is fixed or variable
// size.
if (m_fileType == PLYFileType::ASCII) {
for (uint32_t row = 0; row < elem.count; row++) {
next_line();
}
}
else if (elem.fixedSize) {
int64_t elementStart = static_cast<int64_t>(m_pos - m_buf);
int64_t elementSize = elem.rowStride * elem.count;
int64_t elementEnd = elementStart + elementSize;
if (elementEnd >= kPLYReadBufferSize) {
m_bufOffset += elementEnd;
file_seek(m_f, m_bufOffset, SEEK_SET);
m_bufEnd = m_buf + kPLYReadBufferSize;
m_pos = m_bufEnd;
m_end = m_bufEnd;
refill_buffer();
}
else {
m_pos = m_buf + elementEnd;
m_end = m_pos;
}
}
else if (m_fileType == PLYFileType::Binary) {
for (uint32_t row = 0; row < elem.count; row++) {
for (const PLYProperty& prop : elem.properties) {
if (prop.countType == PLYPropertyType::None) {
uint32_t numBytes = kPLYPropertySize[uint32_t(prop.type)];
if (m_pos + numBytes > m_bufEnd) {
if (!refill_buffer() || m_pos + numBytes > m_bufEnd) {
m_valid = false;
return;
}
}
m_pos += numBytes;
m_end = m_pos;
continue;
}