Coverage Report

Created: 2025-11-11 06:59

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