Coverage Report

Created: 2025-08-28 06:38

/src/assimp/code/AssetLib/MDL/MDLLoader.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2025, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
42
/// @file MDLLoader.h
43
/// @brief Declaration of the loader for MDL files
44
#pragma once
45
#ifndef AI_MDLLOADER_H_INCLUDED
46
#define AI_MDLLOADER_H_INCLUDED
47
48
#include <assimp/BaseImporter.h>
49
#include "MDLFileData.h"
50
#include "AssetLib/HMP/HalfLifeFileData.h"
51
#include "HalfLife/HL1ImportSettings.h"
52
53
struct aiNode;
54
struct aiTexture;
55
56
namespace Assimp {
57
58
using namespace MDL;
59
60
// --------------------------------------------------------------------------------------
61
// Include file/line information in debug builds
62
#ifdef ASSIMP_BUILD_DEBUG
63
0
#   define VALIDATE_FILE_SIZE(msg) SizeCheck(msg,__FILE__,__LINE__)
64
#else
65
#   define VALIDATE_FILE_SIZE(msg) SizeCheck(msg)
66
#endif
67
68
// --------------------------------------------------------------------------------------
69
/** @brief Class to load MDL files.
70
 *
71
 *  Several subformats exist:
72
 *   <ul>
73
 *      <li>Quake I</li>
74
 *      <li>3D Game Studio MDL3, MDL4</li>
75
 *      <li>3D Game Studio MDL5</li>
76
 *      <li>3D Game Studio MDL7</li>
77
 *      <li>Halflife 1</li>
78
 *      <li>Halflife 2</li>
79
 *   </ul>
80
 *  These formats are partially identical and it would be possible to load
81
 *  them all with a single 1000-line function-beast. However, it has been
82
 *  split into several code paths to make the code easier to read and maintain.
83
*/
84
class MDLImporter : public BaseImporter {
85
public:
86
    MDLImporter();
87
758
    ~MDLImporter() override = default;
88
89
    // -------------------------------------------------------------------
90
    /** Returns whether the class can handle the format of the given file.
91
    * See BaseImporter::CanRead() for details.  */
92
    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
93
        bool checkSig) const override;
94
95
    // -------------------------------------------------------------------
96
    /** Called prior to ReadFile().
97
    * The function is a request to the importer to update its configuration
98
    * basing on the Importer's configuration property list.
99
    */
100
    void SetupProperties(const Importer* pImp) override;
101
102
protected:
103
    // -------------------------------------------------------------------
104
    /** Return importer meta information.
105
     * See #BaseImporter::GetInfo for the details
106
     */
107
    const aiImporterDesc* GetInfo () const override;
108
109
    // -------------------------------------------------------------------
110
    /** Imports the given file into the given scene structure.
111
    * See BaseImporter::InternReadFile() for details
112
    */
113
    void InternReadFile( const std::string& pFile, aiScene* pScene,
114
        IOSystem* pIOHandler) override;
115
116
    // -------------------------------------------------------------------
117
    /** Import a quake 1 MDL file (IDPO)
118
    */
119
    void InternReadFile_Quake1( );
120
121
    // -------------------------------------------------------------------
122
    /** Import a GameStudio A4/A5 file (MDL 3,4,5)
123
    */
124
    void InternReadFile_3DGS_MDL345( );
125
126
    // -------------------------------------------------------------------
127
    /** Import a GameStudio A7 file (MDL 7)
128
    */
129
    void InternReadFile_3DGS_MDL7( );
130
131
    // -------------------------------------------------------------------
132
    /** Import a Half-Life 1 MDL file
133
    */
134
    void InternReadFile_HL1(const std::string& pFile, const uint32_t iMagicWord);
135
136
    // -------------------------------------------------------------------
137
    /** Import a CS:S/HL2 MDL file (not fully implemented)
138
    */
139
    AI_WONT_RETURN void InternReadFile_HL2( ) AI_WONT_RETURN_SUFFIX;
140
141
    // -------------------------------------------------------------------
142
    /** Check whether a given position is inside the valid range
143
     *  Throw a DeadlyImportError if it is not
144
     * \param szPos Cursor position
145
     * \param szFile Name of the source file from which the function was called
146
     * \param iLine Source code line from which the function was called
147
    */
148
    void SizeCheck(const void* szPos);
149
    void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine);
