/src/assimp/code/AssetLib/STL/STLLoader.cpp
Line | Count | Source (jump to first uncovered line) |
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 Implementation of the STL importer class */ |
43 | | |
44 | | #ifndef ASSIMP_BUILD_NO_STL_IMPORTER |
45 | | |
46 | | #include "STLLoader.h" |
47 | | #include <assimp/ParsingUtils.h> |
48 | | #include <assimp/fast_atof.h> |
49 | | #include <assimp/importerdesc.h> |
50 | | #include <assimp/scene.h> |
51 | | #include <assimp/DefaultLogger.hpp> |
52 | | #include <assimp/IOSystem.hpp> |
53 | | #include <memory> |
54 | | |
55 | | namespace Assimp { |
56 | | |
57 | | namespace { |
58 | | |
59 | | static constexpr aiImporterDesc desc = { |
60 | | "Stereolithography (STL) Importer", |
61 | | "", |
62 | | "", |
63 | | "", |
64 | | aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour, |
65 | | 0, |
66 | | 0, |
67 | | 0, |
68 | | 0, |
69 | | "stl" |
70 | | }; |
71 | | |
72 | | // A valid binary STL buffer should consist of the following elements, in order: |
73 | | // 1) 80 byte header |
74 | | // 2) 4 byte face count |
75 | | // 3) 50 bytes per face |
76 | 8 | static bool IsBinarySTL(const char *buffer, size_t fileSize) { |
77 | 8 | if (fileSize < 84) { |
78 | 0 | return false; |
79 | 0 | } |
80 | | |
81 | 8 | const char *facecount_pos = buffer + 80; |
82 | 8 | uint32_t faceCount(0); |
83 | 8 | ::memcpy(&faceCount, facecount_pos, sizeof(uint32_t)); |
84 | 8 | const uint32_t expectedBinaryFileSize = faceCount * 50 + 84; |
85 | | |
86 | 8 | return expectedBinaryFileSize == fileSize; |
87 | 8 | } |
88 | | |
89 | | static const size_t BufferSize = 500; |
90 | | static const char UnicodeBoundary = 127; |
91 | | |
92 | | // An ascii STL buffer will begin with "solid NAME", where NAME is optional. |
93 | | // Note: The "solid NAME" check is necessary, but not sufficient, to determine |
94 | | // if the buffer is ASCII; a binary header could also begin with "solid NAME". |
95 | 6 | static bool IsAsciiSTL(const char *buffer, size_t fileSize) { |
96 | 6 | if (IsBinarySTL(buffer, fileSize)) |
97 | 0 | return false; |
98 | | |
99 | 6 | const char *bufferEnd = buffer + fileSize; |
100 | | |
101 | 6 | if (!SkipSpaces(&buffer, bufferEnd)) { |
102 | 1 | return false; |
103 | 1 | } |
104 | | |
105 | 5 | if (buffer + 5 >= bufferEnd) { |
106 | 0 | return false; |
107 | 0 | } |
108 | | |
109 | 5 | bool isASCII(strncmp(buffer, "solid", 5) == 0); |
110 | 5 | if (isASCII) { |
111 | | // A lot of importers are write solid even if the file is binary. So we have to check for ASCII-characters. |
112 | 4 | if (fileSize >= BufferSize) { |
113 | 4 | isASCII = true; |
114 | 2.00k | for (unsigned int i = 0; i < BufferSize; i++) { |
115 | 2.00k | if (buffer[i] > UnicodeBoundary) { |
116 | 0 | isASCII = false; |
117 | 0 | break; |
118 | 0 | } |
119 | 2.00k | } |
120 | 4 | } |
121 | 4 | } |
122 | 5 | return isASCII; |
123 | 5 | } |
124 | | } // namespace |
125 | | |
126 | | // ------------------------------------------------------------------------------------------------ |
127 | | // Constructor to be privately used by Importer |
128 | | STLImporter::STLImporter() : |
129 | | mBuffer(), |
130 | 379 | mFileSize(0), |
131 | 379 | mScene() { |
132 | | // empty |
133 | 379 | } |
134 | | |
135 | | // ------------------------------------------------------------------------------------------------ |
136 | | // Destructor, private as well |
137 | 379 | STLImporter::~STLImporter() = default; |
138 | | |
139 | | // ------------------------------------------------------------------------------------------------ |
140 | | // Returns whether the class can handle the format of the given file. |
141 | 220 | bool STLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { |
142 | 220 | static const char *tokens[] = { "STL", "solid" }; |
143 | 220 | return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); |
144 | 220 | } |
145 | | |
146 | | // ------------------------------------------------------------------------------------------------ |
147 | 401 | const aiImporterDesc *STLImporter::GetInfo() const { |
148 | 401 | return &desc; |
149 | 401 | } |
150 | | |
151 | 3 | void addFacesToMesh(aiMesh *pMesh) { |
152 | 3 | pMesh->mFaces = new aiFace[pMesh->mNumFaces]; |
153 | 3 | for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces; ++i) { |
154 | |
|
155 | 0 | aiFace &face = pMesh->mFaces[i]; |
156 | 0 | face.mIndices = new unsigned int[face.mNumIndices = 3]; |
157 | 0 | for (unsigned int o = 0; o < 3; ++o, ++p) { |
158 | 0 | face.mIndices[o] = p; |
159 | 0 | } |
160 | 0 | } |
161 | 3 | } |
162 | | |
163 | | // ------------------------------------------------------------------------------------------------ |
164 | | // Imports the given file into the given scene structure. |
165 | 2 | void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { |
166 | 2 | std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb")); |
167 | | |
168 | | // Check whether we can read from the file |
169 | 2 | if (file == nullptr) { |
170 | 0 | throw DeadlyImportError("Failed to open STL file ", pFile, "."); |
171 | 0 | } |
172 | | |
173 | 2 | mFileSize = file->FileSize(); |
174 | | |
175 | | // allocate storage and copy the contents of the file to a memory buffer |
176 | | // (terminate it with zero) |
177 | 2 | std::vector<char> buffer2; |
178 | 2 | TextFileToBuffer(file.get(), buffer2); |
179 | | |
180 | 2 | mScene = pScene; |
181 | 2 | mBuffer = &buffer2[0]; |
182 | | |
183 | | // the default vertex color is light gray. |
184 | 2 | mClrColorDefault.r = mClrColorDefault.g = mClrColorDefault.b = mClrColorDefault.a = 0.6f; |
185 | | |
186 | | // allocate a single node |
187 | 2 | mScene->mRootNode = new aiNode(); |
188 | | |
189 | 2 | bool bMatClr = false; |
190 | | |
191 | 2 | if (IsBinarySTL(mBuffer, mFileSize)) { |
192 | 0 | bMatClr = LoadBinaryFile(); |
193 | 2 | } else if (IsAsciiSTL(mBuffer, mFileSize)) { |
194 | 1 | LoadASCIIFile(mScene->mRootNode); |
195 | 1 | } else { |
196 | 1 | throw DeadlyImportError("Failed to determine STL storage representation for ", pFile, "."); |
197 | 1 | } |
198 | | |
199 | | // create a single default material, using a white diffuse color for consistency with |
200 | | // other geometric types (e.g., PLY). |
201 | 1 | aiMaterial *pcMat = new aiMaterial(); |
202 | 1 | aiString s; |
203 | 1 | s.Set(AI_DEFAULT_MATERIAL_NAME); |
204 | 1 | pcMat->AddProperty(&s, AI_MATKEY_NAME); |
205 | | |
206 | 1 | aiColor4D clrDiffuse(ai_real(1.0), ai_real(1.0), ai_real(1.0), ai_real(1.0)); |
207 | 1 | if (bMatClr) { |
208 | 0 | clrDiffuse = mClrColorDefault; |
209 | 0 | } |
210 | 1 | pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); |
211 | 1 | pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_SPECULAR); |
212 | 1 | clrDiffuse = aiColor4D(0.05f, 0.05f, 0.05f, 1.0f); |
213 | 1 | pcMat->AddProperty(&clrDiffuse, 1, AI_MATKEY_COLOR_AMBIENT); |
214 | | |
215 | 1 | mScene->mNumMaterials = 1; |
216 | 1 | mScene->mMaterials = new aiMaterial *[1]; |
217 | 1 | mScene->mMaterials[0] = pcMat; |
218 | | |
219 | 1 | mBuffer = nullptr; |
220 | 1 | } |
221 | | |
222 | | // ------------------------------------------------------------------------------------------------ |
223 | | // Read an ASCII STL file |
224 | 1 | void STLImporter::LoadASCIIFile(aiNode *root) { |
225 | 1 | MeshArray meshes; |
226 | 1 | std::vector<aiNode *> nodes; |
227 | 1 | const char *sz = mBuffer; |
228 | 1 | const char *bufferEnd = mBuffer + mFileSize; |
229 | 1 | std::vector<aiVector3D> positionBuffer; |
230 | 1 | std::vector<aiVector3D> normalBuffer; |
231 | | |
232 | | // try to guess how many vertices we could have |
233 | | // assume we'll need 160 bytes for each face |
234 | 1 | size_t sizeEstimate = std::max(1ull, mFileSize / 160ull) * 3ull; |
235 | 1 | positionBuffer.reserve(sizeEstimate); |
236 | 1 | normalBuffer.reserve(sizeEstimate); |
237 | | |
238 | 4 | while (IsAsciiSTL(sz, static_cast<unsigned int>(bufferEnd - sz))) { |
239 | 3 | std::vector<unsigned int> meshIndices; |
240 | 3 | aiMesh *pMesh = new aiMesh(); |
241 | 3 | pMesh->mMaterialIndex = 0; |
242 | 3 | meshIndices.push_back((unsigned int)meshes.size()); |
243 | 3 | meshes.push_back(pMesh); |
244 | 3 | aiNode *node = new aiNode; |
245 | 3 | node->mParent = root; |
246 | 3 | nodes.push_back(node); |
247 | 3 | SkipSpaces(&sz, bufferEnd); |
248 | 3 | ai_assert(!IsLineEnd(sz)); |
249 | | |
250 | 3 | sz += 5; // skip the "solid" |
251 | 3 | SkipSpaces(&sz, bufferEnd); |
252 | 3 | const char *szMe = sz; |
253 | 52 | while (!IsSpaceOrNewLine(*sz)) { |
254 | 49 | sz++; |
255 | 49 | } |
256 | | |
257 | 3 | size_t temp = (size_t)(sz - szMe); |
258 | | // setup the name of the node |
259 | 3 | if (temp) { |
260 | 3 | if (temp >= AI_MAXLEN) { |
261 | 0 | throw DeadlyImportError("STL: Node name too long"); |
262 | 0 | } |
263 | 3 | std::string name(szMe, temp); |
264 | 3 | node->mName.Set(name.c_str()); |
265 | 3 | pMesh->mName.Set(name.c_str()); |
266 | 3 | } else { |
267 | 0 | mScene->mRootNode->mName.Set("<STL_ASCII>"); |
268 | 0 | } |
269 | | |
270 | 3 | unsigned int faceVertexCounter = 3; |
271 | 90 | for (;;) { |
272 | | // go to the next token |
273 | 90 | if (!SkipSpacesAndLineEnd(&sz, bufferEnd)) { |
274 | | // seems we're finished although there was no end marker |
275 | 1 | ASSIMP_LOG_WARN("STL: unexpected EOF. \'endsolid\' keyword was expected"); |
276 | 1 | break; |
277 | 1 | } |
278 | | // facet normal -0.13 -0.13 -0.98 |
279 | 89 | if (!strncmp(sz, "facet", 5) && IsSpaceOrNewLine(*(sz + 5)) && *(sz + 5) != '\0') { |
280 | |
|
281 | 0 | if (faceVertexCounter != 3) { |
282 | 0 | ASSIMP_LOG_WARN("STL: A new facet begins but the old is not yet complete"); |
283 | 0 | } |
284 | 0 | faceVertexCounter = 0; |
285 | |
|
286 | 0 | sz += 6; |
287 | 0 | SkipSpaces(&sz, bufferEnd); |
288 | 0 | if (strncmp(sz, "normal", 6)) { |
289 | 0 | ASSIMP_LOG_WARN("STL: a facet normal vector was expected but not found"); |
290 | 0 | } else { |
291 | 0 | if (sz[6] == '\0') { |
292 | 0 | throw DeadlyImportError("STL: unexpected EOF while parsing facet"); |
293 | 0 | } |
294 | 0 | aiVector3D vn; |
295 | 0 | sz += 7; |
296 | 0 | SkipSpaces(&sz, bufferEnd); |
297 | 0 | sz = fast_atoreal_move(sz, vn.x); |
298 | 0 | SkipSpaces(&sz, bufferEnd); |
299 | 0 | sz = fast_atoreal_move(sz, vn.y); |
300 | 0 | SkipSpaces(&sz, bufferEnd); |
301 | 0 | sz = fast_atoreal_move(sz, vn.z); |
302 | 0 | normalBuffer.emplace_back(vn); |
303 | 0 | normalBuffer.emplace_back(vn); |
304 | 0 | normalBuffer.emplace_back(vn); |
305 | 0 | } |
306 | 89 | } else if (!strncmp(sz, "vertex", 6) && IsSpaceOrNewLine(*(sz + 6))) { // vertex 1.50000 1.50000 0.00000 |
307 | 0 | if (faceVertexCounter >= 3) { |
308 | 0 | ASSIMP_LOG_ERROR("STL: a facet with more than 3 vertices has been found"); |
309 | 0 | ++sz; |
310 | 0 | } else { |
311 | 0 | if (sz[6] == '\0') { |
312 | 0 | throw DeadlyImportError("STL: unexpected EOF while parsing facet"); |
313 | 0 | } |
314 | 0 | sz += 7; |
315 | 0 | SkipSpaces(&sz, bufferEnd); |
316 | 0 | positionBuffer.emplace_back(); |
317 | 0 | aiVector3D *vn = &positionBuffer.back(); |
318 | 0 | sz = fast_atoreal_move(sz, vn->x); |
319 | 0 | SkipSpaces(&sz, bufferEnd); |
320 | 0 | sz = fast_atoreal_move(sz, vn->y); |
321 | 0 | SkipSpaces(&sz, bufferEnd); |
322 | 0 | sz = fast_atoreal_move(sz, vn->z); |
323 | 0 | faceVertexCounter++; |
324 | 0 | } |
325 | 89 | } else if (!::strncmp(sz, "endsolid", 8)) { |
326 | 19 | do { |
327 | 19 | ++sz; |
328 | 19 | } while (!IsLineEnd(*sz)); |
329 | 2 | SkipSpacesAndLineEnd(&sz, bufferEnd); |
330 | | // finished! |
331 | 2 | break; |
332 | 87 | } else { // else skip the whole identifier |
333 | 512 | do { |
334 | 512 | ++sz; |
335 | 512 | } while (!IsSpaceOrNewLine(*sz)); |
336 | 87 | } |
337 | 89 | } |
338 | | |
339 | 3 | if (positionBuffer.empty()) { |
340 | 3 | pMesh->mNumFaces = 0; |
341 | 3 | ASSIMP_LOG_WARN("STL: mesh is empty or invalid; no data loaded"); |
342 | 3 | } |
343 | 3 | if (positionBuffer.size() % 3 != 0) { |
344 | 0 | pMesh->mNumFaces = 0; |
345 | 0 | throw DeadlyImportError("STL: Invalid number of vertices"); |
346 | 0 | } |
347 | 3 | if (normalBuffer.size() != positionBuffer.size()) { |
348 | 0 | pMesh->mNumFaces = 0; |
349 | 0 | throw DeadlyImportError("Normal buffer size does not match position buffer size"); |
350 | 0 | } |
351 | | |
352 | | // only process position buffer when filled, else exception when accessing with index operator |
353 | | // see line 353: only warning is triggered |
354 | | // see line 373(now): access to empty position buffer with index operator forced exception |
355 | 3 | if (!positionBuffer.empty()) { |
356 | 0 | pMesh->mNumFaces = static_cast<unsigned int>(positionBuffer.size() / 3); |
357 | 0 | pMesh->mNumVertices = static_cast<unsigned int>(positionBuffer.size()); |
358 | 0 | pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; |
359 | 0 | for (size_t i = 0; i < pMesh->mNumVertices; ++i) { |
360 | 0 | pMesh->mVertices[i].x = positionBuffer[i].x; |
361 | 0 | pMesh->mVertices[i].y = positionBuffer[i].y; |
362 | 0 | pMesh->mVertices[i].z = positionBuffer[i].z; |
363 | 0 | } |
364 | 0 | positionBuffer.clear(); |
365 | 0 | } |
366 | | // also only process normalBuffer when filled, else exception when accessing with index operator |
367 | 3 | if (!normalBuffer.empty()) { |
368 | 0 | pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; |
369 | 0 | for (size_t i = 0; i < pMesh->mNumVertices; ++i) { |
370 | 0 | pMesh->mNormals[i].x = normalBuffer[i].x; |
371 | 0 | pMesh->mNormals[i].y = normalBuffer[i].y; |
372 | 0 | pMesh->mNormals[i].z = normalBuffer[i].z; |
373 | 0 | } |
374 | 0 | normalBuffer.clear(); |
375 | 0 | } |
376 | | |
377 | | // now copy faces |
378 | 3 | addFacesToMesh(pMesh); |
379 | | |
380 | | // assign the meshes to the current node |
381 | 3 | pushMeshesToNode(meshIndices, node); |
382 | 3 | } |
383 | | |
384 | | // now add the loaded meshes |
385 | 1 | mScene->mNumMeshes = (unsigned int)meshes.size(); |
386 | 1 | mScene->mMeshes = new aiMesh *[mScene->mNumMeshes]; |
387 | 4 | for (size_t i = 0; i < meshes.size(); i++) { |
388 | 3 | mScene->mMeshes[i] = meshes[i]; |
389 | 3 | } |
390 | | |
391 | 1 | root->mNumChildren = (unsigned int)nodes.size(); |
392 | 1 | root->mChildren = new aiNode *[root->mNumChildren]; |
393 | 4 | for (size_t i = 0; i < nodes.size(); ++i) { |
394 | 3 | root->mChildren[i] = nodes[i]; |
395 | 3 | } |
396 | 1 | } |
397 | | |
398 | | // ------------------------------------------------------------------------------------------------ |
399 | | // Read a binary STL file |
400 | 0 | bool STLImporter::LoadBinaryFile() { |
401 | | // allocate one mesh |
402 | 0 | mScene->mNumMeshes = 1; |
403 | 0 | mScene->mMeshes = new aiMesh *[1]; |
404 | 0 | aiMesh *pMesh = mScene->mMeshes[0] = new aiMesh(); |
405 | 0 | pMesh->mMaterialIndex = 0; |
406 | | |
407 | | // skip the first 80 bytes |
408 | 0 | if (mFileSize < 84) { |
409 | 0 | throw DeadlyImportError("STL: file is too small for the header"); |
410 | 0 | } |
411 | 0 | bool bIsMaterialise = false; |
412 | | |
413 | | // search for an occurrence of "COLOR=" in the header |
414 | 0 | const unsigned char *sz2 = (const unsigned char *)mBuffer; |
415 | 0 | const unsigned char *const szEnd = sz2 + 80; |
416 | 0 | while (sz2 < szEnd) { |
417 | |
|
418 | 0 | if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ && |
419 | 0 | 'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++) { |
420 | | |
421 | | // read the default vertex color for facets |
422 | 0 | bIsMaterialise = true; |
423 | 0 | ASSIMP_LOG_INFO("STL: Taking code path for Materialise files"); |
424 | 0 | const ai_real invByte = (ai_real)1.0 / (ai_real)255.0; |
425 | 0 | mClrColorDefault.r = (*sz2++) * invByte; |
426 | 0 | mClrColorDefault.g = (*sz2++) * invByte; |
427 | 0 | mClrColorDefault.b = (*sz2++) * invByte; |
428 | 0 | mClrColorDefault.a = (*sz2++) * invByte; |
429 | 0 | break; |
430 | 0 | } |
431 | 0 | } |
432 | 0 | const unsigned char *sz = (const unsigned char *)mBuffer + 80; |
433 | | |
434 | | // now read the number of facets |
435 | 0 | mScene->mRootNode->mName.Set("<STL_BINARY>"); |
436 | |
|
437 | 0 | pMesh->mNumFaces = *((uint32_t *)sz); |
438 | 0 | sz += 4; |
439 | |
|
440 | 0 | if (mFileSize < 84ull + pMesh->mNumFaces * 50ull) { |
441 | 0 | throw DeadlyImportError("STL: file is too small to hold all facets"); |
442 | 0 | } |
443 | | |
444 | 0 | if (!pMesh->mNumFaces) { |
445 | 0 | throw DeadlyImportError("STL: file is empty. There are no facets defined"); |
446 | 0 | } |
447 | | |
448 | 0 | pMesh->mNumVertices = pMesh->mNumFaces * 3; |
449 | |
|
450 | 0 | aiVector3D *vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; |
451 | 0 | aiVector3D *vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; |
452 | |
|
453 | 0 | aiVector3f *theVec; |
454 | 0 | aiVector3f theVec3F; |
455 | |
|
456 | 0 | for (unsigned int i = 0; i < pMesh->mNumFaces; ++i) { |
457 | | // NOTE: Blender sometimes writes empty normals ... this is not |
458 | | // our fault ... the RemoveInvalidData helper step should fix that |
459 | | |
460 | | // There's one normal for the face in the STL; use it three times |
461 | | // for vertex normals |
462 | 0 | theVec = (aiVector3f *)sz; |
463 | 0 | ::memcpy(&theVec3F, theVec, sizeof(aiVector3f)); |
464 | 0 | vn->x = theVec3F.x; |
465 | 0 | vn->y = theVec3F.y; |
466 | 0 | vn->z = theVec3F.z; |
467 | 0 | *(vn + 1) = *vn; |
468 | 0 | *(vn + 2) = *vn; |
469 | 0 | ++theVec; |
470 | 0 | vn += 3; |
471 | | |
472 | | // vertex 1 |
473 | 0 | ::memcpy(&theVec3F, theVec, sizeof(aiVector3f)); |
474 | 0 | vp->x = theVec3F.x; |
475 | 0 | vp->y = theVec3F.y; |
476 | 0 | vp->z = theVec3F.z; |
477 | 0 | ++theVec; |
478 | 0 | ++vp; |
479 | | |
480 | | // vertex 2 |
481 | 0 | ::memcpy(&theVec3F, theVec, sizeof(aiVector3f)); |
482 | 0 | vp->x = theVec3F.x; |
483 | 0 | vp->y = theVec3F.y; |
484 | 0 | vp->z = theVec3F.z; |
485 | 0 | ++theVec; |
486 | 0 | ++vp; |
487 | | |
488 | | // vertex 3 |
489 | 0 | ::memcpy(&theVec3F, theVec, sizeof(aiVector3f)); |
490 | 0 | vp->x = theVec3F.x; |
491 | 0 | vp->y = theVec3F.y; |
492 | 0 | vp->z = theVec3F.z; |
493 | 0 | ++theVec; |
494 | 0 | ++vp; |
495 | |
|
496 | 0 | sz = (const unsigned char *)theVec; |
497 | |
|
498 | 0 | uint16_t color = *((uint16_t *)sz); |
499 | 0 | sz += 2; |
500 | |
|
501 | 0 | if (color & (1 << 15)) { |
502 | | // seems we need to take the color |
503 | 0 | if (!pMesh->mColors[0]) { |
504 | 0 | pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; |
505 | 0 | for (unsigned int j = 0; j < pMesh->mNumVertices; ++j) { |
506 | 0 | *pMesh->mColors[0]++ = mClrColorDefault; |
507 | 0 | } |
508 | 0 | pMesh->mColors[0] -= pMesh->mNumVertices; |
509 | |
|
510 | 0 | ASSIMP_LOG_INFO("STL: Mesh has vertex colors"); |
511 | 0 | } |
512 | 0 | aiColor4D *clr = &pMesh->mColors[0][i * 3]; |
513 | 0 | clr->a = 1.0; |
514 | 0 | const ai_real invVal((ai_real)1.0 / (ai_real)31.0); |
515 | 0 | if (bIsMaterialise) // this is reversed |
516 | 0 | { |
517 | 0 | clr->r = (color & 0x1fu) * invVal; |
518 | 0 | clr->g = ((color & (0x1fu << 5)) >> 5u) * invVal; |
519 | 0 | clr->b = ((color & (0x1fu << 10)) >> 10u) * invVal; |
520 | 0 | } else { |
521 | 0 | clr->b = (color & 0x1fu) * invVal; |
522 | 0 | clr->g = ((color & (0x1fu << 5)) >> 5u) * invVal; |
523 | 0 | clr->r = ((color & (0x1fu << 10)) >> 10u) * invVal; |
524 | 0 | } |
525 | | // assign the color to all vertices of the face |
526 | 0 | *(clr + 1) = *clr; |
527 | 0 | *(clr + 2) = *clr; |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | | // now copy faces |
532 | 0 | addFacesToMesh(pMesh); |
533 | |
|
534 | 0 | aiNode *root = mScene->mRootNode; |
535 | | |
536 | | // allocate one node |
537 | 0 | aiNode *node = new aiNode(); |
538 | 0 | node->mParent = root; |
539 | |
|
540 | 0 | root->mNumChildren = 1u; |
541 | 0 | root->mChildren = new aiNode *[root->mNumChildren]; |
542 | 0 | root->mChildren[0] = node; |
543 | | |
544 | | // add all created meshes to the single node |
545 | 0 | node->mNumMeshes = mScene->mNumMeshes; |
546 | 0 | node->mMeshes = new unsigned int[mScene->mNumMeshes]; |
547 | 0 | for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) { |
548 | 0 | node->mMeshes[i] = i; |
549 | 0 | } |
550 | |
|
551 | 0 | if (bIsMaterialise && !pMesh->mColors[0]) { |
552 | | // use the color as diffuse material color |
553 | 0 | return true; |
554 | 0 | } |
555 | 0 | return false; |
556 | 0 | } |
557 | | |
558 | 3 | void STLImporter::pushMeshesToNode(std::vector<unsigned int> &meshIndices, aiNode *node) { |
559 | 3 | ai_assert(nullptr != node); |
560 | 3 | if (meshIndices.empty()) { |
561 | 0 | return; |
562 | 0 | } |
563 | | |
564 | 3 | node->mNumMeshes = static_cast<unsigned int>(meshIndices.size()); |
565 | 3 | node->mMeshes = new unsigned int[meshIndices.size()]; |
566 | 6 | for (size_t i = 0; i < meshIndices.size(); ++i) { |
567 | 3 | node->mMeshes[i] = meshIndices[i]; |
568 | 3 | } |
569 | 3 | meshIndices.clear(); |
570 | 3 | } |
571 | | |
572 | | } // namespace Assimp |
573 | | |
574 | | #endif // !! ASSIMP_BUILD_NO_STL_IMPORTER |