Coverage Report

Created: 2026-10-28 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/Ply/PlyLoader.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  PlyLoader.cpp
43
 *  @brief Implementation of the PLY importer class
44
 */
45
46
#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
47
48
// internal headers
49
#include "PlyLoader.h"
50
51
// standard headers
52
#include <assimp/IOStreamBuffer.h>
53
#include <assimp/importerdesc.h>
54
#include <assimp/scene.h>
55
#include <assimp/IOSystem.hpp>
56
57
// other headers
58
#include <memory>
59
60
namespace Assimp {
61
62
    // ------------------------------------------------------------------------------------------------
63
    static constexpr ai_uint NotSet = 0xFFFFFFFF;
64
65
    static constexpr aiImporterDesc desc = {
66
        "Stanford Polygon Library (PLY) Importer",
67
        "",
68
        "",
69
        "",
70
        aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportTextFlavour,
71
        0,
72
        0,
73
        0,
74
        0,
75
        "ply"
76
    };
77
78
    namespace { // Internal stuff
79
        // ------------------------------------------------------------------------------------------------
80
        // Checks that property index is within range
81
        template <class T>
82
585k
        const T &GetProperty(const std::vector<T> &props, int idx) {
83
585k
            if (static_cast<size_t>(idx) >= props.size()) {
84
0
                throw DeadlyImportError("Invalid .ply file: Property index is out of range.");
85
0
            }
86
87
585k
            return props[idx];
88
585k
        }
89
90
        // ------------------------------------------------------------------------------------------------
91
6
        bool isBigEndian(const char *szMe) {
92
6
            ai_assert(nullptr != szMe);
93
94
            // binary_little_endian
95
            // binary_big_endian
96
6
            bool isBigEndian{ false };
97
    #if (defined AI_BUILD_BIG_ENDIAN)
98
            if ('l' == *szMe || 'L' == *szMe) {
99
                isBigEndian = true;
100
            }
101
    #else
102
6
            if ('b' == *szMe || 'B' == *szMe) {
103
0
                isBigEndian = true;
104
0
            }
105
6
    #endif // ! AI_BUILD_BIG_ENDIAN
106
107
6
            return isBigEndian;
108
6
        }
109
110
        // ------------------------------------------------------------------------------------------------
111
        // Convert a color component to [0...1]
112
5
        ai_real NormalizeColorValue(PropertyInstance::ValueUnion val, EDataType eType) {
113
5
            switch (eType) {
114
5
                case EDT_Float:
115
5
                    return val.fFloat;
116
0
                case EDT_Double:
117
0
                    return static_cast<ai_real>(val.fDouble);
118
0
                case EDT_UChar:
119
0
                    return static_cast<ai_real>(val.iUInt) / static_cast<ai_real>(0xFF);
120
0
                case EDT_Char:
121
0
                    return static_cast<ai_real>(val.iInt + (0xFF / 2)) / static_cast<ai_real>(0xFF);
122
0
                case EDT_UShort:
123
0
                    return static_cast<ai_real>(val.iUInt) / static_cast<ai_real>(0xFFFF);
124
0
                case EDT_Short:
125
0
                    return static_cast<ai_real>(val.iInt + (0xFFFF / 2)) / static_cast<ai_real>(0xFFFF);
126
0
                case EDT_UInt:
127
0
                    return static_cast<ai_real>(val.iUInt) / static_cast<ai_real>(0xFFFF);
128
0
                case EDT_Int:
129
0
                    return (static_cast<ai_real>(val.iInt) / static_cast<ai_real>(0xFF)) + 0.5f;
130
0
                default:
131
0
                    break;
132
5
            }
133
134
0
            return 0.0f;
135
5
        }
136
137
        // ------------------------------------------------------------------------------------------------
138
        // Get a RGBA color in [0...1] range
139
        void GetMaterialColor(const std::vector<PLY::PropertyInstance> &avList, unsigned int positions[4], EDataType types[4],
140
0
                aiColor4D *clrOut) {
141
0
            ai_assert(nullptr != clrOut);
142
143
0
            if (NotSet == positions[0]) {
144
0
                clrOut->r = 0.0f;
145
0
            } else {
146
0
                clrOut->r = NormalizeColorValue(GetProperty(avList, positions[0]).avList.front(), types[0]);
147
0
            }
148
149
0
            if (NotSet == positions[1]) {
150
0
                clrOut->g = 0.0f;
151
0
            } else {
152
0
                clrOut->g = NormalizeColorValue(GetProperty(avList, positions[1]).avList.front(), types[1]);
153
0
            }
154
155
0
            if (NotSet == positions[2]) {
156
0
                clrOut->b = 0.0f;
157
0
            } else {
158
0
                clrOut->b = NormalizeColorValue(GetProperty(avList, positions[2]).avList.front(), types[2]);
159
0
            }
160
161
            // assume 1.0 for the alpha channel ifit is not set
162
0
            if (NotSet == positions[3]) {
163
0
                clrOut->a = 1.0f;
164
0
            } else {
165
0
                clrOut->a = NormalizeColorValue(GetProperty(avList, positions[3]).avList.front(), types[3]);
166
0
            }
167
0
        }
168
169
    } // namespace
170
171
    // ------------------------------------------------------------------------------------------------
172
130k
    PLYImporter::~PLYImporter() {
173
130k
        delete mGeneratedMesh;
174
130k
    }
175
176
    // ------------------------------------------------------------------------------------------------
177
    // Returns whether the class can handle the format of the given file.
178
12.3k
    bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool) const {
179
12.3k
        static const char *tokens[] = { "ply" };
180
12.3k
        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
181
12.3k
    }
