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> |
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 |
93 | PLYImporter::PLYImporter() |
94 | : mBuffer() |
95 | , pcDOM() |
96 | , mGeneratedMesh(NULL){ |
97 | // empty |
98 | } |
99 | |
100 | // ------------------------------------------------------------------------------------------------ |
101 | // Destructor, private as well |
102 | PLYImporter::~PLYImporter() { |
103 | // empty |
104 | } |
105 | |
106 | // ------------------------------------------------------------------------------------------------ |
107 | // Returns whether the class can handle the format of the given file. |
108 | 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 | // ------------------------------------------------------------------------------------------------ |
124 | const aiImporterDesc* PLYImporter::GetInfo() const |
125 | { |
126 | return &desc; |
127 | } |
128 | |
129 | // ------------------------------------------------------------------------------------------------ |
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. |
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> ; |
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 | |
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] |
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 |
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 |
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 |
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 | |