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