Coverage Report

Created: 2026-02-05 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/MS3D/MS3DLoader.cpp
Line
Count
Source
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2026, assimp team
7
8
All rights reserved.
9
10
Redistribution and use of this software in source and binary forms,
11
with or without modification, are permitted provided that the following
12
conditions are met:
13
14
* Redistributions of source code must retain the above
15
  copyright notice, this list of conditions and the
16
  following disclaimer.
17
18
* Redistributions in binary form must reproduce the above
19
  copyright notice, this list of conditions and the
20
  following disclaimer in the documentation and/or other
21
  materials provided with the distribution.
22
23
* Neither the name of the assimp team, nor the names of its
24
  contributors may be used to endorse or promote products
25
  derived from this software without specific prior
26
  written permission of the assimp team.
27
28
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
---------------------------------------------------------------------------
40
*/
41
42
/** @file  MS3DLoader.cpp
43
 *  @brief Implementation of the Ms3D importer class.
44
 *  Written against http://chumbalum.swissquake.ch/ms3d/ms3dspec.txt
45
 */
46
47
#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
48
49
#include "MS3DLoader.h"
50
#include <assimp/StreamReader.h>
51
#include <assimp/DefaultLogger.hpp>
52
#include <assimp/scene.h>
53
#include <assimp/IOSystem.hpp>
54
#include <assimp/importerdesc.h>
55
#include <map>
56
57
using namespace Assimp;
58
59
static constexpr aiImporterDesc desc = {
60
    "Milkshape 3D Importer",
61
    "",
62
    "",
63
    "http://chumbalum.swissquake.ch/",
64
    aiImporterFlags_SupportBinaryFlavour,
65
    0,
66
    0,
67
    0,
68
    0,
69
    "ms3d"
70
};
71
72
// ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
73
//   (enable old code path, which generates extra nodes per mesh while
74
//    the newer code uses aiMesh::mName to express the name of the
75
//    meshes (a.k.a. groups in MS3D))
76
77
// ------------------------------------------------------------------------------------------------
78
// Constructor to be privately used by Importer
79
MS3DImporter::MS3DImporter()
80
    : mScene()