182
183
    // ------------------------------------------------------------------------------------------------
184
129k
    const aiImporterDesc *PLYImporter::GetInfo() const {
185
129k
        return &desc;
186
129k
    }
187
188
    // ------------------------------------------------------------------------------------------------
189
    // Imports the given file into the given scene structure.
190
99
    void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
191
99
        const std::string mode = "rb";
192
99
        std::unique_ptr<IOStream> fileStream(pIOHandler->Open(pFile, mode));
193
99
        if (!fileStream) {
194
0
            throw DeadlyImportError("Failed to open file ", pFile, ".");
195
0
        }
196
197
        // Get the file-size
198
99
        const size_t fileSize = fileStream->FileSize();
199
99
        if (0 == fileSize) {
200
0
            throw DeadlyImportError("File ", pFile, " is empty.");
201
0
        }
202
203
99
        IOStreamBuffer<char> streamedBuffer(1024 * 1024);
204
99
        streamedBuffer.open(fileStream.get());
205
206
        // the beginning of the file must be PLY - magic, magic
207
99
        std::vector<char> headerCheck;
208
99
        streamedBuffer.getNextLine(headerCheck);
209
210
99
        if ((headerCheck.size() < 3) ||
211
99
                (headerCheck[0] != 'P' && headerCheck[0] != 'p') ||
212
94
                (headerCheck[1] != 'L' && headerCheck[1] != 'l') ||
213
94
                (headerCheck[2] != 'Y' && headerCheck[2] != 'y')) {
214
5
            streamedBuffer.close();
215
5
            throw DeadlyImportError("Invalid .ply file: Incorrect magic number (expected 'ply' or 'PLY').");
216
5
        }
217
218
94
        std::vector<char> mBuffer2;
219
94
        streamedBuffer.getNextLine(mBuffer2);
220
94
        mBuffer = (unsigned char *)&mBuffer2[0];
221
222
94
        auto szMe = (char *)&this->mBuffer[0];
223
94
        const char *end = &mBuffer2[0] + mBuffer2.size();
224
94
        SkipSpacesAndLineEnd(szMe, (const char **)&szMe, end);
225
226
        // determine the format of the file data and construct the aiMesh
227
94
        DOM sPlyDom;
228
94
        this->pcDOM = &sPlyDom;
229
230
94
        if (TokenMatch(szMe, "format", 6)) {
231
94
            if (TokenMatch(szMe, "ascii", 5)) {
232
88
                SkipLine(szMe, (const char **)&szMe, end);
233
88
                if (!DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) {
234
52
                    if (mGeneratedMesh != nullptr) {
235
0
                        delete (mGeneratedMesh);
236
0
                        mGeneratedMesh = nullptr;
237
0
                    }
238
239
52
                    streamedBuffer.close();
240
52
                    throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#1)");
241
52
                }
242
88
            } else if (!strncmp(szMe, "binary_", 7)) {
243
6
                szMe += 7;
244
245
                // skip the line, parse the rest of the header and build the DOM
246
6
                if (const bool bIsBE = isBigEndian(szMe); !PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) {
247
3
                    if (mGeneratedMesh != nullptr) {
248
0
                        delete (mGeneratedMesh);
249
0
                        mGeneratedMesh = nullptr;
250
0
                    }
251
252
3
                    streamedBuffer.close();
253
3
                    throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)");
254
3
                }
255
6
            } else {
256
0
                if (mGeneratedMesh != nullptr) {
257
0
                    delete mGeneratedMesh;
258
0
                    mGeneratedMesh = nullptr;
259
0
                }
260
261
0
                streamedBuffer.close();
262
0
                throw DeadlyImportError("Invalid .ply file: Unknown file format");
263
0
            }
264
94
        } else {
265
0
            AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
266
0
            if (mGeneratedMesh != nullptr) {
267
0
                delete (mGeneratedMesh);
268
0
                mGeneratedMesh = nullptr;
269
0
            }
270
271
0
            streamedBuffer.close();
272
0
            throw DeadlyImportError("Invalid .ply file: Missing format specification");
273
0
        }
274
275
        // free the file buffer
276
39
        streamedBuffer.close();
277
278
39
        if (mGeneratedMesh == nullptr) {
279
5
            throw DeadlyImportError("Invalid .ply file: Unable to extract mesh data ");
280
5
        }
281
282
        // if no face list is existing we assume that the vertex
283
        // list is containing a list of points
284
34
        bool pointsOnly = mGeneratedMesh->mFaces == nullptr ? true : false;
285
34
        if (pointsOnly) {
286
6
            mGeneratedMesh->mPrimitiveTypes = aiPrimitiveType::aiPrimitiveType_POINT;
287
6
        }
288
289
        // now load a list of all materials
290
34
        std::vector<aiMaterial *> avMaterials;
291
34
        std::string defaultTexture;
292
34
        LoadMaterial(&avMaterials, defaultTexture, pointsOnly);
293
294
        // now generate the output scene object. Fill the material list
295
34
        pScene->mNumMaterials = static_cast<unsigned int>(avMaterials.size());
296
34
        pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
297
51
        for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
298
17
            pScene->mMaterials[i] = avMaterials[i];
299
17
        }
300
301
        // fill the mesh list
302
34
        pScene->mNumMeshes = 1;
303
34
        pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
304
34
        pScene->mMeshes[0] = mGeneratedMesh;
305
306
        // Move the mesh ownership into the scene instance
307
34
        mGeneratedMesh = nullptr;
308
309
        // generate a simple node structure
310
34
        pScene->mRootNode = new aiNode();
311
34
        pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
312
34
        pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
313
314
51
        for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes; ++i) {
315
17
            pScene->mRootNode->mMeshes[i] = i;
316
17
        }
317
34
    }
318
319
67.8k
    void PLYImporter::LoadVertex(const Element *pcElement, const ElementInstance *instElement, unsigned int pos) {
320
67.8k
        ai_assert(nullptr != pcElement);
321
67.8k
        ai_assert(nullptr != instElement);
322
323
67.8k
        ai_uint aiPositions[3] = { NotSet, NotSet, NotSet };
324
67.8k
        EDataType aiTypes[3] = { EDT_Char, EDT_Char, EDT_Char };
325
326
67.8k
        ai_uint aiNormal[3] = { NotSet, NotSet, NotSet };
327
67.8k
        EDataType aiNormalTypes[3] = { EDT_Char, EDT_Char, EDT_Char };
328
329
67.8k
        unsigned int aiColors[4] = { NotSet, NotSet, NotSet, NotSet };
330
67.8k
        EDataType aiColorsTypes[4] = { EDT_Char, EDT_Char, EDT_Char, EDT_Char };
331
332
67.8k
        unsigned int aiTexcoord[2] = { NotSet, NotSet };
333
67.8k
        EDataType aiTexcoordTypes[2] = { EDT_Char, EDT_Char };
334
335
        // now check whether which normal components are available
336
67.8k
        unsigned int _a(0), cnt(0);
337
665k
        for (auto a = pcElement->alProperties.begin(); a != pcElement->alProperties.end(); ++a, ++_a) {
338
597k
            if (a->bIsList) {
339
10
                continue;
340
10
            }
341
342
            // Positions
343
597k
            if (EST_XCoord == a->Semantic) {
344
67.7k
                ++cnt;
345
67.7k
                aiPositions[0] = _a;
346
67.7k
                aiTypes[0] = a->eType;
347
530k
            } else if (EST_YCoord == a->Semantic) {
348
67.7k
                ++cnt;
349
67.7k
                aiPositions[1] = _a;
350
67.7k
                aiTypes[1] = a->eType;
351
462k
            } else if (EST_ZCoord == a->Semantic) {
352
111k
                ++cnt;
353
111k
                aiPositions[2] = _a;
354
111k
                aiTypes[2] = a->eType;
355
351k
            } else if (EST_XNormal == a->Semantic) {
356
                // Normals
357
67.6k
                ++cnt;
358
67.6k
                aiNormal[0] = _a;
359
67.6k
                aiNormalTypes[0] = a->eType;
360
283k
            } else if (EST_YNormal == a->Semantic) {
361
80.6k
                ++cnt;
362
80.6k
                aiNormal[1] = _a;
363
80.6k
                aiNormalTypes[1] = a->eType;
364
202k
            } else if (EST_ZNormal == a->Semantic) {
365
67.4k
                ++cnt;
366
67.4k
                aiNormal[2] = _a;
367
67.4k
                aiNormalTypes[2] = a->eType;
368
135k
            } else if (EST_Red == a->Semantic) {
369
                // Colors
370
5
                ++cnt;
371
5
                aiColors[0] = _a;
372
5
                aiColorsTypes[0] = a->eType;
373
135k
            } else if (EST_Green == a->Semantic) {
374
163
                ++cnt;
375
163
                aiColors[1] = _a;
376
163
                aiColorsTypes[1] = a->eType;
377
135k
            } else if (EST_Blue == a->Semantic) {
378
0
                ++cnt;
379
0
                aiColors[2] = _a;
380
0
                aiColorsTypes[2] = a->eType;
381
135k
            } else if (EST_Alpha == a->Semantic) {
382
0
                ++cnt;
383
0
                aiColors[3] = _a;
384
0
                aiColorsTypes[3] = a->eType;
385
135k
            } else if (EST_UTextureCoord == a->Semantic) {
386
                // Texture coordinates
387
67.4k
                ++cnt;
388
67.4k
                aiTexcoord[0] = _a;
389
67.4k
                aiTexcoordTypes[0] = a->eType;
390
67.6k
            } else if (EST_VTextureCoord == a->Semantic) {
391
67.4k
                ++cnt;
392
67.4k
                aiTexcoord[1] = _a;
393
67.4k
                aiTexcoordTypes[1] = a->eType;
394
67.4k
            }
395
597k
        }
396
397
        // check whether we have a valid source for the vertex data
398
67.8k
        if (0 != cnt) {
399
            // Position
400
67.8k
            aiVector3D vOut;
401
67.8k
            if (NotSet != aiPositions[0]) {
402
67.7k
                vOut.x = PropertyInstance::ConvertTo<ai_real>(
403
67.7k
                        GetProperty(instElement->alProperties, aiPositions[0]).avList.front(), aiTypes[0]);
404
67.7k
            }
405
406
67.8k
            if (NotSet != aiPositions[1]) {
407
67.7k
                vOut.y = PropertyInstance::ConvertTo<ai_real>(
408
67.7k
                        GetProperty(instElement->alProperties, aiPositions[1]).avList.front(), aiTypes[1]);
409
67.7k
            }
410
411
67.8k
            if (NotSet != aiPositions[2]) {
412
67.7k
                vOut.z = PropertyInstance::ConvertTo<ai_real>(
413
67.7k
                        GetProperty(instElement->alProperties, aiPositions[2]).avList.front(), aiTypes[2]);
414
67.7k
            }
415
416
            // Normals
417
67.8k
            aiVector3D nOut;
418
67.8k
            bool haveNormal = false;
419
67.8k
            if (NotSet != aiNormal[0]) {
420
67.6k
                nOut.x = PropertyInstance::ConvertTo<ai_real>(
421
67.6k
                        GetProperty(instElement->alProperties, aiNormal[0]).avList.front(), aiNormalTypes[0]);
422
67.6k
                haveNormal = true;
423
67.6k
            }
424
425
67.8k
            if (NotSet != aiNormal[1]) {
426
67.7k
                nOut.y = PropertyInstance::ConvertTo<ai_real>(
427
67.7k
                        GetProperty(instElement->alProperties, aiNormal[1]).avList.front(), aiNormalTypes[1]);
428
67.7k
                haveNormal = true;
429
67.7k
            }
430
431
67.8k
            if (NotSet != aiNormal[2]) {
432
67.4k
                nOut.z = PropertyInstance::ConvertTo<ai_real>(
433
67.4k
                        GetProperty(instElement->alProperties, aiNormal[2]).avList.front(), aiNormalTypes[2]);
434
67.4k
                haveNormal = true;
435
67.4k
            }
436
437
            // Colors
438
67.8k
            aiColor4D cOut;
439
67.8k
            bool haveColor = false;
440
67.8k
            if (NotSet != aiColors[0]) {
441
4
                cOut.r = NormalizeColorValue(GetProperty(instElement->alProperties,
442
4
                                                    aiColors[0])
443
4
                                                    .avList.front(),
444
4
                        aiColorsTypes[0]);
445
4
                haveColor = true;
446
4
            }
447
448
67.8k
            if (NotSet != aiColors[1]) {
449
1
                cOut.g = NormalizeColorValue(GetProperty(instElement->alProperties,
450
1
                                                    aiColors[1])
451
1
                                                    .avList.front(),
452
1
                        aiColorsTypes[1]);
453
1
                haveColor = true;
454
1
            }
455
456
67.8k
            if (NotSet != aiColors[2]) {
457
0
                cOut.b = NormalizeColorValue(GetProperty(instElement->alProperties,
458
0
                                                    aiColors[2])
459
0
                                                    .avList.front(),
460
0
                        aiColorsTypes[2]);
461
0
                haveColor = true;
462
0
            }
463
464
            // assume 1.0 for the alpha channel if it is not set
465
67.8k
            if (NotSet == aiColors[3]) {
466
67.8k
                cOut.a = 1.0;
467
67.8k
            } else {
468
0
                cOut.a = NormalizeColorValue(GetProperty(instElement->alProperties,
469
0
                                                    aiColors[3])
470
0
                                                    .avList.front(),
471
0
                        aiColorsTypes[3]);
472
473
0
                haveColor = true;
474
0
            }
475
476
            // Texture coordinates
477
67.8k
            aiVector3D tOut;
478
67.8k
            tOut.z = 0;
479
67.8k
            bool haveTextureCoords = false;
480
67.8k
            if (NotSet != aiTexcoord[0]) {
481
67.4k
                tOut.x = PropertyInstance::ConvertTo<ai_real>(
482
67.4k
                        GetProperty(instElement->alProperties, aiTexcoord[0]).avList.front(), aiTexcoordTypes[0]);
483
67.4k
                haveTextureCoords = true;
484
67.4k
            }
485
486
67.8k
            if (NotSet != aiTexcoord[1]) {
487
67.4k
                tOut.y = PropertyInstance::ConvertTo<ai_real>(
488
67.4k
                        GetProperty(instElement->alProperties, aiTexcoord[1]).avList.front(), aiTexcoordTypes[1]);
489
67.4k
                haveTextureCoords = true;
490
67.4k
            }
491
492
            // create aiMesh if needed
493
67.8k
            if (nullptr == mGeneratedMesh) {
494
20
                mGeneratedMesh = new aiMesh();
495
20
                mGeneratedMesh->mMaterialIndex = 0;
496
20
            }
497
498
67.8k
            if (nullptr == mGeneratedMesh->mVertices) {
499
20
                mGeneratedMesh->mNumVertices = pcElement->NumOccur;
500
20
                mGeneratedMesh->mVertices = new aiVector3D[mGeneratedMesh->mNumVertices];
501
20
            }
502
67.8k
            if (pos >= mGeneratedMesh->mNumVertices) {
503
0
                throw DeadlyImportError("Invalid .ply file: Too many vertices");
504
0
            }
505
506
67.8k
            mGeneratedMesh->mVertices[pos] = vOut;
507
508
67.8k
            if (haveNormal) {
509
67.7k
                if (nullptr == mGeneratedMesh->mNormals)
510
10
                    mGeneratedMesh->mNormals = new aiVector3D[mGeneratedMesh->mNumVertices];
511
67.7k
                mGeneratedMesh->mNormals[pos] = nOut;
512
67.7k
            }
513
514
67.8k
            if (haveColor) {
515
4
                if (nullptr == mGeneratedMesh->mColors[0])
516
2
                    mGeneratedMesh->mColors[0] = new aiColor4D[mGeneratedMesh->mNumVertices];
517
4
                mGeneratedMesh->mColors[0][pos] = cOut;
518
4
            }
519
520
67.8k
            if (haveTextureCoords) {
521
67.4k
                if (nullptr == mGeneratedMesh->mTextureCoords[0]) {
522
7
                    mGeneratedMesh->mNumUVComponents[0] = 2;
523
7
                    mGeneratedMesh->mTextureCoords[0] = new aiVector3D[mGeneratedMesh->mNumVertices];
524
7
                }
525
67.4k
                mGeneratedMesh->mTextureCoords[0][pos] = tOut;
526
67.4k
            }
527
67.8k
        }
528
67.8k
    }
