/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 |