Coverage Report

Created: 2026-04-01 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/Ogre/OgreBinarySerializer.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2026, 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
42
#include "OgreBinarySerializer.h"
43
#include "OgreParsingUtils.h"
44
#include "OgreXmlSerializer.h"
45
46
#include <assimp/TinyFormatter.h>
47
#include <assimp/DefaultLogger.hpp>
48
49
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
50
51
// Define as 1 to get verbose logging.
52
#define OGRE_BINARY_SERIALIZER_DEBUG 0
53
54
namespace Assimp {
55
namespace Ogre {
56
57
static constexpr auto MESH_VERSION_1_8 = "[MeshSerializer_v1.8]";
58
static constexpr auto SKELETON_VERSION_1_8 = "[Serializer_v1.80]";
59
static constexpr auto SKELETON_VERSION_1_1 = "[Serializer_v1.10]";
60
61
const unsigned short HEADER_CHUNK_ID = 0x1000;
62
63
const long MSTREAM_OVERHEAD_SIZE = sizeof(uint16_t) + sizeof(uint32_t);
64
const long MSTREAM_BONE_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + sizeof(unsigned short) + (sizeof(float) * 7);
65
const long MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + (sizeof(float) * 8);
66
67
template <>
68
0
inline bool OgreBinarySerializer::Read<bool>() {
69
0
    return (m_reader->GetU1() > 0);
70
0
}
71
72
template <>
73
0
inline char OgreBinarySerializer::Read<char>() {
74
0
    return static_cast<char>(m_reader->GetU1());
75
0
}
76
77
template <>
78
0
inline uint8_t OgreBinarySerializer::Read<uint8_t>() {
79
0
    return m_reader->GetU1();
80
0
}
81
82
template <>
83
0
inline uint16_t OgreBinarySerializer::Read<uint16_t>() {
84
0
    return m_reader->GetU2();
85
0
}
86
87
template <>
88
0
inline uint32_t OgreBinarySerializer::Read<uint32_t>() {
89
0
    return m_reader->GetU4();
90
0
}
91
92
template <>
93
0
inline float OgreBinarySerializer::Read<float>() {
94
0
    return m_reader->GetF4();
95
0
}
96
97
0
void OgreBinarySerializer::ReadBytes(char *dest, size_t numBytes) {
98
0
    ReadBytes(static_cast<void *>(dest), numBytes);
99
0
}
100
101
0
void OgreBinarySerializer::ReadBytes(uint8_t *dest, size_t numBytes) {
102
0
    ReadBytes(static_cast<void *>(dest), numBytes);
103
0
}
104
105
0
void OgreBinarySerializer::ReadBytes(void *dest, size_t numBytes) {
106
0
    m_reader->CopyAndAdvance(dest, numBytes);
107
0
}
108
109
0
uint8_t *OgreBinarySerializer::ReadBytes(size_t numBytes) {
110
0
    uint8_t *bytes = new uint8_t[numBytes];
111
0
    ReadBytes(bytes, numBytes);
112
0
    return bytes;
113
0
}
114
115
0
void OgreBinarySerializer::ReadVector(aiVector3D &vec) {
116
0
    m_reader->CopyAndAdvance(&vec.x, sizeof(float) * 3);
117
0
}
118
119
0
void OgreBinarySerializer::ReadQuaternion(aiQuaternion &quat) {
120
0
    float temp[4];
121
0
    m_reader->CopyAndAdvance(temp, sizeof(float) * 4);
122
0
    quat.x = temp[0];
123
0
    quat.y = temp[1];
124
0
    quat.z = temp[2];
125
0
    quat.w = temp[3];
126
0
}
127
128
0
bool OgreBinarySerializer::AtEnd() const {
129
0
    return (m_reader->GetRemainingSize() == 0);
130
0
}
131
132
0
std::string OgreBinarySerializer::ReadString(size_t len) {
133
0
    std::string str;
134
0
    str.resize(len);
135
0
    ReadBytes(&str[0], len);
136
0
    return str;
137
0
}
138
139
0
std::string OgreBinarySerializer::ReadLine() {
140
0
    std::string str;
141
0
    while (!AtEnd()) {
142
0
        char c = Read<char>();
143
0
        if (c == '\n')
144
0
            break;
145
0
        str += c;
146
0
    }
147
0
    return str;
148
0
}
149
150
0
uint16_t OgreBinarySerializer::ReadHeader(bool readLen) {
151
0
    uint16_t id = Read<uint16_t>();
152
0
    if (readLen)
153
0
        m_currentLen = Read<uint32_t>();
154
155
#if (OGRE_BINARY_SERIALIZER_DEBUG == 1)
156
    if (id != HEADER_CHUNK_ID) {
157
        ASSIMP_LOG_DEBUG((assetMode == AM_Mesh ? MeshHeaderToString(static_cast<MeshChunkId>(id)) : SkeletonHeaderToString(static_cast<SkeletonChunkId>(id))));
158
    }
159
#endif
160
161
0
    return id;
162
0
}
163
164
0
void OgreBinarySerializer::RollbackHeader() {
165
0
    m_reader->IncPtr(-MSTREAM_OVERHEAD_SIZE);
166
0
}
167
168
0
void OgreBinarySerializer::SkipBytes(size_t numBytes) {
169
#if (OGRE_BINARY_SERIALIZER_DEBUG == 1)
170
    ASSIMP_LOG_VERBOSE_DEBUG("Skipping ", numBytes, " bytes");
171
#endif
172
173
0
    m_reader->IncPtr(numBytes);
174
0
}
175
176
// Mesh
177
178
0
Mesh *OgreBinarySerializer::ImportMesh(MemoryStreamReader *stream) {
179
0
    OgreBinarySerializer serializer(stream, OgreBinarySerializer::AM_Mesh);
180
181
0
    uint16_t id = serializer.ReadHeader(false);
182
0
    if (id != HEADER_CHUNK_ID) {
183
0
        throw DeadlyImportError("Invalid Ogre Mesh file header.");
184
0
    }
185
186
    /// @todo Check what we can actually support.
187
0
    std::string version = serializer.ReadLine();
188
0
    if (version != MESH_VERSION_1_8) {
189
0
        throw DeadlyImportError("Mesh version ", version, " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again.",
190
0
                                                    " Supported versions: ", MESH_VERSION_1_8);
191
0
    }
192
193
0
    Mesh *mesh = new Mesh();
194
0
    while (!serializer.AtEnd()) {
195
0
        id = serializer.ReadHeader();
196
0
        switch (id) {
197
0
            case M_MESH: {
198
0
                serializer.ReadMesh(mesh);
199
0
                break;
200
0
            }
201
0
        }
202
0
    }
203
0
    return mesh;
204
0
}
205
206
0
void OgreBinarySerializer::ReadMesh(Mesh *mesh) {
207
0
    mesh->hasSkeletalAnimations = Read<bool>();
208
209
0
    ASSIMP_LOG_VERBOSE_DEBUG("Reading Mesh");
210
0
    ASSIMP_LOG_VERBOSE_DEBUG("  - Skeletal animations: ", mesh->hasSkeletalAnimations ? "true" : "false");
211
212
0
    if (!AtEnd()) {
213
0
        uint16_t id = ReadHeader();
214
0
        while (!AtEnd() &&
215
0
                (id == M_GEOMETRY ||
216
0
                        id == M_SUBMESH ||
217
0
                        id == M_MESH_SKELETON_LINK ||
218
0
                        id == M_MESH_BONE_ASSIGNMENT ||
219
0
                        id == M_MESH_LOD ||
220
0
                        id == M_MESH_BOUNDS ||
221
0
                        id == M_SUBMESH_NAME_TABLE ||
222
0
                        id == M_EDGE_LISTS ||
223
0
                        id == M_POSES ||
224
0
                        id == M_ANIMATIONS ||
225
0
                        id == M_TABLE_EXTREMES)) {
226
0
            switch (id) {
227
0
                case M_GEOMETRY: {
228
0
                    mesh->sharedVertexData = new VertexData();
229
0
                    ReadGeometry(mesh->sharedVertexData);
230
0
                    break;
231
0
                }
232
0
                case M_SUBMESH: {
233
0
                    ReadSubMesh(mesh);
234
0
                    break;
235
0
                }
236
0
                case M_MESH_SKELETON_LINK: {
237
0
                    ReadMeshSkeletonLink(mesh);
238
0
                    break;
239
0
                }
240
0
                case M_MESH_BONE_ASSIGNMENT: {
241
0
                    ReadBoneAssignment(mesh->sharedVertexData);
242
0
                    break;
243
0
                }
244
0
                case M_MESH_LOD: {
245
0
                    ReadMeshLodInfo(mesh);
246
0
                    break;
247
0
                }
248
0
                case M_MESH_BOUNDS: {
249
0
                    ReadMeshBounds(mesh);
250
0
                    break;
251
0
                }
252
0
                case M_SUBMESH_NAME_TABLE: {
253
0
                    ReadSubMeshNames(mesh);
254
0
                    break;
255
0
                }
256
0
                case M_EDGE_LISTS: {
257
0
                    ReadEdgeList(mesh);
258
0
                    break;
259
0
                }
260
0
                case M_POSES: {
261
0
                    ReadPoses(mesh);
262
0
                    break;
263
0
                }
264
0
                case M_ANIMATIONS: {
265
0
                    ReadAnimations(mesh);
266
0
                    break;
267
0
                }
268
0
                case M_TABLE_EXTREMES: {
269
0
                    ReadMeshExtremes(mesh);
270
0
                    break;
271
0
                }
272
0
            }
273
274
0
            if (!AtEnd())
275
0
                id = ReadHeader();
276
0
        }
277
0
        if (!AtEnd())
278
0
            RollbackHeader();
279
0
    }
280
281
0
    NormalizeBoneWeights(mesh->sharedVertexData);
282
0
}
283
284
0
void OgreBinarySerializer::ReadMeshLodInfo(Mesh *mesh) {
285
    // Assimp does not acknowledge LOD levels as far as I can see it. This info is just skipped.
286
    // @todo Put this stuff to scene/mesh custom properties. If manual mesh the app can use the information.
287
0
    ReadLine(); // strategy name
288
0
    uint16_t numLods = Read<uint16_t>();
289
0
    bool manual = Read<bool>();
290
291
    /// @note Main mesh is considered as LOD 0, start from index 1.
292
0
    for (size_t i = 1; i < numLods; ++i) {
293
0
        uint16_t id = ReadHeader();
294
0
        if (id != M_MESH_LOD_USAGE) {
295
0
            throw DeadlyImportError("M_MESH_LOD does not contain a M_MESH_LOD_USAGE for each LOD level");
296
0
        }
297
298
0
        m_reader->IncPtr(sizeof(float)); // user value
299
300
0
        if (manual) {
301
0
            id = ReadHeader();
302
0
            if (id != M_MESH_LOD_MANUAL) {
303
0
                throw DeadlyImportError("Manual M_MESH_LOD_USAGE does not contain M_MESH_LOD_MANUAL");
304
0
            }
305
306
0
            ReadLine(); // manual mesh name (ref to another mesh)
307
0
        } else {
308
0
            for (size_t si = 0, silen = mesh->NumSubMeshes(); si < silen; ++si) {
309
0
                id = ReadHeader();
310
0
                if (id != M_MESH_LOD_GENERATED) {
311
0
                    throw DeadlyImportError("Generated M_MESH_LOD_USAGE does not contain M_MESH_LOD_GENERATED");
312
0
                }
313
314
0
                uint32_t indexCount = Read<uint32_t>();
315
0
                bool is32bit = Read<bool>();
316
317
0
                if (indexCount > 0) {
318
0
                    uint32_t len = indexCount * (is32bit ? sizeof(uint32_t) : sizeof(uint16_t));
319
0
                    m_reader->IncPtr(len);
320
0
                }
321
0
            }
322
0
        }
323
0
    }
324
0
}
325
326
0
void OgreBinarySerializer::ReadMeshSkeletonLink(Mesh *mesh) {
327
0
    mesh->skeletonRef = ReadLine();
328
0
}
329
330
0
void OgreBinarySerializer::ReadMeshBounds(Mesh * /*mesh*/) {
331
    // Skip bounds, not compatible with Assimp.
332
    // 2x float vec3 + 1x float sphere radius
333
0
    SkipBytes(sizeof(float) * 7);
334
0
}
335
336
0
void OgreBinarySerializer::ReadMeshExtremes(Mesh * /*mesh*/) {
337
    // Skip extremes, not compatible with Assimp.
338
0
    size_t numBytes = m_currentLen - MSTREAM_OVERHEAD_SIZE;
339
0
    SkipBytes(numBytes);
340
0
}
341
342
0
void OgreBinarySerializer::ReadBoneAssignment(VertexData *dest) {
343
0
    if (!dest) {
344
0
        throw DeadlyImportError("Cannot read bone assignments, vertex data is null.");
345
0
    }
346
347
0
    VertexBoneAssignment ba;
348
0
    ba.vertexIndex = Read<uint32_t>();
349
0
    ba.boneIndex = Read<uint16_t>();
350
0
    ba.weight = Read<float>();
351
352
0
    dest->boneAssignments.push_back(ba);
353
0
}
354
355
0
void OgreBinarySerializer::ReadSubMesh(Mesh *mesh) {
356
0
    uint16_t id = 0;
357
358
0
    SubMesh *submesh = new SubMesh();
359
0
    submesh->materialRef = ReadLine();
360
0
    submesh->usesSharedVertexData = Read<bool>();
361
362
0
    submesh->indexData->count = Read<uint32_t>();
363
0
    submesh->indexData->faceCount = static_cast<uint32_t>(submesh->indexData->count / 3);
364
0
    submesh->indexData->is32bit = Read<bool>();
365
366
0
    ASSIMP_LOG_VERBOSE_DEBUG("Reading SubMesh ", mesh->subMeshes.size());
367
0
    ASSIMP_LOG_VERBOSE_DEBUG("  - Material: '", submesh->materialRef, "'");
368
0
    ASSIMP_LOG_VERBOSE_DEBUG("  - Uses shared geometry: ", submesh->usesSharedVertexData ? "true" : "false");
369
370
    // Index buffer
371
0
    if (submesh->indexData->count > 0) {
372
0
        uint32_t numBytes = submesh->indexData->count * (submesh->indexData->is32bit ? sizeof(uint32_t) : sizeof(uint16_t));
373
0
        uint8_t *indexBuffer = ReadBytes(numBytes);
374
0
        submesh->indexData->buffer = MemoryStreamPtr(new Assimp::MemoryIOStream(indexBuffer, numBytes, true));
375
376
0
        ASSIMP_LOG_VERBOSE_DEBUG("  - ", submesh->indexData->faceCount,
377
0
                " faces from ", submesh->indexData->count, (submesh->indexData->is32bit ? " 32bit" : " 16bit"),
378
0
                " indexes of ", numBytes, " bytes");
379
0
    }
380
381
    // Vertex buffer if not referencing the shared geometry
382
0
    if (!submesh->usesSharedVertexData) {
383
0
        id = ReadHeader();
384
0
        if (id != M_GEOMETRY) {
385
0
            throw DeadlyImportError("M_SUBMESH does not contain M_GEOMETRY, but shader geometry is set to false");
386
0
        }
387
388
0
        submesh->vertexData = new VertexData();
389
0
        ReadGeometry(submesh->vertexData);
390
0
    }
391
392
    // Bone assignment, submesh operation and texture aliases
393
0
    if (!AtEnd()) {
394
0
        id = ReadHeader();
395
0
        while (!AtEnd() &&
396
0
                (id == M_SUBMESH_OPERATION ||
397
0
                        id == M_SUBMESH_BONE_ASSIGNMENT ||
398
0
                        id == M_SUBMESH_TEXTURE_ALIAS)) {
399
0
            switch (id) {
400
0
                case M_SUBMESH_OPERATION: {
401
0
                    ReadSubMeshOperation(submesh);
402
0
                    break;
403
0
                }
404
0
                case M_SUBMESH_BONE_ASSIGNMENT: {
405
0
                    ReadBoneAssignment(submesh->vertexData);
406
0
                    break;
407
0
                }
408
0
                case M_SUBMESH_TEXTURE_ALIAS: {
409
0
                    ReadSubMeshTextureAlias(submesh);
410
0
                    break;
411
0
                }
412
0
            }
413
414
0
            if (!AtEnd())
415
0
                id = ReadHeader();
416
0
        }
417
0
        if (!AtEnd())
418
0
            RollbackHeader();
419
0
    }
420
421
0
    NormalizeBoneWeights(submesh->vertexData);
422
423
0
    submesh->index = static_cast<unsigned int>(mesh->subMeshes.size());
424
0
    mesh->subMeshes.push_back(submesh);
425
0
}
426
427
0
void OgreBinarySerializer::NormalizeBoneWeights(VertexData *vertexData) const {
428
0
    if (!vertexData || vertexData->boneAssignments.empty())
429
0
        return;
430
431
0
    std::set<uint32_t> influencedVertices;
432
0
    for (VertexBoneAssignmentList::const_iterator baIter = vertexData->boneAssignments.begin(), baEnd = vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) {
433
0
        influencedVertices.insert(baIter->vertexIndex);
434
0
    }
435
436
    /** Normalize bone weights.
437
        Some exporters won't care if the sum of all bone weights
438
        for a single vertex equals 1 or not, so validate here. */
439
0
    const float epsilon = 0.05f;
440
0
    for (const uint32_t vertexIndex : influencedVertices) {
441
0
        float sum = 0.0f;
442
0
        for (VertexBoneAssignmentList::const_iterator baIter = vertexData->boneAssignments.begin(), baEnd = vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) {
443
0
            if (baIter->vertexIndex == vertexIndex)
444
0
                sum += baIter->weight;
445
0
        }
446
0
        if ((sum < (1.0f - epsilon)) || (sum > (1.0f + epsilon))) {
447
0
            for (auto &boneAssign : vertexData->boneAssignments) {
448
0
                if (boneAssign.vertexIndex == vertexIndex)
449
0
                    boneAssign.weight /= sum;
450
0
            }
451
0
        }
452
0
    }
453
0
}
454
455
0
void OgreBinarySerializer::ReadSubMeshOperation(SubMesh *submesh) {
456
0
    submesh->operationType = static_cast<SubMesh::OperationType>(Read<uint16_t>());
457
0
}
458
459
0
void OgreBinarySerializer::ReadSubMeshTextureAlias(SubMesh *submesh) {
460
0
    submesh->textureAliasName = ReadLine();
461
0
    submesh->textureAliasRef = ReadLine();
462
0
}
463
464
0
void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) {
465
0
    uint16_t id = 0;
466
467
0
    if (!AtEnd()) {
468
0
        id = ReadHeader();
469
0
        while (!AtEnd() && id == M_SUBMESH_NAME_TABLE_ELEMENT) {
470
0
            uint16_t submeshIndex = Read<uint16_t>();
471
0
            SubMesh *submesh = mesh->GetSubMesh(submeshIndex);
472
0
            if (!submesh) {
473
0
                throw DeadlyImportError("Ogre Mesh does not include submesh ", submeshIndex, " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file.");
474
0
            }
475
476
0
            submesh->name = ReadLine();
477
0
            ASSIMP_LOG_VERBOSE_DEBUG("  - SubMesh ", submesh->index, " name '", submesh->name, "'");
478
479
0
            if (!AtEnd())
480
0
                id = ReadHeader();
481
0
        }
482
0
        if (!AtEnd())
483
0
            RollbackHeader();
484
0
    }
485
0
}
486
487
0
void OgreBinarySerializer::ReadGeometry(VertexData *dest) {
488
0
    dest->count = Read<uint32_t>();
489
490
0
    ASSIMP_LOG_VERBOSE_DEBUG("  - Reading geometry of ", dest->count, " vertices");
491
492
0
    if (!AtEnd()) {
493
0
        uint16_t id = ReadHeader();
494
0
        while (!AtEnd() &&
495
0
                (id == M_GEOMETRY_VERTEX_DECLARATION ||
496
0
                        id == M_GEOMETRY_VERTEX_BUFFER)) {
497
0
            switch (id) {
498
0
                case M_GEOMETRY_VERTEX_DECLARATION: {
499
0
                    ReadGeometryVertexDeclaration(dest);
500
0
                    break;
501
0
                }
502
0
                case M_GEOMETRY_VERTEX_BUFFER: {
503
0
                    ReadGeometryVertexBuffer(dest);
504
0
                    break;
505
0
                }
506
0
            }
507
508
0
            if (!AtEnd())
509
0
                id = ReadHeader();
510
0
        }
511
0
        if (!AtEnd())
512
0
            RollbackHeader();
513
0
    }
514
0
}
515
516
0
void OgreBinarySerializer::ReadGeometryVertexDeclaration(VertexData *dest) {
517
0
    if (!AtEnd()) {
518
0
        uint16_t id = ReadHeader();
519
0
        while (!AtEnd() && id == M_GEOMETRY_VERTEX_ELEMENT) {
520
0
            ReadGeometryVertexElement(dest);
521
522
0
            if (!AtEnd())
523
0
                id = ReadHeader();
524
0
        }
525
0
        if (!AtEnd())
526
0
            RollbackHeader();
527
0
    }
528
0
}
529
530
0
void OgreBinarySerializer::ReadGeometryVertexElement(VertexData *dest) {
531
0
    VertexElement element;
532
0
    element.source = Read<uint16_t>();
533
0
    element.type = static_cast<VertexElement::Type>(Read<uint16_t>());
534
0
    element.semantic = static_cast<VertexElement::Semantic>(Read<uint16_t>());
535
0
    element.offset = Read<uint16_t>();
536
0
    element.index = Read<uint16_t>();
537
538
0
    ASSIMP_LOG_VERBOSE_DEBUG("    - Vertex element ", element.SemanticToString(), " of type ",
539
0
            element.TypeToString(), " index=", element.index, " source=", element.source);
540
541
0
    dest->vertexElements.push_back(element);
542
0
}
543
544
0
void OgreBinarySerializer::ReadGeometryVertexBuffer(VertexData *dest) {
545
0
    uint16_t bindIndex = Read<uint16_t>();
546
0
    uint16_t vertexSize = Read<uint16_t>();
547
548
0
    uint16_t id = ReadHeader();
549
0
    if (id != M_GEOMETRY_VERTEX_BUFFER_DATA)
550
0
        throw DeadlyImportError("M_GEOMETRY_VERTEX_BUFFER_DATA not found in M_GEOMETRY_VERTEX_BUFFER");
551
552
0
    if (dest->VertexSize(bindIndex) != vertexSize)
553
0
        throw DeadlyImportError("Vertex buffer size does not agree with vertex declaration in M_GEOMETRY_VERTEX_BUFFER");
554
555
0
    size_t numBytes = dest->count * vertexSize;
556
0
    uint8_t *vertexBuffer = ReadBytes(numBytes);
557
0
    dest->vertexBindings[bindIndex] = MemoryStreamPtr(new Assimp::MemoryIOStream(vertexBuffer, numBytes, true));
558
559
0
    ASSIMP_LOG_VERBOSE_DEBUG("    - Read vertex buffer for source ", bindIndex, " of ", numBytes, " bytes");
560
0
}
561
562
0
void OgreBinarySerializer::ReadEdgeList(Mesh * /*mesh*/) {
563
    // Assimp does not acknowledge LOD levels as far as I can see it. This info is just skipped.
564
565
0
    if (!AtEnd()) {
566
0
        uint16_t id = ReadHeader();
567
0
        while (!AtEnd() && id == M_EDGE_LIST_LOD) {
568
0
            m_reader->IncPtr(sizeof(uint16_t)); // lod index
569
0
            bool manual = Read<bool>();
570
571
0
            if (!manual) {
572
0
                m_reader->IncPtr(sizeof(uint8_t));
573
0
                uint32_t numTriangles = Read<uint32_t>();
574
0
                uint32_t numEdgeGroups = Read<uint32_t>();
575
576
0
                size_t skipBytes = (sizeof(uint32_t) * 8 + sizeof(float) * 4) * numTriangles;
577
0
                m_reader->IncPtr(skipBytes);
578
579
0
                for (size_t i = 0; i < numEdgeGroups; ++i) {
580
0
                    uint16_t curId = ReadHeader();
581
0
                    if (curId != M_EDGE_GROUP)
582
0
                        throw DeadlyImportError("M_EDGE_GROUP not found in M_EDGE_LIST_LOD");
583
584
0
                    m_reader->IncPtr(sizeof(uint32_t) * 3);
585
0
                    uint32_t numEdges = Read<uint32_t>();
586
0
                    for (size_t j = 0; j < numEdges; ++j) {
587
0
                        m_reader->IncPtr(sizeof(uint32_t) * 6 + sizeof(uint8_t));
588
0
                    }
589
0
                }
590
0
            }
591
592
0
            if (!AtEnd())
593
0
                id = ReadHeader();
594
0
        }
595
0
        if (!AtEnd())
596
0
            RollbackHeader();
597
0
    }
598
0
}
599
600
0
void OgreBinarySerializer::ReadPoses(Mesh *mesh) {
601
0
    if (!AtEnd()) {
602
0
        uint16_t id = ReadHeader();
603
0
        while (!AtEnd() && id == M_POSE) {
604
0
            Pose *pose = new Pose();
605
0
            pose->name = ReadLine();
606
0
            pose->target = Read<uint16_t>();
607
0
            pose->hasNormals = Read<bool>();
608
609
0
            ReadPoseVertices(pose);
610
611
0
            mesh->poses.push_back(pose);
612
613
0
            if (!AtEnd())
614
0
                id = ReadHeader();
615
0
        }
616
0
        if (!AtEnd())
617
0
            RollbackHeader();
618
0
    }
619
0
}
620
621
0
void OgreBinarySerializer::ReadPoseVertices(Pose *pose) {
622
0
    if (!AtEnd()) {
623
0
        uint16_t id = ReadHeader();
624
0
        while (!AtEnd() && id == M_POSE_VERTEX) {
625
0
            Pose::Vertex v;
626
0
            v.index = Read<uint32_t>();
627
0
            ReadVector(v.offset);
628
0
            if (pose->hasNormals)
629
0
                ReadVector(v.normal);
630
631
0
            pose->vertices[v.index] = v;
632
633
0
            if (!AtEnd())
634
0
                id = ReadHeader();
635
0
        }
636
0
        if (!AtEnd())
637
0
            RollbackHeader();
638
0
    }
639
0
}
640
641
0
void OgreBinarySerializer::ReadAnimations(Mesh *mesh) {
642
0
    if (!AtEnd()) {
643
0
        uint16_t id = ReadHeader();
644
0
        while (!AtEnd() && id == M_ANIMATION) {
645
0
            Animation *anim = new Animation(mesh);
646
0
            anim->name = ReadLine();
647
0
            anim->length = Read<float>();
648
649
0
            ReadAnimation(anim);
650
651
0
            mesh->animations.push_back(anim);
652
653
0
            if (!AtEnd())
654
0
                id = ReadHeader();
655
0
        }
656
0
        if (!AtEnd())
657
0
            RollbackHeader();
658
0
    }
659
0
}
660
661
0
void OgreBinarySerializer::ReadAnimation(Animation *anim) {
662
0
    if (!AtEnd()) {
663
0
        uint16_t id = ReadHeader();
664
0
        if (id == M_ANIMATION_BASEINFO) {
665
0
            anim->baseName = ReadLine();
666
0
            anim->baseTime = Read<float>();
667
668
            // Advance to first track
669
0
            id = ReadHeader();
670
0
        }
671
672
0
        while (!AtEnd() && id == M_ANIMATION_TRACK) {
673
0
            VertexAnimationTrack track;
674
0
            track.type = static_cast<VertexAnimationTrack::Type>(Read<uint16_t>());
675
0
            track.target = Read<uint16_t>();
676
677
0
            ReadAnimationKeyFrames(anim, &track);
678
679
0
            anim->tracks.push_back(track);
680
681
0
            if (!AtEnd())
682
0
                id = ReadHeader();
683
0
        }
684
0
        if (!AtEnd())
685
0
            RollbackHeader();
686
0
    }
687
0
}
688
689
0
void OgreBinarySerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track) {
690
0
    if (!AtEnd()) {
691
0
        uint16_t id = ReadHeader();
692
0
        while (!AtEnd() &&
693
0
                (id == M_ANIMATION_MORPH_KEYFRAME ||
694
0
                        id == M_ANIMATION_POSE_KEYFRAME)) {
695
0
            if (id == M_ANIMATION_MORPH_KEYFRAME) {
696
0
                MorphKeyFrame kf;
697
0
                kf.timePos = Read<float>();
698
0
                bool hasNormals = Read<bool>();
699
700
0
                size_t vertexCount = anim->AssociatedVertexData(track)->count;
701
0
                size_t vertexSize = sizeof(float) * (hasNormals ? 6 : 3);
702
0
                size_t numBytes = vertexCount * vertexSize;
703
704
0
                uint8_t *morphBuffer = ReadBytes(numBytes);
705
0
                kf.buffer = MemoryStreamPtr(new Assimp::MemoryIOStream(morphBuffer, numBytes, true));
706
707
0
                track->morphKeyFrames.push_back(kf);
708
0
            } else if (id == M_ANIMATION_POSE_KEYFRAME) {
709
0
                PoseKeyFrame kf;
710
0
                kf.timePos = Read<float>();
711
712
0
                if (!AtEnd()) {
713
0
                    id = ReadHeader();
714
0
                    while (!AtEnd() && id == M_ANIMATION_POSE_REF) {
715
0
                        PoseRef pr;
716
0
                        pr.index = Read<uint16_t>();
717
0
                        pr.influence = Read<float>();
718
0
                        kf.references.push_back(pr);
719
720
0
                        if (!AtEnd())
721
0
                            id = ReadHeader();
722
0
                    }
723
0
                    if (!AtEnd())
724
0
                        RollbackHeader();
725
0
                }
726
727
0
                track->poseKeyFrames.push_back(kf);
728
0
            }
729
730
0
            if (!AtEnd())
731
0
                id = ReadHeader();
732
0
        }
733
0
        if (!AtEnd())
734
0
            RollbackHeader();
735
0
    }
736
0
}
737
738
// Skeleton
739
740
0
bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh) {
741
0
    if (!mesh || mesh->skeletonRef.empty())
742
0
        return false;
743
744
    // Highly unusual to see in read world cases but support
745
    // binary mesh referencing a XML skeleton file.
746
0
    if (EndsWith(mesh->skeletonRef, ".skeleton.xml", false)) {
747
0
        OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh);
748
0
        return false;
749
0
    }
