1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8All rights reserved.
9
10Redistribution and use of this software in source and binary forms,
11with or without modification, are permitted provided that the following
12conditions are met:
13
14* Redistributions of source code must retain the above
15copyright notice, this list of conditions and the
16following disclaimer.
17
18* Redistributions in binary form must reproduce the above
19copyright notice, this list of conditions and the
20following disclaimer in the documentation and/or other
21materials provided with the distribution.
22
23* Neither the name of the assimp team, nor the names of its
24contributors may be used to endorse or promote products
25derived from this software without specific prior
26written permission of the assimp team.
27
28THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38OF 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
52using namespace Assimp;
53
54// ------------------------------------------------------------------------------------------------
55PLY::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// ------------------------------------------------------------------------------------------------
106PLY::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// ------------------------------------------------------------------------------------------------
238bool 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// ------------------------------------------------------------------------------------------------
306PLY::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// ------------------------------------------------------------------------------------------------
347bool 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// ------------------------------------------------------------------------------------------------
418bool 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
435bool 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
452bool 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
469bool 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
486bool 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// ------------------------------------------------------------------------------------------------
515bool 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// ------------------------------------------------------------------------------------------------
550bool 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// ------------------------------------------------------------------------------------------------
578bool 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// ------------------------------------------------------------------------------------------------
609bool 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// ------------------------------------------------------------------------------------------------
638bool 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// ------------------------------------------------------------------------------------------------
666bool 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// ------------------------------------------------------------------------------------------------
726bool 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// ------------------------------------------------------------------------------------------------
773bool 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// ------------------------------------------------------------------------------------------------
800bool 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// ------------------------------------------------------------------------------------------------
831bool 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// ------------------------------------------------------------------------------------------------
875bool 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// ------------------------------------------------------------------------------------------------
913PLY::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// ------------------------------------------------------------------------------------------------
934bool 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// ------------------------------------------------------------------------------------------------
983bool 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