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