Coverage Report

Created: 2026-07-30 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/Ply/PlyParser.cpp
Line
Count
Source
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2026, assimp team
7
8
All rights reserved.
9
10
Redistribution and use of this software in source and binary forms,
11
with or without modification, are permitted provided that the following
12
conditions are met:
13
14
* Redistributions of source code must retain the above
15
copyright notice, this list of conditions and the
16
following disclaimer.
17
18
* Redistributions in binary form must reproduce the above
19
copyright notice, this list of conditions and the
20
following disclaimer in the documentation and/or other
21
materials provided with the distribution.
22
23
* Neither the name of the assimp team, nor the names of its
24
contributors may be used to endorse or promote products
25
derived from this software without specific prior
26
written permission of the assimp team.
27
28
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
---------------------------------------------------------------------------
40
*/
41
42
/** @file Implementation of the PLY parser class */
43
44
#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
45
46
#include "PlyLoader.h"
47
#include <assimp/ByteSwapper.h>
48
#include <assimp/fast_atof.h>
49
#include <assimp/DefaultLogger.hpp>
50
#include <unordered_set>
51
#include <utility>
52
53
namespace Assimp {
54
55
// ------------------------------------------------------------------------------------------------
56
795
static std::string to_string(EElementSemantic e) {
57
795
    switch (e) {
58
566
        case EEST_Vertex:
59
566
            return std::string{ "vertex" };
60
0
        case EEST_TriStrip:
61
0
            return std::string{ "tristrips" };
62
0
        case EEST_Edge:
63
0
            return std::string{ "edge" };
64
0
        case EEST_Material:
65
0
            return std::string{ "material" };
66
0
        case EEST_TextureFile:
67
0
            return std::string{ "TextureFile" };
68
229
        default:
69
229
            return std::string{ "invalid" };
70
795
    }
71
795
}
72
73
// ------------------------------------------------------------------------------------------------
74
4.40k
EDataType Property::ParseDataType(std::vector<char> &buffer) {
75
4.40k
    ai_assert(!buffer.empty());
76
77
4.40k
    EDataType eOut = EDT_INVALID;
78
79
4.40k
    if (DOM::TokenMatch(buffer, "char", 4) ||
80
4.40k
               DOM::TokenMatch(buffer, "int8", 4)) {
81
13
        eOut = EDT_Char;
82
4.39k
    } else if (DOM::TokenMatch(buffer, "uchar", 5) ||
83
4.11k
               DOM::TokenMatch(buffer, "uint8", 5)) {
84
341
        eOut = EDT_UChar;
85
4.05k
    } else if (DOM::TokenMatch(buffer, "short", 5) ||
86
4.05k
               DOM::TokenMatch(buffer, "int16", 5)) {
87
3
        eOut = EDT_Short;
88
4.05k
    } else if (DOM::TokenMatch(buffer, "ushort", 6) ||
89
4.05k
               DOM::TokenMatch(buffer, "uint16", 6)) {
90
9
        eOut = EDT_UShort;
91
4.04k
    } else if (DOM::TokenMatch(buffer, "int32", 5) || DOM::TokenMatch(buffer, "int", 3)) {
92
177
        eOut = EDT_Int;
93
3.86k
    } else if (DOM::TokenMatch(buffer, "uint32", 6) || DOM::TokenMatch(buffer, "uint", 4)) {
94
88
        eOut = EDT_UInt;
95
3.77k
    } else if (DOM::TokenMatch(buffer, "float", 5) || DOM::TokenMatch(buffer, "float32", 7)) {
96
3.48k
        eOut = EDT_Float;
97
3.48k
    } else if (DOM::TokenMatch(buffer, "double64", 8) || DOM::TokenMatch(buffer, "double", 6) ||
98
293
               DOM::TokenMatch(buffer, "float64", 7)) {
99
46
        eOut = EDT_Double;
100
46
    }
101
4.40k
    if (EDT_INVALID == eOut) {
102
248
        ASSIMP_LOG_INFO("Found unknown data type in PLY file. This is OK");
103
248
    }
104
105
4.40k
    return eOut;
106
4.40k
}
107
108
// ------------------------------------------------------------------------------------------------
109
3.90k
ESemantic Property::ParseSemantic(std::vector<char> &buffer) {
110
3.90k
    ai_assert(!buffer.empty());
111
112
3.90k
    ESemantic eOut = PLY::EST_INVALID;
113
3.90k
    if (DOM::TokenMatch(buffer, "red", 3)) {
114
171
        eOut = EST_Red;
115
3.73k
    } else if (DOM::TokenMatch(buffer, "green", 5)) {
116
134
        eOut = EST_Green;
117
3.59k
    } else if (DOM::TokenMatch(buffer, "blue", 4)) {
118
55
        eOut = EST_Blue;
119
3.54k
    } else if (DOM::TokenMatch(buffer, "alpha", 5)) {
120
47
        eOut = EST_Alpha;
121
3.49k
    } else if (DOM::TokenMatch(buffer, "vertex_index", 12) || PLY::DOM::TokenMatch(buffer, "vertex_indices", 14)) {
122
153
        eOut = EST_VertexIndex;
123
3.34k
    } else if (DOM::TokenMatch(buffer, "texcoord", 8)) // Manage uv coords on faces
124
0
    {
125
0
        eOut = EST_TextureCoordinates;
126
3.34k
    } else if (DOM::TokenMatch(buffer, "material_index", 14)) {
127
0
        eOut = EST_MaterialIndex;
128
3.34k
    } else if (DOM::TokenMatch(buffer, "ambient_red", 11)) {
129
0
        eOut = EST_AmbientRed;
130
3.34k
    } else if (DOM::TokenMatch(buffer, "ambient_green", 13)) {
131
0
        eOut = EST_AmbientGreen;
132
3.34k
    } else if (DOM::TokenMatch(buffer, "ambient_blue", 12)) {
133
0
        eOut = EST_AmbientBlue;
134
3.34k
    } else if (DOM::TokenMatch(buffer, "ambient_alpha", 13)) {
135
0
        eOut = EST_AmbientAlpha;
136
3.34k
    } else if (DOM::TokenMatch(buffer, "diffuse_red", 11)) {
137
0
        eOut = EST_DiffuseRed;
138
3.34k
    } else if (DOM::TokenMatch(buffer, "diffuse_green", 13)) {
139
0
        eOut = EST_DiffuseGreen;
140
3.34k
    } else if (DOM::TokenMatch(buffer, "diffuse_blue", 12)) {
141
0
        eOut = EST_DiffuseBlue;
142
3.34k
    } else if (DOM::TokenMatch(buffer, "diffuse_alpha", 13)) {
143
0
        eOut = EST_DiffuseAlpha;
144
3.34k
    } else if (DOM::TokenMatch(buffer, "specular_red", 12)) {
145
0
        eOut = EST_SpecularRed;
146
3.34k
    } else if (DOM::TokenMatch(buffer, "specular_green", 14)) {
147
0
        eOut = EST_SpecularGreen;
148
3.34k
    } else if (DOM::TokenMatch(buffer, "specular_blue", 13)) {
149
0
        eOut = EST_SpecularBlue;
150
3.34k
    } else if (DOM::TokenMatch(buffer, "specular_alpha", 14)) {
151
0
        eOut = EST_SpecularAlpha;
152
3.34k
    } else if (DOM::TokenMatch(buffer, "opacity", 7)) {
153
0
        eOut = EST_Opacity;
154
3.34k
    } else if (DOM::TokenMatch(buffer, "specular_power", 14)) {
155
0
        eOut = EST_PhongPower;
156
3.34k
    } else if (DOM::TokenMatch(buffer, "r", 1)) {
157
6
        eOut = EST_Red;
158
3.33k
    } else if (DOM::TokenMatch(buffer, "g", 1)) {
159
1
        eOut = EST_Green;
160
3.33k
    } else if (DOM::TokenMatch(buffer, "b", 1)) {
161
4
        eOut = EST_Blue;
162
4
    }
163
164
    // NOTE: Blender3D exports texture coordinates as s,t tuples
165
3.33k
    else if (DOM::TokenMatch(buffer, "u", 1) || PLY::DOM::TokenMatch(buffer, "s", 1) || PLY::DOM::TokenMatch(buffer, "tx", 2) || PLY::DOM::TokenMatch(buffer, "texture_u", 9)) {
166
60
        eOut = EST_UTextureCoord;
167
3.27k
    } else if (DOM::TokenMatch(buffer, "v", 1) || PLY::DOM::TokenMatch(buffer, "t", 1) || PLY::DOM::TokenMatch(buffer, "ty", 2) || PLY::DOM::TokenMatch(buffer, "texture_v", 9)) {
168
36
        eOut = EST_VTextureCoord;
169
3.23k
    } else if (DOM::TokenMatch(buffer, "x", 1)) {
170
671
        eOut = EST_XCoord;
171
2.56k
    } else if (DOM::TokenMatch(buffer, "y", 1)) {
172
1.49k
        eOut = EST_YCoord;
173
1.49k
    } else if (DOM::TokenMatch(buffer, "z", 1)) {
174
506
        eOut = EST_ZCoord;
175
569
    } else if (DOM::TokenMatch(buffer, "nx", 2)) {
176
121
        eOut = EST_XNormal;
177
448
    } else if (DOM::TokenMatch(buffer, "ny", 2)) {
178
99
        eOut = EST_YNormal;
179
349
    } else if (DOM::TokenMatch(buffer, "nz", 2)) {
180
81
        eOut = EST_ZNormal;
181
268
    } else {
182
268
        ASSIMP_LOG_INFO("Found unknown property semantic in file. This is ok");
183
268
        DOM::SkipLine(buffer);
184
268
    }
185
3.90k
    return eOut;
186
3.90k
}
187
188
// ------------------------------------------------------------------------------------------------
189
7.77k
bool PLY::Property::ParseProperty(std::vector<char> &buffer, PLY::Property *pOut) {
190
7.77k
    ai_assert(!buffer.empty());
191
192
    // Forms supported:
193
    // "property float x"
194
    // "property list uchar int vertex_index"
195
196
    // skip leading spaces
197
7.77k
    if (!DOM::SkipSpaces(buffer)) {
198
100
        return false;
199
100
    }
200
201
    // skip the "property" string at the beginning
202
7.67k
    if (!DOM::TokenMatch(buffer, "property", 8)) {
203
        // seems not to be a valid property entry
204
3.50k
        return false;
205
3.50k
    }
206
    // get next word
207
4.16k
    if (!PLY::DOM::SkipSpaces(buffer)) {
208
5
        return false;
209
5
    }
210
4.16k
    if (PLY::DOM::TokenMatch(buffer, "list", 4)) {
211
281
        pOut->bIsList = true;
212
213
        // seems to be a list.
214
281
        if (EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(buffer))) {
215
            // unable to parse list size data type
216
27
            PLY::DOM::SkipLine(buffer);
217
27
            return false;
218
27
        }
219
254
        if (!PLY::DOM::SkipSpaces(buffer)) return false;
220
245
        if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(buffer))) {
221
            // unable to parse list data type
222
17
            PLY::DOM::SkipLine(buffer);
223
17
            return false;
224
17
        }