81
31.7k
{}
82
83
// ------------------------------------------------------------------------------------------------
84
// Returns whether the class can handle the format of the given file.
85
bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
86
277
{
87
277
    static const char* tokens[] = { "MS3D000000" };
88
277
    return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
89
277
}
90
91
// ------------------------------------------------------------------------------------------------
92
const aiImporterDesc* MS3DImporter::GetInfo () const
93
32.0k
{
94
32.0k
    return &desc;
95
32.0k
}
96
97
// ------------------------------------------------------------------------------------------------
98
void ReadColor(StreamReaderLE& stream, aiColor4D& ambient)
99
9.97k
{
100
    // aiColor4D is packed on gcc, implicit binding to float& fails therefore.
101
9.97k
    stream >> (float&)ambient.r >> (float&)ambient.g >> (float&)ambient.b >> (float&)ambient.a;
102
9.97k
}
103
104
// ------------------------------------------------------------------------------------------------
105
void ReadVector(StreamReaderLE& stream, aiVector3D& pos)
106
319k
{
107
    // See note in ReadColor()
108
319k
    stream >> (float&)pos.x >> (float&)pos.y >> (float&)pos.z;
109
319k
}
110
111
// ------------------------------------------------------------------------------------------------
112
template<typename T>
113
void MS3DImporter :: ReadComments(StreamReaderLE& stream, std::vector<T>& outp)
114
34
{
115
34
    uint16_t cnt;
116
34
    stream >> cnt;
117
118
35
    for(unsigned int i = 0; i < cnt; ++i) {
119
1
        uint32_t index, clength;
120
1
        stream >> index >> clength;
121
122
1
        if(index >= outp.size()) {
123
1
            ASSIMP_LOG_WARN("MS3D: Invalid index in comment section");
124
1
        }
125
0
        else if (clength > stream.GetRemainingSize()) {
126
0
            throw DeadlyImportError("MS3D: Failure reading comment, length field is out of range");
127
0
        }
128
0
        else {
129
0
            outp[index].comment = std::string(reinterpret_cast<char*>(stream.GetPtr()),clength);
130
0
        }
131
1
        stream.IncPtr(clength);
132
1
    }
133
34
}
void Assimp::MS3DImporter::ReadComments<Assimp::MS3DImporter::TempGroup>(Assimp::StreamReader<false, false>&, std::__1::vector<Assimp::MS3DImporter::TempGroup, std::__1::allocator<Assimp::MS3DImporter::TempGroup> >&)
Line
Count
Source
114
12
{
115
12
    uint16_t cnt;
116
12
    stream >> cnt;
117
118
13
    for(unsigned int i = 0; i < cnt; ++i) {
119
1
        uint32_t index, clength;
120
1
        stream >> index >> clength;
121
122
1
        if(index >= outp.size()) {
123
1
            ASSIMP_LOG_WARN("MS3D: Invalid index in comment section");
124
1
        }
125
0
        else if (clength > stream.GetRemainingSize()) {
126
0
            throw DeadlyImportError("MS3D: Failure reading comment, length field is out of range");
127
0
        }
128
0
        else {
129
0
            outp[index].comment = std::string(reinterpret_cast<char*>(stream.GetPtr()),clength);
130
0
        }
131
1
        stream.IncPtr(clength);
132
1
    }
133
12
}
void Assimp::MS3DImporter::ReadComments<Assimp::MS3DImporter::TempMaterial>(Assimp::StreamReader<false, false>&, std::__1::vector<Assimp::MS3DImporter::TempMaterial, std::__1::allocator<Assimp::MS3DImporter::TempMaterial> >&)
Line
Count
Source
114
11
{
115
11
    uint16_t cnt;
116
11
    stream >> cnt;
117
118
11
    for(unsigned int i = 0; i < cnt; ++i) {
119
0
        uint32_t index, clength;
120
0
        stream >> index >> clength;
121
122
0
        if(index >= outp.size()) {
123
0
            ASSIMP_LOG_WARN("MS3D: Invalid index in comment section");
124
0
        }
125
0
        else if (clength > stream.GetRemainingSize()) {
126
0
            throw DeadlyImportError("MS3D: Failure reading comment, length field is out of range");
127
0
        }
128
0
        else {
129
0
            outp[index].comment = std::string(reinterpret_cast<char*>(stream.GetPtr()),clength);
130
0
        }
131
0
        stream.IncPtr(clength);
132
0
    }
133
11
}
void Assimp::MS3DImporter::ReadComments<Assimp::MS3DImporter::TempJoint>(Assimp::StreamReader<false, false>&, std::__1::vector<Assimp::MS3DImporter::TempJoint, std::__1::allocator<Assimp::MS3DImporter::TempJoint> >&)
Line
Count
Source
114
11
{
115
11
    uint16_t cnt;
116
11
    stream >> cnt;
117
118
11
    for(unsigned int i = 0; i < cnt; ++i) {
119
0
        uint32_t index, clength;
120
0
        stream >> index >> clength;
121
122
0
        if(index >= outp.size()) {
123
0
            ASSIMP_LOG_WARN("MS3D: Invalid index in comment section");
124
0
        }
125
0
        else if (clength > stream.GetRemainingSize()) {
126
0
            throw DeadlyImportError("MS3D: Failure reading comment, length field is out of range");
127
0
        }
128
0
        else {
129
0
            outp[index].comment = std::string(reinterpret_cast<char*>(stream.GetPtr()),clength);
130
0
        }
131
0
        stream.IncPtr(clength);
132
0
    }
133
11
}
134
135
// ------------------------------------------------------------------------------------------------
136
template <typename T, typename T2, typename T3> bool inrange(const T& in, const T2& lower, const T3& higher)
137
11
{
138
11
    return in > lower && in <= higher;
139
11
}
140
141
// ------------------------------------------------------------------------------------------------
142
void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints,
143
    std::vector<bool>& hadit,