150
    bool IsPosValid(const void* szPos) const;
151
152
    // -------------------------------------------------------------------
153
    /** Validate the header data structure of a game studio MDL7 file
154
     * \param pcHeader Input header to be validated
155
     */
156
    void ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7* pcHeader);
157
158
    // -------------------------------------------------------------------
159
    /** Validate the header data structure of a Quake 1 model
160
     * \param pcHeader Input header to be validated
161
     */
162
    void ValidateHeader_Quake1(const MDL::Header* pcHeader);
163
164
    // -------------------------------------------------------------------
165
    /** Try to load a  palette from the current directory (colormap.lmp)
166
     *  If it is not found the default palette of Quake1 is returned
167
     */
168
    void SearchPalette(const unsigned char** pszColorMap);
169
170
    // -------------------------------------------------------------------
171
    /** Free a palette created with a previous call to SearchPalette()
172
     */
173
    void FreePalette(const unsigned char* pszColorMap);
174
175
    // -------------------------------------------------------------------
176
    /** Load a palletized texture from the file and convert it to 32bpp
177
    */
178
    void CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData);
179
180
    // -------------------------------------------------------------------
181
    /** Used to load textures from MDL3/4
182
     * \param szData Input data
183
     * \param iType Color data type
184
     * \param piSkip Receive: Size to skip, in bytes
185
    */
186
    void CreateTexture_3DGS_MDL4(const unsigned char* szData,
187
        unsigned int iType,
188
        unsigned int* piSkip);
189
190
    // -------------------------------------------------------------------
191
    /** Used to load textures from MDL5
192
     * \param szData Input data
193
     * \param iType Color data type
194
     * \param piSkip Receive: Size to skip, in bytes
195
    */
196
    void CreateTexture_3DGS_MDL5(const unsigned char* szData,
197
        unsigned int iType,
198
        unsigned int* piSkip);
199
200
    // -------------------------------------------------------------------
201
    /** Checks whether a texture can be replaced with a single color
202
     * This is useful for all file formats before MDL7 (all those
203
     * that are not containing material colors separate from textures).
204
     * MED seems to write dummy 8x8 monochrome images instead.
205
     * \param pcTexture Input texture
206
     * \return aiColor.r is set to qnan if the function fails and no
207
     *   color can be found.
208
    */
209
    aiColor4D ReplaceTextureWithColor(const aiTexture* pcTexture);
210
211
    // -------------------------------------------------------------------
212
    /** Converts the absolute texture coordinates in MDL5 files to
213
     *  relative in a range between 0 and 1
214
    */
215
    void CalculateUVCoordinates_MDL5();
216
217
    // -------------------------------------------------------------------
218
    /** Read an UV coordinate from the file. If the file format is not
219
     * MDL5, the function calculates relative texture coordinates
220
     * \param vOut Receives the output UV coord
221
     * \param pcSrc UV coordinate buffer
222
     * \param UV coordinate index
223
    */
224
    void ImportUVCoordinate_3DGS_MDL345( aiVector3D& vOut,
225
        const MDL::TexCoord_MDL3* pcSrc,
226
        unsigned int iIndex);
227
228
    // -------------------------------------------------------------------
229
    /** Setup the material properties for Quake and MDL<7 models.
230
     * These formats don't support more than one material per mesh,
231
     * therefore the method processes only ONE skin and removes
232
     * all others.
233
     */
234
    void SetupMaterialProperties_3DGS_MDL5_Quake1( );
235
236
    // -------------------------------------------------------------------
237
    /** Parse a skin lump in a MDL7/HMP7 file with all of its features
238
     *  variant 1: Current cursor position is the beginning of the skin header
239
     * \param szCurrent Current data pointer
240
     * \param szCurrentOut Output data pointer
241
     * \param pcMats Material list for this group. To be filled ...
242
     */
243
    void ParseSkinLump_3DGS_MDL7(
244
        const unsigned char* szCurrent,
245
        const unsigned char** szCurrentOut,
246
        std::vector<aiMaterial*>& pcMats);
