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