750
751
0
    MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef);
752
0
    if (!reader)
753
0
        return false;
754
755
0
    Skeleton *skeleton = new Skeleton();
756
0
    OgreBinarySerializer serializer(reader.get(), OgreBinarySerializer::AM_Skeleton);
757
0
    serializer.ReadSkeleton(skeleton);
758
0
    mesh->skeleton = skeleton;
759
0
    return true;
760
0
}
761
762
0
bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh) {
763
0
    if (!mesh || mesh->skeletonRef.empty())
764
0
        return false;
765
766
0
    MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef);
767
0
    if (!reader.get())
768
0
        return false;
769
770
0
    Skeleton *skeleton = new Skeleton();
771
0
    OgreBinarySerializer serializer(reader.get(), OgreBinarySerializer::AM_Skeleton);
772
0
    serializer.ReadSkeleton(skeleton);
773
0
    mesh->skeleton = skeleton;
774
0
    return true;
775
0
}
776
777
0
MemoryStreamReaderPtr OgreBinarySerializer::OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename) {
778
0
    if (!EndsWith(filename, ".skeleton", false)) {
779
0
        ASSIMP_LOG_ERROR("Imported Mesh is referencing to unsupported '", filename, "' skeleton file.");
780
0
        return MemoryStreamReaderPtr();
781
0
    }
782
783
0
    if (!pIOHandler->Exists(filename)) {
784
0
        ASSIMP_LOG_ERROR("Failed to find skeleton file '", filename, "' that is referenced by imported Mesh.");
785
0
        return MemoryStreamReaderPtr();
786
0
    }
787
788
0
    IOStream *f = pIOHandler->Open(filename, "rb");
789
0
    if (!f) {
790
0
        throw DeadlyImportError("Failed to open skeleton file ", filename);
791
0
    }
792
793
0
    return MemoryStreamReaderPtr(new MemoryStreamReader(f));
794
0
}
795
796
0
void OgreBinarySerializer::ReadSkeleton(Skeleton *skeleton) {
797
0
    uint16_t id = ReadHeader(false);
798
0
    if (id != HEADER_CHUNK_ID) {
799
0
        throw DeadlyImportError("Invalid Ogre Skeleton file header.");
800
0
    }
801
802
    // This deserialization supports both versions of the skeleton spec
803
0
    std::string version = ReadLine();
804
0
    if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1) {
805
0
        throw DeadlyImportError("Skeleton version ", version, " not supported by this importer.",
806
0
                                                    " Supported versions: ", SKELETON_VERSION_1_8, " and ", SKELETON_VERSION_1_1);
807
0
    }