225
3.88k
    } else {
226
3.88k
        if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(buffer))) {
227
            // unable to parse data type. Skip the property
228
204
            PLY::DOM::SkipLine(buffer);
229
204
            return false;
230
204
        }
231
3.88k
    }
232
233
3.90k
    if (!PLY::DOM::SkipSpaces(buffer))
234
3
        return false;
235
236
3.90k
    pOut->Semantic = PLY::Property::ParseSemantic(buffer);
237
238
3.90k
    if (PLY::EST_INVALID == pOut->Semantic) {
239
268
        ASSIMP_LOG_INFO("Found unknown semantic in PLY file. This is OK");
240
268
        std::string(&buffer[0], &buffer[0] + strlen(&buffer[0]));
241
268
    }
242
243
3.90k
    PLY::DOM::SkipSpacesAndLineEnd(buffer);
244
3.90k
    return true;
245
3.90k
}
246
247
// ------------------------------------------------------------------------------------------------
248
3.88k
PLY::EElementSemantic PLY::Element::ParseSemantic(std::vector<char> &buffer) {
249
3.88k
    ai_assert(!buffer.empty());
250
251
3.88k
    PLY::EElementSemantic eOut = PLY::EEST_INVALID;
252
3.88k
    if (PLY::DOM::TokenMatch(buffer, "vertex", 6)) {
253
582
        eOut = PLY::EEST_Vertex;
254
3.30k
    } else if (PLY::DOM::TokenMatch(buffer, "face", 4)) {
255
229
        eOut = PLY::EEST_Face;
256
3.07k
    } else if (PLY::DOM::TokenMatch(buffer, "tristrips", 9)) {
257
0
        eOut = PLY::EEST_TriStrip;
258
0
    }
259
#if 0
260
  // TODO: maybe implement this?
261
  else if (PLY::DOM::TokenMatch(buffer,"range_grid",10))
262
  {
263
    eOut = PLY::EEST_Face;
264
  }
265
#endif
266
3.07k
    else if (PLY::DOM::TokenMatch(buffer, "edge", 4)) {
267
0
        eOut = PLY::EEST_Edge;
268
3.07k
    } else if (PLY::DOM::TokenMatch(buffer, "material", 8)) {
269
0
        eOut = PLY::EEST_Material;
270
3.07k
    } else if (PLY::DOM::TokenMatch(buffer, "TextureFile", 11)) {
271
0
        eOut = PLY::EEST_TextureFile;
272
0
    }
273
274
3.88k
    return eOut;
275
3.88k
}
276
277
// ------------------------------------------------------------------------------------------------
278
49.0k
bool PLY::Element::ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLY::Element *pOut) {
279
49.0k
    ai_assert(nullptr != pOut);
280
    // Example format: "element vertex 8"
281
282
    // skip leading spaces
283
49.0k
    if (!PLY::DOM::SkipSpaces(buffer)) {
284
4.20k
        return false;
285
4.20k
    }
286
287
    // skip the "element" string at the beginning
288
44.8k
    if (!PLY::DOM::TokenMatch(buffer, "element", 7) && !PLY::DOM::TokenMatch(buffer, "comment", 7)) {
289
        // seems not to be a valid property entry
290
40.9k
        return false;
291
40.9k
    }
292
    // get next word
293
3.91k
    if (!PLY::DOM::SkipSpaces(buffer))
294
33
        return false;
295
296
    // parse the semantic of the element
297
3.88k
    pOut->eSemantic = PLY::Element::ParseSemantic(buffer);
298
3.88k
    if (PLY::EEST_INVALID == pOut->eSemantic) {
299
        // if the exact semantic can't be determined, just store
300
        // the original string identifier
301
3.07k
        pOut->szName = std::string(&buffer[0], &buffer[0] + strlen(&buffer[0]));
302
3.07k
        auto pos = pOut->szName.find_last_of(' ');
303
3.07k
        if (pos != std::string::npos) {
304
2.95k
            pOut->szName.erase(pos, pOut->szName.size());
305
2.95k
        }
306
3.07k
    }
307
308
3.88k
    if (!PLY::DOM::SkipSpaces(buffer))
309
16
        return false;
310
311
3.87k
    if (PLY::EEST_TextureFile == pOut->eSemantic) {
312
0
        char *endPos = &buffer[0] + (strlen(&buffer[0]) - 1);
313
0
        pOut->szName = std::string(&buffer[0], endPos);
314
315
        // go to the next line
316
0
        PLY::DOM::SkipSpacesAndLineEnd(buffer);
317
318
0
        return true;
319
0
    }
320
321
    // parse the number of occurrences of this element
322
3.87k
    const char *pCur = (char *)&buffer[0];
323
3.87k
    pOut->NumOccur = strtoul10(pCur, &pCur);
324
325
    // go to the next line
326
3.87k
    PLY::DOM::SkipSpacesAndLineEnd(buffer);
327
328
    // now parse all properties of the element
329
8.07k
    while (true) {
330
8.07k
        streamBuffer.getNextLine(buffer);
331
8.07k
        pCur = (char *)&buffer[0];
332
333
        // skip all comments and go to next line
334
8.07k
        if (PLY::DOM::SkipComments(buffer)) continue;
335
336
7.77k
        PLY::Property prop;
337
7.77k
        if (!PLY::Property::ParseProperty(buffer, &prop))
338
3.87k
            break;
339
340
3.90k
        pOut->alProperties.push_back(prop);
341
3.90k
    }
342
343
3.87k
    return true;
344
3.87k
}
345
346
// ------------------------------------------------------------------------------------------------
347
421k
bool PLY::DOM::SkipSpaces(std::vector<char> &buffer) {
348
421k
    const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
349
421k
    const char *end = pCur + buffer.size();
350
421k
    bool ret = false;
351
421k
    if (pCur) {
352
421k
        const char *szCur = pCur;
353
421k
        ret = Assimp::SkipSpaces(pCur, &pCur, end);
354
355
421k
        uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
356
421k
        buffer.erase(buffer.begin(), buffer.begin() + iDiff);
357
421k
        return ret;
358
421k
    }
359
360
0
    return ret;
361
421k
}
362
363
// ------------------------------------------------------------------------------------------------
364
291k
bool PLY::DOM::SkipLine(std::vector<char> &buffer) {
365
291k
    const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
366
291k
    const char *end = pCur + buffer.size();
367
291k
    bool ret = false;
368
291k
    if (pCur) {
369
291k
        const char *szCur = pCur;
370
291k
        ret = Assimp::SkipLine(pCur, &pCur, end);
371
372
291k
        uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
373
291k
        buffer.erase(buffer.begin(), buffer.begin() + iDiff);
374
291k
        return ret;
375
291k
    }
376
377
0
    return ret;
378
291k
}
379
380
// ------------------------------------------------------------------------------------------------
381
392k
bool PLY::DOM::TokenMatch(std::vector<char> &buffer, const char *token, unsigned int len) {
382
392k
    const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
383
392k
    bool ret = false;
384
392k
    if (pCur) {
385
392k
        const char *szCur = pCur;
386
392k
        ret = Assimp::TokenMatch(pCur, token, len);
387
388
392k
        uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
389
392k
        buffer.erase(buffer.begin(), buffer.begin() + iDiff);
390
392k
        return ret;
391
392k
    }
392
393
0
    return ret;
394
392k
}
395
396
// ------------------------------------------------------------------------------------------------
397
8.12k
bool PLY::DOM::SkipSpacesAndLineEnd(std::vector<char> &buffer) {
398
8.12k
    const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
399
8.12k
    const char *end = pCur + buffer.size();
400
8.12k
    bool ret = false;
401
8.12k
    if (pCur) {
402
8.12k
        const char *szCur = pCur;
403
8.12k
        ret = Assimp::SkipSpacesAndLineEnd(pCur, &pCur, end);
404
405
8.12k
        uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
406
8.12k
        buffer.erase(buffer.begin(), buffer.begin() + iDiff);
407
8.12k
        return ret;
408
8.12k
    }
409
410
0
    return ret;
411
8.12k
}
412
413
// ------------------------------------------------------------------------------------------------
414
347k
bool PLY::DOM::SkipComments(std::vector<char> buffer) {
415
347k
    ai_assert(!buffer.empty());
416
417
347k
    std::vector<char> nbuffer = std::move(buffer);
418
    // skip spaces
419
347k
    if (!SkipSpaces(nbuffer)) {
420
293k
        return false;
421
293k
    }
422
423
53.3k
    if (TokenMatch(nbuffer, "comment", 7)) {
424
845
        if (!SkipSpaces(nbuffer))
425
8
            SkipLine(nbuffer);
426
427
845
        if (!TokenMatch(nbuffer, "TextureFile", 11)) {
428
845
            SkipLine(nbuffer);
429
845
            buffer = nbuffer;
430
845
            return true;
431
845
        }
432
433
0
        return true;
434
845
    }
435
436
52.5k
    return false;
437
53.3k
}
438
439
// ------------------------------------------------------------------------------------------------
440
792
bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, bool isBinary) {
441
792
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() begin");
442
443
792
    std::unordered_set<std::string> definedAlElements;
444
    // parse all elements
445
49.0k
    while (!buffer.empty()) {
446
        // skip all comments
447
49.0k
        PLY::DOM::SkipComments(buffer);
448
449
49.0k
        PLY::Element out;
450
49.0k
        if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) {
451
            // add the element to the list of elements
452
453
3.87k
            const auto propertyName = (out.szName.empty()) ? to_string(out.eSemantic) : out.szName;
454
3.87k
            auto alreadyDefined = definedAlElements.find(propertyName);
455
3.87k
            if (alreadyDefined != definedAlElements.end()) {
456
80
                throw DeadlyImportError("Property '" + propertyName + "' in header already defined ");
457
80
            }
458
3.79k
            definedAlElements.insert(propertyName);
459
3.79k
            alElements.push_back(out);
460
45.1k
        } else if (TokenMatch(buffer, "end_header", 10)) {
461
            // we have reached the end of the header
462
355
            break;
463
44.8k
        } else {
464
            // ignore unknown header elements
465
44.8k
            if (!streamBuffer.getNextLine(buffer))
466
357
                return false;
467
44.8k
        }
468
49.0k
    }