247
248
    // -------------------------------------------------------------------
249
    /** Parse a skin lump in a MDL7/HMP7 file with all of its features
250
     *  variant 2: Current cursor position is the beginning of the skin data
251
     * \param szCurrent Current data pointer
252
     * \param szCurrentOut Output data pointer
253
     * \param pcMatOut Output material
254
     * \param iType header.typ
255
     * \param iWidth header.width
256
     * \param iHeight header.height
257
     */
258
    void ParseSkinLump_3DGS_MDL7(
259
        const unsigned char* szCurrent,
260
        const unsigned char** szCurrentOut,
261
        aiMaterial* pcMatOut,
262
        unsigned int iType,
263
        unsigned int iWidth,
264
        unsigned int iHeight);
265
266
    // -------------------------------------------------------------------
267
    /** Skip a skin lump in a MDL7/HMP7 file
268
     * \param szCurrent Current data pointer
269
     * \param szCurrentOut Output data pointer. Points to the byte just
270
     * behind the last byte of the skin.
271
     * \param iType header.typ
272
     * \param iWidth header.width
273
     * \param iHeight header.height
274
     */
275
    void SkipSkinLump_3DGS_MDL7(const unsigned char* szCurrent,
276
        const unsigned char** szCurrentOut,
277
        unsigned int iType,
278
        unsigned int iWidth,
279
        unsigned int iHeight);
280
281
    // -------------------------------------------------------------------
282
    /** Parse texture color data for MDL5, MDL6 and MDL7 formats
283
     * \param szData Current data pointer
284
     * \param iType type of the texture data. No DDS or external
285
     * \param piSkip Receive the number of bytes to skip
286
     * \param pcNew Must point to fully initialized data. Width and
287
     *        height must be set. If pcNew->pcData is set to UINT_MAX,
288
     *        piSkip will receive the size of the texture, in bytes, but no
289
     *        color data will be read.
290
     */
291
    void ParseTextureColorData(const unsigned char* szData,
292
        unsigned int iType,
293
        unsigned int* piSkip,
294
        aiTexture* pcNew);
295
296
    // -------------------------------------------------------------------
297
    /** Join two materials / skins. Setup UV source ... etc
298
     * \param pcMat1 First input material
299
     * \param pcMat2 Second input material
300
     * \param pcMatOut Output material instance to be filled. Must be empty
301
     */
302
    void JoinSkins_3DGS_MDL7(aiMaterial* pcMat1,
303
        aiMaterial* pcMat2,
304
        aiMaterial* pcMatOut);
305
306
    // -------------------------------------------------------------------
307
    /** Add a bone transformation key to an animation
308
     * \param iTrafo Index of the transformation (always==frame index?)
309
     * No need to validate this index, it is always valid.
310
     * \param pcBoneTransforms Bone transformation for this index
311
     * \param apcOutBones Output bones array
312
     */
313
    void AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo,
314
        const MDL::BoneTransform_MDL7* pcBoneTransforms,
315
        MDL::IntBone_MDL7** apcBonesOut);
316
317
    // -------------------------------------------------------------------
318
    /** Load the bone list of a MDL7 file
319
     * \return If the bones could be loaded successfully, a valid
320
     *   array containing pointers to a temporary bone
321
     *   representation. nullptr if the bones could not be loaded.
322
     */
323
    MDL::IntBone_MDL7** LoadBones_3DGS_MDL7();
324
325
    // -------------------------------------------------------------------
326
    /** Load bone transformation keyframes from a file chunk
327
     * \param groupInfo -> doc of data structure
328
     * \param frame -> doc of data structure
329
     * \param shared -> doc of data structure
330
     */
331
    void ParseBoneTrafoKeys_3DGS_MDL7(
332
        const MDL::IntGroupInfo_MDL7& groupInfo,
333
        IntFrameInfo_MDL7& frame,
334
        MDL::IntSharedData_MDL7& shared);
335
336
    // -------------------------------------------------------------------
337
    /** Calculate absolute bone animation matrices for each bone
338
     * \param apcOutBones Output bones array
339
     */
340
    void CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7** apcOutBones);
341
342
    // -------------------------------------------------------------------
