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