469
470
355
    if (!isBinary) // it would occur an error, if binary data start with values as space or line end.
471
348
        SkipSpacesAndLineEnd(buffer);
472
473
355
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() succeeded");
474
355
    return true;
475
792
}
476
477
// ------------------------------------------------------------------------------------------------
478
348
bool PLY::DOM::ParseElementInstanceLists(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLYImporter *loader) {
479
348
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceLists() begin");
480
348
    alElementData.resize(alElements.size());
481
482
348
    std::vector<PLY::Element>::const_iterator i = alElements.begin();
483
348
    std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
484
485
    // parse all element instances
486
    // construct vertices and faces
487
1.02k
    for (; i != alElements.end(); ++i, ++a) {
488
672
        if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip) {
489
470
            PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), nullptr, loader);
490
470
        } else {
491
202
            (*a).alInstances.resize((*i).NumOccur);
492
202
            PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), &(*a), nullptr);
493
202
        }
494
672
    }
495
496
348
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceLists() succeeded");
497
348
    return true;
498
348
}
499
500
// ------------------------------------------------------------------------------------------------
501
bool PLY::DOM::ParseElementInstanceListsBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer,
502
        const char *&pCur,
503
        unsigned int &bufferSize,
504
        PLYImporter *loader,
505
7
        bool p_bBE) {
506
7
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceListsBinary() begin");
507
7
    alElementData.resize(alElements.size());
508
509
7
    std::vector<PLY::Element>::const_iterator i = alElements.begin();
510
7
    std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
511
512
    // parse all element instances
513
52
    for (; i != alElements.end(); ++i, ++a) {
514
45
        if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip) {
515
9
            PLY::ElementInstanceList::ParseInstanceListBinary(streamBuffer, buffer, pCur, bufferSize, &(*i), nullptr, loader, p_bBE);
516
36
        } else {
517
36
            (*a).alInstances.resize((*i).NumOccur);
518
36
            PLY::ElementInstanceList::ParseInstanceListBinary(streamBuffer, buffer, pCur, bufferSize, &(*i), &(*a), nullptr, p_bBE);
519
36
        }
520
45
    }