343
    /** Add all bones to the nodegraph (as children of the root node)
344
     * \param apcBonesOut List of bones
345
     * \param pcParent Parent node. New nodes will be added to this node
346
     * \param iParentIndex Index of the parent bone
347
     */
348
    void AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7** apcBonesOut,
349
        aiNode* pcParent,uint16_t iParentIndex);
350
351
    // -------------------------------------------------------------------
352
    /** Build output animations
353
     * \param apcBonesOut List of bones
354
     */
355
    void BuildOutputAnims_3DGS_MDL7(const MDL::IntBone_MDL7** apcBonesOut);
356
357
    // -------------------------------------------------------------------
358
    /** Handles materials that are just referencing another material
359
     * There is no test file for this feature, but Conitec's doc
360
     * say it is used.
361
     */
362
    void HandleMaterialReferences_3DGS_MDL7();
363
364
    // -------------------------------------------------------------------
365
    /** Copies only the material that are referenced by at least one
366
     * mesh to the final output material list. All other materials
367
     * will be discarded.
368
     * \param shared -> doc of data structure
369
     */
370
    void CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared);
371
372
    // -------------------------------------------------------------------
373
    /** Process the frame section at the end of a group
374
     * \param groupInfo -> doc of data structure
375
     * \param shared -> doc of data structure
376
     * \param szCurrent Pointer to the start of the frame section
377
     * \param szCurrentOut Receives a pointer to the first byte of the
378
     *   next data section.
379
     * \return false to read no further groups (a small workaround for
380
     *   some tiny and unsolved problems ... )
381
     */
382
    bool ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
383
        MDL::IntGroupData_MDL7& groupData,
384
        MDL::IntSharedData_MDL7& shared,
385
        const unsigned char* szCurrent,
386
        const unsigned char** szCurrentOut);
387
388
    // -------------------------------------------------------------------
389
    /** Sort all faces by their materials. If the mesh is using
390
     * multiple materials per face (that are blended together) the function
391
     * might create new materials.
392
     * \param groupInfo -> doc of data structure
393
     * \param groupData -> doc of data structure
394
     * \param splitGroupData -> doc of data structure
395
     */
396
    void SortByMaterials_3DGS_MDL7(
397
        const MDL::IntGroupInfo_MDL7& groupInfo,
398
        MDL::IntGroupData_MDL7& groupData,
399
        MDL::IntSplitGroupData_MDL7& splitGroupData);
400
401
    // -------------------------------------------------------------------
402
    /** Read all faces and vertices from a MDL7 group. The function fills
403
     *  preallocated memory buffers.
404
     * \param groupInfo -> doc of data structure
405
     * \param groupData -> doc of data structure
406
     */
407
    void ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
408
        MDL::IntGroupData_MDL7& groupData);
409
410
    // -------------------------------------------------------------------
411
    /** Generate the final output meshes for a7 models
412
     * \param groupData -> doc of data structure
413
     * \param splitGroupData -> doc of data structure
414
     */
415
    void GenerateOutputMeshes_3DGS_MDL7(
416
        MDL::IntGroupData_MDL7& groupData,
417
        MDL::IntSplitGroupData_MDL7& splitGroupData);
418
419
protected:
420
421
    /** Configuration option: frame to be loaded */
422
    unsigned int configFrameID;
423
424
    /** Configuration option: palette to be used to decode palletized images*/
425
    std::string configPalette;
426
427
    /** Buffer to hold the loaded file */
428
    unsigned char* mBuffer;
429
430
    /** For GameStudio MDL files: The number in the magic word, either 3,4 or 5
431
     * (MDL7 doesn't need this, the format has a separate loader) */
432
    unsigned int iGSFileVersion;
433
434
    /** Output I/O handler. used to load external lmp files */
435
    IOSystem* mIOHandler;
436
437
    /** Output scene to be filled */
438
    aiScene* pScene;
439
440
    /** Size of the input file in bytes */
441
    unsigned int iFileSize;
442
443
    /* Configuration for HL1 MDL */
444
    HalfLife::HL1ImportSettings mHL1ImportSettings;
445
};
446
447
} // end of namespace Assimp
448
449
#endif // AI_3DSIMPORTER_H_INC