144
    aiNode* nd,
145
    const aiMatrix4x4& absTrafo)
146
0
{
147
0
    unsigned int cnt = 0;
148
0
    for(size_t i = 0; i < joints.size(); ++i) {
149
0
        if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
150
0
            ++cnt;
151
0
        }
152
0
    }
153
154
0
    nd->mChildren = new aiNode*[nd->mNumChildren = cnt];
155
0
    cnt = 0;
156
0
    for(size_t i = 0; i < joints.size(); ++i) {
157
0
        if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
158
0
            aiNode* ch = nd->mChildren[cnt++] = new aiNode(joints[i].name);
159
0
            ch->mParent = nd;
160
161
0
            ch->mTransformation = aiMatrix4x4::Translation(joints[i].position,aiMatrix4x4()=aiMatrix4x4())*
162
0
                aiMatrix4x4().FromEulerAnglesXYZ(joints[i].rotation);
163
164
0
            const aiMatrix4x4 abs = absTrafo*ch->mTransformation;
165
0
            for(unsigned int a = 0; a < mScene->mNumMeshes; ++a) {
166
0
                aiMesh* const msh = mScene->mMeshes[a];
167
0
                for(unsigned int n = 0; n < msh->mNumBones; ++n) {
168
0
                    aiBone* const bone = msh->mBones[n];
169
170
0
                    if(bone->mName == ch->mName) {
171
0
                        bone->mOffsetMatrix = aiMatrix4x4(abs).Inverse();
172
0
                    }
173
0
                }
174
0
            }
175
176
0
            hadit[i] = true;
177
0
            CollectChildJoints(joints,hadit,ch,abs);
178
0
        }
179
0
    }
180
0
}
181
182
// ------------------------------------------------------------------------------------------------
183
void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints, aiNode* nd)
184
0
{
185
0
     std::vector<bool> hadit(joints.size(),false);
186
0
     aiMatrix4x4 trafo;
187
188
0
     CollectChildJoints(joints,hadit,nd,trafo);
189
0
}
190
191
// ------------------------------------------------------------------------------------------------
192
// Imports the given file into the given scene structure.
193
void MS3DImporter::InternReadFile( const std::string& pFile,
194
    aiScene* pScene, IOSystem* pIOHandler)
