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 Implementation of the PLY parser class */
43 
44 
45 #ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
46 
47 #include "fast_atof.h"
48 #include <assimp/DefaultLogger.hpp>
49 #include "ByteSwapper.h"
50 #include "PlyLoader.h"
51 
52 using namespace Assimp;
53 
54 // ------------------------------------------------------------------------------------------------
ParseDataType(std::vector<char> & buffer)55 PLY::EDataType PLY::Property::ParseDataType(std::vector<char> &buffer) {
56   ai_assert(!buffer.empty());
57 
58   PLY::EDataType eOut = PLY::EDT_INVALID;
59 
60   if (PLY::DOM::TokenMatch(buffer, "char", 4) ||
61     PLY::DOM::TokenMatch(buffer, "int8", 4))
62   {
63     eOut = PLY::EDT_Char;
64   }
65   else if (PLY::DOM::TokenMatch(buffer, "uchar", 5) ||
66     PLY::DOM::TokenMatch(buffer, "uint8", 5))
67   {
68     eOut = PLY::EDT_UChar;
69   }
70   else if (PLY::DOM::TokenMatch(buffer, "short", 5) ||
71     PLY::DOM::TokenMatch(buffer, "int16", 5))
72   {
73     eOut = PLY::EDT_Short;
74   }
75   else if (PLY::DOM::TokenMatch(buffer, "ushort", 6) ||
76     PLY::DOM::TokenMatch(buffer, "uint16", 6))
77   {
78     eOut = PLY::EDT_UShort;
79   }
80   else if (PLY::DOM::TokenMatch(buffer, "int32", 5) || PLY::DOM::TokenMatch(buffer, "int", 3))
81   {
82     eOut = PLY::EDT_Int;
83   }
84   else if (PLY::DOM::TokenMatch(buffer, "uint32", 6) || PLY::DOM::TokenMatch(buffer, "uint", 4))
85   {
86     eOut = PLY::EDT_UInt;
87   }
88   else if (PLY::DOM::TokenMatch(buffer, "float", 5) || PLY::DOM::TokenMatch(buffer, "float32", 7))
89   {
90     eOut = PLY::EDT_Float;
91   }
92   else if (PLY::DOM::TokenMatch(buffer, "double64", 8) || PLY::DOM::TokenMatch(buffer, "double", 6) ||
93     PLY::DOM::TokenMatch(buffer, "float64", 7))
94   {
95     eOut = PLY::EDT_Double;
96   }
97   if (PLY::EDT_INVALID == eOut)
98   {
99     DefaultLogger::get()->info("Found unknown data type in PLY file. This is OK");
100   }
101 
102   return eOut;
103 }
104 
105 // ------------------------------------------------------------------------------------------------
ParseSemantic(std::vector<char> & buffer)106 PLY::ESemantic PLY::Property::ParseSemantic(std::vector<char> &buffer) {
107   ai_assert(!buffer.empty());
108 
109   PLY::ESemantic eOut = PLY::EST_INVALID;
110   if (PLY::DOM::TokenMatch(buffer, "red", 3)) {
111     eOut = PLY::EST_Red;
112   }
113   else if (PLY::DOM::TokenMatch(buffer, "green", 5)) {
114     eOut = PLY::EST_Green;
115   }
116   else if (PLY::DOM::TokenMatch(buffer, "blue", 4)) {
117     eOut = PLY::EST_Blue;
118   }
119   else if (PLY::DOM::TokenMatch(buffer, "alpha", 5)) {
120     eOut = PLY::EST_Alpha;
121   }
122   else if (PLY::DOM::TokenMatch(buffer, "vertex_index", 12) || PLY::DOM::TokenMatch(buffer, "vertex_indices", 14)) {
123     eOut = PLY::EST_VertexIndex;
124   }
125   else if (PLY::DOM::TokenMatch(buffer, "texcoord", 8)) // Manage uv coords on faces
126   {
127     eOut = PLY::EST_TextureCoordinates;
128   }
129   else if (PLY::DOM::TokenMatch(buffer, "material_index", 14))
130   {
131     eOut = PLY::EST_MaterialIndex;
132   }
133   else if (PLY::DOM::TokenMatch(buffer, "ambient_red", 11))
134   {
135     eOut = PLY::EST_AmbientRed;
136   }
137   else if (PLY::DOM::TokenMatch(buffer, "ambient_green", 13))
138   {
139     eOut = PLY::EST_AmbientGreen;
140   }
141   else if (PLY::DOM::TokenMatch(buffer, "ambient_blue", 12))
142   {
143     eOut = PLY::EST_AmbientBlue;
144   }
145   else if (PLY::DOM::TokenMatch(buffer, "ambient_alpha", 13))
146   {
147     eOut = PLY::EST_AmbientAlpha;
148   }
149   else if (PLY::DOM::TokenMatch(buffer, "diffuse_red", 11))
150   {
151     eOut = PLY::EST_DiffuseRed;
152   }
153   else if (PLY::DOM::TokenMatch(buffer, "diffuse_green", 13))
154   {
155     eOut = PLY::EST_DiffuseGreen;
156   }
157   else if (PLY::DOM::TokenMatch(buffer, "diffuse_blue", 12))
158   {
159     eOut = PLY::EST_DiffuseBlue;
160   }
161   else if (PLY::DOM::TokenMatch(buffer, "diffuse_alpha", 13))
162   {
163     eOut = PLY::EST_DiffuseAlpha;
164   }
165   else if (PLY::DOM::TokenMatch(buffer, "specular_red", 12))
166   {
167     eOut = PLY::EST_SpecularRed;
168   }
169   else if (PLY::DOM::TokenMatch(buffer, "specular_green", 14))
170   {
171     eOut = PLY::EST_SpecularGreen;
172   }
173   else if (PLY::DOM::TokenMatch(buffer, "specular_blue", 13))
174   {
175     eOut = PLY::EST_SpecularBlue;
176   }
177   else if (PLY::DOM::TokenMatch(buffer, "specular_alpha", 14))
178   {
179     eOut = PLY::EST_SpecularAlpha;
180   }
181   else if (PLY::DOM::TokenMatch(buffer, "opacity", 7))
182   {
183     eOut = PLY::EST_Opacity;
184   }
185   else if (PLY::DOM::TokenMatch(buffer, "specular_power", 14))
186   {
187     eOut = PLY::EST_PhongPower;
188   }
189   else if (PLY::DOM::TokenMatch(buffer, "r", 1))
190   {
191     eOut = PLY::EST_Red;
192   }
193   else if (PLY::DOM::TokenMatch(buffer, "g", 1))
194   {
195     eOut = PLY::EST_Green;
196   }
197   else if (PLY::DOM::TokenMatch(buffer, "b", 1))
198   {
199     eOut = PLY::EST_Blue;
200   }
201 
202   // NOTE: Blender3D exports texture coordinates as s,t tuples
203   else if (PLY::DOM::TokenMatch(buffer, "u", 1) || PLY::DOM::TokenMatch(buffer, "s", 1) || PLY::DOM::TokenMatch(buffer, "tx", 2) || PLY::DOM::TokenMatch(buffer, "texture_u", 9))
204   {
205     eOut = PLY::EST_UTextureCoord;
206   }
207   else if (PLY::DOM::TokenMatch(buffer, "v", 1) || PLY::DOM::TokenMatch(buffer, "t", 1) || PLY::DOM::TokenMatch(buffer, "ty", 2) || PLY::DOM::TokenMatch(buffer, "texture_v", 9))
208   {
209     eOut = PLY::EST_VTextureCoord;
210   }
211   else if (PLY::DOM::TokenMatch(buffer, "x", 1))
212   {
213     eOut = PLY::EST_XCoord;
214   }
215   else if (PLY::DOM::TokenMatch(buffer, "y", 1)) {
216     eOut = PLY::EST_YCoord;
217   }
218   else if (PLY::DOM::TokenMatch(buffer, "z", 1)) {
219     eOut = PLY::EST_ZCoord;
220   }
221   else if (PLY::DOM::TokenMatch(buffer, "nx", 2)) {
222     eOut = PLY::EST_XNormal;
223   }
224   else if (PLY::DOM::TokenMatch(buffer, "ny", 2)) {
225     eOut = PLY::EST_YNormal;
226   }
227   else if (PLY::DOM::TokenMatch(buffer, "nz", 2)) {
228     eOut = PLY::EST_ZNormal;
229   }
230   else {
231     DefaultLogger::get()->info("Found unknown property semantic in file. This is ok");
232     PLY::DOM::SkipLine(buffer);
233   }
234   return eOut;
235 }
236 
237 // ------------------------------------------------------------------------------------------------
ParseProperty(std::vector<char> & buffer,PLY::Property * pOut)238 bool PLY::Property::ParseProperty(std::vector<char> &buffer, PLY::Property* pOut)
239 {
240   ai_assert(!buffer.empty());
241 
242   // Forms supported:
243   // "property float x"
244   // "property list uchar int vertex_index"
245 
246   // skip leading spaces
247   if (!PLY::DOM::SkipSpaces(buffer)) {
248     return false;
249   }
250 
251   // skip the "property" string at the beginning
252   if (!PLY::DOM::TokenMatch(buffer, "property", 8))
253   {
254     // seems not to be a valid property entry
255     return false;
256   }
257   // get next word
258   if (!PLY::DOM::SkipSpaces(buffer)) {
259     return false;
260   }
261   if (PLY::DOM::TokenMatch(buffer, "list", 4))
262   {
263     pOut->bIsList = true;
264 
265     // seems to be a list.
266     if (EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(buffer)))
267     {
268       // unable to parse list size data type
269       PLY::DOM::SkipLine(buffer);
270       return false;
271     }
272     if (!PLY::DOM::SkipSpaces(buffer))return false;
273     if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(buffer)))
274     {
275       // unable to parse list data type
276       PLY::DOM::SkipLine(buffer);
277       return false;
278     }
279   }
280   else
281   {
282     if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(buffer)))
283     {
284       // unable to parse data type. Skip the property
285       PLY::DOM::SkipLine(buffer);
286       return false;
287     }
288   }
289 
290   if (!PLY::DOM::SkipSpaces(buffer))
291     return false;
292 
293   pOut->Semantic = PLY::Property::ParseSemantic(buffer);
294 
295   if (PLY::EST_INVALID == pOut->Semantic)
296   {
297     DefaultLogger::get()->info("Found unknown semantic in PLY file. This is OK");
298     std::string(&buffer[0], &buffer[0] + strlen(&buffer[0]));
299   }
300 
301   PLY::DOM::SkipSpacesAndLineEnd(buffer);
302   return true;
303 }
304 
305 // ------------------------------------------------------------------------------------------------
ParseSemantic(std::vector<char> & buffer)306 PLY::EElementSemantic PLY::Element::ParseSemantic(std::vector<char> &buffer)
307 {
308   ai_assert(!buffer.empty());
309 
310   PLY::EElementSemantic eOut = PLY::EEST_INVALID;
311   if (PLY::DOM::TokenMatch(buffer, "vertex", 6))
312   {
313     eOut = PLY::EEST_Vertex;
314   }
315   else if (PLY::DOM::TokenMatch(buffer, "face", 4))
316   {
317     eOut = PLY::EEST_Face;
318   }
319   else if (PLY::DOM::TokenMatch(buffer, "tristrips", 9))
320   {
321     eOut = PLY::EEST_TriStrip;
322   }
323 #if 0
324   // TODO: maybe implement this?
325   else if (PLY::DOM::TokenMatch(buffer,"range_grid",10))
326   {
327     eOut = PLY::EEST_Face;
328   }
329 #endif
330   else if (PLY::DOM::TokenMatch(buffer, "edge", 4))
331   {
332     eOut = PLY::EEST_Edge;
333   }
334   else if (PLY::DOM::TokenMatch(buffer, "material", 8))
335   {
336     eOut = PLY::EEST_Material;
337   }
338   else if (PLY::DOM::TokenMatch(buffer, "TextureFile", 11))
339   {
340     eOut = PLY::EEST_TextureFile;
341   }
342 
343   return eOut;
344 }
345 
346 // ------------------------------------------------------------------------------------------------
ParseElement(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,PLY::Element * pOut)347 bool PLY::Element::ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLY::Element* pOut)
348 {
349   ai_assert(NULL != pOut);
350   // Example format: "element vertex 8"
351 
352   // skip leading spaces
353   if (!PLY::DOM::SkipSpaces(buffer))
354   {
355     return false;
356   }
357 
358   // skip the "element" string at the beginning
359   if (!PLY::DOM::TokenMatch(buffer, "element", 7) && !PLY::DOM::TokenMatch(buffer, "comment", 7))
360   {
361     // seems not to be a valid property entry
362     return false;
363   }
364   // get next word
365   if (!PLY::DOM::SkipSpaces(buffer))
366     return false;
367 
368   // parse the semantic of the element
369   pOut->eSemantic = PLY::Element::ParseSemantic(buffer);
370   if (PLY::EEST_INVALID == pOut->eSemantic)
371   {
372     // if the exact semantic can't be determined, just store
373     // the original string identifier
374     pOut->szName = std::string(&buffer[0], &buffer[0] + strlen(&buffer[0]));
375   }
376 
377   if (!PLY::DOM::SkipSpaces(buffer))
378     return false;
379 
380   if (PLY::EEST_TextureFile == pOut->eSemantic)
381   {
382     char* endPos = &buffer[0] + (strlen(&buffer[0]) - 1);
383     pOut->szName = std::string(&buffer[0], endPos);
384 
385     // go to the next line
386     PLY::DOM::SkipSpacesAndLineEnd(buffer);
387 
388     return true;
389   }
390 
391   //parse the number of occurrences of this element
392   const char* pCur = (char*)&buffer[0];
393   pOut->NumOccur = strtoul10(pCur, &pCur);
394 
395   // go to the next line
396   PLY::DOM::SkipSpacesAndLineEnd(buffer);
397 
398   // now parse all properties of the element
399   while (true)
400   {
401     streamBuffer.getNextLine(buffer);
402     pCur = (char*)&buffer[0];
403 
404     // skip all comments
405     PLY::DOM::SkipComments(buffer);
406 
407     PLY::Property prop;
408     if (!PLY::Property::ParseProperty(buffer, &prop))
409       break;
410 
411     pOut->alProperties.push_back(prop);
412   }
413 
414   return true;
415 }
416 
417 // ------------------------------------------------------------------------------------------------
SkipSpaces(std::vector<char> & buffer)418 bool PLY::DOM::SkipSpaces(std::vector<char> &buffer)
419 {
420   const char* pCur = buffer.empty() ? NULL : (char*)&buffer[0];
421   bool ret = false;
422   if (pCur)
423   {
424     const char* szCur = pCur;
425     ret = Assimp::SkipSpaces(pCur, &pCur);
426 
427     uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
428     buffer.erase(buffer.begin(), buffer.begin() + iDiff);
429     return ret;
430   }
431 
432   return ret;
433 }
434 
SkipLine(std::vector<char> & buffer)435 bool PLY::DOM::SkipLine(std::vector<char> &buffer)
436 {
437   const char* pCur = buffer.empty() ? NULL : (char*)&buffer[0];
438   bool ret = false;
439   if (pCur)
440   {
441     const char* szCur = pCur;
442     ret = Assimp::SkipLine(pCur, &pCur);
443 
444     uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
445     buffer.erase(buffer.begin(), buffer.begin() + iDiff);
446     return ret;
447   }
448 
449   return ret;
450 }
451 
TokenMatch(std::vector<char> & buffer,const char * token,unsigned int len)452 bool PLY::DOM::TokenMatch(std::vector<char> &buffer, const char* token, unsigned int len)
453 {
454   const char* pCur = buffer.empty() ? NULL : (char*)&buffer[0];
455   bool ret = false;
456   if (pCur)
457   {
458     const char* szCur = pCur;
459     ret = Assimp::TokenMatch(pCur, token, len);
460 
461     uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
462     buffer.erase(buffer.begin(), buffer.begin() + iDiff);
463     return ret;
464   }
465 
466   return ret;
467 }
468 
SkipSpacesAndLineEnd(std::vector<char> & buffer)469 bool PLY::DOM::SkipSpacesAndLineEnd(std::vector<char> &buffer)
470 {
471   const char* pCur = buffer.empty() ? NULL : (char*)&buffer[0];
472   bool ret = false;
473   if (pCur)
474   {
475     const char* szCur = pCur;
476     ret = Assimp::SkipSpacesAndLineEnd(pCur, &pCur);
477 
478     uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
479     buffer.erase(buffer.begin(), buffer.begin() + iDiff);
480     return ret;
481   }
482 
483   return ret;
484 }
485 
SkipComments(std::vector<char> & buffer)486 bool PLY::DOM::SkipComments(std::vector<char> &buffer)
487 {
488   ai_assert(!buffer.empty());
489 
490   std::vector<char> nbuffer = buffer;
491   // skip spaces
492   if (!SkipSpaces(nbuffer)) {
493     return false;
494   }
495 
496   if (TokenMatch(nbuffer, "comment", 7))
497   {
498     if (!SkipSpaces(nbuffer))
499       SkipLine(nbuffer);
500 
501     if (!TokenMatch(nbuffer, "TextureFile", 11))
502     {
503       SkipLine(nbuffer);
504       buffer = nbuffer;
505       return true;
506     }
507 
508     return true;
509   }
510 
511   return false;
512 }
513 
514 // ------------------------------------------------------------------------------------------------
ParseHeader(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,bool isBinary)515 bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, bool isBinary) {
516   DefaultLogger::get()->debug("PLY::DOM::ParseHeader() begin");
517 
518   // parse all elements
519   while (!buffer.empty())
520   {
521     // skip all comments
522     PLY::DOM::SkipComments(buffer);
523 
524     PLY::Element out;
525     if (PLY::Element::ParseElement(streamBuffer, buffer, &out))
526     {
527       // add the element to the list of elements
528       alElements.push_back(out);
529     }
530     else if (TokenMatch(buffer, "end_header", 10))
531     {
532       // we have reached the end of the header
533       break;
534     }
535     else
536     {
537       // ignore unknown header elements
538       streamBuffer.getNextLine(buffer);
539     }
540   }
541 
542   if (!isBinary) // it would occur an error, if binary data start with values as space or line end.
543     SkipSpacesAndLineEnd(buffer);
544 
545   DefaultLogger::get()->debug("PLY::DOM::ParseHeader() succeeded");
546   return true;
547 }
548 
549 // ------------------------------------------------------------------------------------------------
ParseElementInstanceLists(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,PLYImporter * loader)550 bool PLY::DOM::ParseElementInstanceLists(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLYImporter* loader)
551 {
552   DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceLists() begin");
553   alElementData.resize(alElements.size());
554 
555   std::vector<PLY::Element>::const_iterator i = alElements.begin();
556   std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
557 
558   // parse all element instances
559   //construct vertices and faces
560   for (; i != alElements.end(); ++i, ++a)
561   {
562     if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip)
563     {
564       PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), NULL, loader);
565     }
566     else
567     {
568       (*a).alInstances.resize((*i).NumOccur);
569       PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), &(*a), NULL);
570     }
571   }
572 
573   DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceLists() succeeded");
574   return true;
575 }
576 
577 // ------------------------------------------------------------------------------------------------
ParseElementInstanceListsBinary(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,const char * & pCur,unsigned int & bufferSize,PLYImporter * loader,bool p_bBE)578 bool PLY::DOM::ParseElementInstanceListsBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer,
579     const char* &pCur,
580     unsigned int &bufferSize,
581     PLYImporter* loader,
582     bool p_bBE)
583 {
584   DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceListsBinary() begin");
585   alElementData.resize(alElements.size());
586 
587   std::vector<PLY::Element>::const_iterator i = alElements.begin();
588   std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
589 
590   // parse all element instances
591   for (; i != alElements.end(); ++i, ++a)
592   {
593     if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip)
594     {
595       PLY::ElementInstanceList::ParseInstanceListBinary(streamBuffer, buffer, pCur, bufferSize, &(*i), NULL, loader, p_bBE);
596     }
597     else
598     {
599       (*a).alInstances.resize((*i).NumOccur);
600       PLY::ElementInstanceList::ParseInstanceListBinary(streamBuffer, buffer, pCur, bufferSize, &(*i), &(*a), NULL, p_bBE);
601     }
602   }
603 
604   DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceListsBinary() succeeded");
605   return true;
606 }
607 
608 // ------------------------------------------------------------------------------------------------
ParseInstanceBinary(IOStreamBuffer<char> & streamBuffer,DOM * p_pcOut,PLYImporter * loader,bool p_bBE)609 bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, DOM* p_pcOut, PLYImporter* loader, bool p_bBE)
610 {
611   ai_assert(NULL != p_pcOut);
612   ai_assert(NULL != loader);
613 
614   std::vector<char> buffer;
615   streamBuffer.getNextLine(buffer);
616 
617   DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() begin");
618 
619   if (!p_pcOut->ParseHeader(streamBuffer, buffer, true))
620   {
621     DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() failure");
622     return false;
623   }
624 
625   streamBuffer.getNextBlock(buffer);
626   unsigned int bufferSize = static_cast<unsigned int>(buffer.size());
627   const char* pCur = (char*)&buffer[0];
628   if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE))
629   {
630     DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() failure");
631     return false;
632   }
633   DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() succeeded");
634   return true;
635 }
636 
637 // ------------------------------------------------------------------------------------------------
ParseInstance(IOStreamBuffer<char> & streamBuffer,DOM * p_pcOut,PLYImporter * loader)638 bool PLY::DOM::ParseInstance(IOStreamBuffer<char> &streamBuffer, DOM* p_pcOut, PLYImporter* loader)
639 {
640   ai_assert(NULL != p_pcOut);
641   ai_assert(NULL != loader);
642 
643   std::vector<char> buffer;
644   streamBuffer.getNextLine(buffer);
645 
646   DefaultLogger::get()->debug("PLY::DOM::ParseInstance() begin");
647 
648   if (!p_pcOut->ParseHeader(streamBuffer, buffer, false))
649   {
650     DefaultLogger::get()->debug("PLY::DOM::ParseInstance() failure");
651     return false;
652   }
653 
654   //get next line after header
655   streamBuffer.getNextLine(buffer);
656   if (!p_pcOut->ParseElementInstanceLists(streamBuffer, buffer, loader))
657   {
658     DefaultLogger::get()->debug("PLY::DOM::ParseInstance() failure");
659     return false;
660   }
661   DefaultLogger::get()->debug("PLY::DOM::ParseInstance() succeeded");
662   return true;
663 }
664 
665 // ------------------------------------------------------------------------------------------------
ParseInstanceList(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,const PLY::Element * pcElement,PLY::ElementInstanceList * p_pcOut,PLYImporter * loader)666 bool PLY::ElementInstanceList::ParseInstanceList(
667   IOStreamBuffer<char> &streamBuffer,
668   std::vector<char> &buffer,
669   const PLY::Element* pcElement,
670   PLY::ElementInstanceList* p_pcOut,
671   PLYImporter* loader)
672 {
673   ai_assert(NULL != pcElement);
674 
675   // parse all elements
676   if (EEST_INVALID == pcElement->eSemantic || pcElement->alProperties.empty())
677   {
678     // if the element has an unknown semantic we can skip all lines
679     // However, there could be comments
680     for (unsigned int i = 0; i < pcElement->NumOccur; ++i)
681     {
682       PLY::DOM::SkipComments(buffer);
683       PLY::DOM::SkipLine(buffer);
684       streamBuffer.getNextLine(buffer);
685     }
686   }
687   else
688   {
689     const char* pCur = (const char*)&buffer[0];
690     // be sure to have enough storage
691     for (unsigned int i = 0; i < pcElement->NumOccur; ++i)
692     {
693       if (p_pcOut)
694         PLY::ElementInstance::ParseInstance(pCur, pcElement, &p_pcOut->alInstances[i]);
695       else
696       {
697         ElementInstance elt;
698         PLY::ElementInstance::ParseInstance(pCur, pcElement, &elt);
699 
700         // Create vertex or face
701         if (pcElement->eSemantic == EEST_Vertex)
702         {
703           //call loader instance from here
704           loader->LoadVertex(pcElement, &elt, i);
705         }
706         else if (pcElement->eSemantic == EEST_Face)
707         {
708           //call loader instance from here
709           loader->LoadFace(pcElement, &elt, i);
710         }
711         else if (pcElement->eSemantic == EEST_TriStrip)
712         {
713           //call loader instance from here
714           loader->LoadFace(pcElement, &elt, i);
715         }
716       }
717 
718       streamBuffer.getNextLine(buffer);
719       pCur = (buffer.empty()) ? NULL : (const char*)&buffer[0];
720     }
721   }
722   return true;
723 }
724 
725 // ------------------------------------------------------------------------------------------------
ParseInstanceListBinary(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,const char * & pCur,unsigned int & bufferSize,const PLY::Element * pcElement,PLY::ElementInstanceList * p_pcOut,PLYImporter * loader,bool p_bBE)726 bool PLY::ElementInstanceList::ParseInstanceListBinary(
727   IOStreamBuffer<char> &streamBuffer,
728   std::vector<char> &buffer,
729   const char* &pCur,
730   unsigned int &bufferSize,
731   const PLY::Element* pcElement,
732   PLY::ElementInstanceList* p_pcOut,
733   PLYImporter* loader,
734   bool p_bBE /* = false */)
735 {
736   ai_assert(NULL != pcElement);
737 
738   // we can add special handling code for unknown element semantics since
739   // we can't skip it as a whole block (we don't know its exact size
740   // due to the fact that lists could be contained in the property list
741   // of the unknown element)
742   for (unsigned int i = 0; i < pcElement->NumOccur; ++i)
743   {
744     if (p_pcOut)
745       PLY::ElementInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, pcElement, &p_pcOut->alInstances[i], p_bBE);
746     else
747     {
748       ElementInstance elt;
749       PLY::ElementInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, pcElement, &elt, p_bBE);
750 
751       // Create vertex or face
752       if (pcElement->eSemantic == EEST_Vertex)
753       {
754         //call loader instance from here
755         loader->LoadVertex(pcElement, &elt, i);
756       }
757       else if (pcElement->eSemantic == EEST_Face)
758       {
759         //call loader instance from here
760         loader->LoadFace(pcElement, &elt, i);
761       }
762       else if (pcElement->eSemantic == EEST_TriStrip)
763       {
764         //call loader instance from here
765         loader->LoadFace(pcElement, &elt, i);
766       }
767     }
768   }
769   return true;
770 }
771 
772 // ------------------------------------------------------------------------------------------------
ParseInstance(const char * & pCur,const PLY::Element * pcElement,PLY::ElementInstance * p_pcOut)773 bool PLY::ElementInstance::ParseInstance(const char* &pCur,
774   const PLY::Element* pcElement,
775   PLY::ElementInstance* p_pcOut)
776 {
777   ai_assert(NULL != pcElement);
778   ai_assert(NULL != p_pcOut);
779 
780   // allocate enough storage
781   p_pcOut->alProperties.resize(pcElement->alProperties.size());
782 
783   std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
784   std::vector<PLY::Property>::const_iterator  a = pcElement->alProperties.begin();
785   for (; i != p_pcOut->alProperties.end(); ++i, ++a)
786   {
787     if (!(PLY::PropertyInstance::ParseInstance(pCur, &(*a), &(*i))))
788     {
789       DefaultLogger::get()->warn("Unable to parse property instance. "
790         "Skipping this element instance");
791 
792       PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
793       (*i).avList.push_back(v);
794     }
795   }
796   return true;
797 }
798 
799 // ------------------------------------------------------------------------------------------------
ParseInstanceBinary(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,const char * & pCur,unsigned int & bufferSize,const PLY::Element * pcElement,PLY::ElementInstance * p_pcOut,bool p_bBE)800 bool PLY::ElementInstance::ParseInstanceBinary(
801   IOStreamBuffer<char> &streamBuffer,
802   std::vector<char> &buffer,
803   const char* &pCur,
804   unsigned int &bufferSize,
805   const PLY::Element* pcElement,
806   PLY::ElementInstance* p_pcOut,
807   bool p_bBE /* = false */)
808 {
809   ai_assert(NULL != pcElement);
810   ai_assert(NULL != p_pcOut);
811 
812   // allocate enough storage
813   p_pcOut->alProperties.resize(pcElement->alProperties.size());
814 
815   std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
816   std::vector<PLY::Property>::const_iterator   a = pcElement->alProperties.begin();
817   for (; i != p_pcOut->alProperties.end(); ++i, ++a)
818   {
819     if (!(PLY::PropertyInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, &(*a), &(*i), p_bBE)))
820     {
821       DefaultLogger::get()->warn("Unable to parse binary property instance. "
822         "Skipping this element instance");
823 
824       (*i).avList.push_back(PLY::PropertyInstance::DefaultValue((*a).eType));
825     }
826   }
827   return true;
828 }
829 
830 // ------------------------------------------------------------------------------------------------
ParseInstance(const char * & pCur,const PLY::Property * prop,PLY::PropertyInstance * p_pcOut)831 bool PLY::PropertyInstance::ParseInstance(const char* &pCur,
832   const PLY::Property* prop, PLY::PropertyInstance* p_pcOut)
833 {
834   ai_assert(NULL != prop);
835   ai_assert(NULL != p_pcOut);
836 
837   // skip spaces at the beginning
838   if (!SkipSpaces(&pCur))
839   {
840     return false;
841   }
842 
843   if (prop->bIsList)
844   {
845     // parse the number of elements in the list
846     PLY::PropertyInstance::ValueUnion v;
847     PLY::PropertyInstance::ParseValue(pCur, prop->eFirstType, &v);
848 
849     // convert to unsigned int
850     unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v, prop->eFirstType);
851 
852     // parse all list elements
853     p_pcOut->avList.resize(iNum);
854     for (unsigned int i = 0; i < iNum; ++i)
855     {
856       if (!SkipSpaces(&pCur))
857         return false;
858 
859       PLY::PropertyInstance::ParseValue(pCur, prop->eType, &p_pcOut->avList[i]);
860     }
861   }
862   else
863   {
864     // parse the property
865     PLY::PropertyInstance::ValueUnion v;
866 
867     PLY::PropertyInstance::ParseValue(pCur, prop->eType, &v);
868     p_pcOut->avList.push_back(v);
869   }
870   SkipSpacesAndLineEnd(&pCur);
871   return true;
872 }
873 
874 // ------------------------------------------------------------------------------------------------
ParseInstanceBinary(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,const char * & pCur,unsigned int & bufferSize,const PLY::Property * prop,PLY::PropertyInstance * p_pcOut,bool p_bBE)875 bool PLY::PropertyInstance::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer,
876   const char* &pCur,
877   unsigned int &bufferSize,
878   const PLY::Property* prop,
879   PLY::PropertyInstance* p_pcOut,
880   bool p_bBE)
881 {
882   ai_assert(NULL != prop);
883   ai_assert(NULL != p_pcOut);
884 
885   // parse all elements
886   if (prop->bIsList)
887   {
888     // parse the number of elements in the list
889     PLY::PropertyInstance::ValueUnion v;
890     PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eFirstType, &v, p_bBE);
891 
892     // convert to unsigned int
893     unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v, prop->eFirstType);
894 
895     // parse all list elements
896     p_pcOut->avList.resize(iNum);
897     for (unsigned int i = 0; i < iNum; ++i)
898     {
899       PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eType, &p_pcOut->avList[i], p_bBE);
900     }
901   }
902   else
903   {
904     // parse the property
905     PLY::PropertyInstance::ValueUnion v;
906     PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eType, &v, p_bBE);
907     p_pcOut->avList.push_back(v);
908   }
909   return true;
910 }
911 
912 // ------------------------------------------------------------------------------------------------
DefaultValue(PLY::EDataType eType)913 PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue(PLY::EDataType eType)
914 {
915   PLY::PropertyInstance::ValueUnion out;
916 
917   switch (eType)
918   {
919   case EDT_Float:
920     out.fFloat = 0.f;
921     return out;
922 
923   case EDT_Double:
924     out.fDouble = 0.;
925     return out;
926 
927   default:;
928   };
929   out.iUInt = 0;
930   return out;
931 }
932 
933 // ------------------------------------------------------------------------------------------------
ParseValue(const char * & pCur,PLY::EDataType eType,PLY::PropertyInstance::ValueUnion * out)934 bool PLY::PropertyInstance::ParseValue(const char* &pCur,
935   PLY::EDataType eType,
936   PLY::PropertyInstance::ValueUnion* out)
937 {
938   ai_assert(NULL != pCur);
939   ai_assert(NULL != out);
940 
941   //calc element size
942   bool ret = true;
943   switch (eType)
944   {
945   case EDT_UInt:
946   case EDT_UShort:
947   case EDT_UChar:
948 
949     out->iUInt = (uint32_t)strtoul10(pCur, &pCur);
950     break;
951 
952   case EDT_Int:
953   case EDT_Short:
954   case EDT_Char:
955 
956     out->iInt = (int32_t)strtol10(pCur, &pCur);
957     break;
958 
959   case EDT_Float:
960     // technically this should cast to float, but people tend to use float descriptors for double data
961     // this is the best way to not risk losing precision on import and it doesn't hurt to do this
962     ai_real f;
963     pCur = fast_atoreal_move<ai_real>(pCur, f);
964     out->fFloat = (ai_real)f;
965     break;
966 
967   case EDT_Double:
968     double d;
969     pCur = fast_atoreal_move<double>(pCur, d);
970     out->fDouble = (double)d;
971     break;
972 
973   case EDT_INVALID:
974   default:
975     ret = false;
976     break;
977   }
978 
979   return ret;
980 }
981 
982 // ------------------------------------------------------------------------------------------------
ParseValueBinary(IOStreamBuffer<char> & streamBuffer,std::vector<char> & buffer,const char * & pCur,unsigned int & bufferSize,PLY::EDataType eType,PLY::PropertyInstance::ValueUnion * out,bool p_bBE)983 bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
984   std::vector<char> &buffer,
985   const char* &pCur,
986   unsigned int &bufferSize,
987   PLY::EDataType eType,
988   PLY::PropertyInstance::ValueUnion* out,
989   bool p_bBE)
990 {
991   ai_assert(NULL != out);
992 
993   //calc element size
994   unsigned int lsize = 0;
995   switch (eType)
996   {
997   case EDT_Char:
998   case EDT_UChar:
999     lsize = 1;
1000     break;
1001 
1002   case EDT_UShort:
1003   case EDT_Short:
1004     lsize = 2;
1005     break;
1006 
1007   case EDT_UInt:
1008   case EDT_Int:
1009   case EDT_Float:
1010     lsize = 4;
1011     break;
1012 
1013   case EDT_Double:
1014     lsize = 8;
1015     break;
1016 
1017   case EDT_INVALID:
1018   default:
1019       break;
1020   }
1021 
1022   //read the next file block if needed
1023   if (bufferSize < lsize)
1024   {
1025     std::vector<char> nbuffer;
1026     if (streamBuffer.getNextBlock(nbuffer))
1027     {
1028       //concat buffer contents
1029       buffer = std::vector<char>(buffer.end() - bufferSize, buffer.end());
1030       buffer.insert(buffer.end(), nbuffer.begin(), nbuffer.end());
1031       nbuffer.clear();
1032       bufferSize = static_cast<unsigned int>(buffer.size());
1033       pCur = (char*)&buffer[0];
1034     }
1035     else
1036     {
1037       throw DeadlyImportError("Invalid .ply file: File corrupted");
1038     }
1039   }
1040 
1041   bool ret = true;
1042   switch (eType)
1043   {
1044   case EDT_UInt:
1045     out->iUInt = (uint32_t)*((uint32_t*)pCur);
1046     pCur += 4;
1047 
1048     // Swap endianness
1049     if (p_bBE)ByteSwap::Swap((int32_t*)&out->iUInt);
1050     break;
1051 
1052   case EDT_UShort:
1053   {
1054     uint16_t i = *((uint16_t*)pCur);
1055 
1056     // Swap endianness
1057     if (p_bBE)ByteSwap::Swap(&i);
1058     out->iUInt = (uint32_t)i;
1059     pCur += 2;
1060     break;
1061   }
1062 
1063   case EDT_UChar:
1064   {
1065     out->iUInt = (uint32_t)(*((uint8_t*)pCur));
1066     pCur++;
1067     break;
1068   }
1069 
1070   case EDT_Int:
1071     out->iInt = *((int32_t*)pCur);
1072     pCur += 4;
1073 
1074     // Swap endianness
1075     if (p_bBE)ByteSwap::Swap(&out->iInt);
1076     break;
1077 
1078   case EDT_Short:
1079   {
1080     int16_t i = *((int16_t*)pCur);
1081 
1082     // Swap endianness
1083     if (p_bBE)ByteSwap::Swap(&i);
1084     out->iInt = (int32_t)i;
1085     pCur += 2;
1086     break;
1087   }
1088 
1089   case EDT_Char:
1090     out->iInt = (int32_t)*((int8_t*)pCur);
1091     pCur++;
1092     break;
1093 
1094   case EDT_Float:
1095   {
1096     out->fFloat = *((float*)pCur);
1097 
1098     // Swap endianness
1099     if (p_bBE)ByteSwap::Swap((int32_t*)&out->fFloat);
1100     pCur += 4;
1101     break;
1102   }
1103   case EDT_Double:
1104   {
1105     out->fDouble = *((double*)pCur);
1106 
1107     // Swap endianness
1108     if (p_bBE)ByteSwap::Swap((int64_t*)&out->fDouble);
1109     pCur += 8;
1110     break;
1111   }
1112   default:
1113     ret = false;
1114   }
1115 
1116   bufferSize -= lsize;
1117 
1118   return ret;
1119 }
1120 
1121 #endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER
1122