529
530
    // ------------------------------------------------------------------------------------------------
531
    // Try to extract proper faces from the PLY DOM
532
    void PLYImporter::LoadFace(const Element *pcElement, const ElementInstance *instElement,
533
22.4k
            unsigned int pos) {
534
22.4k
        ai_assert(nullptr != pcElement);
535
22.4k
        ai_assert(nullptr != instElement);
536
537
22.4k
        if (mGeneratedMesh == nullptr) {
538
1
            throw DeadlyImportError("Invalid .ply file: Vertices should be declared before faces");
539
1
        }
540
541
22.4k
        bool bOne = false;
542
543
        // index of the vertex index list
544
22.4k
        unsigned int iProperty = NotSet;
545
22.4k
        EDataType eType = EDT_Char;
546
22.4k
        bool bIsTriStrip = false;
547
548
        // texture coordinates
549
22.4k
        unsigned int iTextureCoord = NotSet;
550
22.4k
        EDataType eType3 = EDT_Char;
551
552
        // face = unique number of vertex indices
553
22.4k
        if (EEST_Face == pcElement->eSemantic) {
554
22.4k
            unsigned int _a = 0;
555
22.4k
            for (std::vector<Property>::const_iterator a = pcElement->alProperties.begin();
556
44.8k
                    a != pcElement->alProperties.end(); ++a, ++_a) {
557
22.4k
                if (EST_VertexIndex == a->Semantic) {
558
                    // must be a dynamic list!
559
22.4k
                    if (!a->bIsList) {
560
0
                        continue;
561
0
                    }
562
563
22.4k
                    iProperty = _a;
564
22.4k
                    bOne = true;
565
22.4k
                    eType = a->eType;
566
22.4k
                } else if (EST_TextureCoordinates == a->Semantic) {
567
                    // must be a dynamic list!
568
0
                    if (!a->bIsList) {
569
0
                        continue;
570
0
                    }
571
0
                    iTextureCoord = _a;
572
0
                    bOne = true;
573
0
                    eType3 = a->eType;
574
0
                }
575
22.4k
            }
576
22.4k
        }
577
        // triangle strip
578
        // TODO: material index support???
579
0
        else if (EEST_TriStrip == pcElement->eSemantic) {
580
0
            unsigned int _a = 0;
581
0
            for (auto a = pcElement->alProperties.begin(); a != pcElement->alProperties.end(); ++a, ++_a) {
582
                // must be a dynamic list!
583
0
                if (!a->bIsList) {
584
0
                    continue;
585
0
                }
586
0
                iProperty = _a;
587
0
                bOne = true;
588
0
                bIsTriStrip = true;
589
0
                eType = a->eType;
590
0
                break;
591
0
            }
592
0
        }
593
594
        // check whether we have at least one per-face information set
595
22.4k
        if (bOne) {
596
22.4k
            if (mGeneratedMesh->mFaces == nullptr) {
597
11
                mGeneratedMesh->mNumFaces = pcElement->NumOccur;
598
11
                mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces];
599
22.4k
            } else {
600
22.4k
                if (mGeneratedMesh->mNumFaces < pcElement->NumOccur) {
601
0
                    throw DeadlyImportError("Invalid .ply file: Too many faces");
602
0
                }
603
22.4k
            }
604
605
22.4k
            if (!bIsTriStrip) {
606
                // parse the list of vertex indices
607
22.4k
                if (NotSet != iProperty) {
608
22.4k
                    const unsigned int iNum = static_cast<unsigned int>(GetProperty(instElement->alProperties, iProperty).avList.size());
609
22.4k
                    mGeneratedMesh->mFaces[pos].mNumIndices = iNum;
610
22.4k
                    mGeneratedMesh->mFaces[pos].mIndices = new unsigned int[iNum];
611
612
22.4k
                    std::vector<PropertyInstance::ValueUnion>::const_iterator p =
613
22.4k
                            GetProperty(instElement->alProperties, iProperty).avList.begin();
614
615
89.3k
                    for (unsigned int a = 0; a < iNum; ++a, ++p) {
616
66.9k
                        mGeneratedMesh->mFaces[pos].mIndices[a] = PropertyInstance::ConvertTo<unsigned int>(*p, eType);
617
66.9k
                    }
618
22.4k
                }
619
620
22.4k
                if (NotSet != iTextureCoord) {
621
0
                    const auto iNum = static_cast<unsigned int>(GetProperty(instElement->alProperties, iTextureCoord).avList.size());
622
623
                    // should be 6 coords
624
0
                    auto p = GetProperty(instElement->alProperties, iTextureCoord).avList.begin();
625
0
                    if ((iNum / 3) == 2) { // X Y coord
626
0
                        for (unsigned int a = 0; a < iNum; ++a, ++p) {
627
                            // The texture-coordinate list length is independent of the
628
                            // vertex-index list length, so a / 2 may run past the face's
629
                            // index array (which may even be absent). Bound it before use.
630
0
                            const unsigned int faceIndexPos = a / 2;
631
0
                            if (mGeneratedMesh->mFaces[pos].mIndices == nullptr ||
632
0
                                    faceIndexPos >= mGeneratedMesh->mFaces[pos].mNumIndices) {
633
0
                                continue;
634
0
                            }
635
0
                            unsigned int vindex = mGeneratedMesh->mFaces[pos].mIndices[faceIndexPos];
636
0
                            if (vindex < mGeneratedMesh->mNumVertices) {
637
0
                                if (mGeneratedMesh->mTextureCoords[0] == nullptr) {
638
0
                                    mGeneratedMesh->mNumUVComponents[0] = 2;
639
0
                                    mGeneratedMesh->mTextureCoords[0] = new aiVector3D[mGeneratedMesh->mNumVertices];
640
0
                                }
641
642
0
                                if (a % 2 == 0) {
643
0
                                    mGeneratedMesh->mTextureCoords[0][vindex].x = PLY::PropertyInstance::ConvertTo<ai_real>(*p, eType3);
644
0
                                } else {
645
0
                                    mGeneratedMesh->mTextureCoords[0][vindex].y = PLY::PropertyInstance::ConvertTo<ai_real>(*p, eType3);
646
0
                                }
647
648
0
                                mGeneratedMesh->mTextureCoords[0][vindex].z = 0;
649
0
                            }
650
0
                        }
651
0
                    }
652
0
                }
653
22.4k
            } else { // triangle strips
654
                // normally we have only one triangle strip instance where
655
                // a value of -1 indicates a restart of the strip
656
0
                createFromeTriStrip(instElement, iProperty, eType);
657
0
            }
658
22.4k
        }
659
22.4k
    }
660
661
    // ------------------------------------------------------------------------------------------------
662
0
    void PLYImporter::createFromeTriStrip(const ElementInstance *instElement, unsigned int iProperty, EDataType eType) {
663
0
        bool flip = false;
664
0
        const auto &quak = GetProperty(instElement->alProperties, iProperty).avList;
665
666
0
        std::vector<aiFace> cache;
667
0
        int aiTable[2] = { -1, -1 };
668
0
        for (auto a = quak.begin(); a != quak.end(); ++a) {
669
0
            const int p = PropertyInstance::ConvertTo<int>(*a, eType);
670
671
0
            if (-1 == p) {
672
                // restart the strip ...
673
0
                aiTable[0] = aiTable[1] = -1;
674
0
                flip = false;
675
0
                continue;
676
0
            }
677
            
678
0
            if (-1 == aiTable[0]) {
679
0
                aiTable[0] = p;
680
0
                continue;
681
0
            }
682
            
683
0
            if (-1 == aiTable[1]) {
684
0
                aiTable[1] = p;
685
0
                continue;
686
0
            }
687
0
            aiFace face;
688
0
            face.mNumIndices = 3;
689
0
            face.mIndices = new unsigned int[3];
690
0
            face.mIndices[0] = aiTable[0];
691
0
            face.mIndices[1] = aiTable[1];
692
0
            face.mIndices[2] = p;
693
0
            if (flip) {
694
0
                std::swap(face.mIndices[0], face.mIndices[1]);
695
0
            }
696
0
            cache.push_back(face);
697
            // every second pass swap the indices.
698
0
            flip = !flip;
699
700
0
            aiTable[0] = aiTable[1];
701
0
            aiTable[1] = p;
702
0
        }
703
0
        if (mGeneratedMesh->mFaces != nullptr) {
704
0
            delete[] mGeneratedMesh->mFaces;
705
0
        }
706
0
        mGeneratedMesh->mNumFaces = static_cast<unsigned int>(cache.size());
707
0
        mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces];
708
0
        std::copy(cache.begin(), cache.end(), mGeneratedMesh->mFaces);
709
0
    }
710
711
    // ------------------------------------------------------------------------------------------------
712
    // Extract a material from the PLY DOM
713
17
    void PLYImporter::LoadMaterial(std::vector<aiMaterial *> *pvOut, std::string &defaultTexture, const bool pointsOnly) {
714
17
        ai_assert(nullptr != pvOut);
715
716
        // diffuse[4], specular[4], ambient[4]
717
        // rgba order
718
17
        unsigned int aaiPositions[3][4] = {
719
17
            { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF },
720
17
            { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF },
721
17
            { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF },
722
17
        };
723
724
17
        EDataType aaiTypes[3][4] = {
725
17
            { EDT_Char, EDT_Char, EDT_Char, EDT_Char },
726
17
            { EDT_Char, EDT_Char, EDT_Char, EDT_Char },
727
17
            { EDT_Char, EDT_Char, EDT_Char, EDT_Char }
728
17
        };
729
17
        ElementInstanceList *pcList = nullptr;
730
731
17
        unsigned int iPhong = 0xFFFFFFFF;
732
17
        EDataType ePhong = EDT_Char;
733
734
17
        unsigned int iOpacity = 0xFFFFFFFF;
735
17
        EDataType eOpacity = EDT_Char;
736
737
        // search in the DOM for a vertex entry
738
17
        unsigned int _i = 0;
739
17
        for (std::vector<Element>::const_iterator i = this->pcDOM->alElements.begin();
740
54
                i != this->pcDOM->alElements.end(); ++i, ++_i) {
741
37
            if (EEST_Material == i->eSemantic) {
742
0
                pcList = &this->pcDOM->alElementData[_i];
743
744
                // now check whether which coordinate sets are available
745
0
                unsigned int _a = 0;
746
0
                for (std::vector<Property>::const_iterator a = i->alProperties.begin(); a != i->alProperties.end(); ++a, ++_a) {
747
0
                    if (a->bIsList) {
748
0
                        continue;
749
0
                    }
750
751
                    // pohng specularity      -----------------------------------
752
0
                    if (EST_PhongPower == (*a).Semantic) {
753
0
                        iPhong = _a;
754
0
                        ePhong = (*a).eType;
755
0
                    }
756
757
                    // general opacity        -----------------------------------
758
0
                    if (EST_Opacity == (*a).Semantic) {
759
0
                        iOpacity = _a;
760
0
                        eOpacity = (*a).eType;
761
0
                    }
762
763
                    // diffuse color channels -----------------------------------
764
0
                    if (EST_DiffuseRed == (*a).Semantic) {
765
0
                        aaiPositions[0][0] = _a;
766
0
                        aaiTypes[0][0] = (*a).eType;
767
0
                    } else if (EST_DiffuseGreen == (*a).Semantic) {
768
0
                        aaiPositions[0][1] = _a;
769
0
                        aaiTypes[0][1] = (*a).eType;
770
0
                    } else if (EST_DiffuseBlue == (*a).Semantic) {
771
0
                        aaiPositions[0][2] = _a;
772
0
                        aaiTypes[0][2] = (*a).eType;
773
0
                    } else if (EST_DiffuseAlpha == (*a).Semantic) {
774
0
                        aaiPositions[0][3] = _a;
775
0
                        aaiTypes[0][3] = (*a).eType;
776
0
                    }
777
                    // specular color channels -----------------------------------
778
0
                    else if (EST_SpecularRed == (*a).Semantic) {
779
0
                        aaiPositions[1][0] = _a;
780
0
                        aaiTypes[1][0] = (*a).eType;
781
0
                    } else if (EST_SpecularGreen == (*a).Semantic) {
782
0
                        aaiPositions[1][1] = _a;
783
0
                        aaiTypes[1][1] = (*a).eType;
784
0
                    } else if (EST_SpecularBlue == (*a).Semantic) {
785
0
                        aaiPositions[1][2] = _a;
786
0
                        aaiTypes[1][2] = (*a).eType;
787
0
                    } else if (EST_SpecularAlpha == (*a).Semantic) {
788
0
                        aaiPositions[1][3] = _a;
789
0
                        aaiTypes[1][3] = (*a).eType;
790
0
                    }
791
                    // ambient color channels -----------------------------------
792
0
                    else if (EST_AmbientRed == (*a).Semantic) {
793
0
                        aaiPositions[2][0] = _a;
794
0
                        aaiTypes[2][0] = (*a).eType;
795
0
                    } else if (EST_AmbientGreen == (*a).Semantic) {
796
0
                        aaiPositions[2][1] = _a;
797
0
                        aaiTypes[2][1] = (*a).eType;
798
0
                    } else if (EST_AmbientBlue == (*a).Semantic) {
799
0
                        aaiPositions[2][2] = _a;
800
0
                        aaiTypes[2][2] = (*a).eType;
801
0
                    } else if (EST_AmbientAlpha == (*a).Semantic) {
802
0
                        aaiPositions[2][3] = _a;
803
0
                        aaiTypes[2][3] = (*a).eType;
804
0
                    }
805
0
                }
806
0
                break;
807
37
            } else if (EEST_TextureFile == i->eSemantic) {
808
0
                defaultTexture = i->szName;
809
0
            }
810
37
        }
811
        // check whether we have a valid source for the material data
812
17
        if (nullptr != pcList) {
813
0
            for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin(); i != pcList->alInstances.end(); ++i) {
814
0
                aiColor4D clrOut;
815
0
                aiMaterial *pcHelper = new aiMaterial();
816
817
                // build the diffuse material color
818
0
                GetMaterialColor((*i).alProperties, aaiPositions[0], aaiTypes[0], &clrOut);
819
0
                pcHelper->AddProperty<aiColor4D>(&clrOut, 1, AI_MATKEY_COLOR_DIFFUSE);
820
821
                // build the specular material color
822
0
                GetMaterialColor((*i).alProperties, aaiPositions[1], aaiTypes[1], &clrOut);
823
0
                pcHelper->AddProperty<aiColor4D>(&clrOut, 1, AI_MATKEY_COLOR_SPECULAR);
824
825
                // build the ambient material color
826
0
                GetMaterialColor((*i).alProperties, aaiPositions[2], aaiTypes[2], &clrOut);
827
0
                pcHelper->AddProperty<aiColor4D>(&clrOut, 1, AI_MATKEY_COLOR_AMBIENT);
828
829
                // handle phong power and shading mode
830
0
                int iMode = aiShadingMode_Gouraud;
831
0
                if (0xFFFFFFFF != iPhong) {
832
0
                    ai_real fSpec = PropertyInstance::ConvertTo<ai_real>(GetProperty((*i).alProperties, iPhong).avList.front(), ePhong);
833
834
                    // if shininess is 0 (and the pow() calculation would therefore always
835
                    // become 1, not depending on the angle), use gouraud lighting
836
0
                    if (fSpec) {
837
                        // scale this with 15 ... hopefully this is correct
838
0
                        fSpec *= 15;
839
0
                        pcHelper->AddProperty<ai_real>(&fSpec, 1, AI_MATKEY_SHININESS);
840
841
0
                        iMode = static_cast<int>(aiShadingMode_Phong);
842
0
                    }
843
0
                }
844
0
                pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
845
846
                // handle opacity
847
0
                if (0xFFFFFFFF != iOpacity) {
848
0
                    ai_real fOpacity = PropertyInstance::ConvertTo<ai_real>(GetProperty((*i).alProperties, iPhong).avList.front(), eOpacity);
849
0
                    pcHelper->AddProperty<ai_real>(&fOpacity, 1, AI_MATKEY_OPACITY);
850
0
                }
851
852
                // The face order is absolutely undefined for PLY, so we have to
853
                // use two-sided rendering to be sure it's ok.
854
0
                const int two_sided = 1;
855
0
                pcHelper->AddProperty(&two_sided, 1, AI_MATKEY_TWOSIDED);
856
857
                // default texture
858
0
                if (!defaultTexture.empty()) {
859
0
                    const aiString name(defaultTexture.c_str());
860
0
                    pcHelper->AddProperty(&name, _AI_MATKEY_TEXTURE_BASE, aiTextureType_DIFFUSE, 0);
861
0
                }
862
863
0
                if (!pointsOnly) {
864
0
                    pcHelper->AddProperty(&two_sided, 1, AI_MATKEY_TWOSIDED);
865
0
                }
866
867
                // set to wireframe, so when using this material info we can switch to points rendering
868
0
                if (pointsOnly) {
869
0
                    constexpr int wireframe = 1;
870
0
                    pcHelper->AddProperty(&wireframe, 1, AI_MATKEY_ENABLE_WIREFRAME);
871
0
                }
872
873
                // add the newly created material instance to the list
874
0
                pvOut->push_back(pcHelper);
875
0
            }
876
17
        } else {
877
            // generate a default material
878
17
            aiMaterial *pcHelper = new aiMaterial();
879
880
            // fill in a default material
881
17
            int iMode = aiShadingMode_Gouraud;
882
17
            pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
883
884
            // generate white material most 3D engine just multiply ambient / diffuse color with actual ambient / light color
885
17
            aiColor3D clr;
886
17
            clr.b = clr.g = clr.r = 1.0f;
887
17
            pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
888
17
            pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_SPECULAR);