521
522
7
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceListsBinary() succeeded");
523
7
    return true;
524
7
}
525
526
// ------------------------------------------------------------------------------------------------
527
28
bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, DOM *p_pcOut, PLYImporter *loader, bool p_bBE) {
528
28
    ai_assert(nullptr != p_pcOut);
529
28
    ai_assert(nullptr != loader);
530
531
28
    std::vector<char> buffer;
532
28
    streamBuffer.getNextLine(buffer);
533
534
28
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() begin");
535
536
28
    if (!p_pcOut->ParseHeader(streamBuffer, buffer, true)) {
537
11
        ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() failure");
538
11
        return false;
539
11
    }
540
541
17
    streamBuffer.getNextBlock(buffer);
542
543
17
    unsigned int bufferSize = static_cast<unsigned int>(buffer.size());
544
17
    const char *pCur = (char *)&buffer[0];
545
17
    if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE)) {
546
0
        ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() failure");
547
0
        return false;
548
0
    }
549
17
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() succeeded");
550
17
    return true;
551
17
}
552
553
// ------------------------------------------------------------------------------------------------
554
764
bool PLY::DOM::ParseInstance(IOStreamBuffer<char> &streamBuffer, DOM *p_pcOut, PLYImporter *loader) {
555
764
    ai_assert(nullptr != p_pcOut);
556
764
    ai_assert(nullptr != loader);
557
558
764
    std::vector<char> buffer;
559
764
    streamBuffer.getNextLine(buffer);
560
561
764
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() begin");
562
563
764
    if (!p_pcOut->ParseHeader(streamBuffer, buffer, false)) {
564
346
        ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() failure");
565
346
        return false;
566
346
    }
567
568
    // get next line after header
569
418
    streamBuffer.getNextLine(buffer);
570
418
    if (!p_pcOut->ParseElementInstanceLists(streamBuffer, buffer, loader)) {
571
0
        ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() failure");
572
0
        return false;
573
0
    }
574
418
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() succeeded");
575
418
    return true;
576
418
}
577
578
// ------------------------------------------------------------------------------------------------
579
bool PLY::ElementInstanceList::ParseInstanceList(
580
        IOStreamBuffer<char> &streamBuffer,
581
        std::vector<char> &buffer,
582
        const PLY::Element *pcElement,
583
        PLY::ElementInstanceList *p_pcOut,
584
672
        PLYImporter *loader) {
585
672
    ai_assert(nullptr != pcElement);
586
587
    // parse all elements
588
672
    if (EEST_INVALID == pcElement->eSemantic || pcElement->alProperties.empty()) {
589
        // if the element has an unknown semantic we can skip all lines
590
        // However, there could be comments
591
290k
        for (unsigned int i = 0; i < pcElement->NumOccur; ++i) {
592
290k
            PLY::DOM::SkipComments(buffer);
593
290k
            PLY::DOM::SkipLine(buffer);
594
290k
            streamBuffer.getNextLine(buffer);
595
290k
        }
596
417
    } else {
597
417
        const char *pCur = (const char *)&buffer[0];
598
417
        const char *end = pCur + buffer.size();
599
        // be sure to have enough storage
600
485k
        for (unsigned int i = 0; i < pcElement->NumOccur; ++i) {
601
485k
            if (p_pcOut)
602
0
                PLY::ElementInstance::ParseInstance(pCur, end, pcElement, &p_pcOut->alInstances[i]);
603
485k
            else {
604
485k
                ElementInstance elt;
605
485k
                PLY::ElementInstance::ParseInstance(pCur, end, pcElement, &elt);
606
607
                // Create vertex or face
608
485k
                if (pcElement->eSemantic == EEST_Vertex) {
609
                    // call loader instance from here
610
261k
                    loader->LoadVertex(pcElement, &elt, i);
611
261k
                } else if (pcElement->eSemantic == EEST_Face) {
612
                    // call loader instance from here
613
224k
                    loader->LoadFace(pcElement, &elt, i);
614
224k
                } else if (pcElement->eSemantic == EEST_TriStrip) {
615
                    // call loader instance from here
616
0
                    loader->LoadFace(pcElement, &elt, i);
617
0
                }
618
485k
            }
619
620
485k
            streamBuffer.getNextLine(buffer);
621
485k
            pCur = (buffer.empty()) ? nullptr : (const char *)&buffer[0];
622
485k
        }
623
417
    }
624
672
    return true;
625
672
}
626
627
// ------------------------------------------------------------------------------------------------
628
bool PLY::ElementInstanceList::ParseInstanceListBinary(
629
        IOStreamBuffer<char> &streamBuffer,
630
        std::vector<char> &buffer,
631
        const char *&pCur,
632
        unsigned int &bufferSize,
633
        const PLY::Element *pcElement,
634
        PLY::ElementInstanceList *p_pcOut,
635
        PLYImporter *loader,
636
45
        bool p_bBE /* = false */) {
637
45
    ai_assert(nullptr != pcElement);
638
639
    // we can add special handling code for unknown element semantics since
640
    // we can't skip it as a whole block (we don't know its exact size
641
    // due to the fact that lists could be contained in the property list
642
    // of the unknown element)
643
340
    for (unsigned int i = 0; i < pcElement->NumOccur; ++i) {
644
295
        if (p_pcOut)
645
0
            PLY::ElementInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, pcElement, &p_pcOut->alInstances[i], p_bBE);
646
295
        else {
647
295
            ElementInstance elt;
648
295
            PLY::ElementInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, pcElement, &elt, p_bBE);
649
650
            // Create vertex or face
651
295
            if (pcElement->eSemantic == EEST_Vertex) {
652
                // call loader instance from here
653
49
                loader->LoadVertex(pcElement, &elt, i);
654
246
            } else if (pcElement->eSemantic == EEST_Face) {
655
                // call loader instance from here
656
246
                loader->LoadFace(pcElement, &elt, i);
657
246
            } else if (pcElement->eSemantic == EEST_TriStrip) {
658
                // call loader instance from here
659
0
                loader->LoadFace(pcElement, &elt, i);
660
0
            }
661
295
        }