808
809
0
    ASSIMP_LOG_VERBOSE_DEBUG("Reading Skeleton");
810
811
0
    bool firstBone = true;
812
0
    bool firstAnim = true;
813
814
0
    while (!AtEnd()) {
815
0
        id = ReadHeader();
816
0
        switch (id) {
817
0
            case SKELETON_BLENDMODE: {
818
0
                skeleton->blendMode = static_cast<Skeleton::BlendMode>(Read<uint16_t>());
819
0
                break;
820
0
            }
821
0
            case SKELETON_BONE: {
822
0
                if (firstBone) {
823
0
                    ASSIMP_LOG_VERBOSE_DEBUG("  - Bones");
824
0
                    firstBone = false;
825
0
                }
826
827
0
                ReadBone(skeleton);
828
0
                break;
829
0
            }
830
0
            case SKELETON_BONE_PARENT: {
831
0
                ReadBoneParent(skeleton);
832
0
                break;
833
0
            }
834
0
            case SKELETON_ANIMATION: {
835
0
                if (firstAnim) {
836
0
                    ASSIMP_LOG_VERBOSE_DEBUG("  - Animations");
837
0
                    firstAnim = false;
838
0
                }
839
840
0
                ReadSkeletonAnimation(skeleton);
841
0
                break;
842
0
            }
843
0
            case SKELETON_ANIMATION_LINK: {
844
0
                ReadSkeletonAnimationLink(skeleton);
845
0
                break;
846
0
            }
847
0
        }
848
0
    }
