Coverage Report

Created: 2025-06-22 07:30

/src/assimp/code/AssetLib/3MF/XmlSerializer.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2025, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
#include "XmlSerializer.h"
42
#include "D3MFOpcPackage.h"
43
#include "3MFXmlTags.h"
44
#include "3MFTypes.h"
45
#include <assimp/scene.h>
46
47
#include <utility>
48
49
namespace Assimp {
50
namespace D3MF {
51
52
static constexpr int IdNotSet = -1;
53
54
namespace {
55
56
static constexpr size_t ColRGBA_Len = 9;
57
static constexpr size_t ColRGB_Len = 7;
58
59
// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
60
0
bool validateColorString(const std::string color) {
61
0
    const size_t len = color.size();
62
0
    if (ColRGBA_Len != len && ColRGB_Len != len) {
63
0
        return false;
64
0
    }
65
66
0
    return true;
67
0
}
68
69
0
aiFace ReadTriangle(XmlNode &node, int &texId0, int &texId1, int &texId2) {
70
0
    aiFace face;
71
72
0
    face.mNumIndices = 3;
73
0
    face.mIndices = new unsigned int[face.mNumIndices];
74
0
    face.mIndices[0] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v1).as_string()));
75
0
    face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
76
0
    face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
77
78
0
    texId0 = texId1 = texId2 = IdNotSet;
79
0
    XmlParser::getIntAttribute(node, XmlTag::p1, texId0);
80
0
    XmlParser::getIntAttribute(node, XmlTag::p2, texId1);
81
0
    XmlParser::getIntAttribute(node, XmlTag::p3, texId2);
82
83
0
    return face;
84
0
}
85
86
0
aiVector3D ReadVertex(XmlNode &node) {
87
0
    aiVector3D vertex;
88
0
    vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr);
89
0
    vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr);
90
0
    vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr);
91
92
0
    return vertex;
93
0
}
94
95
0
bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) {
96
0
    pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str());
97
0
    if (!objectAttribute.empty()) {
98
0
        value = objectAttribute.as_string();
99
0
        return true;
100
0
    }
101
102
0
    return false;
103
0
}
104
105
0
bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) {
106
0
    std::string strValue;
107
0
    const bool ret = getNodeAttribute(node, attribute, strValue);
108
0
    if (ret) {
109
0
        value = std::atoi(strValue.c_str());
110
0
        return true;
111
0
    }
112
113
0
    return false;
114
0
}
115
116
0
aiMatrix4x4 parseTransformMatrix(const std::string& matrixStr) {
117
    // split the string
118
0
    std::vector<float> numbers;
119
0
    std::string currentNumber;
120
0
    for (char c : matrixStr) {
121
0
        if (c == ' ') {
122
0
            if (!currentNumber.empty()) {
123
0
                float f = std::stof(currentNumber);
124
0
                numbers.push_back(f);
125
0
                currentNumber.clear();
126
0
            }
127
0
        } else {
128
0
            currentNumber.push_back(c);
129
0
        }
130
0
    }
131
0
    if (!currentNumber.empty()) {
132
0
        const float f = std::stof(currentNumber);
133
0
        numbers.push_back(f);
134
0
    }
135
136
0
    aiMatrix4x4 transformMatrix;
137
0
    transformMatrix.a1 = numbers[0];
138
0
    transformMatrix.b1 = numbers[1];
139
0
    transformMatrix.c1 = numbers[2];
140
0
    transformMatrix.d1 = 0;
141
142
0
    transformMatrix.a2 = numbers[3];
143
0
    transformMatrix.b2 = numbers[4];
144
0
    transformMatrix.c2 = numbers[5];
145
0
    transformMatrix.d2 = 0;
146
147
0
    transformMatrix.a3 = numbers[6];
148
0
    transformMatrix.b3 = numbers[7];
149
0
    transformMatrix.c3 = numbers[8];
150
0
    transformMatrix.d3 = 0;
151
152
0
    transformMatrix.a4 = numbers[9];
153
0
    transformMatrix.b4 = numbers[10];
154
0
    transformMatrix.c4 = numbers[11];
155
0
    transformMatrix.d4 = 1;
156
157
0
    return transformMatrix;
158
0
}
159
160
0
bool parseColor(const std::string &color, aiColor4D &diffuse) {
161
0
    if (color.empty()) {
162
0
        return false;
163
0
    }
164
165
0
    if (!validateColorString(color)) {
166
0
        return false;
167
0
    }
168
169
0
    if ('#' != color[0]) {
170
0
        return false;
171
0
    }
172
173
0
    char r[3] = { color[1], color[2], '\0' };
174
0
    diffuse.r = static_cast<ai_real>(strtol(r, nullptr, 16)) / ai_real(255.0);
175
176
0
    char g[3] = { color[3], color[4], '\0' };
177
0
    diffuse.g = static_cast<ai_real>(strtol(g, nullptr, 16)) / ai_real(255.0);
178
179
0
    char b[3] = { color[5], color[6], '\0' };
180
0
    diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
181
0
    const size_t len = color.size();
182
0
    if (ColRGB_Len == len) {
183
0
        return true;
184
0
    }
185
186
0
    char a[3] = { color[7], color[8], '\0' };
187
0
    diffuse.a = static_cast<ai_real>(strtol(a, nullptr, 16)) / ai_real(255.0);
188
189
0
    return true;
190
0
}
191
192
0
void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
193
0
    const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string();