662
295
    }
663
45
    return true;
664
45
}
665
666
// ------------------------------------------------------------------------------------------------
667
bool PLY::ElementInstance::ParseInstance(const char *&pCur, const char *end,
668
        const PLY::Element *pcElement,
669
485k
        PLY::ElementInstance *p_pcOut) {
670
485k
    ai_assert(nullptr != pcElement);
671
485k
    ai_assert(nullptr != p_pcOut);
672
673
    // allocate enough storage
674
485k
    p_pcOut->alProperties.resize(pcElement->alProperties.size());
675
676
485k
    std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
677
485k
    std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
678
1.54M
    for (; i != p_pcOut->alProperties.end(); ++i, ++a) {
679
1.05M
        if (!(PLY::PropertyInstance::ParseInstance(pCur, end, &(*a), &(*i)))) {
680
64.3k
            ASSIMP_LOG_WARN("Unable to parse property instance. "
681
64.3k
                            "Skipping this element instance");
682
683
64.3k
            PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
684
64.3k
            (*i).avList.push_back(v);
685
64.3k
        }
686
1.05M
    }
687
485k
    return true;
688
485k
}
689
690
// ------------------------------------------------------------------------------------------------
691
bool PLY::ElementInstance::ParseInstanceBinary(
692
        IOStreamBuffer<char> &streamBuffer,
693
        std::vector<char> &buffer,
694
        const char *&pCur,
695
        unsigned int &bufferSize,
696
        const PLY::Element *pcElement,
697
        PLY::ElementInstance *p_pcOut,
698
295
        bool p_bBE /* = false */) {
699
295
    ai_assert(nullptr != pcElement);
700
295
    ai_assert(nullptr != p_pcOut);
701
702
    // allocate enough storage
703
295
    p_pcOut->alProperties.resize(pcElement->alProperties.size());
704
705
295
    std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
706
295
    std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
707
1.16k
    for (; i != p_pcOut->alProperties.end(); ++i, ++a) {
708
869
        if (!(PLY::PropertyInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, &(*a), &(*i), p_bBE))) {
709
0
            ASSIMP_LOG_WARN("Unable to parse binary property instance. "
710
0
                            "Skipping this element instance");
711
712
0
            (*i).avList.push_back(PLY::PropertyInstance::DefaultValue((*a).eType));
713
0
        }
714
869
    }
715
295
    return true;
716
295
}
717
718
// ------------------------------------------------------------------------------------------------
719
bool PLY::PropertyInstance::ParseInstance(const char *&pCur, const char *end, const PLY::Property *prop,
720
1.05M
        PLY::PropertyInstance *p_pcOut) {
721
1.05M
    ai_assert(nullptr != prop);
722
1.05M
    ai_assert(nullptr != p_pcOut);
723
724
    // skip spaces at the beginning
725
1.05M
    if (!SkipSpaces(&pCur, end)) {
726
64.2k
        return false;
727
64.2k
    }
728
729
990k
    if (prop->bIsList) {
730
        // parse the number of elements in the list
731
289k
        PLY::PropertyInstance::ValueUnion v;
732
289k
        PLY::PropertyInstance::ParseValue(pCur, prop->eFirstType, &v);
733
734
        // convert to unsigned int
735
289k
        unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v, prop->eFirstType);
736
737
        // parse all list elements
738
289k
        p_pcOut->avList.resize(iNum);
739
6.13M
        for (unsigned int i = 0; i < iNum; ++i) {
740
5.84M
            if (!SkipSpaces(&pCur, end))
741
69
                return false;
742
743
5.84M
            PLY::PropertyInstance::ParseValue(pCur, prop->eType, &p_pcOut->avList[i]);
744
5.84M
        }
745
701k
    } else {
746
        // parse the property
747
701k
        PLY::PropertyInstance::ValueUnion v;
748
749
701k
        PLY::PropertyInstance::ParseValue(pCur, prop->eType, &v);
750
701k
        p_pcOut->avList.push_back(v);
751
701k
    }
752
990k
    SkipSpacesAndLineEnd(&pCur, end);
753
990k
    return true;
754
990k
}
755
756
// ------------------------------------------------------------------------------------------------
757
bool PLY::PropertyInstance::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer,
758
        const char *&pCur,
759
        unsigned int &bufferSize,
760
        const PLY::Property *prop,
761
        PLY::PropertyInstance *p_pcOut,
762
869
        bool p_bBE) {
763
869
    ai_assert(nullptr != prop);
764
869
    ai_assert(nullptr != p_pcOut);
765
766
    // parse all elements
767
869
    if (prop->bIsList) {
768
        // parse the number of elements in the list
769
243
        PLY::PropertyInstance::ValueUnion v;
770
243
        PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eFirstType, &v, p_bBE);
771
772
        // convert to unsigned int
773
243
        unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v, prop->eFirstType);
774
775
        // parse all list elements
776
243
        p_pcOut->avList.resize(iNum);
777
4.26k
        for (unsigned int i = 0; i < iNum; ++i) {
778
4.02k
            PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eType, &p_pcOut->avList[i], p_bBE);
779
4.02k
        }
780
626
    } else {
781
        // parse the property
782
626
        PLY::PropertyInstance::ValueUnion v;
783
626
        PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eType, &v, p_bBE);
784
626
        p_pcOut->avList.push_back(v);
785
626
    }
786
869
    return true;
787
869
}
788
789
// ------------------------------------------------------------------------------------------------
790
64.3k
PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue(PLY::EDataType eType) {
791
64.3k
    PLY::PropertyInstance::ValueUnion out;
792
793
64.3k
    switch (eType) {
794
64.0k
    case EDT_Float:
795
64.0k
        out.fFloat = 0.f;
796
64.0k
        return out;
797
798
27
    case EDT_Double:
799
27
        out.fDouble = 0.;
800
27
        return out;
801
802
316
    default:;
803
64.3k
    };
804
316
    out.iUInt = 0;
805
316
    return out;
806
64.3k
}
807
808
// ------------------------------------------------------------------------------------------------
809
bool PLY::PropertyInstance::ParseValue(const char *&pCur,
810
        PLY::EDataType eType,
811
6.84M
        PLY::PropertyInstance::ValueUnion *out) {
812
6.84M
    ai_assert(nullptr != pCur);
813
6.84M
    ai_assert(nullptr != out);
814
815
    // calc element size
816
6.84M
    bool ret = true;
817
6.84M
    switch (eType) {
818
5.40M
    case EDT_UInt:
819
5.40M
    case EDT_UShort:
820
5.69M
    case EDT_UChar:
821
822
5.69M
        out->iUInt = (uint32_t)strtoul10(pCur, &pCur);
823
5.69M
        break;
824
825
445k
    case EDT_Int:
826
445k
    case EDT_Short:
827
445k
    case EDT_Char:
828
829
445k
        out->iInt = (int32_t)strtol10(pCur, &pCur);
830
445k
        break;
831
832
700k
    case EDT_Float:
833
        // technically this should cast to float, but people tend to use float descriptors for double data
834
        // this is the best way to not risk losing precision on import and it doesn't hurt to do this
835
700k
        ai_real f;
836
700k
        pCur = fast_atoreal_move(pCur, f);
837
700k
        out->fFloat = (ai_real)f;
838
700k
        break;
839
840
122
    case EDT_Double:
841
122
        double d;
842
122
        pCur = fast_atoreal_move(pCur, d);
843
122
        out->fDouble = d;
844
122
        break;
845
846
0
    case EDT_INVALID:
847
0
    default:
848
0
        ret = false;
849
0
        break;
850
6.84M
    }
851
852
6.84M
    return ret;
853
6.84M
}
854
855
// ------------------------------------------------------------------------------------------------
856
bool PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
857
        std::vector<char> &buffer,
858
        const char *&pCur,
859
        unsigned int &bufferSize,
860
        EDataType eType,
861
        PropertyInstance::ValueUnion *out,
862
4.89k
        bool p_bBE) {
863
4.89k
    ai_assert(nullptr != out);
864
865
    // calc element size
866
4.89k
    unsigned int lsize = 0;
867
4.89k
    switch (eType) {
868
0
    case EDT_Char:
869
275
    case EDT_UChar:
870
275
        lsize = 1;
871
275
        break;
872
873
0
    case EDT_UShort:
874
0
    case EDT_Short:
875
0
        lsize = 2;
876
0
        break;
877
878
0
    case EDT_UInt:
879
4.02k
    case EDT_Int:
880
4.61k
    case EDT_Float:
881
4.61k
        lsize = 4;
882
4.61k
        break;
883
884
0
    case EDT_Double:
885
0
        lsize = 8;
886
0
        break;
887
888
0
    case EDT_INVALID:
889
0
    default:
890
0
        break;
891
4.89k
    }
892
893
    // read the next file block if needed
894
4.89k
    if (bufferSize < lsize) {
895
0
        std::vector<char> nbuffer;
896
0
        if (streamBuffer.getNextBlock(nbuffer)) {
897
            // concat buffer contents
898
0
            buffer = std::vector<char>(buffer.end() - bufferSize, buffer.end());
899
0
            buffer.insert(buffer.end(), nbuffer.begin(), nbuffer.end());
900
0
            nbuffer.clear();
901
0
            bufferSize = static_cast<unsigned int>(buffer.size());
902
0
            pCur = (char *)&buffer[0];
903
0
        } else {
904
0
            throw DeadlyImportError("Invalid .ply file: File corrupted");
905
0
        }
906
0
    }
907
908
4.89k
    bool ret = true;
909
4.89k
    switch (eType) {
910
0
    case EDT_UInt: {
911
0
        uint32_t t;
912
0
        memcpy(&t, pCur, sizeof(uint32_t));
913
0
        pCur += sizeof(uint32_t);
914
915
        // Swap endianness
916
0
        if (p_bBE) ByteSwap::Swap(&t);
917
0
        out->iUInt = t;
918
0
        break;
919
0
    }
920
921
0
    case EDT_UShort: {
922
0
        uint16_t t;
923
0
        memcpy(&t, pCur, sizeof(uint16_t));
924
0
        pCur += sizeof(uint16_t);
925
926
        // Swap endianness
927
0
        if (p_bBE) ByteSwap::Swap(&t);
928
0
        out->iUInt = t;
929
0
        break;
930
0
    }
931
932
275
    case EDT_UChar: {
933
275
        uint8_t t;
934
275
        memcpy(&t, pCur, sizeof(uint8_t));
935
275
        pCur += sizeof(uint8_t);
936
275
        out->iUInt = t;
937
275
        break;
938
0
    }
939
940
4.02k
    case EDT_Int: {
941
4.02k
        int32_t t;
942
4.02k
        memcpy(&t, pCur, sizeof(int32_t));
943
4.02k
        pCur += sizeof(int32_t);
944
945
        // Swap endianness
946
4.02k
        if (p_bBE) ByteSwap::Swap(&t);
947
4.02k
        out->iInt = t;
948
4.02k
        break;
949
0
    }
950
951
0
    case EDT_Short: {
952
0
        int16_t t;
953
0
        memcpy(&t, pCur, sizeof(int16_t));
954
0
        pCur += sizeof(int16_t);
955
956
        // Swap endianness
957
0
        if (p_bBE) ByteSwap::Swap(&t);
958
0
        out->iInt = t;
959
0
        break;
960
0
    }
961
962
0
    case EDT_Char: {
963
0
        int8_t t;
964
0
        memcpy(&t, pCur, sizeof(int8_t));
965
0
        pCur += sizeof(int8_t);
966
0
        out->iInt = t;
967
0
        break;
968
0
    }
969
970
594
    case EDT_Float: {
971
594
        float t;
972
594
        memcpy(&t, pCur, sizeof(float));
973
594
        pCur += sizeof(float);
974
975
        // Swap endianness
976
594
        if (p_bBE) ByteSwap::Swap(&t);
977
594
        out->fFloat = t;
978
594
        break;
979
0
    }
980
0
    case EDT_Double: {
981
0
        double t;
982
0
        memcpy(&t, pCur, sizeof(double));
983
0
        pCur += sizeof(double);
984
985
        // Swap endianness
986
0
        if (p_bBE) ByteSwap::Swap(&t);
987
0
        out->fDouble = t;
988
0
        break;
989
0
    }
990
0
    default:
991
0
        ret = false;
992
4.89k
    }
993
994
4.89k
    bufferSize -= lsize;
995
996
4.89k
    return ret;
997
4.89k
}
998
999
// ------------------------------------------------------------------------------------------------
1000
1001
} // namespace Assimp
1002
1003
#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER