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