194
0
    aiColor4D diffuse;
195
0
    if (parseColor(color, diffuse)) {
196
0
        mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
197
0
    }
198
0
}
199
200
} // namespace
201
202
XmlSerializer::XmlSerializer(XmlParser &xmlParser) :
203
0
        mResourcesDictionnary(),
204
0
        mMeshCount(0),
205
0
        mXmlParser(xmlParser) {
206
    // empty
207
0
}
208
209
0
XmlSerializer::~XmlSerializer() {
210
0
    for (auto &it : mResourcesDictionnary) {
211
0
        delete it.second;
212
0
    }
213
0
}
214
215
0
void XmlSerializer::ImportXml(aiScene *scene) {
216
0
    if (nullptr == scene) {
217
0
        return;
218
0
    }
219
220
0
    scene->mRootNode = new aiNode(XmlTag::RootTag);
221
0
    XmlNode node = mXmlParser.getRootNode().child(XmlTag::model);
222
0
    if (node.empty()) {
223
0
        return;
224
0
    }
225
226
0
    XmlNode resNode = node.child(XmlTag::resources);
227
0
    for (auto &currentNode : resNode.children()) {
228
0
        const std::string currentNodeName = currentNode.name();
229
0
        if (currentNodeName == XmlTag::texture_2d) {
230
0
            ReadEmbeddecTexture(currentNode);
231
0
        } else if (currentNodeName == XmlTag::texture_group) {
232
0
            ReadTextureGroup(currentNode);
233
0
        } else if (currentNodeName == XmlTag::object) {
234
0
            ReadObject(currentNode);
235
0
        } else if (currentNodeName == XmlTag::basematerials) {
236
0
            ReadBaseMaterials(currentNode);
237
0
        } else if (currentNodeName == XmlTag::meta) {
238
0
            ReadMetadata(currentNode);
239
0
        } else if (currentNodeName == XmlTag::colorgroup) {
240
0
            ReadColorGroup(currentNode);
241
0
        }
242
0
    }
243
0
    StoreMaterialsInScene(scene);
244
0
    XmlNode buildNode = node.child(XmlTag::build);
245
0
    if (buildNode.empty()) {
246
0
        return;
247
0
    }
248
249
0
    for (auto &currentNode : buildNode.children()) {
250
0
        const std::string currentNodeName = currentNode.name();
251
0
        if (currentNodeName == XmlTag::item) {
252
0
            int objectId = IdNotSet;
253
0
            std::string transformationMatrixStr;
254
0
            aiMatrix4x4 transformationMatrix;
255
0
            getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId);
256
0
            bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr);
257
258
0
            auto it = mResourcesDictionnary.find(objectId);
259
0
            if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
260
0
                Object *obj = static_cast<Object *>(it->second);
261
0
                if (hasTransform) {
262
0
                    transformationMatrix = parseTransformMatrix(transformationMatrixStr);
263
0
                }
264
265
0
                addObjectToNode(scene->mRootNode, obj, transformationMatrix);
266
0
            }
267
0
        }
268
0
    }
269
270
    // import the metadata
271
0
    if (!mMetaData.empty()) {
272
0
        const size_t numMeta = mMetaData.size();
273
0
        scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
274
0
        for (size_t i = 0; i < numMeta; ++i) {
275
0
            aiString val(mMetaData[i].value);
276
0
            scene->mMetaData->Set(static_cast<unsigned int>(i), mMetaData[i].name, val);
277
0
        }
278
0
    }
279
280
    // import the meshes, materials are already stored