195
91
{
196
197
91
    auto file = pIOHandler->Open(pFile, "rb");
198
91
    if (!file)
199
0
        throw DeadlyImportError("MS3D: Could not open ", pFile);
200
201
91
    StreamReaderLE stream(file);
202
203
    // CanRead() should have done this already
204
91
    char head[10];
205
91
    int32_t version;
206
207
91
    mScene = pScene;
208
209
210
    // 1 ------------ read into temporary data structures mirroring the original file
211
212
91
    stream.CopyAndAdvance(head,10);
213
91
    stream >> version;
214
91
    if (strncmp(head,"MS3D000000",10)) {
215
0
        throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: ", pFile);
216
0
    }
217
218
91
    if (version != 4) {
219
0
        throw DeadlyImportError("MS3D: Unsupported file format version, 4 was expected");
220
0
    }
221
222
91
    uint16_t verts;
223
91
    stream >> verts;
224
225
91
    std::vector<TempVertex> vertices(verts);
226
84.9k
    for (unsigned int i = 0; i < verts; ++i) {
227
84.8k
        TempVertex& v = vertices[i];
228
229
84.8k
        stream.IncPtr(1);
230
84.8k
        ReadVector(stream,v.pos);
231
84.8k
        v.bone_id[0] = stream.GetI1();
232
84.8k
        v.ref_cnt = stream.GetI1();
233
234
84.8k
        v.bone_id[1] = v.bone_id[2] = v.bone_id[3] = UINT_MAX;
235
84.8k
        v.weights[1] = v.weights[2] = v.weights[3] = 0.f;
236
84.8k
        v.weights[0] = 1.f;
237
84.8k
    }
238
239
91
    uint16_t tris;
240
91
    stream >> tris;
241
242
91
    std::vector<TempTriangle> triangles(tris);
243
59.9k
    for (unsigned int i = 0;i < tris; ++i) {
244
59.8k
        TempTriangle& t = triangles[i];
245
246
59.8k
        stream.IncPtr(2);
247
239k
        for (unsigned int j = 0; j < 3; ++j) {
248
179k
            t.indices[j] = stream.GetI2();
249
179k
        }
250
251
239k
        for (unsigned int j = 0; j < 3; ++j) {
252
179k
            ReadVector(stream,t.normals[j]);
253
179k
        }
254
255
239k
        for (unsigned int j = 0; j < 3; ++j) {
256
179k
            stream >> (float&)(t.uv[j].x); // see note in ReadColor()
257
179k
        }
258
239k
        for (unsigned int j = 0; j < 3; ++j) {
259
179k
            stream >> (float&)(t.uv[j].y);
260
179k
        }
261
262
59.8k
        t.sg    = stream.GetI1();
263
59.8k
        t.group = stream.GetI1();
264
59.8k
    }
265
266
91
    uint16_t grp;
267
91
    stream >> grp;
268
269
91
    bool need_default = false;
270
91
    std::vector<TempGroup> groups(grp);
271
200
    for (unsigned int i = 0;i < grp; ++i) {
272
109
        TempGroup& t = groups[i];
273
274
109
        stream.IncPtr(1);
275
109
        stream.CopyAndAdvance(t.name,32);
276
277
109
        t.name[32] = '\0';
278
109
        uint16_t num;
279
109
        stream >> num;
280
281
109
        t.triangles.resize(num);
282
553k
        for (unsigned int j = 0; j < num; ++j) {
283
553k
            t.triangles[j] = stream.GetI2();
284
553k
        }
285
109
        t.mat = stream.GetI1();
286
109
        if (t.mat == UINT_MAX) {
287
11
            need_default = true;
288
11
        }
289
109
    }
290
291
91
    uint16_t mat;
292
91
    stream >> mat;
293
294
91
    std::vector<TempMaterial> materials(mat);
295
2.58k
    for (unsigned int j = 0;j < mat; ++j) {
296
2.49k
        TempMaterial& t = materials[j];
297
298
2.49k
        stream.CopyAndAdvance(t.name,32);
299
2.49k
        t.name[32] = '\0';
300
301
2.49k
        ReadColor(stream,t.ambient);
302
2.49k
        ReadColor(stream,t.diffuse);
303
2.49k
        ReadColor(stream,t.specular);
304
2.49k
        ReadColor(stream,t.emissive);
305
2.49k
        stream >> t.shininess  >> t.transparency;
306
307
2.49k
        stream.IncPtr(1);
308
309
2.49k
        stream.CopyAndAdvance(t.texture,128);
310
2.49k
        t.texture[128] = '\0';
311
312
2.49k
        stream.CopyAndAdvance(t.alphamap,128);
313
2.49k
        t.alphamap[128] = '\0';
314
2.49k
    }
315
316
91
    float animfps, currenttime;
317
91
    uint32_t totalframes;
318
91
    stream >> animfps >> currenttime >> totalframes;
319
320
91
    uint16_t joint;
321
91
    stream >> joint;
322
323
91
    std::vector<TempJoint> joints(joint);
324
121
    for(unsigned int ii = 0; ii < joint; ++ii) {
325
30
        TempJoint& j = joints[ii];
326
327
30
        stream.IncPtr(1);
328
30
        stream.CopyAndAdvance(j.name,32);
329
30
        j.name[32] = '\0';
330
331
30
        stream.CopyAndAdvance(j.parentName,32);
332
30
        j.parentName[32] = '\0';
333
334
30
        ReadVector(stream,j.rotation);
335
30
        ReadVector(stream,j.position);
336
337
30
        j.rotFrames.resize(stream.GetI2());
338
30
        j.posFrames.resize(stream.GetI2());
339
340
31.6k
        for(unsigned int a = 0; a < j.rotFrames.size(); ++a) {
341
31.6k
            TempKeyFrame& kf = j.rotFrames[a];
342
31.6k
            stream >> kf.time;
343
31.6k
            ReadVector(stream,kf.value);
344
31.6k
        }
345
23.4k
        for(unsigned int a = 0; a < j.posFrames.size(); ++a) {
346
23.4k
            TempKeyFrame& kf = j.posFrames[a];
347
23.4k
            stream >> kf.time;
348
23.4k
            ReadVector(stream,kf.value);
349
23.4k
        }
350
30
    }
351
352
91
    if(stream.GetRemainingSize() > 4) {
353
29
        uint32_t subversion;
354
29
        stream >> subversion;
355
29
        if (subversion == 1) {
356
12
            ReadComments<TempGroup>(stream,groups);
357
12
            ReadComments<TempMaterial>(stream,materials);
358
12
            ReadComments<TempJoint>(stream,joints);
359
360
            // model comment - print it for we have such a nice log.
361
12
            if (stream.GetI4()) {
362
0
                const size_t len = static_cast<size_t>(stream.GetI4());
363
0
                if (len > stream.GetRemainingSize()) {
364
0
                    throw DeadlyImportError("MS3D: Model comment is too long");
365
0
                }
366
367
0
                const std::string& s = std::string(reinterpret_cast<char*>(stream.GetPtr()),len);
368
0
                ASSIMP_LOG_DEBUG("MS3D: Model comment: ", s);
369
0
            }
370
371
12
            if(stream.GetRemainingSize() > 4 && inrange((stream >> subversion,subversion),1u,3u)) {
372
0
                for(unsigned int i = 0; i < verts; ++i) {
373
0
                    TempVertex& v = vertices[i];
374
0
                    v.weights[3]=1.f;
375
0
                    for(unsigned int n = 0; n < 3; v.weights[3]-=v.weights[n++]) {
376
0
                        v.bone_id[n+1] = stream.GetI1();
377
0
                        v.weights[n] = static_cast<float>(static_cast<unsigned int>(stream.GetI1()))/255.f;
378
0
                    }
379
0
                    stream.IncPtr((subversion-1)<<2u);
380
0
                }
381
382
                // even further extra data is not of interest for us, at least now now.
383
0
            }
384
12
        }
385
29
    }
386
387
    // 2 ------------ convert to proper aiXX data structures -----------------------------------
388
389
91
    if (need_default && materials.size()) {
390
0
        ASSIMP_LOG_WARN("MS3D: Found group with no material assigned, spawning default material");
391
          // if one of the groups has no material assigned, but there are other
392
        // groups with materials, a default material needs to be added (
393
        // scenepreprocessor adds a default material only if nummat==0).
394
0
        materials.emplace_back();
395
0
        TempMaterial& m = materials.back();
396
0
        constexpr char DefaultMat[18] = "<MS3D_DefaultMat>";
397
0
        strncpy(m.name, DefaultMat, sizeof(m.name));
398
0
        m.name[18]='\0';
399
0
        m.diffuse = aiColor4D(0.6f,0.6f,0.6f,1.0);
400
0
        m.transparency = 1.f;
401
0
        m.shininess = 0.f;
402
403
        // this is because these TempXXX struct's have no c'tors.
404
0
        m.texture[0] = m.alphamap[0] = '\0';
405
406
0
        for (unsigned int i = 0; i < groups.size(); ++i) {
407
0
            TempGroup& g = groups[i];
408
0
            if (g.mat == UINT_MAX) {
409
0
                g.mat = static_cast<unsigned int>(materials.size()-1);
410
0
            }
411
0
        }
412
0
    }
413
414
    // convert materials to our generic key-value dict-alike
415
91
    if (materials.size()) {
416
17
        pScene->mMaterials = new aiMaterial*[materials.size()];
417
895
        for (size_t i = 0; i < materials.size(); ++i) {
418
419
878
            aiMaterial* mo = new aiMaterial();
420
878
            pScene->mMaterials[pScene->mNumMaterials++] = mo;
421
422
878
            const TempMaterial& mi = materials[i];
423
424
878
            aiString tmp;
425
878
            if (0[mi.alphamap]) {
426
592
                tmp = aiString(mi.alphamap);
427
592
                mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_OPACITY(0));
428
592
            }
429
878
            if (0[mi.texture]) {
430
608
                tmp = aiString(mi.texture);
431
608
                mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_DIFFUSE(0));
432
608
            }
433
878
            if (0[mi.name]) {
434
632
                tmp = aiString(mi.name);
435
632
                mo->AddProperty(&tmp,AI_MATKEY_NAME);
436
632
            }
437
438
878
            mo->AddProperty(&mi.ambient,1,AI_MATKEY_COLOR_AMBIENT);
439
878
            mo->AddProperty(&mi.diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
440
878
            mo->AddProperty(&mi.specular,1,AI_MATKEY_COLOR_SPECULAR);
441
878
            mo->AddProperty(&mi.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
442
443
878
            mo->AddProperty(&mi.shininess,1,AI_MATKEY_SHININESS);
444
878
            mo->AddProperty(&mi.transparency,1,AI_MATKEY_OPACITY);
445
446
878
            const int sm = mi.shininess>0.f?aiShadingMode_Phong:aiShadingMode_Gouraud;
447
878
            mo->AddProperty(&sm,1,AI_MATKEY_SHADING_MODEL);
448
878
        }
449
17
    }
450
451
    // convert groups to meshes
452
91
    if (groups.empty()) {
453
16
        throw DeadlyImportError("MS3D: Didn't get any group records, file is malformed");
454
16
    }
455
456
75
    pScene->mMeshes = new aiMesh*[pScene->mNumMeshes=static_cast<unsigned int>(groups.size())]();
457
83
    for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
458
459
15
        aiMesh* m = pScene->mMeshes[i] = new aiMesh();
460
15
        const TempGroup& g = groups[i];
461
462
15
        if (pScene->mNumMaterials && g.mat > pScene->mNumMaterials) {
463
0
            throw DeadlyImportError("MS3D: Encountered invalid material index, file is malformed");
464
0
        } // no error if no materials at all - scenepreprocessor adds one then
465
466
15
        m->mMaterialIndex  = g.mat;
467
15
        m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
468
469
15
        m->mFaces = new aiFace[m->mNumFaces = static_cast<unsigned int>(g.triangles.size())];
470
15
        m->mNumVertices = m->mNumFaces*3;
471
472
        // storage for vertices - verbose format, as requested by the postprocessing pipeline
473
15
        m->mVertices = new aiVector3D[m->mNumVertices];
474
15
        m->mNormals  = new aiVector3D[m->mNumVertices];
475
15
        m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
476
15
        m->mNumUVComponents[0] = 2;
477
478
15
        typedef std::map<unsigned int,unsigned int> BoneSet;
479
15
        BoneSet mybones;
480
481
16.0k
        for (unsigned int j = 0,n = 0; j < m->mNumFaces; ++j) {
482
16.0k
            aiFace& f = m->mFaces[j];
483
16.0k
            if (g.triangles[j] >= triangles.size()) {
484
2
                throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
485
2
            }
486
487
16.0k
            TempTriangle& t = triangles[g.triangles[j]];
488
16.0k
            f.mIndices = new unsigned int[f.mNumIndices=3];
489
490
64.1k
            for (unsigned int k = 0; k < 3; ++k,++n) {
491
48.1k
                if (t.indices[k] >= vertices.size()) {
492
5
                    throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
493
5
                }
494
495
48.1k
                const TempVertex& v = vertices[t.indices[k]];
496
240k
                for(unsigned int a = 0; a < 4; ++a) {
497
192k
                    if (v.bone_id[a] != UINT_MAX) {
498
0
                        if (v.bone_id[a] >= joints.size()) {
499
0
                            throw DeadlyImportError("MS3D: Encountered invalid bone index, file is malformed");
500
0
                        }
501
0
                        if (mybones.find(v.bone_id[a]) == mybones.end()) {
502
0
                             mybones[v.bone_id[a]] = 1;
503
0
                        }
504
0
                        else ++mybones[v.bone_id[a]];
505
0
                    }
506
192k
                }
507
508
                // collect vertex components
509
48.1k
                m->mVertices[n] = v.pos;
510
511
48.1k
                m->mNormals[n] = t.normals[k];
512
48.1k
                m->mTextureCoords[0][n] = aiVector3D(t.uv[k].x,1.f-t.uv[k].y,0.0);
513
48.1k
                f.mIndices[k] = n;
514
48.1k
            }
515
16.0k
        }
516
517
        // allocate storage for bones
518
8
        if(!mybones.empty()) {
519
0
            std::vector<unsigned int> bmap(joints.size());
520
0
            m->mBones = new aiBone*[mybones.size()]();
521
0
            for(BoneSet::const_iterator it = mybones.begin(); it != mybones.end(); ++it) {
522
0
                aiBone* const bn = m->mBones[m->mNumBones] = new aiBone();
523
0
                const TempJoint& jnt = joints[(*it).first];
524
525
0
                bn->mName.Set(jnt.name);
526
0
                bn->mWeights = new aiVertexWeight[(*it).second];
527
528
0
                bmap[(*it).first] = m->mNumBones++;
529
0
            }
530
531
            // .. and collect bone weights
532
0
            for (unsigned int j = 0,n = 0; j < m->mNumFaces; ++j) {
533
0
                TempTriangle& t = triangles[g.triangles[j]];
534
535
0
                for (unsigned int k = 0; k < 3; ++k,++n) {
536
0
                    const TempVertex& v = vertices[t.indices[k]];
537
0
                    for(unsigned int a = 0; a < 4; ++a) {
538
0
                        const unsigned int bone = v.bone_id[a];
539
0
                        if(bone==UINT_MAX){
540
0
                            continue;
541
0
                        }
542
543
0
                        aiBone* const outbone = m->mBones[bmap[bone]];
544
0
                        aiVertexWeight& outwght = outbone->mWeights[outbone->mNumWeights++];
545
546
0
                        outwght.mVertexId = n;
547
0
                        outwght.mWeight = v.weights[a];
548
0
                    }
549
0
                }
550
0
            }
551
0
        }
552
8
    }
553
554
    // ... add dummy nodes under a single root, each holding a reference to one
555
    // mesh. If we didn't do this, we'd lose the group name.
556
68
    aiNode* rt = pScene->mRootNode = new aiNode("<MS3DRoot>");
557
558
#ifdef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
559
    rt->mChildren = new aiNode*[rt->mNumChildren=pScene->mNumMeshes+(joints.size()?1:0)]();
560
561
    for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
562
        aiNode* nd = rt->mChildren[i] = new aiNode();
563
564
        const TempGroup& g = groups[i];
565
566
        // we need to generate an unique name for all mesh nodes.
567
        // since we want to keep the group name, a prefix is
568
        // prepended.
569
        nd->mName = aiString("<MS3DMesh>_");
570
        nd->mName.Append(g.name);
571
        nd->mParent = rt;
572
573
        nd->mMeshes = new unsigned int[nd->mNumMeshes = 1];
574
        nd->mMeshes[0] = i;
575
    }
576
#else
577
68
    rt->mMeshes = new unsigned int[pScene->mNumMeshes];
578
75
    for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
579
7
        rt->mMeshes[rt->mNumMeshes++] = i;
580
7
    }
581
68
#endif
582
583
    // convert animations as well
584
68
    if(joints.size()) {
585
0
#ifndef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
586
0
        rt->mChildren = new aiNode*[1]();
587
0
        rt->mNumChildren = 1;
588
589
0
        aiNode* jt = rt->mChildren[0] = new aiNode();
590
#else
591
        aiNode* jt = rt->mChildren[pScene->mNumMeshes] = new aiNode();
592
#endif
593
0
        jt->mParent = rt;
594
0
        CollectChildJoints(joints,jt);
595
0
        jt->mName.Set("<MS3DJointRoot>");
596
597
0
        pScene->mAnimations = new aiAnimation*[ pScene->mNumAnimations = 1 ];
598
0
        aiAnimation* const anim = pScene->mAnimations[0] = new aiAnimation();
599
600
0
        anim->mName.Set("<MS3DMasterAnim>");
601
602
        // carry the fps info to the user by scaling all times with it
603
0
        anim->mTicksPerSecond = animfps;
604
605
        // leave duration at its default, so ScenePreprocessor will fill an appropriate
606
        // value (the values taken from some MS3D files seem to be too unreliable
607
        // to pass the validation)
608
        // anim->mDuration = totalframes/animfps;
609
610
0
        anim->mChannels = new aiNodeAnim*[joints.size()]();
611
0
        for(std::vector<TempJoint>::const_iterator it = joints.begin(); it != joints.end(); ++it) {
612
0
            if ((*it).rotFrames.empty() && (*it).posFrames.empty()) {
613
0
                continue;
614
0
            }
615
616
0
            aiNodeAnim* nd = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
617
0
            nd->mNodeName.Set((*it).name);
618
619
0
            if ((*it).rotFrames.size()) {
620
0
                nd->mRotationKeys = new aiQuatKey[(*it).rotFrames.size()];
621
0
                for(std::vector<TempKeyFrame>::const_iterator rot = (*it).rotFrames.begin(); rot != (*it).rotFrames.end(); ++rot) {
622
0
                    aiQuatKey& q = nd->mRotationKeys[nd->mNumRotationKeys++];
623
624
0
                    q.mTime = (*rot).time*animfps;
625
0
                    q.mValue = aiQuaternion(aiMatrix3x3(aiMatrix4x4().FromEulerAnglesXYZ((*it).rotation)*
626
0
                        aiMatrix4x4().FromEulerAnglesXYZ((*rot).value)));
627
0
                }
628
0
            }
629
630
0
            if ((*it).posFrames.size()) {
631
0
                nd->mPositionKeys = new aiVectorKey[(*it).posFrames.size()];
632
633
0
                aiQuatKey* qu = nd->mRotationKeys;
634
0
                for(std::vector<TempKeyFrame>::const_iterator pos = (*it).posFrames.begin(); pos != (*it).posFrames.end(); ++pos,++qu) {
635
0
                    aiVectorKey& v = nd->mPositionKeys[nd->mNumPositionKeys++];
636
637
0
                    v.mTime = (*pos).time*animfps;
638
0
                    v.mValue = (*it).position + (*pos).value;
639
0
                }
640
0
            }
641
0
        }
642
        // fixup to pass the validation if not a single animation channel is non-trivial
643
0
        if (!anim->mNumChannels) {
644
0
            anim->mChannels = nullptr;
645
0
        }
646
0
    }
647
68
}
648
649
#endif