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