889
890
17
            clr.b = clr.g = clr.r = 1.0f;
891
17
            pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_AMBIENT);
892
893
            // The face order is absolutely undefined for PLY, so we have to
894
            // use two-sided rendering to be sure it's ok.
895
17
            if (!pointsOnly) {
896
11
                const int two_sided = 1;
897
11
                pcHelper->AddProperty(&two_sided, 1, AI_MATKEY_TWOSIDED);
898
11
            }
899
900
            // default texture
901
17
            if (!defaultTexture.empty()) {
902
0
                const aiString name(defaultTexture.c_str());
903
0
                pcHelper->AddProperty(&name, _AI_MATKEY_TEXTURE_BASE, aiTextureType_DIFFUSE, 0);
904
17
            } else {
905
17
                const aiString name(AI_DEFAULT_MATERIAL_NAME);
906
17
                pcHelper->AddProperty(&name, AI_MATKEY_NAME);
907
17
            }
908
909
            // set to wireframe, so when using this material info we can switch to points rendering
910
17
            if (pointsOnly) {
911
6
                constexpr int wireframe = 1;
912
6
                pcHelper->AddProperty(&wireframe, 1, AI_MATKEY_ENABLE_WIREFRAME);
913
6
            }
914
915
17
            pvOut->push_back(pcHelper);
916
17
        }
917
17
    }
918
919
} // namespace Assimp
920
921
#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER