/src/assimp/code/AssetLib/AMF/AMFImporter_Postprocess.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 AMFImporter_Postprocess.cpp |
43 | | /// \brief Convert built scenegraph and objects to Assimp scenegraph. |
44 | | /// \date 2016 |
45 | | /// \author smal.root@gmail.com |
46 | | |
47 | | #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER |
48 | | |
49 | | #include "AMFImporter.hpp" |
50 | | |
51 | | #include <assimp/SceneCombiner.h> |
52 | | #include <assimp/StandardShapes.h> |
53 | | #include <assimp/StringUtils.h> |
54 | | |
55 | | #include <iterator> |
56 | | |
57 | | namespace Assimp { |
58 | | |
59 | 0 | aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*pY*/, const float /*pZ*/) const { |
60 | 0 | aiColor4D tcol; |
61 | | |
62 | | // Check if stored data are supported. |
63 | 0 | if (!Composition.empty()) { |
64 | 0 | throw DeadlyImportError("IME. GetColor for composition"); |
65 | 0 | } |
66 | | |
67 | 0 | if (Color->Composed) { |
68 | 0 | throw DeadlyImportError("IME. GetColor, composed color"); |
69 | 0 | } |
70 | | |
71 | 0 | tcol = Color->Color; |
72 | | |
73 | | // Check if default color must be used |
74 | 0 | if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) { |
75 | 0 | tcol.r = 0.5f; |
76 | 0 | tcol.g = 0.5f; |
77 | 0 | tcol.b = 0.5f; |
78 | 0 | tcol.a = 1; |
79 | 0 | } |
80 | |
|
81 | 0 | return tcol; |
82 | 0 | } |
83 | | |
84 | | void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &nodeElement, std::vector<aiVector3D> &vertexCoordinateArray, |
85 | 0 | std::vector<AMFColor *> &pVertexColorArray) const { |
86 | 0 | AMFVertices *vn = nullptr; |
87 | 0 | size_t col_idx; |
88 | | |
89 | | // All data stored in "vertices", search for it. |
90 | 0 | for (AMFNodeElementBase *ne_child : nodeElement.Child) { |
91 | 0 | if (ne_child->Type == AMFNodeElementBase::ENET_Vertices) { |
92 | 0 | vn = (AMFVertices*)ne_child; |
93 | 0 | } |
94 | 0 | } |
95 | | |
96 | | // If "vertices" not found then no work for us. |
97 | 0 | if (vn == nullptr) { |
98 | 0 | return; |
99 | 0 | } |
100 | | |
101 | | // all coordinates stored as child and we need to reserve space for future push_back's. |
102 | 0 | vertexCoordinateArray.reserve(vn->Child.size()); |
103 | | |
104 | | // colors count equal vertices count. |
105 | 0 | pVertexColorArray.resize(vn->Child.size()); |
106 | 0 | col_idx = 0; |
107 | | |
108 | | // Inside vertices collect all data and place to arrays |
109 | 0 | for (AMFNodeElementBase *vn_child : vn->Child) { |
110 | | // vertices, colors |
111 | 0 | if (vn_child->Type == AMFNodeElementBase::ENET_Vertex) { |
112 | | // by default clear color for current vertex |
113 | 0 | pVertexColorArray[col_idx] = nullptr; |
114 | |
|
115 | 0 | for (AMFNodeElementBase *vtx : vn_child->Child) { |
116 | 0 | if (vtx->Type == AMFNodeElementBase::ENET_Coordinates) { |
117 | 0 | vertexCoordinateArray.push_back(((AMFCoordinates *)vtx)->Coordinate); |
118 | 0 | continue; |
119 | 0 | } |
120 | | |
121 | 0 | if (vtx->Type == AMFNodeElementBase::ENET_Color) { |
122 | 0 | pVertexColorArray[col_idx] = (AMFColor *)vtx; |
123 | 0 | continue; |
124 | 0 | } |
125 | 0 | } |
126 | |
|
127 | 0 | ++col_idx; |
128 | 0 | } |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | 0 | size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &r, const std::string &g, const std::string &b, const std::string &a) { |
133 | 0 | if (r.empty() && g.empty() && b.empty() && a.empty()) { |
134 | 0 | throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined."); |
135 | 0 | } |
136 | | |
137 | 0 | std::string TextureConverted_ID = r + "_" + g + "_" + b + "_" + a; |
138 | 0 | size_t TextureConverted_Index = 0; |
139 | 0 | for (const SPP_Texture &tex_convd : mTexture_Converted) { |
140 | 0 | if (tex_convd.ID == TextureConverted_ID) { |
141 | 0 | return TextureConverted_Index; |
142 | 0 | } else { |
143 | 0 | ++TextureConverted_Index; |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | | // Converted texture not found, create it. |
148 | 0 | AMFTexture *src_texture[4] { |
149 | 0 | nullptr |
150 | 0 | }; |
151 | 0 | std::vector<AMFTexture *> src_texture_4check; |
152 | 0 | SPP_Texture converted_texture; |
153 | |
|
154 | 0 | { // find all specified source textures |
155 | 0 | AMFNodeElementBase *t_tex = nullptr; |
156 | | |
157 | | // R |
158 | 0 | if (!r.empty()) { |
159 | 0 | if (!Find_NodeElement(r, AMFNodeElementBase::EType::ENET_Texture, &t_tex)) { |
160 | 0 | Throw_ID_NotFound(r); |
161 | 0 | } |
162 | |
|
163 | 0 | src_texture[0] = (AMFTexture *)t_tex; |
164 | 0 | src_texture_4check.push_back((AMFTexture *)t_tex); |
165 | 0 | } else { |
166 | 0 | src_texture[0] = nullptr; |
167 | 0 | } |
168 | | |
169 | | // G |
170 | 0 | if (!g.empty()) { |
171 | 0 | if (!Find_NodeElement(g, AMFNodeElementBase::ENET_Texture, &t_tex)) { |
172 | 0 | Throw_ID_NotFound(g); |
173 | 0 | } |
174 | |
|
175 | 0 | src_texture[1] = (AMFTexture *)t_tex; |
176 | 0 | src_texture_4check.push_back((AMFTexture *)t_tex); |
177 | 0 | } else { |
178 | 0 | src_texture[1] = nullptr; |
179 | 0 | } |
180 | | |
181 | | // B |
182 | 0 | if (!b.empty()) { |
183 | 0 | if (!Find_NodeElement(b, AMFNodeElementBase::ENET_Texture, &t_tex)) { |
184 | 0 | Throw_ID_NotFound(b); |
185 | 0 | } |
186 | |
|
187 | 0 | src_texture[2] = (AMFTexture *)t_tex; |
188 | 0 | src_texture_4check.push_back((AMFTexture *)t_tex); |
189 | 0 | } else { |
190 | 0 | src_texture[2] = nullptr; |
191 | 0 | } |
192 | | |
193 | | // A |
194 | 0 | if (!a.empty()) { |
195 | 0 | if (!Find_NodeElement(a, AMFNodeElementBase::ENET_Texture, &t_tex)) { |
196 | 0 | Throw_ID_NotFound(a); |
197 | 0 | } |
198 | |
|
199 | 0 | src_texture[3] = (AMFTexture *)t_tex; |
200 | 0 | src_texture_4check.push_back((AMFTexture *)t_tex); |
201 | 0 | } else { |
202 | 0 | src_texture[3] = nullptr; |
203 | 0 | } |
204 | 0 | } // END: find all specified source textures |
205 | | |
206 | | // check that all textures has same size |
207 | 0 | if (src_texture_4check.size() > 1) { |
208 | 0 | for (size_t i = 0, i_e = (src_texture_4check.size() - 1); i < i_e; i++) { |
209 | 0 | if ((src_texture_4check[i]->Width != src_texture_4check[i + 1]->Width) || (src_texture_4check[i]->Height != src_texture_4check[i + 1]->Height) || |
210 | 0 | (src_texture_4check[i]->Depth != src_texture_4check[i + 1]->Depth)) { |
211 | 0 | throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. Source texture must has the same size."); |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } // if(src_texture_4check.size() > 1) |
215 | | |
216 | | // set texture attributes |
217 | 0 | converted_texture.Width = src_texture_4check[0]->Width; |
218 | 0 | converted_texture.Height = src_texture_4check[0]->Height; |
219 | 0 | converted_texture.Depth = src_texture_4check[0]->Depth; |
220 | | // if one of source texture is tiled then converted texture is tiled too. |
221 | 0 | converted_texture.Tiled = false; |
222 | 0 | for (uint8_t i = 0; i < src_texture_4check.size(); ++i) { |
223 | 0 | converted_texture.Tiled |= src_texture_4check[i]->Tiled; |
224 | 0 | } |
225 | | |
226 | | // Create format hint. |
227 | 0 | constexpr char templateColor[] = "rgba0000"; |
228 | 0 | memcpy(converted_texture.FormatHint, templateColor, 8); |
229 | 0 | if (!r.empty()) converted_texture.FormatHint[4] = '8'; |
230 | 0 | if (!g.empty()) converted_texture.FormatHint[5] = '8'; |
231 | 0 | if (!b.empty()) converted_texture.FormatHint[6] = '8'; |
232 | 0 | if (!a.empty()) converted_texture.FormatHint[7] = '8'; |
233 | | |
234 | | // Сopy data of textures. |
235 | 0 | size_t tex_size = 0; |
236 | 0 | size_t step = 0; |
237 | 0 | size_t off_g = 0; |
238 | 0 | size_t off_b = 0; |
239 | | |
240 | | // Calculate size of the target array and rule how data will be copied. |
241 | 0 | if (!r.empty() && nullptr != src_texture[0]) { |
242 | 0 | tex_size += src_texture[0]->Data.size(); |
243 | 0 | step++, off_g++, off_b++; |
244 | 0 | } |
245 | 0 | if (!g.empty() && nullptr != src_texture[1]) { |
246 | 0 | tex_size += src_texture[1]->Data.size(); |
247 | 0 | step++, off_b++; |
248 | 0 | } |
249 | 0 | if (!b.empty() && nullptr != src_texture[2]) { |
250 | 0 | tex_size += src_texture[2]->Data.size(); |
251 | 0 | step++; |
252 | 0 | } |
253 | 0 | if (!a.empty() && nullptr != src_texture[3]) { |
254 | 0 | tex_size += src_texture[3]->Data.size(); |
255 | 0 | step++; |
256 | 0 | } |
257 | | |
258 | | // Create target array. |
259 | 0 | converted_texture.Data = new uint8_t[tex_size]; |
260 | | // And copy data |
261 | 0 | auto CopyTextureData = [&](const std::string &pID, const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void { |
262 | 0 | if (!pID.empty()) { |
263 | 0 | for (size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) { |
264 | 0 | AMFTexture *tex = src_texture[pSrcTexNum]; |
265 | 0 | ai_assert(tex); |
266 | 0 | converted_texture.Data[idx_target] = tex->Data.at(idx_src); |
267 | 0 | } |
268 | 0 | } |
269 | 0 | }; // auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void |
270 | |
|
271 | 0 | CopyTextureData(r, 0, step, 0); |
272 | 0 | CopyTextureData(g, off_g, step, 1); |
273 | 0 | CopyTextureData(b, off_b, step, 2); |
274 | 0 | CopyTextureData(a, step - 1, step, 3); |
275 | | |
276 | | // Store new converted texture ID |
277 | 0 | converted_texture.ID = TextureConverted_ID; |
278 | | // Store new converted texture |
279 | 0 | mTexture_Converted.push_back(converted_texture); |
280 | |
|
281 | 0 | return TextureConverted_Index; |
282 | 0 | } |
283 | | |
284 | 0 | void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace> &pInputList, std::list<std::list<SComplexFace>> &pOutputList_Separated) { |
285 | 0 | auto texmap_is_equal = [](const AMFTexMap *pTexMap1, const AMFTexMap *pTexMap2) -> bool { |
286 | 0 | if ((pTexMap1 == nullptr) && (pTexMap2 == nullptr)) return true; |
287 | 0 | if (pTexMap1 == nullptr) return false; |
288 | 0 | if (pTexMap2 == nullptr) return false; |
289 | | |
290 | 0 | if (pTexMap1->TextureID_R != pTexMap2->TextureID_R) return false; |
291 | 0 | if (pTexMap1->TextureID_G != pTexMap2->TextureID_G) return false; |
292 | 0 | if (pTexMap1->TextureID_B != pTexMap2->TextureID_B) return false; |
293 | 0 | if (pTexMap1->TextureID_A != pTexMap2->TextureID_A) return false; |
294 | | |
295 | 0 | return true; |
296 | 0 | }; |
297 | |
|
298 | 0 | pOutputList_Separated.clear(); |
299 | 0 | if (pInputList.empty()) return; |
300 | | |
301 | 0 | do { |
302 | 0 | SComplexFace face_start = pInputList.front(); |
303 | 0 | std::list<SComplexFace> face_list_cur; |
304 | |
|
305 | 0 | for (std::list<SComplexFace>::iterator it = pInputList.begin(), it_end = pInputList.end(); it != it_end;) { |
306 | 0 | if (texmap_is_equal(face_start.TexMap, it->TexMap)) { |
307 | 0 | auto it_old = it; |
308 | |
|
309 | 0 | ++it; |
310 | 0 | face_list_cur.push_back(*it_old); |
311 | 0 | pInputList.erase(it_old); |
312 | 0 | } else { |
313 | 0 | ++it; |
314 | 0 | } |
315 | 0 | } |
316 | |
|
317 | 0 | if (!face_list_cur.empty()) pOutputList_Separated.push_back(face_list_cur); |
318 | |
|
319 | 0 | } while (!pInputList.empty()); |
320 | 0 | } |
321 | | |
322 | 0 | void AMFImporter::Postprocess_AddMetadata(const AMFMetaDataArray &metadataList, aiNode &sceneNode) const { |
323 | 0 | if (metadataList.empty()) { |
324 | 0 | return; |
325 | 0 | } |
326 | | |
327 | 0 | if (sceneNode.mMetaData != nullptr) { |
328 | 0 | throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); |
329 | 0 | } |
330 | | |
331 | | // copy collected metadata to output node. |
332 | 0 | sceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metadataList.size())); |
333 | 0 | size_t meta_idx(0); |
334 | |
|
335 | 0 | for (const AMFMetadata *metadata : metadataList) { |
336 | 0 | sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata->MetaType, aiString(metadata->Value)); |
337 | 0 | } |
338 | 0 | } |
339 | | |
340 | 0 | void AMFImporter::Postprocess_BuildNodeAndObject(const AMFObject &pNodeElement, MeshArray &meshList, aiNode **pSceneNode) { |
341 | 0 | AMFColor *object_color = nullptr; |
342 | | |
343 | | // create new aiNode and set name as <object> has. |
344 | 0 | *pSceneNode = new aiNode; |
345 | 0 | (*pSceneNode)->mName = pNodeElement.ID; |
346 | | // read mesh and color |
347 | 0 | for (const AMFNodeElementBase *ne_child : pNodeElement.Child) { |
348 | 0 | std::vector<aiVector3D> vertex_arr; |
349 | 0 | std::vector<AMFColor *> color_arr; |
350 | | |
351 | | // color for object |
352 | 0 | if (ne_child->Type == AMFNodeElementBase::ENET_Color) { |
353 | 0 | object_color = (AMFColor *) ne_child; |
354 | 0 | } |
355 | |
|
356 | 0 | if (ne_child->Type == AMFNodeElementBase::ENET_Mesh) { |
357 | | // Create arrays from children of mesh: vertices. |
358 | 0 | PostprocessHelper_CreateMeshDataArray(*((AMFMesh *)ne_child), vertex_arr, color_arr); |
359 | | // Use this arrays as a source when creating every aiMesh |
360 | 0 | Postprocess_BuildMeshSet(*((AMFMesh *)ne_child), vertex_arr, color_arr, object_color, meshList, **pSceneNode); |
361 | 0 | } |
362 | 0 | } // for(const CAMFImporter_NodeElement* ne_child: pNodeElement) |
363 | 0 | } |
364 | | |
365 | | void AMFImporter::Postprocess_BuildMeshSet(const AMFMesh &pNodeElement, const std::vector<aiVector3D> &pVertexCoordinateArray, |
366 | 0 | const std::vector<AMFColor *> &pVertexColorArray, const AMFColor *pObjectColor, MeshArray &pMeshList, aiNode &pSceneNode) { |
367 | 0 | std::list<unsigned int> mesh_idx; |
368 | | |
369 | | // all data stored in "volume", search for it. |
370 | 0 | for (const AMFNodeElementBase *ne_child : pNodeElement.Child) { |
371 | 0 | const AMFColor *ne_volume_color = nullptr; |
372 | 0 | const SPP_Material *cur_mat = nullptr; |
373 | |
|
374 | 0 | if (ne_child->Type == AMFNodeElementBase::ENET_Volume) { |
375 | | /******************* Get faces *******************/ |
376 | 0 | const AMFVolume *ne_volume = reinterpret_cast<const AMFVolume *>(ne_child); |
377 | |
|
378 | 0 | std::list<SComplexFace> complex_faces_list; // List of the faces of the volume. |
379 | 0 | std::list<std::list<SComplexFace>> complex_faces_toplist; // List of the face list for every mesh. |
380 | | |
381 | | // check if volume use material |
382 | 0 | if (!ne_volume->MaterialID.empty()) { |
383 | 0 | if (!Find_ConvertedMaterial(ne_volume->MaterialID, &cur_mat)) { |
384 | 0 | Throw_ID_NotFound(ne_volume->MaterialID); |
385 | 0 | } |
386 | 0 | } |
387 | | |
388 | | // inside "volume" collect all data and place to arrays or create new objects |
389 | 0 | for (const AMFNodeElementBase *ne_volume_child : ne_volume->Child) { |
390 | | // color for volume |
391 | 0 | if (ne_volume_child->Type == AMFNodeElementBase::ENET_Color) { |
392 | 0 | ne_volume_color = reinterpret_cast<const AMFColor *>(ne_volume_child); |
393 | 0 | } else if (ne_volume_child->Type == AMFNodeElementBase::ENET_Triangle) // triangles, triangles colors |
394 | 0 | { |
395 | 0 | const AMFTriangle &tri_al = *reinterpret_cast<const AMFTriangle *>(ne_volume_child); |
396 | |
|
397 | 0 | SComplexFace complex_face; |
398 | | |
399 | | // initialize pointers |
400 | 0 | complex_face.Color = nullptr; |
401 | 0 | complex_face.TexMap = nullptr; |
402 | | // get data from triangle children: color, texture coordinates. |
403 | 0 | if (tri_al.Child.size()) { |
404 | 0 | for (const AMFNodeElementBase *ne_triangle_child : tri_al.Child) { |
405 | 0 | if (ne_triangle_child->Type == AMFNodeElementBase::ENET_Color) |
406 | 0 | complex_face.Color = reinterpret_cast<const AMFColor *>(ne_triangle_child); |
407 | 0 | else if (ne_triangle_child->Type == AMFNodeElementBase::ENET_TexMap) |
408 | 0 | complex_face.TexMap = reinterpret_cast<const AMFTexMap *>(ne_triangle_child); |
409 | 0 | } |
410 | 0 | } // if(tri_al.Child.size()) |
411 | | |
412 | | // create new face and store it. |
413 | 0 | complex_face.Face.mNumIndices = 3; |
414 | 0 | complex_face.Face.mIndices = new unsigned int[3]; |
415 | 0 | complex_face.Face.mIndices[0] = static_cast<unsigned int>(tri_al.V[0]); |
416 | 0 | complex_face.Face.mIndices[1] = static_cast<unsigned int>(tri_al.V[1]); |
417 | 0 | complex_face.Face.mIndices[2] = static_cast<unsigned int>(tri_al.V[2]); |
418 | 0 | complex_faces_list.push_back(complex_face); |
419 | 0 | } |
420 | 0 | } // for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child) |
421 | | |
422 | | /**** Split faces list: one list per mesh ****/ |
423 | 0 | PostprocessHelper_SplitFacesByTextureID(complex_faces_list, complex_faces_toplist); |
424 | | |
425 | | /***** Create mesh for every faces list ******/ |
426 | 0 | for (std::list<SComplexFace> &face_list_cur : complex_faces_toplist) { |
427 | 0 | auto VertexIndex_GetMinimal = [](const std::list<SComplexFace> &pFaceList, const size_t *pBiggerThan) -> size_t { |
428 | 0 | size_t rv = 0; |
429 | |
|
430 | 0 | if (pBiggerThan != nullptr) { |
431 | 0 | bool found = false; |
432 | 0 | const size_t biggerThan = *pBiggerThan; |
433 | 0 | for (const SComplexFace &face : pFaceList) { |
434 | 0 | for (size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++) { |
435 | 0 | if (face.Face.mIndices[idx_vert] > biggerThan) { |
436 | 0 | rv = face.Face.mIndices[idx_vert]; |
437 | 0 | found = true; |
438 | 0 | break; |
439 | 0 | } |
440 | 0 | } |
441 | |
|
442 | 0 | if (found) { |
443 | 0 | break; |
444 | 0 | } |
445 | 0 | } |
446 | |
|
447 | 0 | if (!found) { |
448 | 0 | return *pBiggerThan; |
449 | 0 | } |
450 | 0 | } else { |
451 | 0 | rv = pFaceList.front().Face.mIndices[0]; |
452 | 0 | } // if(pBiggerThan != nullptr) else |
453 | | |
454 | 0 | for (const SComplexFace &face : pFaceList) { |
455 | 0 | for (size_t vi = 0; vi < face.Face.mNumIndices; vi++) { |
456 | 0 | if (face.Face.mIndices[vi] < rv) { |
457 | 0 | if (pBiggerThan != nullptr) { |
458 | 0 | if (face.Face.mIndices[vi] > *pBiggerThan) rv = face.Face.mIndices[vi]; |
459 | 0 | } else { |
460 | 0 | rv = face.Face.mIndices[vi]; |
461 | 0 | } |
462 | 0 | } |
463 | 0 | } |
464 | 0 | } // for(const SComplexFace& face: pFaceList) |
465 | |
|
466 | 0 | return rv; |
467 | 0 | }; // auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t |
468 | |
|
469 | 0 | auto VertexIndex_Replace = [](std::list<SComplexFace> &pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void { |
470 | 0 | for (const SComplexFace &face : pFaceList) { |
471 | 0 | for (size_t vi = 0; vi < face.Face.mNumIndices; vi++) { |
472 | 0 | if (face.Face.mIndices[vi] == pIdx_From) face.Face.mIndices[vi] = static_cast<unsigned int>(pIdx_To); |
473 | 0 | } |
474 | 0 | } |
475 | 0 | }; // auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void |
476 | |
|
477 | 0 | auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D { |
478 | | // Color priorities(In descending order): |
479 | | // 1. triangle color; |
480 | | // 2. vertex color; |
481 | | // 3. volume color; |
482 | | // 4. object color; |
483 | | // 5. material; |
484 | | // 6. default - invisible coat. |
485 | | // |
486 | | // Fill vertices colors in color priority list above that's points from 1 to 6. |
487 | 0 | if ((pIdx < pVertexColorArray.size()) && (pVertexColorArray[pIdx] != nullptr)) // check for vertex color |
488 | 0 | { |
489 | 0 | if (pVertexColorArray[pIdx]->Composed) |
490 | 0 | throw DeadlyImportError("IME: vertex color composed"); |
491 | 0 | else |
492 | 0 | return pVertexColorArray[pIdx]->Color; |
493 | 0 | } else if (ne_volume_color != nullptr) // check for volume color |
494 | 0 | { |
495 | 0 | if (ne_volume_color->Composed) |
496 | 0 | throw DeadlyImportError("IME: volume color composed"); |
497 | 0 | else |
498 | 0 | return ne_volume_color->Color; |
499 | 0 | } else if (pObjectColor != nullptr) // check for object color |
500 | 0 | { |
501 | 0 | if (pObjectColor->Composed) |
502 | 0 | throw DeadlyImportError("IME: object color composed"); |
503 | 0 | else |
504 | 0 | return pObjectColor->Color; |
505 | 0 | } else if (cur_mat != nullptr) // check for material |
506 | 0 | { |
507 | 0 | return cur_mat->GetColor(pVertexCoordinateArray.at(pIdx).x, pVertexCoordinateArray.at(pIdx).y, pVertexCoordinateArray.at(pIdx).z); |
508 | 0 | } else // set default color. |
509 | 0 | { |
510 | 0 | return { 0, 0, 0, 0 }; |
511 | 0 | } // if((vi < pVertexColorArray.size()) && (pVertexColorArray[vi] != nullptr)) else |
512 | 0 | }; // auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D |
513 | |
|
514 | 0 | aiMesh *tmesh = new aiMesh; |
515 | |
|
516 | 0 | tmesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // Only triangles is supported by AMF. |
517 | | // |
518 | | // set geometry and colors (vertices) |
519 | | // |
520 | | // copy faces/triangles |
521 | 0 | tmesh->mNumFaces = static_cast<unsigned int>(face_list_cur.size()); |
522 | 0 | tmesh->mFaces = new aiFace[tmesh->mNumFaces]; |
523 | | |
524 | | // Create vertices list and optimize indices. Optimization mean following.In AMF all volumes use one big list of vertices. And one volume |
525 | | // can use only part of vertices list, for example: vertices list contain few thousands of vertices and volume use vertices 1, 3, 10. |
526 | | // Do you need all this thousands of garbage? Of course no. So, optimization step transform sparse indices set to continuous. |
527 | 0 | size_t VertexCount_Max = tmesh->mNumFaces * 3; // 3 - triangles. |
528 | 0 | std::vector<aiVector3D> vert_arr, texcoord_arr; |
529 | 0 | std::vector<aiColor4D> col_arr; |
530 | |
|
531 | 0 | vert_arr.reserve(VertexCount_Max * 2); // "* 2" - see below TODO. |
532 | 0 | col_arr.reserve(VertexCount_Max * 2); |
533 | |
|
534 | 0 | { // fill arrays |
535 | 0 | size_t vert_idx_from, vert_idx_to; |
536 | | |
537 | | // first iteration. |
538 | 0 | vert_idx_to = 0; |
539 | 0 | vert_idx_from = VertexIndex_GetMinimal(face_list_cur, nullptr); |
540 | 0 | vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from)); |
541 | 0 | col_arr.push_back(Vertex_CalculateColor(vert_idx_from)); |
542 | 0 | if (vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to); |
543 | | |
544 | | // rest iterations |
545 | 0 | do { |
546 | 0 | vert_idx_from = VertexIndex_GetMinimal(face_list_cur, &vert_idx_to); |
547 | 0 | if (vert_idx_from == vert_idx_to) break; // all indices are transferred, |
548 | | |
549 | 0 | vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from)); |
550 | 0 | col_arr.push_back(Vertex_CalculateColor(vert_idx_from)); |
551 | 0 | vert_idx_to++; |
552 | 0 | if (vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to); |
553 | |
|
554 | 0 | } while (true); |
555 | 0 | } // fill arrays. END. |
556 | | |
557 | | // |
558 | | // check if triangle colors are used and create additional faces if needed. |
559 | | // |
560 | 0 | for (const SComplexFace &face_cur : face_list_cur) { |
561 | 0 | if (face_cur.Color != nullptr) { |
562 | 0 | aiColor4D face_color; |
563 | 0 | size_t vert_idx_new = vert_arr.size(); |
564 | |
|
565 | 0 | if (face_cur.Color->Composed) |
566 | 0 | throw DeadlyImportError("IME: face color composed"); |
567 | 0 | else |
568 | 0 | face_color = face_cur.Color->Color; |
569 | | |
570 | 0 | for (size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) { |
571 | 0 | vert_arr.push_back(vert_arr.at(face_cur.Face.mIndices[idx_ind])); |
572 | 0 | col_arr.push_back(face_color); |
573 | 0 | face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(vert_idx_new++); |
574 | 0 | } |
575 | 0 | } // if(face_cur.Color != nullptr) |
576 | 0 | } // for(const SComplexFace& face_cur: face_list_cur) |
577 | | |
578 | | // |
579 | | // if texture is used then copy texture coordinates too. |
580 | | // |
581 | 0 | if (face_list_cur.front().TexMap != nullptr) { |
582 | 0 | size_t idx_vert_new = vert_arr.size(); |
583 | | ///TODO: clean unused vertices. "* 2": in certain cases - mesh full of triangle colors - vert_arr will contain duplicated vertices for |
584 | | /// colored triangles and initial vertices (for colored vertices) which in real became unused. This part need more thinking about |
585 | | /// optimization. |
586 | 0 | bool *idx_vert_used; |
587 | |
|
588 | 0 | idx_vert_used = new bool[VertexCount_Max * 2]; |
589 | 0 | for (size_t i = 0, i_e = VertexCount_Max * 2; i < i_e; i++) |
590 | 0 | idx_vert_used[i] = false; |
591 | | |
592 | | // This ID's will be used when set materials ID in scene. |
593 | 0 | tmesh->mMaterialIndex = static_cast<unsigned int>(PostprocessHelper_GetTextureID_Or_Create(face_list_cur.front().TexMap->TextureID_R, |
594 | 0 | face_list_cur.front().TexMap->TextureID_G, |
595 | 0 | face_list_cur.front().TexMap->TextureID_B, |
596 | 0 | face_list_cur.front().TexMap->TextureID_A)); |
597 | 0 | texcoord_arr.resize(VertexCount_Max * 2); |
598 | 0 | for (const SComplexFace &face_cur : face_list_cur) { |
599 | 0 | for (size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) { |
600 | 0 | const size_t idx_vert = face_cur.Face.mIndices[idx_ind]; |
601 | |
|
602 | 0 | if (!idx_vert_used[idx_vert]) { |
603 | 0 | texcoord_arr.at(idx_vert) = face_cur.TexMap->TextureCoordinate[idx_ind]; |
604 | 0 | idx_vert_used[idx_vert] = true; |
605 | 0 | } else if (texcoord_arr.at(idx_vert) != face_cur.TexMap->TextureCoordinate[idx_ind]) { |
606 | | // in that case one vertex is shared with many texture coordinates. We need to duplicate vertex with another texture |
607 | | // coordinates. |
608 | 0 | vert_arr.push_back(vert_arr.at(idx_vert)); |
609 | 0 | col_arr.push_back(col_arr.at(idx_vert)); |
610 | 0 | texcoord_arr.at(idx_vert_new) = face_cur.TexMap->TextureCoordinate[idx_ind]; |
611 | 0 | face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(idx_vert_new++); |
612 | 0 | } |
613 | 0 | } // for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) |
614 | 0 | } // for(const SComplexFace& face_cur: face_list_cur) |
615 | |
|
616 | 0 | delete[] idx_vert_used; |
617 | | // shrink array |
618 | 0 | texcoord_arr.resize(idx_vert_new); |
619 | 0 | } // if(face_list_cur.front().TexMap != nullptr) |
620 | | |
621 | | // |
622 | | // copy collected data to mesh |
623 | | // |
624 | 0 | tmesh->mNumVertices = static_cast<unsigned int>(vert_arr.size()); |
625 | 0 | tmesh->mVertices = new aiVector3D[tmesh->mNumVertices]; |
626 | 0 | tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices]; |
627 | |
|
628 | 0 | memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D)); |
629 | 0 | memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D)); |
630 | 0 | if (texcoord_arr.size() > 0) { |
631 | 0 | tmesh->mTextureCoords[0] = new aiVector3D[tmesh->mNumVertices]; |
632 | 0 | memcpy(tmesh->mTextureCoords[0], texcoord_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D)); |
633 | 0 | tmesh->mNumUVComponents[0] = 2; // U and V stored in "x", "y" of aiVector3D. |
634 | 0 | } |
635 | |
|
636 | 0 | size_t idx_face = 0; |
637 | 0 | for (const SComplexFace &face_cur : face_list_cur) |
638 | 0 | tmesh->mFaces[idx_face++] = face_cur.Face; |
639 | | |
640 | | // store new aiMesh |
641 | 0 | mesh_idx.push_back(static_cast<unsigned int>(pMeshList.size())); |
642 | 0 | pMeshList.push_back(tmesh); |
643 | 0 | } // for(const std::list<SComplexFace>& face_list_cur: complex_faces_toplist) |
644 | 0 | } // if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume) |
645 | 0 | } // for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child) |
646 | | |
647 | | // if meshes was created then assign new indices with current aiNode |
648 | 0 | if (!mesh_idx.empty()) { |
649 | 0 | std::list<unsigned int>::const_iterator mit = mesh_idx.begin(); |
650 | |
|
651 | 0 | pSceneNode.mNumMeshes = static_cast<unsigned int>(mesh_idx.size()); |
652 | 0 | pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; |
653 | 0 | for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) |
654 | 0 | pSceneNode.mMeshes[i] = *mit++; |
655 | 0 | } // if(mesh_idx.size() > 0) |
656 | 0 | } |
657 | | |
658 | 0 | void AMFImporter::Postprocess_BuildMaterial(const AMFMaterial &pMaterial) { |
659 | 0 | SPP_Material new_mat; |
660 | |
|
661 | 0 | new_mat.ID = pMaterial.ID; |
662 | 0 | for (const AMFNodeElementBase *mat_child : pMaterial.Child) { |
663 | 0 | if (mat_child->Type == AMFNodeElementBase::ENET_Color) { |
664 | 0 | new_mat.Color = (AMFColor*)mat_child; |
665 | 0 | } else if (mat_child->Type == AMFNodeElementBase::ENET_Metadata) { |
666 | 0 | new_mat.Metadata.push_back((AMFMetadata *)mat_child); |
667 | 0 | } |
668 | 0 | } // for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child) |
669 | | |
670 | | // place converted material to special list |
671 | 0 | mMaterial_Converted.push_back(new_mat); |
672 | 0 | } |
673 | | |
674 | 0 | void AMFImporter::Postprocess_BuildConstellation(AMFConstellation &pConstellation, NodeArray &nodeArray) const { |
675 | 0 | aiNode *con_node; |
676 | 0 | std::list<aiNode *> ch_node; |
677 | | |
678 | | // We will build next hierarchy: |
679 | | // aiNode as parent (<constellation>) for set of nodes as a children |
680 | | // |- aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid") |
681 | | // ... |
682 | | // \_ aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid") |
683 | 0 | con_node = new aiNode; |
684 | 0 | con_node->mName = pConstellation.ID; |
685 | | // Walk through children and search for instances of another objects, constellations. |
686 | 0 | for (const AMFNodeElementBase *ne : pConstellation.Child) { |
687 | 0 | aiMatrix4x4 tmat; |
688 | 0 | aiNode *t_node; |
689 | 0 | aiNode *found_node; |
690 | |
|
691 | 0 | if (ne->Type == AMFNodeElementBase::ENET_Metadata) continue; |
692 | 0 | if (ne->Type != AMFNodeElementBase::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>."); |
693 | | |
694 | | // create alias for convenience |
695 | 0 | AMFInstance &als = *((AMFInstance *)ne); |
696 | | // find referenced object |
697 | 0 | if (!Find_ConvertedNode(als.ObjectID, nodeArray, &found_node)) Throw_ID_NotFound(als.ObjectID); |
698 | | |
699 | | // create node for applying transformation |
700 | 0 | t_node = new aiNode; |
701 | 0 | t_node->mParent = con_node; |
702 | | // apply transformation |
703 | 0 | aiMatrix4x4::Translation(als.Delta, tmat), t_node->mTransformation *= tmat; |
704 | 0 | aiMatrix4x4::RotationX(als.Rotation.x, tmat), t_node->mTransformation *= tmat; |
705 | 0 | aiMatrix4x4::RotationY(als.Rotation.y, tmat), t_node->mTransformation *= tmat; |
706 | 0 | aiMatrix4x4::RotationZ(als.Rotation.z, tmat), t_node->mTransformation *= tmat; |
707 | | // create array for one child node |
708 | 0 | t_node->mNumChildren = 1; |
709 | 0 | t_node->mChildren = new aiNode *[t_node->mNumChildren]; |
710 | 0 | SceneCombiner::Copy(&t_node->mChildren[0], found_node); |
711 | 0 | t_node->mChildren[0]->mParent = t_node; |
712 | 0 | ch_node.push_back(t_node); |
713 | 0 | } // for(const CAMFImporter_NodeElement* ne: pConstellation.Child) |
714 | | |
715 | | // copy found aiNode's as children |
716 | 0 | if (ch_node.empty()) throw DeadlyImportError("<constellation> must have at least one <instance>."); |
717 | | |
718 | 0 | size_t ch_idx = 0; |
719 | |
|
720 | 0 | con_node->mNumChildren = static_cast<unsigned int>(ch_node.size()); |
721 | 0 | con_node->mChildren = new aiNode *[con_node->mNumChildren]; |
722 | 0 | for (aiNode *node : ch_node) |
723 | 0 | con_node->mChildren[ch_idx++] = node; |
724 | | |
725 | | // and place "root" of <constellation> node to node list |
726 | 0 | nodeArray.push_back(con_node); |
727 | 0 | } |
728 | | |
729 | 0 | void AMFImporter::Postprocess_BuildScene(aiScene *pScene) { |
730 | 0 | NodeArray nodeArray; |
731 | 0 | MeshArray mesh_list; |
732 | 0 | AMFMetaDataArray meta_list; |
733 | | |
734 | | // |
735 | | // Because for AMF "material" is just complex colors mixing so aiMaterial will not be used. |
736 | | // For building aiScene we are must to do few steps: |
737 | | // at first creating root node for aiScene. |
738 | 0 | pScene->mRootNode = new aiNode; |
739 | 0 | pScene->mRootNode->mParent = nullptr; |
740 | 0 | pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED; |
741 | | // search for root(<amf>) element |
742 | 0 | AMFNodeElementBase *root_el = nullptr; |
743 | |
|
744 | 0 | for (AMFNodeElementBase *ne : mNodeElement_List) { |
745 | 0 | if (ne->Type != AMFNodeElementBase::ENET_Root) { |
746 | 0 | continue; |
747 | 0 | } |
748 | | |
749 | 0 | root_el = ne; |
750 | 0 | break; |
751 | 0 | } // for(const CAMFImporter_NodeElement* ne: mNodeElement_List) |
752 | | |
753 | | // Check if root element are found. |
754 | 0 | if (root_el == nullptr) { |
755 | 0 | throw DeadlyImportError("Root(<amf>) element not found."); |
756 | 0 | } |
757 | | |
758 | | // after that walk through children of root and collect data. Five types of nodes can be placed at top level - in <amf>: <object>, <material>, <texture>, |
759 | | // <constellation> and <metadata>. But at first we must read <material> and <texture> because they will be used in <object>. <metadata> can be read |
760 | | // at any moment. |
761 | | // |
762 | | // 1. <material> |
763 | | // 2. <texture> will be converted later when processing triangles list. \sa Postprocess_BuildMeshSet |
764 | 0 | for (const AMFNodeElementBase *root_child : root_el->Child) { |
765 | 0 | if (root_child->Type == AMFNodeElementBase::ENET_Material) { |
766 | 0 | Postprocess_BuildMaterial(*((AMFMaterial *)root_child)); |
767 | 0 | } |
768 | 0 | } |
769 | | |
770 | | // After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>. |
771 | | // |
772 | | // 3. <object> |
773 | 0 | for (const AMFNodeElementBase *root_child : root_el->Child) { |
774 | 0 | if (root_child->Type == AMFNodeElementBase::ENET_Object) { |
775 | 0 | aiNode *tnode = nullptr; |
776 | | |
777 | | // for <object> mesh and node must be built: object ID assigned to aiNode name and will be used in future for <instance> |
778 | 0 | Postprocess_BuildNodeAndObject(*((AMFObject *)root_child), mesh_list, &tnode); |
779 | 0 | if (tnode != nullptr) { |
780 | 0 | nodeArray.push_back(tnode); |
781 | 0 | } |
782 | 0 | } |
783 | 0 | } // for(const CAMFImporter_NodeElement* root_child: root_el->Child) |
784 | | |
785 | | // And finally read rest of nodes. |
786 | | // |
787 | 0 | for (const AMFNodeElementBase *root_child : root_el->Child) { |
788 | | // 4. <constellation> |
789 | 0 | if (root_child->Type == AMFNodeElementBase::ENET_Constellation) { |
790 | | // <object> and <constellation> at top of self abstraction use aiNode. So we can use only aiNode list for creating new aiNode's. |
791 | 0 | Postprocess_BuildConstellation(*((AMFConstellation *)root_child), nodeArray); |
792 | 0 | } |
793 | | |
794 | | // 5, <metadata> |
795 | 0 | if (root_child->Type == AMFNodeElementBase::ENET_Metadata) meta_list.push_back((AMFMetadata *)root_child); |
796 | 0 | } // for(const CAMFImporter_NodeElement* root_child: root_el->Child) |
797 | | |
798 | | // at now we can add collected metadata to root node |
799 | 0 | Postprocess_AddMetadata(meta_list, *pScene->mRootNode); |
800 | | // |
801 | | // Check constellation children |
802 | | // |
803 | | // As said in specification: |
804 | | // "When multiple objects and constellations are defined in a single file, only the top level objects and constellations are available for printing." |
805 | | // What that means? For example: if some object is used in constellation then you must show only constellation but not original object. |
806 | | // And at this step we are checking that relations. |
807 | 0 | nl_clean_loop: |
808 | |
|
809 | 0 | if (nodeArray.size() > 1) { |
810 | | // walk through all nodes |
811 | 0 | for (NodeArray::iterator nl_it = nodeArray.begin(); nl_it != nodeArray.end(); ++nl_it) { |
812 | | // and try to find them in another top nodes. |
813 | 0 | NodeArray::const_iterator next_it = nl_it; |
814 | |
|
815 | 0 | ++next_it; |
816 | 0 | for (; next_it != nodeArray.end(); ++next_it) { |
817 | 0 | if ((*next_it)->FindNode((*nl_it)->mName) != nullptr) { |
818 | | // if current top node(nl_it) found in another top node then erase it from node_list and restart search loop. |
819 | | // FIXME: this leaks memory on test models test8.amf and test9.amf |
820 | 0 | nodeArray.erase(nl_it); |
821 | |
|
822 | 0 | goto nl_clean_loop; |
823 | 0 | } |
824 | 0 | } // for(; next_it != node_list.end(); next_it++) |
825 | 0 | } // for(std::list<aiNode*>::const_iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++) |
826 | 0 | } |
827 | | |
828 | | // |
829 | | // move created objects to aiScene |
830 | | // |
831 | | // |
832 | | // Nodes |
833 | 0 | if (!nodeArray.empty()) { |
834 | 0 | NodeArray::const_iterator nl_it = nodeArray.begin(); |
835 | |
|
836 | 0 | pScene->mRootNode->mNumChildren = static_cast<unsigned int>(nodeArray.size()); |
837 | 0 | pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren]; |
838 | 0 | for (size_t i = 0; i < pScene->mRootNode->mNumChildren; i++) { |
839 | | // Objects and constellation that must be showed placed at top of hierarchy in <amf> node. So all aiNode's in node_list must have |
840 | | // mRootNode only as parent. |
841 | 0 | (*nl_it)->mParent = pScene->mRootNode; |
842 | 0 | pScene->mRootNode->mChildren[i] = *nl_it++; |
843 | 0 | } |
844 | 0 | } // if(node_list.size() > 0) |
845 | | |
846 | | // |
847 | | // Meshes |
848 | 0 | if (!mesh_list.empty()) { |
849 | 0 | MeshArray::const_iterator ml_it = mesh_list.begin(); |
850 | |
|
851 | 0 | pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size()); |
852 | 0 | pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; |
853 | 0 | for (size_t i = 0; i < pScene->mNumMeshes; i++) |
854 | 0 | pScene->mMeshes[i] = *ml_it++; |
855 | 0 | } // if(mesh_list.size() > 0) |
856 | | |
857 | | // |
858 | | // Textures |
859 | 0 | pScene->mNumTextures = static_cast<unsigned int>(mTexture_Converted.size()); |
860 | 0 | if (pScene->mNumTextures > 0) { |
861 | 0 | size_t idx; |
862 | |
|
863 | 0 | idx = 0; |
864 | 0 | pScene->mTextures = new aiTexture *[pScene->mNumTextures]; |
865 | 0 | for (const SPP_Texture &tex_convd : mTexture_Converted) { |
866 | 0 | pScene->mTextures[idx] = new aiTexture; |
867 | 0 | pScene->mTextures[idx]->mWidth = static_cast<unsigned int>(tex_convd.Width); |
868 | 0 | pScene->mTextures[idx]->mHeight = static_cast<unsigned int>(tex_convd.Height); |
869 | 0 | pScene->mTextures[idx]->pcData = (aiTexel *)tex_convd.Data; |
870 | | // texture format description. |
871 | 0 | strncpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint, HINTMAXTEXTURELEN); |
872 | 0 | idx++; |
873 | 0 | } // for(const SPP_Texture& tex_convd: mTexture_Converted) |
874 | | |
875 | | // Create materials for embedded textures. |
876 | 0 | idx = 0; |
877 | 0 | pScene->mNumMaterials = static_cast<unsigned int>(mTexture_Converted.size()); |
878 | 0 | pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; |
879 | 0 | for (const SPP_Texture &tex_convd : mTexture_Converted) { |
880 | 0 | const aiString texture_id(AI_EMBEDDED_TEXNAME_PREFIX + ai_to_string(idx)); |
881 | 0 | const int mode = aiTextureOp_Multiply; |
882 | 0 | const int repeat = tex_convd.Tiled ? 1 : 0; |
883 | |
|
884 | 0 | pScene->mMaterials[idx] = new aiMaterial; |
885 | 0 | pScene->mMaterials[idx]->AddProperty(&texture_id, AI_MATKEY_TEXTURE_DIFFUSE(0)); |
886 | 0 | pScene->mMaterials[idx]->AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); |
887 | 0 | pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); |
888 | 0 | pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); |
889 | 0 | idx++; |
890 | 0 | } |
891 | 0 | } // if(pScene->mNumTextures > 0) |
892 | 0 | } // END: after that walk through children of root and collect data |
893 | | |
894 | | } // namespace Assimp |
895 | | |
896 | | #endif // !ASSIMP_BUILD_NO_AMF_IMPORTER |