281
0
    scene->mNumMeshes = static_cast<unsigned int>(mMeshCount);
282
0
    if (scene->mNumMeshes != 0) {
283
0
        scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
284
0
        for (auto &it : mResourcesDictionnary) {
285
0
            if (it.second->getType() == ResourceType::RT_Object) {
286
0
                Object *obj = static_cast<Object *>(it.second);
287
0
                ai_assert(nullptr != obj);
288
0
                for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) {
289
0
                    scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i];
290
0
                }
291
0
            }
292
0
        }
293
0
    }
294
0
}
295
296
0
void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) {
297
0
    ai_assert(nullptr != obj);
298
299
0
    aiNode *sceneNode = new aiNode(obj->mName);
300
0
    sceneNode->mNumMeshes = static_cast<unsigned int>(obj->mMeshes.size());
301
0
    sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes];
302
0
    std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes);
303
304
0
    sceneNode->mTransformation = nodeTransform;
305
0
    if (nullptr != parent) {
306
0
        parent->addChildren(1, &sceneNode);
307
0
    }
308
309
0
    for (Assimp::D3MF::Component c : obj->mComponents) {
310
0
        auto it = mResourcesDictionnary.find(c.mObjectId);
311
0
        if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
312
0
            addObjectToNode(sceneNode, static_cast<Object *>(it->second), c.mTransformation);
313
0
        }
314
0
    }
315
0
}
316
317
0
void XmlSerializer::ReadObject(XmlNode &node) {
318
0
    int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet;
319
0
    bool hasId = getNodeAttribute(node, XmlTag::id, id);
320
0
    if (!hasId) {
321
0
        return;
322
0
    }
323
324
0
    bool hasPid = getNodeAttribute(node, XmlTag::pid, pid);
325
0
    bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex);
326
327
0
    Object *obj = new Object(id);
328
0
    for (XmlNode &currentNode : node.children()) {
329
0
        const std::string currentName = currentNode.name();
330
0
        if (currentName == D3MF::XmlTag::mesh) {
331
0
            auto mesh = ReadMesh(currentNode);
332
0
            mesh->mName.Set(ai_to_string(id));
333
334
0
            if (hasPid) {
335
0
                auto it = mResourcesDictionnary.find(pid);
336
0
                if (hasPindex && it != mResourcesDictionnary.end()) {
337
0
                    if (it->second->getType() == ResourceType::RT_BaseMaterials) {
338
0
                        BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
339
0
                        mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
340
0
                    } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
341
0
                        Texture2DGroup *group = static_cast<Texture2DGroup *>(it->second);
342
0
                        if (mesh->mTextureCoords[0] == nullptr) {
343
0
                            mesh->mNumUVComponents[0] = 2;
344
0
                            for (unsigned int i = 1; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
345
0
                                mesh->mNumUVComponents[i] = 0;
346
0
                            }
347
348
0
                            const std::string name = ai_to_string(group->mTexId);
349
0
                            for (size_t i = 0; i < mMaterials.size(); ++i) {
350
0
                                if (name == mMaterials[i]->GetName().C_Str()) {
351
0
                                    mesh->mMaterialIndex = static_cast<unsigned int>(i);
352
0
                                }
353
0
                            }
354
355
0
                            mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
356
0
                            for (unsigned int vertex_idx = 0; vertex_idx < mesh->mNumVertices; vertex_idx++) {
357
0
                                mesh->mTextureCoords[0][vertex_idx] =
358
0
                                        aiVector3D(group->mTex2dCoords[pindex].x, group->mTex2dCoords[pindex].y, 0.0f);
359
0
                            }
360
0
                        } else {
361
0
                            for (unsigned int vertex_idx = 0; vertex_idx < mesh->mNumVertices; vertex_idx++) {
362
0
                                if (mesh->mTextureCoords[0][vertex_idx].z < 0) {
363
                                    // use default
364
0
                                    mesh->mTextureCoords[0][vertex_idx] =
365
0
                                            aiVector3D(group->mTex2dCoords[pindex].x, group->mTex2dCoords[pindex].y, 0.0f);
366
0
                                }
367
0
                            }
368
0
                        }
369
0
                    }else if (it->second->getType() == ResourceType::RT_ColorGroup) {
370
0
                        if (mesh->mColors[0] == nullptr) {
371
0
                            mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
372
373
0
                            ColorGroup *group = static_cast<ColorGroup *>(it->second);
374
0
                            for (unsigned int vertex_idx = 0; vertex_idx < mesh->mNumVertices; vertex_idx++) {
375
0
                                mesh->mColors[0][vertex_idx] = group->mColors[pindex];
376
0
                            }
377
0
                        }
378
0
                    }
379
0
                }
380
0
            }
381
382
0
            obj->mMeshes.push_back(mesh);
383
0
            obj->mMeshIndex.push_back(mMeshCount);
384
0
            mMeshCount++;
385
0
        } else if (currentName == D3MF::XmlTag::components) {
386
0
            for (XmlNode &currentSubNode : currentNode.children()) {
387
0
                const std::string subNodeName = currentSubNode.name();
388
0
                if (subNodeName == D3MF::XmlTag::component) {
389
0
                    int objectId = IdNotSet;
390
0
                    std::string componentTransformStr;
391
0
                    aiMatrix4x4 componentTransform;
392
0
                    if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) {
393
0
                        componentTransform = parseTransformMatrix(componentTransformStr);
394
0
                    }
395
396
0
                    if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) {
397
0
                        obj->mComponents.push_back({ objectId, componentTransform });
398
0
                    }
399
0
                }