849
850
    // Calculate bone matrices for root bones. Recursively calculates their children.
851
0
    for (size_t i = 0, len = skeleton->bones.size(); i < len; ++i) {
852
0
        Bone *bone = skeleton->bones[i];
853
0
        if (!bone->IsParented())
854
0
            bone->CalculateWorldMatrixAndDefaultPose(skeleton);
855
0
    }
856
0
}
857
858
0
void OgreBinarySerializer::ReadBone(Skeleton *skeleton) {
859
0
    Bone *bone = new Bone();
860
0
    bone->name = ReadLine();
861
0
    bone->id = Read<uint16_t>();
862
863
    // Pos and rot
864
0
    ReadVector(bone->position);
865
0
    ReadQuaternion(bone->rotation);
866
867
    // Scale (optional)
868
0
    if (m_currentLen > MSTREAM_BONE_SIZE_WITHOUT_SCALE)
869
0
        ReadVector(bone->scale);
870
871
    // Bone indexes need to start from 0 and be contiguous
872
0
    if (bone->id != skeleton->bones.size()) {
873
0
        throw DeadlyImportError("Ogre Skeleton bone indexes not contiguous. Error at bone index ", bone->id);
874
0
    }
875
876
0
    ASSIMP_LOG_VERBOSE_DEBUG("    ", bone->id, " ", bone->name);
877
878
0
    skeleton->bones.push_back(bone);
879
0
}
880
881
0
void OgreBinarySerializer::ReadBoneParent(Skeleton *skeleton) {
882
0
    uint16_t childId = Read<uint16_t>();
883
0
    uint16_t parentId = Read<uint16_t>();
884
885
0
    Bone *child = skeleton->BoneById(childId);
886
0
    Bone *parent = skeleton->BoneById(parentId);
887
888
0
    if (child && parent)
889
0
        parent->AddChild(child);
890
0
    else
891
0
        throw DeadlyImportError("Failed to find bones for parenting: Child id ", childId, " for parent id ", parentId);
892
0
}
893
894
0
void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton) {
895
0
    Animation *anim = new Animation(skeleton);
896
0
    anim->name = ReadLine();
897
0
    anim->length = Read<float>();
898
899
0
    if (!AtEnd()) {
900
0
        uint16_t id = ReadHeader();
901
0
        if (id == SKELETON_ANIMATION_BASEINFO) {
902
0
            anim->baseName = ReadLine();
903
0
            anim->baseTime = Read<float>();
904
905
            // Advance to first track
906
0
            id = ReadHeader();
907
0
        }
908
909
0
        while (!AtEnd() && id == SKELETON_ANIMATION_TRACK) {
910
0
            ReadSkeletonAnimationTrack(skeleton, anim);
911
912
0
            if (!AtEnd())
913
0
                id = ReadHeader();
914
0
        }
915
0
        if (!AtEnd())
916
0
            RollbackHeader();
917
0
    }
918
919
0
    skeleton->animations.push_back(anim);
920
921
0
    ASSIMP_LOG_VERBOSE_DEBUG("    ", anim->name, " (", anim->length, " sec, ", anim->tracks.size(), " tracks)");
922
0
}
923
924
0
void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton * /*skeleton*/, Animation *dest) {
925
0
    uint16_t boneId = Read<uint16_t>();
926
0
    Bone *bone = dest->parentSkeleton->BoneById(boneId);
927
0
    if (!bone) {
928
0
        throw DeadlyImportError("Cannot read animation track, target bone ", boneId, " not in target Skeleton");
929
0
    }
930
931
0
    VertexAnimationTrack track;
932
0
    track.type = VertexAnimationTrack::VAT_TRANSFORM;
933
0
    track.boneName = bone->name;
934
935
0
    uint16_t id = ReadHeader();
936
0
    while (!AtEnd() && id == SKELETON_ANIMATION_TRACK_KEYFRAME) {
937
0
        ReadSkeletonAnimationKeyFrame(&track);
938
939
0
        if (!AtEnd())
940
0
            id = ReadHeader();
941
0
    }
942
0
    if (!AtEnd())
943
0
        RollbackHeader();
944
945
0
    dest->tracks.push_back(track);
946
0
}
947
948
0
void OgreBinarySerializer::ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *dest) {
949
0
    TransformKeyFrame keyframe;
950
0
    keyframe.timePos = Read<float>();
951
952
    // Rot and pos
953
0
    ReadQuaternion(keyframe.rotation);
954
0
    ReadVector(keyframe.position);
955
956
    // Scale (optional)
957
0
    if (m_currentLen > MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE)
958
0
        ReadVector(keyframe.scale);
959
960
0
    dest->transformKeyFrames.push_back(keyframe);
961
0
}
962
963
0
void OgreBinarySerializer::ReadSkeletonAnimationLink(Skeleton * /*skeleton*/) {
964
    // Skip bounds, not compatible with Assimp.
965
0
    ReadLine(); // skeleton name
966
0
    SkipBytes(sizeof(float) * 3); // scale
967
0
}
968
969
} // namespace Ogre
970
} // namespace Assimp
971
972
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER