Coverage Report

Created: 2025-11-11 06:59

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-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  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
891
{}
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
165
{
87
165
    static const char* tokens[] = { "MS3D000000" };
88
165
    return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
89
165
}
90
91
// ------------------------------------------------------------------------------------------------
92
const aiImporterDesc* MS3DImporter::GetInfo () const
93
877
{
94
877
    return &desc;
95
877
}
96
97
// ------------------------------------------------------------------------------------------------
98
void ReadColor(StreamReaderLE& stream, aiColor4D& ambient)
99
0
{
100
    // aiColor4D is packed on gcc, implicit binding to float& fails therefore.
101
0
    stream >> (float&)ambient.r >> (float&)ambient.g >> (float&)ambient.b >> (float&)ambient.a;
102
0
}
103
104
// ------------------------------------------------------------------------------------------------
105
void ReadVector(StreamReaderLE& stream, aiVector3D& pos)
106
0
{
107
    // See note in ReadColor()
108
0
    stream >> (float&)pos.x >> (float&)pos.y >> (float&)pos.z;
109
0
}
110
111
// ------------------------------------------------------------------------------------------------
112
template<typename T>
113
void MS3DImporter :: ReadComments(StreamReaderLE& stream, std::vector<T>& outp)
114
0
{
115
0
    uint16_t cnt;
116
0
    stream >> cnt;
117
118
0
    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
0
}
Unexecuted instantiation: void Assimp::MS3DImporter::ReadComments<Assimp::MS3DImporter::TempGroup>(Assimp::StreamReader<false, false>&, std::__1::vector<Assimp::MS3DImporter::TempGroup, std::__1::allocator<Assimp::MS3DImporter::TempGroup> >&)
Unexecuted instantiation: void Assimp::MS3DImporter::ReadComments<Assimp::MS3DImporter::TempMaterial>(Assimp::StreamReader<false, false>&, std::__1::vector<Assimp::MS3DImporter::TempMaterial, std::__1::allocator<Assimp::MS3DImporter::TempMaterial> >&)
Unexecuted instantiation: void Assimp::MS3DImporter::ReadComments<Assimp::MS3DImporter::TempJoint>(Assimp::StreamReader<false, false>&, std::__1::vector<Assimp::MS3DImporter::TempJoint, std::__1::allocator<Assimp::MS3DImporter::TempJoint> >&)
134
135
// ------------------------------------------------------------------------------------------------
136
template <typename T, typename T2, typename T3> bool inrange(const T& in, const T2& lower, const T3& higher)
137
0
{
138
0
    return in > lower && in <= higher;
139
0
}
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
0
{
196
197
0
    auto file = pIOHandler->Open(pFile, "rb");
198
0
    if (!file)
199
0
        throw DeadlyImportError("MS3D: Could not open ", pFile);
200
201
0
    StreamReaderLE stream(file);
202
203
    // CanRead() should have done this already
204
0
    char head[10];
205
0
    int32_t version;
206
207
0
    mScene = pScene;
208
209
210
    // 1 ------------ read into temporary data structures mirroring the original file
211
212
0
    stream.CopyAndAdvance(head,10);
213
0
    stream >> version;
214
0
    if (strncmp(head,"MS3D000000",10)) {
215
0
        throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: ", pFile);
216
0
    }
217
218
0
    if (version != 4) {
219
0
        throw DeadlyImportError("MS3D: Unsupported file format version, 4 was expected");
220
0
    }
221
222
0
    uint16_t verts;
223
0
    stream >> verts;
224
225
0
    std::vector<TempVertex> vertices(verts);
226
0
    for (unsigned int i = 0; i < verts; ++i) {
227
0
        TempVertex& v = vertices[i];
228
229
0
        stream.IncPtr(1);
230
0
        ReadVector(stream,v.pos);
231
0
        v.bone_id[0] = stream.GetI1();
232
0
        v.ref_cnt = stream.GetI1();
233
234
0
        v.bone_id[1] = v.bone_id[2] = v.bone_id[3] = UINT_MAX;
235
0
        v.weights[1] = v.weights[2] = v.weights[3] = 0.f;
236
0
        v.weights[0] = 1.f;
237
0
    }
238
239
0
    uint16_t tris;
240
0
    stream >> tris;
241
242
0
    std::vector<TempTriangle> triangles(tris);
243
0
    for (unsigned int i = 0;i < tris; ++i) {
244
0
        TempTriangle& t = triangles[i];
245
246
0
        stream.IncPtr(2);
247
0
        for (unsigned int j = 0; j < 3; ++j) {
248
0
            t.indices[j] = stream.GetI2();
249
0
        }
250
251
0
        for (unsigned int j = 0; j < 3; ++j) {
252
0
            ReadVector(stream,t.normals[j]);
253
0
        }
254
255
0
        for (unsigned int j = 0; j < 3; ++j) {
256
0
            stream >> (float&)(t.uv[j].x); // see note in ReadColor()
257
0
        }
258
0
        for (unsigned int j = 0; j < 3; ++j) {
259
0
            stream >> (float&)(t.uv[j].y);
260
0
        }
261
262
0
        t.sg    = stream.GetI1();
263
0
        t.group = stream.GetI1();
264
0
    }
265
266
0
    uint16_t grp;
267
0
    stream >> grp;
268
269
0
    bool need_default = false;
270
0
    std::vector<TempGroup> groups(grp);
271
0
    for (unsigned int i = 0;i < grp; ++i) {
272
0
        TempGroup& t = groups[i];
273
274
0
        stream.IncPtr(1);
275
0
        stream.CopyAndAdvance(t.name,32);
276
277
0
        t.name[32] = '\0';
278
0
        uint16_t num;
279
0
        stream >> num;
280
281
0
        t.triangles.resize(num);
282
0
        for (unsigned int j = 0; j < num; ++j) {
283
0
            t.triangles[j] = stream.GetI2();
284
0
        }
285
0
        t.mat = stream.GetI1();
286
0
        if (t.mat == UINT_MAX) {
287
0
            need_default = true;
288
0
        }
289
0
    }
290
291
0
    uint16_t mat;
292
0
    stream >> mat;
293
294
0
    std::vector<TempMaterial> materials(mat);
295
0
    for (unsigned int j = 0;j < mat; ++j) {
296
0
        TempMaterial& t = materials[j];
297
298
0
        stream.CopyAndAdvance(t.name,32);
299
0
        t.name[32] = '\0';
300
301
0
        ReadColor(stream,t.ambient);
302
0
        ReadColor(stream,t.diffuse);
303
0
        ReadColor(stream,t.specular);
304
0
        ReadColor(stream,t.emissive);
305
0
        stream >> t.shininess  >> t.transparency;
306
307
0
        stream.IncPtr(1);
308
309
0
        stream.CopyAndAdvance(t.texture,128);
310
0
        t.texture[128] = '\0';
311
312
0
        stream.CopyAndAdvance(t.alphamap,128);
313
0
        t.alphamap[128] = '\0';
314
0
    }
315
316
0
    float animfps, currenttime;
317
0
    uint32_t totalframes;
318
0
    stream >> animfps >> currenttime >> totalframes;
319
320
0
    uint16_t joint;
321
0
    stream >> joint;
322
323
0
    std::vector<TempJoint> joints(joint);
324
0
    for(unsigned int ii = 0; ii < joint; ++ii) {
325
0
        TempJoint& j = joints[ii];
326
327
0
        stream.IncPtr(1);
328
0
        stream.CopyAndAdvance(j.name,32);
329
0
        j.name[32] = '\0';
330
331
0
        stream.CopyAndAdvance(j.parentName,32);
332
0
        j.parentName[32] = '\0';
333
334
0
        ReadVector(stream,j.rotation);
335
0
        ReadVector(stream,j.position);
336
337
0
        j.rotFrames.resize(stream.GetI2());
338
0
        j.posFrames.resize(stream.GetI2());
339
340
0
        for(unsigned int a = 0; a < j.rotFrames.size(); ++a) {
341
0
            TempKeyFrame& kf = j.rotFrames[a];
342
0
            stream >> kf.time;
343
0
            ReadVector(stream,kf.value);
344
0
        }
345
0
        for(unsigned int a = 0; a < j.posFrames.size(); ++a) {
346
0
            TempKeyFrame& kf = j.posFrames[a];
347
0
            stream >> kf.time;
348
0
            ReadVector(stream,kf.value);
349
0
        }
350
0
    }
351
352
0
    if(stream.GetRemainingSize() > 4) {
353
0
        uint32_t subversion;
354
0
        stream >> subversion;
355
0
        if (subversion == 1) {
356
0
            ReadComments<TempGroup>(stream,groups);
357
0
            ReadComments<TempMaterial>(stream,materials);
358
0
            ReadComments<TempJoint>(stream,joints);
359
360
            // model comment - print it for we have such a nice log.
361
0
            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
0
            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
0
        }
385
0
    }
386
387
    // 2 ------------ convert to proper aiXX data structures -----------------------------------
388
389
0
    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
0
    if (materials.size()) {
416
0
        pScene->mMaterials = new aiMaterial*[materials.size()];
417
0
        for (size_t i = 0; i < materials.size(); ++i) {
418
419
0
            aiMaterial* mo = new aiMaterial();
420
0
            pScene->mMaterials[pScene->mNumMaterials++] = mo;
421
422
0
            const TempMaterial& mi = materials[i];
423
424
0
            aiString tmp;
425
0
            if (0[mi.alphamap]) {
426
0
                tmp = aiString(mi.alphamap);
427
0
                mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_OPACITY(0));
428
0
            }
429
0
            if (0[mi.texture]) {
430
0
                tmp = aiString(mi.texture);
431
0
                mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_DIFFUSE(0));
432
0
            }
433
0
            if (0[mi.name]) {
434
0
                tmp = aiString(mi.name);
435
0
                mo->AddProperty(&tmp,AI_MATKEY_NAME);
436
0
            }
437
438
0
            mo->AddProperty(&mi.ambient,1,AI_MATKEY_COLOR_AMBIENT);
439
0
            mo->AddProperty(&mi.diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
440
0
            mo->AddProperty(&mi.specular,1,AI_MATKEY_COLOR_SPECULAR);
441
0
            mo->AddProperty(&mi.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
442
443
0
            mo->AddProperty(&mi.shininess,1,AI_MATKEY_SHININESS);
444
0
            mo->AddProperty(&mi.transparency,1,AI_MATKEY_OPACITY);
445
446
0
            const int sm = mi.shininess>0.f?aiShadingMode_Phong:aiShadingMode_Gouraud;
447
0
            mo->AddProperty(&sm,1,AI_MATKEY_SHADING_MODEL);
448
0
        }
449
0
    }
450
451
    // convert groups to meshes
452
0
    if (groups.empty()) {
453
0
        throw DeadlyImportError("MS3D: Didn't get any group records, file is malformed");
454
0
    }
455
456
0
    pScene->mMeshes = new aiMesh*[pScene->mNumMeshes=static_cast<unsigned int>(groups.size())]();
457
0
    for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
458
459
0
        aiMesh* m = pScene->mMeshes[i] = new aiMesh();
460
0
        const TempGroup& g = groups[i];
461
462
0
        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
0
        m->mMaterialIndex  = g.mat;
467
0
        m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
468
469
0
        m->mFaces = new aiFace[m->mNumFaces = static_cast<unsigned int>(g.triangles.size())];
470
0
        m->mNumVertices = m->mNumFaces*3;
471
472
        // storage for vertices - verbose format, as requested by the postprocessing pipeline
473
0
        m->mVertices = new aiVector3D[m->mNumVertices];
474
0
        m->mNormals  = new aiVector3D[m->mNumVertices];
475
0
        m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
476
0
        m->mNumUVComponents[0] = 2;
477
478
0
        typedef std::map<unsigned int,unsigned int> BoneSet;
479
0
        BoneSet mybones;
480
481
0
        for (unsigned int j = 0,n = 0; j < m->mNumFaces; ++j) {
482
0
            aiFace& f = m->mFaces[j];
483
0
            if (g.triangles[j] >= triangles.size()) {
484
0
                throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
485
0
            }
486
487
0
            TempTriangle& t = triangles[g.triangles[j]];
488
0
            f.mIndices = new unsigned int[f.mNumIndices=3];
489
490
0
            for (unsigned int k = 0; k < 3; ++k,++n) {
491
0
                if (t.indices[k] >= vertices.size()) {
492
0
                    throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
493
0
                }
494
495
0
                const TempVertex& v = vertices[t.indices[k]];
496
0
                for(unsigned int a = 0; a < 4; ++a) {
497
0
                    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
0
                }
507
508
                // collect vertex components
509
0
                m->mVertices[n] = v.pos;
510
511
0
                m->mNormals[n] = t.normals[k];
512
0
                m->mTextureCoords[0][n] = aiVector3D(t.uv[k].x,1.f-t.uv[k].y,0.0);
513
0
                f.mIndices[k] = n;
514
0
            }
515
0
        }
516
517
        // allocate storage for bones
518
0
        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
0
    }
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
0
    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
0
    rt->mMeshes = new unsigned int[pScene->mNumMeshes];
578
0
    for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
579
0
        rt->mMeshes[rt->mNumMeshes++] = i;
580
0
    }
581
0
#endif
582
583
    // convert animations as well
584
0
    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
0
}
648
649
#endif