400
0
            }
401
0
        }
402
0
    }
403
404
0
    mResourcesDictionnary.insert(std::make_pair(id, obj));
405
0
}
406
407
0
aiMesh *XmlSerializer::ReadMesh(XmlNode &node) {
408
0
    if (node.empty()) {
409
0
        return nullptr;
410
0
    }
411
412
0
    aiMesh *mesh = new aiMesh();
413
0
    for (XmlNode &currentNode : node.children()) {
414
0
        const std::string currentName = currentNode.name();
415
0
        if (currentName == XmlTag::vertices) {
416
0
            ImportVertices(currentNode, mesh);
417
0
        } else if (currentName == XmlTag::triangles) {
418
0
            ImportTriangles(currentNode, mesh);
419
0
        }
420
0
    }
421
422
0
    return mesh;
423
0
}
424
425
0
void XmlSerializer::ReadMetadata(XmlNode &node) {
426
0
    pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name);
427
0
    const std::string name = attribute.as_string();
428
0
    const std::string value = node.value();
429
0
    if (name.empty()) {
430
0
        return;
431
0
    }
432
433
0
    MetaEntry entry;
434
0
    entry.name = name;
435
0
    entry.value = value;
436
0
    mMetaData.push_back(entry);
437
0
}
438
439
0
void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) {
440
0
    ai_assert(nullptr != mesh);
441
442
0
    std::vector<aiVector3D> vertices;
443
0
    for (XmlNode &currentNode : node.children()) {
444
0
        const std::string currentName = currentNode.name();
445
0
        if (currentName == XmlTag::vertex) {
446
0
            vertices.push_back(ReadVertex(currentNode));
447
0
        }
448
0
    }
449
450
0
    mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
451
0
    mesh->mVertices = new aiVector3D[mesh->mNumVertices];
452
0
    std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
453
0
}
454
455
0
void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
456
0
    std::vector<aiFace> faces;
457
0
    for (XmlNode &currentNode : node.children()) {
458
0
        const std::string currentName = currentNode.name();
459
0
        if (currentName == XmlTag::triangle) {
460
0
            int pid = IdNotSet;
461
0
            bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
462
463
0
            int pindex[3];
464
0
            aiFace face = ReadTriangle(currentNode, pindex[0], pindex[1], pindex[2]);
465
0
            if (hasPid && (pindex[0] != IdNotSet || pindex[1] != IdNotSet || pindex[2] != IdNotSet)) {
466
0
                auto it = mResourcesDictionnary.find(pid);
467
0
                if (it != mResourcesDictionnary.end()) {
468
0
                    if (it->second->getType() == ResourceType::RT_BaseMaterials) {
469
0
                        BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
470
471
0
                        auto update_material = [&](int idx) {
472
0
                            if (pindex[idx] != IdNotSet) {
473
0
                                mesh->mMaterialIndex = baseMaterials->mMaterialIndex[pindex[idx]];
474
0
                            }
475
0
                        };
476
477
0
                        update_material(0);
478
0
                        update_material(1);
479
0
                        update_material(2);
480
481
0
                    } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
482
                        // Load texture coordinates into mesh, when any
483
0
                        Texture2DGroup *group = static_cast<Texture2DGroup *>(it->second); // fix bug
484
0
                        if (mesh->mTextureCoords[0] == nullptr) {
485
0
                            mesh->mNumUVComponents[0] = 2;
486
0
                            for (unsigned int i = 1; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
487
0
                                mesh->mNumUVComponents[i] = 0;
488
0
                            }
489
490
0
                            const std::string name = ai_to_string(group->mTexId);
491
0
                            for (size_t i = 0; i < mMaterials.size(); ++i) {
492
0
                                if (name == mMaterials[i]->GetName().C_Str()) {
493
0
                                    mesh->mMaterialIndex = static_cast<unsigned int>(i);
494
0
                                }
495
0
                            }
496
0
                            mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
497
0
                            for (unsigned int vertex_index = 0; vertex_index < mesh->mNumVertices; vertex_index++) {
498
0
                                mesh->mTextureCoords[0][vertex_index].z = IdNotSet;//mark not set
499
0
                            }
500
0
                        }
501
502
0
                        auto update_texture = [&](int idx) {
503
0
                            if (pindex[idx] != IdNotSet) {
504
0
                                size_t vertex_index = face.mIndices[idx];
505
0
                                mesh->mTextureCoords[0][vertex_index] =
506
0
                                        aiVector3D(group->mTex2dCoords[pindex[idx]].x, group->mTex2dCoords[pindex[idx]].y, 0.0f);
507
0
                            }
508
0
                        };
509
510
0
                        update_texture(0);
511
0
                        update_texture(1);
512
0
                        update_texture(2);
513
514
0
                    } else if (it->second->getType() == ResourceType::RT_ColorGroup) {
515
                        // Load vertex color into mesh, when any
516
0
                        ColorGroup *group = static_cast<ColorGroup *>(it->second);
517
0
                        if (mesh->mColors[0] == nullptr) {
518
0
                            mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
519
0
                        }
520
521
0
                        auto update_color = [&](int idx) {
522
0
                            if (pindex[idx] != IdNotSet) {
523
0
                                size_t vertex_index = face.mIndices[idx];
524
0
                                mesh->mColors[0][vertex_index] = group->mColors[pindex[idx]];
525
0
                            }
526
0
                        };
527
528
0
                        update_color(0);
529
0
                        update_color(1);
530
0
                        update_color(2);
531
0
                    }
532
0
                }
533
0
            }
534
535
0
            faces.push_back(face);
536
0
        }
537
0
    }
538
539
0
    mesh->mNumFaces = static_cast<unsigned int>(faces.size());
540
0
    mesh->mFaces = new aiFace[mesh->mNumFaces];
541
0
    mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
542
543
0
    std::copy(faces.begin(), faces.end(), mesh->mFaces);
544
0
}
545
546
0
void XmlSerializer::ReadBaseMaterials(XmlNode &node) {
547
0
    int id = IdNotSet;
548
0
    if (getNodeAttribute(node, D3MF::XmlTag::id, id)) {
549
0
        BaseMaterials *baseMaterials = new BaseMaterials(id);
550
551
0
        for (XmlNode &currentNode : node.children()) {
552
0
            const std::string currentName = currentNode.name();
553
0
            if (currentName == XmlTag::basematerials_base) {
554
0
                baseMaterials->mMaterialIndex.push_back(static_cast<unsigned int>(mMaterials.size()));
555
0
                mMaterials.push_back(readMaterialDef(currentNode, id));
556
0
            }
557
0
        }
558
559
0
        mResourcesDictionnary.insert(std::make_pair(id, baseMaterials));
560
0
    }
561
0
}
562
563
0
void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) {
564
0
    if (node.empty()) {
565
0
        return;
566
0
    }
567
568
0
    std::string value;
569
0
    EmbeddedTexture *tex2D = nullptr;
570
0
    if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) {
571
0
        tex2D = new EmbeddedTexture(atoi(value.c_str()));
572
0
    }
573
0
    if (nullptr == tex2D) {
574
0
        return;
575
0
    }
576
577
0
    if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) {
578
0
        tex2D->mPath = value;
579
0
    }
580
0
    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) {
581
0
        tex2D->mContentType = value;
582
0
    }
583
0
    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) {
584
0
        tex2D->mTilestyleU = value;
585
0
    }
586
0
    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) {
587
0
        tex2D->mTilestyleV = value;
588
0
    }
589
0
    mEmbeddedTextures.emplace_back(tex2D);
590
0
    StoreEmbeddedTexture(tex2D);
591
0
}
592
593
0
void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) {
594
0
    aiMaterial *mat = new aiMaterial;
595
0
    aiString s;
596
0
    s.Set(ai_to_string(tex->mId).c_str());
597
0
    mat->AddProperty(&s, AI_MATKEY_NAME);
598
0
    const std::string name = "*" + tex->mPath;
599
0
    s.Set(name);
600
0
    mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
601
602
0
    aiColor3D col;
603
0
    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_DIFFUSE);
604
0
    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_AMBIENT);
605
0
    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_EMISSIVE);
606
0
    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_SPECULAR);
607
0
    mMaterials.emplace_back(mat);
608
0
}
609
610
0
void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) {
611
0
    if (node.empty() || nullptr == tex2DGroup) {
612
0
        return;
613
0
    }
614
615
0
    int id = IdNotSet;
616
0
    if (XmlParser::getIntAttribute(node, "texid", id)) {
617
0
        tex2DGroup->mTexId = id;
618
0
    }
619
620
0
    double value = 0.0;
621
0
    for (XmlNode currentNode : node.children()) {
622
0
        const std::string currentName = currentNode.name();
623
0
        aiVector2D texCoord;
624
0
        if (currentName == XmlTag::texture_2d_coord) {
625
0
            XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value);
626
0
            texCoord.x = (ai_real)value;
627
0
            XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value);
628
0
            texCoord.y = (ai_real)value;
629
0
            tex2DGroup->mTex2dCoords.push_back(texCoord);
630
0
        }
631
0
    }
632
0
}
633
634
0
void XmlSerializer::ReadTextureGroup(XmlNode &node) {
635
0
    if (node.empty()) {
636
0
        return;
637
0
    }
638
639
0
    int id = IdNotSet;
640
0
    if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) {
641
0
        return;
642
0
    }
643
644
0
    Texture2DGroup *group = new Texture2DGroup(id);
645
0
    ReadTextureCoords2D(node, group);
646
0
    mResourcesDictionnary.insert(std::make_pair(id, group));
647
0
}
648
649
0
aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) {
650
0
    aiMaterial *material = new aiMaterial();
651
0
    material->mNumProperties = 0;
652
0
    std::string name;
653
0
    bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name);
654
655
0
    std::string stdMaterialName;
656
0
    const std::string strId(ai_to_string(basematerialsId));
657
0
    stdMaterialName += "id";
658
0
    stdMaterialName += strId;
659
0
    stdMaterialName += "_";
660
0
    if (hasName) {
661
0
        stdMaterialName += name;
662
0
    } else {
663
0
        stdMaterialName += "basemat_";
664
0
        stdMaterialName += ai_to_string(mMaterials.size());
665
0
    }
666
667
0
    aiString assimpMaterialName(stdMaterialName);
668
0
    material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME);
669
670
0
    assignDiffuseColor(node, material);
671
672
0
    return material;
673
0
}
674
675
0
void XmlSerializer::ReadColor(XmlNode &node, ColorGroup *colorGroup) {
676
0
    if (node.empty() || nullptr == colorGroup) {
677
0
        return;
678
0
    }
679
680
0
    for (XmlNode currentNode : node.children()) {
681
0
        const std::string currentName = currentNode.name();
682
0
        if (currentName == XmlTag::color_item) {
683
0
            const char *color = currentNode.attribute(XmlTag::color_vaule).as_string();
684
0
            aiColor4D color_value;
685
0
            if (parseColor(color, color_value)) {
686
0
                colorGroup->mColors.push_back(color_value);
687
0
            }
688
0
        }
689
0
    }
690
0
}
691
692
0
void XmlSerializer::ReadColorGroup(XmlNode &node) {
693
0
    if (node.empty()) {
694
0
        return;
695
0
    }
696
697
0
    int id = IdNotSet;
698
0
    if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) {
699
0
        return;
700
0
    }
701
702
0
    ColorGroup *group = new ColorGroup(id);
703
0
    ReadColor(node, group);
704
0
    mResourcesDictionnary.insert(std::make_pair(id, group));
705
0
}
706
707
0
void XmlSerializer::StoreMaterialsInScene(aiScene *scene) {
708
0
    if (nullptr == scene) {
709
0
        return;
710
0
    }
711
712
0
    scene->mNumMaterials = static_cast<unsigned int>(mMaterials.size());
713
0
    if (scene->mNumMaterials == 0) {
714
0
        return;
715
0
    }
716
717
0
    scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
718
0
    for (size_t i = 0; i < mMaterials.size(); ++i) {
719
0
        scene->mMaterials[i] = mMaterials[i];
720
0
    }
721
0
}
722
723
} // namespace D3MF
724
} // namespace Assimp