1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2019, 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 HL1MDLLoader.cpp
43 * @brief Implementation for the Half-Life 1 MDL loader.
44 */
45
46 #include "HL1MDLLoader.h"
47 #include "HL1ImportDefinitions.h"
48 #include "HL1MeshTrivert.h"
49 #include "UniqueNameGenerator.h"
50
51 #include <assimp/BaseImporter.h>
52 #include <assimp/StringUtils.h>
53 #include <assimp/ai_assert.h>
54 #include <assimp/qnan.h>
55 #include <assimp/DefaultLogger.hpp>
56 #include <assimp/Importer.hpp>
57
58 #include <iomanip>
59 #include <sstream>
60
61 #ifdef MDL_HALFLIFE_LOG_WARN_HEADER
62 #undef MDL_HALFLIFE_LOG_WARN_HEADER
63 #endif
64 #define MDL_HALFLIFE_LOG_HEADER "[Half-Life 1 MDL] "
65 #include "LogFunctions.h"
66
67 namespace Assimp {
68 namespace MDL {
69 namespace HalfLife {
70
71 // ------------------------------------------------------------------------------------------------
HL1MDLLoader(aiScene * scene,IOSystem * io,const unsigned char * buffer,const std::string & file_path,const HL1ImportSettings & import_settings)72 HL1MDLLoader::HL1MDLLoader(
73 aiScene *scene,
74 IOSystem *io,
75 const unsigned char *buffer,
76 const std::string &file_path,
77 const HL1ImportSettings &import_settings) :
78 scene_(scene),
79 io_(io),
80 buffer_(buffer),
81 file_path_(file_path),
82 import_settings_(import_settings),
83 header_(nullptr),
84 texture_header_(nullptr),
85 anim_headers_(nullptr),
86 texture_buffer_(nullptr),
87 anim_buffers_(nullptr),
88 num_sequence_groups_(0),
89 rootnode_children_(),
90 unique_name_generator_(),
91 unique_sequence_names_(),
92 unique_sequence_groups_names_(),
93 temp_bones_(),
94 num_blend_controllers_(0),
95 total_models_(0) {
96 load_file();
97 }
98
99 // ------------------------------------------------------------------------------------------------
~HL1MDLLoader()100 HL1MDLLoader::~HL1MDLLoader() {
101 release_resources();
102 }
103
104 // ------------------------------------------------------------------------------------------------
release_resources()105 void HL1MDLLoader::release_resources() {
106 if (buffer_ != texture_buffer_) {
107 delete[] texture_buffer_;
108 texture_buffer_ = nullptr;
109 }
110
111 if (num_sequence_groups_ && anim_buffers_) {
112 for (int i = 1; i < num_sequence_groups_; ++i) {
113 if (anim_buffers_[i]) {
114 delete[] anim_buffers_[i];
115 anim_buffers_[i] = nullptr;
116 }
117 }
118
119 delete[] anim_buffers_;
120 anim_buffers_ = nullptr;
121 }
122
123 if (anim_headers_) {
124 delete[] anim_headers_;
125 anim_headers_ = nullptr;
126 }
127 }
128
129 // ------------------------------------------------------------------------------------------------
load_file()130 void HL1MDLLoader::load_file() {
131
132 try {
133 header_ = (const Header_HL1 *)buffer_;
134 validate_header(header_, false);
135
136 // Create the root scene node.
137 scene_->mRootNode = new aiNode(AI_MDL_HL1_NODE_ROOT);
138
139 load_texture_file();
140
141 if (import_settings_.read_animations)
142 load_sequence_groups_files();
143
144 read_textures();
145 read_skins();
146
147 read_bones();
148 read_meshes();
149
150 if (import_settings_.read_animations) {
151 read_sequence_groups_info();
152 read_animations();
153 read_sequence_infos();
154 if (import_settings_.read_sequence_transitions)
155 read_sequence_transitions();
156 }
157
158 if (import_settings_.read_attachments)
159 read_attachments();
160
161 if (import_settings_.read_hitboxes)
162 read_hitboxes();
163
164 if (import_settings_.read_bone_controllers)
165 read_bone_controllers();
166
167 read_global_info();
168
169 // Append children to root node.
170 if (rootnode_children_.size()) {
171 scene_->mRootNode->addChildren(
172 static_cast<unsigned int>(rootnode_children_.size()),
173 rootnode_children_.data());
174 }
175
176 release_resources();
177
178 } catch (const std::exception &e) {
179 release_resources();
180 throw e;
181 }
182 }
183
184 // ------------------------------------------------------------------------------------------------
validate_header(const Header_HL1 * header,bool is_texture_header)185 void HL1MDLLoader::validate_header(const Header_HL1 *header, bool is_texture_header) {
186 if (is_texture_header) {
187 // Every single Half-Life model is assumed to have at least one texture.
188 if (!header->numtextures)
189 throw DeadlyImportError(MDL_HALFLIFE_LOG_HEADER "There are no textures in the file");
190
191 if (header->numtextures > AI_MDL_HL1_MAX_TEXTURES)
192 log_warning_limit_exceeded<AI_MDL_HL1_MAX_TEXTURES>(header->numtextures, "textures");
193
194 if (header->numskinfamilies > AI_MDL_HL1_MAX_SKIN_FAMILIES)
195 log_warning_limit_exceeded<AI_MDL_HL1_MAX_SKIN_FAMILIES>(header->numskinfamilies, "skin families");
196
197 } else {
198 // Every single Half-Life model is assumed to have at least one bodypart.
199 if (!header->numbodyparts)
200 throw DeadlyImportError(MDL_HALFLIFE_LOG_HEADER "Model has no bodyparts");
201
202 // Every single Half-Life model is assumed to have at least one bone.
203 if (!header->numbones)
204 throw DeadlyImportError(MDL_HALFLIFE_LOG_HEADER "Model has no bones");
205
206 // Every single Half-Life model is assumed to have at least one sequence group,
207 // which is the "default" sequence group.
208 if (!header->numseqgroups)
209 throw DeadlyImportError(MDL_HALFLIFE_LOG_HEADER "Model has no sequence groups");
210
211 if (header->numbodyparts > AI_MDL_HL1_MAX_BODYPARTS)
212 log_warning_limit_exceeded<AI_MDL_HL1_MAX_BODYPARTS>(header->numbodyparts, "bodyparts");
213
214 if (header->numbones > AI_MDL_HL1_MAX_BONES)
215 log_warning_limit_exceeded<AI_MDL_HL1_MAX_BONES>(header->numbones, "bones");
216
217 if (header->numbonecontrollers > AI_MDL_HL1_MAX_BONE_CONTROLLERS)
218 log_warning_limit_exceeded<AI_MDL_HL1_MAX_BONE_CONTROLLERS>(header->numbonecontrollers, "bone controllers");
219
220 if (header->numseq > AI_MDL_HL1_MAX_SEQUENCES)
221 log_warning_limit_exceeded<AI_MDL_HL1_MAX_SEQUENCES>(header->numseq, "sequences");
222
223 if (header->numseqgroups > AI_MDL_HL1_MAX_SEQUENCE_GROUPS)
224 log_warning_limit_exceeded<AI_MDL_HL1_MAX_SEQUENCE_GROUPS>(header->numseqgroups, "sequence groups");
225
226 if (header->numattachments > AI_MDL_HL1_MAX_ATTACHMENTS)
227 log_warning_limit_exceeded<AI_MDL_HL1_MAX_ATTACHMENTS>(header->numattachments, "attachments");
228 }
229 }
230
231 // ------------------------------------------------------------------------------------------------
232 /*
233 Load textures.
234
235 There are two ways for textures to be stored in a Half-Life model:
236
237 1. Directly in the MDL file (filePath) or
238 2. In an external MDL file.
239
240 Due to the way StudioMDL works (tool used to compile SMDs into MDLs),
241 it is assumed that an external texture file follows the naming
242 convention: <YourModelName>T.mdl. Note the extra (T) at the end of the
243 model name.
244
245 .e.g For a given model named MyModel.mdl
246
247 The external texture file name would be MyModelT.mdl
248 */
load_texture_file()249 void HL1MDLLoader::load_texture_file() {
250 if (header_->numtextures == 0) {
251 // Load an external MDL texture file.
252 std::string texture_file_path =
253 DefaultIOSystem::absolutePath(file_path_) + io_->getOsSeparator() +
254 DefaultIOSystem::completeBaseName(file_path_) + "T." +
255 BaseImporter::GetExtension(file_path_);
256
257 load_file_into_buffer<Header_HL1>(texture_file_path, texture_buffer_);
258 } else {
259 /* Model has no external texture file. This means the texture
260 is stored inside the main MDL file. */
261 texture_buffer_ = const_cast<unsigned char *>(buffer_);
262 }
263
264 texture_header_ = (const Header_HL1 *)texture_buffer_;
265
266 // Validate texture header.
267 validate_header(texture_header_, true);
268 }
269
270 // ------------------------------------------------------------------------------------------------
271 /*
272 Load sequence group files if any.
273
274 Due to the way StudioMDL works (tool used to compile SMDs into MDLs),
275 it is assumed that a sequence group file follows the naming
276 convention: <YourModelName>0X.mdl. Note the extra (0X) at the end of
277 the model name, where (X) is the sequence group.
278
279 .e.g For a given model named MyModel.mdl
280
281 Sequence group 1 => MyModel01.mdl
282 Sequence group 2 => MyModel02.mdl
283 Sequence group X => MyModel0X.mdl
284
285 */
load_sequence_groups_files()286 void HL1MDLLoader::load_sequence_groups_files() {
287 if (header_->numseqgroups <= 1)
288 return;
289
290 num_sequence_groups_ = header_->numseqgroups;
291
292 anim_buffers_ = new unsigned char *[num_sequence_groups_];
293 anim_headers_ = new SequenceHeader_HL1 *[num_sequence_groups_];
294 for (int i = 0; i < num_sequence_groups_; ++i) {
295 anim_buffers_[i] = NULL;
296 anim_headers_[i] = NULL;
297 }
298
299 std::string file_path_without_extension =
300 DefaultIOSystem::absolutePath(file_path_) +
301 io_->getOsSeparator() +
302 DefaultIOSystem::completeBaseName(file_path_);
303
304 for (int i = 1; i < num_sequence_groups_; ++i) {
305 std::stringstream ss;
306 ss << file_path_without_extension;
307 ss << std::setw(2) << std::setfill('0') << i;
308 ss << '.' << BaseImporter::GetExtension(file_path_);
309
310 std::string sequence_file_path = ss.str();
311
312 load_file_into_buffer<SequenceHeader_HL1>(sequence_file_path, anim_buffers_[i]);
313
314 anim_headers_[i] = (SequenceHeader_HL1 *)anim_buffers_[i];
315 }
316 }
317
318 // ------------------------------------------------------------------------------------------------
319 /** @brief Read an MDL texture.
320 *
321 * @note This method is taken from HL1 source code.
322 * source: file: studio_utils.c
323 * function(s): UploadTexture
324 */
read_texture(const Texture_HL1 * ptexture,uint8_t * data,uint8_t * pal,aiTexture * pResult,aiColor3D & last_palette_color)325 void HL1MDLLoader::read_texture(const Texture_HL1 *ptexture,
326 uint8_t *data, uint8_t *pal, aiTexture *pResult,
327 aiColor3D &last_palette_color) {
328 int outwidth, outheight;
329 int i, j;
330 int row1[256], row2[256], col1[256], col2[256];
331 unsigned char *pix1, *pix2, *pix3, *pix4;
332
333 // convert texture to power of 2
334 for (outwidth = 1; outwidth < ptexture->width; outwidth <<= 1)
335 ;
336
337 if (outwidth > 256)
338 outwidth = 256;
339
340 for (outheight = 1; outheight < ptexture->height; outheight <<= 1)
341 ;
342
343 if (outheight > 256)
344 outheight = 256;
345
346 pResult->mFilename = ptexture->name;
347 pResult->mWidth = outwidth;
348 pResult->mHeight = outheight;
349 pResult->achFormatHint[0] = 'b';
350 pResult->achFormatHint[1] = 'g';
351 pResult->achFormatHint[2] = 'r';
352 pResult->achFormatHint[3] = 'a';
353 pResult->achFormatHint[4] = '8';
354 pResult->achFormatHint[5] = '8';
355 pResult->achFormatHint[6] = '8';
356 pResult->achFormatHint[7] = '8';
357 pResult->achFormatHint[8] = '\0';
358
359 aiTexel *out = pResult->pcData = new aiTexel[outwidth * outheight];
360
361 for (i = 0; i < outwidth; i++) {
362 col1[i] = (int)((i + 0.25) * (ptexture->width / (float)outwidth));
363 col2[i] = (int)((i + 0.75) * (ptexture->width / (float)outwidth));
364 }
365
366 for (i = 0; i < outheight; i++) {
367 row1[i] = (int)((i + 0.25) * (ptexture->height / (float)outheight)) * ptexture->width;
368 row2[i] = (int)((i + 0.75) * (ptexture->height / (float)outheight)) * ptexture->width;
369 }
370
371 // scale down and convert to 32bit RGB
372 for (i = 0; i < outheight; i++) {
373 for (j = 0; j < outwidth; j++, out++) {
374 pix1 = &pal[data[row1[i] + col1[j]] * 3];
375 pix2 = &pal[data[row1[i] + col2[j]] * 3];
376 pix3 = &pal[data[row2[i] + col1[j]] * 3];
377 pix4 = &pal[data[row2[i] + col2[j]] * 3];
378
379 out->r = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2;
380 out->g = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2;
381 out->b = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2;
382 out->a = 0xFF;
383 }
384 }
385
386 // Get the last palette color.
387 last_palette_color.r = pal[255 * 3];
388 last_palette_color.g = pal[255 * 3 + 1];
389 last_palette_color.b = pal[255 * 3 + 2];
390 }
391
392 // ------------------------------------------------------------------------------------------------
read_textures()393 void HL1MDLLoader::read_textures() {
394 const Texture_HL1 *ptexture = (const Texture_HL1 *)((uint8_t *)texture_header_ + texture_header_->textureindex);
395 unsigned char *pin = texture_buffer_;
396
397 scene_->mNumTextures = scene_->mNumMaterials = texture_header_->numtextures;
398 scene_->mTextures = new aiTexture *[scene_->mNumTextures];
399 scene_->mMaterials = new aiMaterial *[scene_->mNumMaterials];
400
401 for (int i = 0; i < texture_header_->numtextures; ++i) {
402 scene_->mTextures[i] = new aiTexture();
403
404 aiColor3D last_palette_color;
405 read_texture(&ptexture[i],
406 pin + ptexture[i].index,
407 pin + ptexture[i].width * ptexture[i].height + ptexture[i].index,
408 scene_->mTextures[i],
409 last_palette_color);
410
411 aiMaterial *scene_material = scene_->mMaterials[i] = new aiMaterial();
412
413 const aiTextureType texture_type = aiTextureType_DIFFUSE;
414 aiString texture_name(ptexture[i].name);
415 scene_material->AddProperty(&texture_name, AI_MATKEY_TEXTURE(texture_type, 0));
416
417 // Is this a chrome texture?
418 int chrome = ptexture[i].flags & AI_MDL_HL1_STUDIO_NF_CHROME ? 1 : 0;
419 scene_material->AddProperty(&chrome, 1, AI_MDL_HL1_MATKEY_CHROME(texture_type, 0));
420
421 if (ptexture[i].flags & AI_MDL_HL1_STUDIO_NF_FLATSHADE) {
422 // Flat shading.
423 const aiShadingMode shading_mode = aiShadingMode_Flat;
424 scene_material->AddProperty(&shading_mode, 1, AI_MATKEY_SHADING_MODEL);
425 }
426
427 if (ptexture[i].flags & AI_MDL_HL1_STUDIO_NF_ADDITIVE) {
428 // Additive texture.
429 const aiBlendMode blend_mode = aiBlendMode_Additive;
430 scene_material->AddProperty(&blend_mode, 1, AI_MATKEY_BLEND_FUNC);
431 } else if (ptexture[i].flags & AI_MDL_HL1_STUDIO_NF_MASKED) {
432 // Texture with 1 bit alpha test.
433 const aiTextureFlags use_alpha = aiTextureFlags_UseAlpha;
434 scene_material->AddProperty(&use_alpha, 1, AI_MATKEY_TEXFLAGS(texture_type, 0));
435 scene_material->AddProperty(&last_palette_color, 1, AI_MATKEY_COLOR_TRANSPARENT);
436 }
437 }
438 }
439
440 // ------------------------------------------------------------------------------------------------
read_skins()441 void HL1MDLLoader::read_skins() {
442 // Read skins, if any.
443 if (texture_header_->numskinfamilies <= 1)
444 return;
445
446 // Pointer to base texture index.
447 short *default_skin_ptr = (short *)((uint8_t *)texture_header_ + texture_header_->skinindex);
448
449 // Start at first replacement skin.
450 short *replacement_skin_ptr = default_skin_ptr + texture_header_->numskinref;
451
452 for (int i = 1; i < texture_header_->numskinfamilies; ++i, replacement_skin_ptr += texture_header_->numskinref) {
453 for (int j = 0; j < texture_header_->numskinref; ++j) {
454 if (default_skin_ptr[j] != replacement_skin_ptr[j]) {
455 // Save replacement textures.
456 aiString skinMaterialId(scene_->mTextures[replacement_skin_ptr[j]]->mFilename);
457 scene_->mMaterials[default_skin_ptr[j]]->AddProperty(&skinMaterialId, AI_MATKEY_TEXTURE_DIFFUSE(i));
458 }
459 }
460 }
461 }
462
463 // ------------------------------------------------------------------------------------------------
read_bones()464 void HL1MDLLoader::read_bones() {
465 const Bone_HL1 *pbone = (const Bone_HL1 *)((uint8_t *)header_ + header_->boneindex);
466
467 std::vector<std::string> unique_bones_names(header_->numbones);
468 for (int i = 0; i < header_->numbones; ++i)
469 unique_bones_names[i] = pbone[i].name;
470
471 // Ensure bones have unique names.
472 unique_name_generator_.set_template_name("Bone");
473 unique_name_generator_.make_unique(unique_bones_names);
474
475 temp_bones_.resize(header_->numbones);
476
477 aiNode *bones_node = new aiNode(AI_MDL_HL1_NODE_BONES);
478 rootnode_children_.push_back(bones_node);
479 bones_node->mNumChildren = static_cast<unsigned int>(header_->numbones);
480 bones_node->mChildren = new aiNode *[bones_node->mNumChildren];
481
482 // Create bone matrices in local space.
483 for (int i = 0; i < header_->numbones; ++i) {
484 aiNode *bone_node = temp_bones_[i].node = bones_node->mChildren[i] = new aiNode(unique_bones_names[i]);
485
486 aiVector3D angles(pbone[i].value[3], pbone[i].value[4], pbone[i].value[5]);
487 temp_bones_[i].absolute_transform = bone_node->mTransformation =
488 aiMatrix4x4(aiVector3D(1), aiQuaternion(angles.y, angles.z, angles.x),
489 aiVector3D(pbone[i].value[0], pbone[i].value[1], pbone[i].value[2]));
490
491 if (pbone[i].parent == -1) {
492 bone_node->mParent = scene_->mRootNode;
493 } else {
494 bone_node->mParent = bones_node->mChildren[pbone[i].parent];
495
496 temp_bones_[i].absolute_transform =
497 temp_bones_[pbone[i].parent].absolute_transform * bone_node->mTransformation;
498 }
499
500 temp_bones_[i].offset_matrix = temp_bones_[i].absolute_transform;
501 temp_bones_[i].offset_matrix.Inverse();
502 }
503 }
504
505 // ------------------------------------------------------------------------------------------------
506 /*
507 Read meshes.
508
509 Half-Life MDLs are structured such that each MDL
510 contains one or more 'bodypart(s)', which contain one
511 or more 'model(s)', which contains one or more mesh(es).
512
513 * Bodyparts are used to group models that may be replaced
514 in the game .e.g a character could have a 'heads' group,
515 'torso' group, 'shoes' group, with each group containing
516 different 'model(s)'.
517
518 * Models, also called 'sub models', contain vertices as
519 well as a reference to each mesh used by the sub model.
520
521 * Meshes contain a list of tris, also known as 'triverts'.
522 Each tris contains the following information:
523
524 1. The index of the position to use for the vertex.
525 2. The index of the normal to use for the vertex.
526 3. The S coordinate to use for the vertex UV.
527 4. The T coordinate ^
528
529 These tris represent the way to represent the triangles
530 for each mesh. Depending on how the tool compiled the MDL,
531 those triangles were saved as strips and or fans.
532
533 NOTE: Each tris is NOT unique. This means that you
534 might encounter the same vertex index but with a different
535 normal index, S coordinate, T coordinate.
536
537 In addition, each mesh contains the texture's index.
538
539 ------------------------------------------------------
540 With the details above, there are several things to
541 take into consideration.
542
543 * The Half-Life models store the vertices by sub model
544 rather than by mesh. Due to Assimp's structure, it
545 is necessary to remap each model vertex to be used
546 per mesh. Unfortunately, this has the consequence
547 to duplicate vertices.
548
549 * Because the mesh triangles are comprised of strips and
550 fans, it is necessary to convert each primitive to
551 triangles, respectively (3 indices per face).
552 */
read_meshes()553 void HL1MDLLoader::read_meshes() {
554
555 int total_verts = 0;
556 int total_triangles = 0;
557 total_models_ = 0;
558
559 const Bodypart_HL1 *pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
560 const Model_HL1 *pmodel = nullptr;
561 const Mesh_HL1 *pmesh = nullptr;
562
563 const Texture_HL1 *ptexture = (const Texture_HL1 *)((uint8_t *)texture_header_ + texture_header_->textureindex);
564 short *pskinref = (short *)((uint8_t *)texture_header_ + texture_header_->skinindex);
565
566 scene_->mNumMeshes = 0;
567
568 std::vector<std::string> unique_bodyparts_names;
569 unique_bodyparts_names.resize(header_->numbodyparts);
570
571 // Count the number of meshes.
572
573 for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart) {
574 unique_bodyparts_names[i] = pbodypart->name;
575
576 pmodel = (Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
577 for (int j = 0; j < pbodypart->nummodels; ++j, ++pmodel) {
578 scene_->mNumMeshes += pmodel->nummesh;
579 total_verts += pmodel->numverts;
580 }
581
582 total_models_ += pbodypart->nummodels;
583 }
584
585 // Display limit infos.
586 if (total_verts > AI_MDL_HL1_MAX_VERTICES)
587 log_warning_limit_exceeded<AI_MDL_HL1_MAX_VERTICES>(total_verts, "vertices");
588
589 if (scene_->mNumMeshes > AI_MDL_HL1_MAX_MESHES)
590 log_warning_limit_exceeded<AI_MDL_HL1_MAX_MESHES>(scene_->mNumMeshes, "meshes");
591
592 if (total_models_ > AI_MDL_HL1_MAX_MODELS)
593 log_warning_limit_exceeded<AI_MDL_HL1_MAX_MODELS>(total_models_, "models");
594
595 // Ensure bodyparts have unique names.
596 unique_name_generator_.set_template_name("Bodypart");
597 unique_name_generator_.make_unique(unique_bodyparts_names);
598
599 // Now do the same for each model.
600 pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
601
602 // Prepare template name for bodypart models.
603 std::vector<std::string> unique_models_names;
604 unique_models_names.resize(total_models_);
605
606 unsigned int model_index = 0;
607
608 for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart) {
609 pmodel = (Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
610 for (int j = 0; j < pbodypart->nummodels; ++j, ++pmodel, ++model_index)
611 unique_models_names[model_index] = pmodel->name;
612 }
613
614 unique_name_generator_.set_template_name("Model");
615 unique_name_generator_.make_unique(unique_models_names);
616
617 unsigned int mesh_index = 0;
618
619 scene_->mMeshes = new aiMesh *[scene_->mNumMeshes];
620
621 pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
622
623 /* Create a node that will represent the mesh hierarchy.
624
625 <MDL_bodyparts>
626 |
627 +-- bodypart --+-- model -- [mesh index, mesh index, ...]
628 | |
629 | +-- model -- [mesh index, mesh index, ...]
630 | |
631 | ...
632 |
633 |-- bodypart -- ...
634 |
635 ...
636 */
637 aiNode *bodyparts_node = new aiNode(AI_MDL_HL1_NODE_BODYPARTS);
638 rootnode_children_.push_back(bodyparts_node);
639 bodyparts_node->mNumChildren = static_cast<unsigned int>(header_->numbodyparts);
640 bodyparts_node->mChildren = new aiNode *[bodyparts_node->mNumChildren];
641 aiNode **bodyparts_node_ptr = bodyparts_node->mChildren;
642
643 // The following variables are defined here so they don't have
644 // to be recreated every iteration.
645
646 // Model_HL1 vertices, in bind pose space.
647 std::vector<aiVector3D> bind_pose_vertices;
648
649 // Model_HL1 normals, in bind pose space.
650 std::vector<aiVector3D> bind_pose_normals;
651
652 // Used to contain temporary information for building a mesh.
653 std::vector<HL1MeshTrivert> triverts;
654
655 std::vector<short> tricmds;
656
657 // Which triverts to use for the mesh.
658 std::vector<short> mesh_triverts_indices;
659
660 std::vector<HL1MeshFace> mesh_faces;
661
662 /* triverts that have the same vertindex, but have different normindex,s,t values.
663 Similar triverts are mapped from vertindex to a list of similar triverts. */
664 std::map<short, std::set<short>> triverts_similars;
665
666 // triverts per bone.
667 std::map<int, std::set<short>> bone_triverts;
668
669 /** This function adds a trivert index to the list of triverts per bone.
670 * \param[in] bone The bone that affects the trivert at index \p trivert_index.
671 * \param[in] trivert_index The trivert index.
672 */
673 auto AddTrivertToBone = [&](int bone, short trivert_index) {
674 if (bone_triverts.count(bone) == 0)
675 bone_triverts.insert({ bone, std::set<short>{ trivert_index }});
676 else
677 bone_triverts[bone].insert(trivert_index);
678 };
679
680 /** This function creates and appends a new trivert to the list of triverts.
681 * \param[in] trivert The trivert to use as a prototype.
682 * \param[in] bone The bone that affects \p trivert.
683 */
684 auto AddSimilarTrivert = [&](const Trivert &trivert, const int bone) {
685 HL1MeshTrivert new_trivert(trivert);
686 new_trivert.localindex = static_cast<short>(mesh_triverts_indices.size());
687
688 short new_trivert_index = static_cast<short>(triverts.size());
689
690 if (triverts_similars.count(trivert.vertindex) == 0)
691 triverts_similars.insert({ trivert.vertindex, std::set<short>{ new_trivert_index }});
692 else
693 triverts_similars[trivert.vertindex].insert(new_trivert_index);
694
695 triverts.push_back(new_trivert);
696
697 mesh_triverts_indices.push_back(new_trivert_index);
698 tricmds.push_back(new_trivert.localindex);
699 AddTrivertToBone(bone, new_trivert.localindex);
700 };
701
702 model_index = 0;
703
704 for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart, ++bodyparts_node_ptr) {
705 pmodel = (const Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
706
707 // Create bodypart node for the mesh tree hierarchy.
708 aiNode *bodypart_node = (*bodyparts_node_ptr) = new aiNode(unique_bodyparts_names[i]);
709 bodypart_node->mParent = bodyparts_node;
710 bodypart_node->mMetaData = aiMetadata::Alloc(1);
711 bodypart_node->mMetaData->Set(0, "Base", pbodypart->base);
712
713 bodypart_node->mNumChildren = static_cast<unsigned int>(pbodypart->nummodels);
714 bodypart_node->mChildren = new aiNode *[bodypart_node->mNumChildren];
715 aiNode **bodypart_models_ptr = bodypart_node->mChildren;
716
717 for (int j = 0; j < pbodypart->nummodels;
718 ++j, ++pmodel, ++bodypart_models_ptr, ++model_index) {
719
720 pmesh = (const Mesh_HL1 *)((uint8_t *)header_ + pmodel->meshindex);
721
722 uint8_t *pvertbone = ((uint8_t *)header_ + pmodel->vertinfoindex);
723 uint8_t *pnormbone = ((uint8_t *)header_ + pmodel->norminfoindex);
724 vec3_t *pstudioverts = (vec3_t *)((uint8_t *)header_ + pmodel->vertindex);
725 vec3_t *pstudionorms = (vec3_t *)((uint8_t *)header_ + pmodel->normindex);
726
727 // Each vertex and normal is in local space, so transform
728 // each of them to bring them in bind pose.
729 bind_pose_vertices.resize(pmodel->numverts);
730 bind_pose_normals.resize(pmodel->numnorms);
731 for (size_t k = 0; k < bind_pose_vertices.size(); ++k) {
732 const vec3_t &vert = pstudioverts[k];
733 bind_pose_vertices[k] = temp_bones_[pvertbone[k]].absolute_transform * aiVector3D(vert[0], vert[1], vert[2]);
734 }
735 for (size_t k = 0; k < bind_pose_normals.size(); ++k) {
736 const vec3_t &norm = pstudionorms[k];
737 // Compute the normal matrix to transform the normal into bind pose,
738 // without affecting its length.
739 const aiMatrix4x4 normal_matrix = aiMatrix4x4(temp_bones_[pnormbone[k]].absolute_transform).Inverse().Transpose();
740 bind_pose_normals[k] = normal_matrix * aiVector3D(norm[0], norm[1], norm[2]);
741 }
742
743 // Create model node for the mesh tree hierarchy.
744 aiNode *model_node = (*bodypart_models_ptr) = new aiNode(unique_models_names[model_index]);
745 model_node->mParent = bodypart_node;
746 model_node->mNumMeshes = static_cast<unsigned int>(pmodel->nummesh);
747 model_node->mMeshes = new unsigned int[model_node->mNumMeshes];
748 unsigned int *model_meshes_ptr = model_node->mMeshes;
749
750 for (int k = 0; k < pmodel->nummesh; ++k, ++pmesh, ++mesh_index, ++model_meshes_ptr) {
751 *model_meshes_ptr = mesh_index;
752
753 // Read triverts.
754 short *ptricmds = (short *)((uint8_t *)header_ + pmesh->triindex);
755 float texcoords_s_scale = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].width;
756 float texcoords_t_scale = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].height;
757
758 // Reset the data for the upcoming mesh.
759 triverts.clear();
760 triverts.resize(pmodel->numverts);
761 mesh_triverts_indices.clear();
762 mesh_faces.clear();
763 triverts_similars.clear();
764 bone_triverts.clear();
765
766 int l;
767 while ((l = *(ptricmds++))) {
768 bool is_triangle_fan = false;
769
770 if (l < 0) {
771 l = -l;
772 is_triangle_fan = true;
773 }
774
775 // Clear the list of tris for the upcoming tris.
776 tricmds.clear();
777
778 for (; l > 0; l--, ptricmds += 4) {
779 const Trivert *input_trivert = reinterpret_cast<const Trivert *>(ptricmds);
780 const int bone = pvertbone[input_trivert->vertindex];
781
782 HL1MeshTrivert *private_trivert = &triverts[input_trivert->vertindex];
783 if (private_trivert->localindex == -1) {
784 // First time referenced.
785 *private_trivert = *input_trivert;
786 private_trivert->localindex = static_cast<short>(mesh_triverts_indices.size());
787 mesh_triverts_indices.push_back(input_trivert->vertindex);
788 tricmds.push_back(private_trivert->localindex);
789 AddTrivertToBone(bone, private_trivert->localindex);
790 } else if (*private_trivert == *input_trivert) {
791 // Exists and is the same.
792 tricmds.push_back(private_trivert->localindex);
793 } else {
794 // No similar trivert associated to the trivert currently processed.
795 if (triverts_similars.count(input_trivert->vertindex) == 0)
796 AddSimilarTrivert(*input_trivert, bone);
797 else {
798 // Search in the list of similar triverts to see if the
799 // trivert in process is already registered.
800 short similar_index = -1;
801 for (auto it = triverts_similars[input_trivert->vertindex].cbegin();
802 similar_index == -1 && it != triverts_similars[input_trivert->vertindex].cend();
803 ++it) {
804 if (triverts[*it] == *input_trivert)
805 similar_index = *it;
806 }
807
808 // If a similar trivert has been found, reuse it.
809 // Otherwise, add it.
810 if (similar_index == -1)
811 AddSimilarTrivert(*input_trivert, bone);
812 else
813 tricmds.push_back(triverts[similar_index].localindex);
814 }
815 }
816 }
817
818 // Build mesh faces.
819 const int num_faces = static_cast<int>(tricmds.size() - 2);
820 mesh_faces.reserve(num_faces);
821
822 if (is_triangle_fan) {
823 for (int i = 0; i < num_faces; ++i) {
824 mesh_faces.push_back(HL1MeshFace{
825 tricmds[0],
826 tricmds[i + 1],
827 tricmds[i + 2] });
828 }
829 } else {
830 for (int i = 0; i < num_faces; ++i) {
831 if (i & 1) {
832 // Preserve winding order.
833 mesh_faces.push_back(HL1MeshFace{
834 tricmds[i + 1],
835 tricmds[i],
836 tricmds[i + 2] });
837 } else {
838 mesh_faces.push_back(HL1MeshFace{
839 tricmds[i],
840 tricmds[i + 1],
841 tricmds[i + 2] });
842 }
843 }
844 }
845
846 total_triangles += num_faces;
847 }
848
849 // Create the scene mesh.
850 aiMesh *scene_mesh = scene_->mMeshes[mesh_index] = new aiMesh();
851 scene_mesh->mPrimitiveTypes = aiPrimitiveType::aiPrimitiveType_TRIANGLE;
852 scene_mesh->mMaterialIndex = pskinref[pmesh->skinref];
853
854 scene_mesh->mNumVertices = static_cast<unsigned int>(mesh_triverts_indices.size());
855
856 if (scene_mesh->mNumVertices) {
857 scene_mesh->mVertices = new aiVector3D[scene_mesh->mNumVertices];
858 scene_mesh->mNormals = new aiVector3D[scene_mesh->mNumVertices];
859
860 scene_mesh->mNumUVComponents[0] = 2;
861 scene_mesh->mTextureCoords[0] = new aiVector3D[scene_mesh->mNumVertices];
862
863 // Add vertices.
864 for (unsigned int v = 0; v < scene_mesh->mNumVertices; ++v) {
865 const HL1MeshTrivert *pTrivert = &triverts[mesh_triverts_indices[v]];
866 scene_mesh->mVertices[v] = bind_pose_vertices[pTrivert->vertindex];
867 scene_mesh->mNormals[v] = bind_pose_normals[pTrivert->normindex];
868 scene_mesh->mTextureCoords[0][v] = aiVector3D(
869 pTrivert->s * texcoords_s_scale,
870 pTrivert->t * texcoords_t_scale, 0);
871 }
872
873 // Add face and indices.
874 scene_mesh->mNumFaces = static_cast<unsigned int>(mesh_faces.size());
875 scene_mesh->mFaces = new aiFace[scene_mesh->mNumFaces];
876
877 for (unsigned int f = 0; f < scene_mesh->mNumFaces; ++f) {
878 aiFace *face = &scene_mesh->mFaces[f];
879 face->mNumIndices = 3;
880 face->mIndices = new unsigned int[3];
881 face->mIndices[0] = mesh_faces[f].v0;
882 face->mIndices[1] = mesh_faces[f].v1;
883 face->mIndices[2] = mesh_faces[f].v2;
884 }
885
886 // Add mesh bones.
887 scene_mesh->mNumBones = static_cast<unsigned int>(bone_triverts.size());
888 scene_mesh->mBones = new aiBone *[scene_mesh->mNumBones];
889
890 aiBone **scene_bone_ptr = scene_mesh->mBones;
891
892 for (auto bone_it = bone_triverts.cbegin();
893 bone_it != bone_triverts.cend();
894 ++bone_it, ++scene_bone_ptr) {
895 const int bone_index = bone_it->first;
896
897 aiBone *scene_bone = (*scene_bone_ptr) = new aiBone();
898 scene_bone->mName = temp_bones_[bone_index].node->mName;
899
900 scene_bone->mOffsetMatrix = temp_bones_[bone_index].offset_matrix;
901
902 auto vertex_ids = bone_triverts.at(bone_index);
903
904 // Add vertex weight per bone.
905 scene_bone->mNumWeights = static_cast<unsigned int>(vertex_ids.size());
906 aiVertexWeight *vertex_weight_ptr = scene_bone->mWeights = new aiVertexWeight[scene_bone->mNumWeights];
907
908 for (auto vertex_it = vertex_ids.begin();
909 vertex_it != vertex_ids.end();
910 ++vertex_it, ++vertex_weight_ptr) {
911 vertex_weight_ptr->mVertexId = *vertex_it;
912 vertex_weight_ptr->mWeight = 1.0f;
913 }
914 }
915 }
916 }
917 }
918 }
919
920 if (total_triangles > AI_MDL_HL1_MAX_TRIANGLES)
921 log_warning_limit_exceeded<AI_MDL_HL1_MAX_TRIANGLES>(total_triangles, "triangles");
922 }
923
924 // ------------------------------------------------------------------------------------------------
read_animations()925 void HL1MDLLoader::read_animations() {
926 if (!header_->numseq)
927 return;
928
929 const SequenceDesc_HL1 *pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
930 const SequenceGroup_HL1 *pseqgroup = nullptr;
931 const AnimValueOffset_HL1 *panim = nullptr;
932 const AnimValue_HL1 *panimvalue = nullptr;
933
934 unique_sequence_names_.resize(header_->numseq);
935 for (int i = 0; i < header_->numseq; ++i)
936 unique_sequence_names_[i] = pseqdesc[i].label;
937
938 // Ensure sequences have unique names.
939 unique_name_generator_.set_template_name("Sequence");
940 unique_name_generator_.make_unique(unique_sequence_names_);
941
942 scene_->mNumAnimations = 0;
943
944 int highest_num_blend_animations = SequenceBlendMode_HL1::NoBlend;
945
946 // Count the total number of animations.
947 for (int i = 0; i < header_->numseq; ++i, ++pseqdesc) {
948 scene_->mNumAnimations += pseqdesc->numblends;
949 highest_num_blend_animations = std::max(pseqdesc->numblends, highest_num_blend_animations);
950 }
951
952 // Get the number of available blend controllers for global info.
953 get_num_blend_controllers(highest_num_blend_animations, num_blend_controllers_);
954
955 pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
956
957 aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations];
958
959 for (int sequence = 0; sequence < header_->numseq; ++sequence, ++pseqdesc) {
960 pseqgroup = (const SequenceGroup_HL1 *)((uint8_t *)header_ + header_->seqgroupindex) + pseqdesc->seqgroup;
961
962 if (pseqdesc->seqgroup == 0)
963 panim = (const AnimValueOffset_HL1 *)((uint8_t *)header_ + pseqgroup->unused2 + pseqdesc->animindex);
964 else
965 panim = (const AnimValueOffset_HL1 *)((uint8_t *)anim_headers_[pseqdesc->seqgroup] + pseqdesc->animindex);
966
967 for (int blend = 0; blend < pseqdesc->numblends; ++blend, ++scene_animations_ptr) {
968
969 const Bone_HL1 *pbone = (const Bone_HL1 *)((uint8_t *)header_ + header_->boneindex);
970
971 aiAnimation *scene_animation = (*scene_animations_ptr) = new aiAnimation();
972
973 scene_animation->mName = unique_sequence_names_[sequence];
974 scene_animation->mTicksPerSecond = pseqdesc->fps;
975 scene_animation->mDuration = static_cast<double>(pseqdesc->fps) * pseqdesc->numframes;
976 scene_animation->mNumChannels = static_cast<unsigned int>(header_->numbones);
977 scene_animation->mChannels = new aiNodeAnim *[scene_animation->mNumChannels];
978
979 for (int bone = 0; bone < header_->numbones; bone++, ++pbone, ++panim) {
980 aiNodeAnim *node_anim = scene_animation->mChannels[bone] = new aiNodeAnim();
981 node_anim->mNodeName = temp_bones_[bone].node->mName;
982
983 node_anim->mNumPositionKeys = pseqdesc->numframes;
984 node_anim->mNumRotationKeys = node_anim->mNumPositionKeys;
985 node_anim->mNumScalingKeys = 0;
986
987 node_anim->mPositionKeys = new aiVectorKey[node_anim->mNumPositionKeys];
988 node_anim->mRotationKeys = new aiQuatKey[node_anim->mNumRotationKeys];
989
990 for (int frame = 0; frame < pseqdesc->numframes; ++frame) {
991 aiVectorKey *position_key = &node_anim->mPositionKeys[frame];
992 aiQuatKey *rotation_key = &node_anim->mRotationKeys[frame];
993
994 aiVector3D angle1;
995 for (int j = 0; j < 3; ++j) {
996 if (panim->offset[j + 3] != 0) {
997 // Read compressed rotation delta.
998 panimvalue = (const AnimValue_HL1 *)((uint8_t *)panim + panim->offset[j + 3]);
999 extract_anim_value(panimvalue, frame, pbone->scale[j + 3], angle1[j]);
1000 }
1001
1002 // Add the default rotation value.
1003 angle1[j] += pbone->value[j + 3];
1004
1005 if (panim->offset[j] != 0) {
1006 // Read compressed position delta.
1007 panimvalue = (const AnimValue_HL1 *)((uint8_t *)panim + panim->offset[j]);
1008 extract_anim_value(panimvalue, frame, pbone->scale[j], position_key->mValue[j]);
1009 }
1010
1011 // Add the default position value.
1012 position_key->mValue[j] += pbone->value[j];
1013 }
1014
1015 position_key->mTime = rotation_key->mTime = static_cast<double>(frame);
1016 /* The Half-Life engine uses X as forward, Y as left, Z as up. Therefore,
1017 pitch,yaw,roll is represented as (YZX). */
1018 rotation_key->mValue = aiQuaternion(angle1.y, angle1.z, angle1.x);
1019 rotation_key->mValue.Normalize();
1020 }
1021 }
1022 }
1023 }
1024 }
1025
1026 // ------------------------------------------------------------------------------------------------
read_sequence_groups_info()1027 void HL1MDLLoader::read_sequence_groups_info() {
1028
1029 aiNode *sequence_groups_node = new aiNode(AI_MDL_HL1_NODE_SEQUENCE_GROUPS);
1030 rootnode_children_.push_back(sequence_groups_node);
1031
1032 sequence_groups_node->mNumChildren = static_cast<unsigned int>(header_->numseqgroups);
1033 sequence_groups_node->mChildren = new aiNode *[sequence_groups_node->mNumChildren];
1034
1035 const SequenceGroup_HL1 *pseqgroup = (const SequenceGroup_HL1 *)((uint8_t *)header_ + header_->seqgroupindex);
1036
1037 unique_sequence_groups_names_.resize(header_->numseqgroups);
1038 for (int i = 0; i < header_->numseqgroups; ++i)
1039 unique_sequence_groups_names_[i] = pseqgroup[i].label;
1040
1041 // Ensure sequence groups have unique names.
1042 unique_name_generator_.set_template_name("SequenceGroup");
1043 unique_name_generator_.make_unique(unique_sequence_groups_names_);
1044
1045 for (int i = 0; i < header_->numseqgroups; ++i, ++pseqgroup) {
1046 aiNode *sequence_group_node = sequence_groups_node->mChildren[i] = new aiNode(unique_sequence_groups_names_[i]);
1047 sequence_group_node->mParent = sequence_groups_node;
1048
1049 aiMetadata *md = sequence_group_node->mMetaData = aiMetadata::Alloc(1);
1050 if (i == 0) {
1051 /* StudioMDL does not write the file name for the default sequence group,
1052 so we will write it. */
1053 md->Set(0, "File", aiString(file_path_));
1054 } else {
1055 md->Set(0, "File", aiString(pseqgroup->name));
1056 }
1057 }
1058 }
1059
1060 // ------------------------------------------------------------------------------------------------
read_sequence_infos()1061 void HL1MDLLoader::read_sequence_infos() {
1062 if (!header_->numseq)
1063 return;
1064
1065 const SequenceDesc_HL1 *pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
1066
1067 aiNode *sequence_infos_node = new aiNode(AI_MDL_HL1_NODE_SEQUENCE_INFOS);
1068 rootnode_children_.push_back(sequence_infos_node);
1069
1070 sequence_infos_node->mNumChildren = static_cast<unsigned int>(header_->numseq);
1071 sequence_infos_node->mChildren = new aiNode *[sequence_infos_node->mNumChildren];
1072
1073 std::vector<aiNode *> sequence_info_node_children;
1074
1075 int animation_index = 0;
1076 for (int i = 0; i < header_->numseq; ++i, ++pseqdesc) {
1077 // Clear the list of children for the upcoming sequence info node.
1078 sequence_info_node_children.clear();
1079
1080 aiNode *sequence_info_node = sequence_infos_node->mChildren[i] = new aiNode(unique_sequence_names_[i]);
1081 sequence_info_node->mParent = sequence_infos_node;
1082
1083 // Setup sequence info node Metadata.
1084 aiMetadata *md = sequence_info_node->mMetaData = aiMetadata::Alloc(16);
1085 md->Set(0, "AnimationIndex", animation_index);
1086 animation_index += pseqdesc->numblends;
1087
1088 // Reference the sequence group by name. This allows us to search a particular
1089 // sequence group by name using aiNode(s).
1090 md->Set(1, "SequenceGroup", aiString(unique_sequence_groups_names_[pseqdesc->seqgroup]));
1091 md->Set(2, "FramesPerSecond", pseqdesc->fps);
1092 md->Set(3, "NumFrames", pseqdesc->numframes);
1093 md->Set(4, "NumBlends", pseqdesc->numblends);
1094 md->Set(5, "Activity", pseqdesc->activity);
1095 md->Set(6, "ActivityWeight", pseqdesc->actweight);
1096 md->Set(7, "MotionFlags", pseqdesc->motiontype);
1097 md->Set(8, "MotionBone", temp_bones_[pseqdesc->motionbone].node->mName);
1098 md->Set(9, "LinearMovement", aiVector3D(pseqdesc->linearmovement[0], pseqdesc->linearmovement[1], pseqdesc->linearmovement[2]));
1099 md->Set(10, "BBMin", aiVector3D(pseqdesc->bbmin[0], pseqdesc->bbmin[1], pseqdesc->bbmin[2]));
1100 md->Set(11, "BBMax", aiVector3D(pseqdesc->bbmax[0], pseqdesc->bbmax[1], pseqdesc->bbmax[2]));
1101 md->Set(12, "EntryNode", pseqdesc->entrynode);
1102 md->Set(13, "ExitNode", pseqdesc->exitnode);
1103 md->Set(14, "NodeFlags", pseqdesc->nodeflags);
1104 md->Set(15, "Flags", pseqdesc->flags);
1105
1106 if (import_settings_.read_blend_controllers) {
1107 int num_blend_controllers;
1108 if (get_num_blend_controllers(pseqdesc->numblends, num_blend_controllers) && num_blend_controllers) {
1109 // Read blend controllers info.
1110 aiNode *blend_controllers_node = new aiNode(AI_MDL_HL1_NODE_BLEND_CONTROLLERS);
1111 sequence_info_node_children.push_back(blend_controllers_node);
1112 blend_controllers_node->mParent = sequence_info_node;
1113 blend_controllers_node->mNumChildren = static_cast<unsigned int>(num_blend_controllers);
1114 blend_controllers_node->mChildren = new aiNode *[blend_controllers_node->mNumChildren];
1115
1116 for (unsigned int j = 0; j < blend_controllers_node->mNumChildren; ++j) {
1117 aiNode *blend_controller_node = blend_controllers_node->mChildren[j] = new aiNode();
1118 blend_controller_node->mParent = blend_controllers_node;
1119
1120 aiMetadata *md = blend_controller_node->mMetaData = aiMetadata::Alloc(3);
1121 md->Set(0, "Start", pseqdesc->blendstart[j]);
1122 md->Set(1, "End", pseqdesc->blendend[j]);
1123 md->Set(2, "MotionFlags", pseqdesc->blendtype[j]);
1124 }
1125 }
1126 }
1127
1128 if (import_settings_.read_animation_events && pseqdesc->numevents) {
1129 // Read animation events.
1130
1131 if (pseqdesc->numevents > AI_MDL_HL1_MAX_EVENTS) {
1132 log_warning_limit_exceeded<AI_MDL_HL1_MAX_EVENTS>(
1133 "Sequence " + std::string(pseqdesc->label),
1134 pseqdesc->numevents, "animation events");
1135 }
1136
1137 const AnimEvent_HL1 *pevent = (const AnimEvent_HL1 *)((uint8_t *)header_ + pseqdesc->eventindex);
1138
1139 aiNode *pEventsNode = new aiNode(AI_MDL_HL1_NODE_ANIMATION_EVENTS);
1140 sequence_info_node_children.push_back(pEventsNode);
1141 pEventsNode->mParent = sequence_info_node;
1142 pEventsNode->mNumChildren = static_cast<unsigned int>(pseqdesc->numevents);
1143 pEventsNode->mChildren = new aiNode *[pEventsNode->mNumChildren];
1144
1145 for (unsigned int j = 0; j < pEventsNode->mNumChildren; ++j, ++pevent) {
1146 aiNode *pEvent = pEventsNode->mChildren[j] = new aiNode();
1147 pEvent->mParent = pEventsNode;
1148
1149 aiMetadata *md = pEvent->mMetaData = aiMetadata::Alloc(3);
1150 md->Set(0, "Frame", pevent->frame);
1151 md->Set(1, "ScriptEvent", pevent->event);
1152 md->Set(2, "Options", aiString(pevent->options));
1153 }
1154 }
1155
1156 if (sequence_info_node_children.size()) {
1157 sequence_info_node->addChildren(
1158 static_cast<unsigned int>(sequence_info_node_children.size()),
1159 sequence_info_node_children.data());
1160 }
1161 }
1162 }
1163
1164 // ------------------------------------------------------------------------------------------------
read_sequence_transitions()1165 void HL1MDLLoader::read_sequence_transitions() {
1166 if (!header_->numtransitions)
1167 return;
1168
1169 // Read sequence transition graph.
1170 aiNode *transition_graph_node = new aiNode(AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH);
1171 rootnode_children_.push_back(transition_graph_node);
1172
1173 uint8_t *ptransitions = ((uint8_t *)header_ + header_->transitionindex);
1174 aiMetadata *md = transition_graph_node->mMetaData = aiMetadata::Alloc(header_->numtransitions * header_->numtransitions);
1175 for (unsigned int i = 0; i < md->mNumProperties; ++i)
1176 md->Set(i, std::to_string(i), static_cast<int>(ptransitions[i]));
1177 }
1178
read_attachments()1179 void HL1MDLLoader::read_attachments() {
1180 if (!header_->numattachments)
1181 return;
1182
1183 const Attachment_HL1 *pattach = (const Attachment_HL1 *)((uint8_t *)header_ + header_->attachmentindex);
1184
1185 aiNode *attachments_node = new aiNode(AI_MDL_HL1_NODE_ATTACHMENTS);
1186 rootnode_children_.push_back(attachments_node);
1187 attachments_node->mNumChildren = static_cast<unsigned int>(header_->numattachments);
1188 attachments_node->mChildren = new aiNode *[attachments_node->mNumChildren];
1189
1190 for (int i = 0; i < header_->numattachments; ++i, ++pattach) {
1191 aiNode *attachment_node = attachments_node->mChildren[i] = new aiNode();
1192 attachment_node->mParent = attachments_node;
1193 attachment_node->mMetaData = aiMetadata::Alloc(2);
1194 attachment_node->mMetaData->Set(0, "Position", aiVector3D(pattach->org[0], pattach->org[1], pattach->org[2]));
1195 // Reference the bone by name. This allows us to search a particular
1196 // bone by name using aiNode(s).
1197 attachment_node->mMetaData->Set(1, "Bone", temp_bones_[pattach->bone].node->mName);
1198 }
1199 }
1200
1201 // ------------------------------------------------------------------------------------------------
read_hitboxes()1202 void HL1MDLLoader::read_hitboxes() {
1203 if (!header_->numhitboxes)
1204 return;
1205
1206 const Hitbox_HL1 *phitbox = (const Hitbox_HL1 *)((uint8_t *)header_ + header_->hitboxindex);
1207
1208 aiNode *hitboxes_node = new aiNode(AI_MDL_HL1_NODE_HITBOXES);
1209 rootnode_children_.push_back(hitboxes_node);
1210 hitboxes_node->mNumChildren = static_cast<unsigned int>(header_->numhitboxes);
1211 hitboxes_node->mChildren = new aiNode *[hitboxes_node->mNumChildren];
1212
1213 for (int i = 0; i < header_->numhitboxes; ++i, ++phitbox) {
1214 aiNode *hitbox_node = hitboxes_node->mChildren[i] = new aiNode();
1215 hitbox_node->mParent = hitboxes_node;
1216
1217 aiMetadata *md = hitbox_node->mMetaData = aiMetadata::Alloc(4);
1218 // Reference the bone by name. This allows us to search a particular
1219 // bone by name using aiNode(s).
1220 md->Set(0, "Bone", temp_bones_[phitbox->bone].node->mName);
1221 md->Set(1, "HitGroup", phitbox->group);
1222 md->Set(2, "BBMin", aiVector3D(phitbox->bbmin[0], phitbox->bbmin[1], phitbox->bbmin[2]));
1223 md->Set(3, "BBMax", aiVector3D(phitbox->bbmax[0], phitbox->bbmax[1], phitbox->bbmax[2]));
1224 }
1225 }
1226
1227 // ------------------------------------------------------------------------------------------------
read_bone_controllers()1228 void HL1MDLLoader::read_bone_controllers() {
1229 if (!header_->numbonecontrollers)
1230 return;
1231
1232 const BoneController_HL1 *pbonecontroller = (const BoneController_HL1 *)((uint8_t *)header_ + header_->bonecontrollerindex);
1233
1234 aiNode *bones_controller_node = new aiNode(AI_MDL_HL1_NODE_BONE_CONTROLLERS);
1235 rootnode_children_.push_back(bones_controller_node);
1236 bones_controller_node->mNumChildren = static_cast<unsigned int>(header_->numbonecontrollers);
1237 bones_controller_node->mChildren = new aiNode *[bones_controller_node->mNumChildren];
1238
1239 for (int i = 0; i < header_->numbonecontrollers; ++i, ++pbonecontroller) {
1240 aiNode *bone_controller_node = bones_controller_node->mChildren[i] = new aiNode();
1241 bone_controller_node->mParent = bones_controller_node;
1242
1243 aiMetadata *md = bone_controller_node->mMetaData = aiMetadata::Alloc(5);
1244 // Reference the bone by name. This allows us to search a particular
1245 // bone by name using aiNode(s).
1246 md->Set(0, "Bone", temp_bones_[pbonecontroller->bone].node->mName);
1247 md->Set(1, "MotionFlags", pbonecontroller->type);
1248 md->Set(2, "Start", pbonecontroller->start);
1249 md->Set(3, "End", pbonecontroller->end);
1250 md->Set(4, "Channel", pbonecontroller->index);
1251 }
1252 }
1253
1254 // ------------------------------------------------------------------------------------------------
read_global_info()1255 void HL1MDLLoader::read_global_info() {
1256 aiNode *global_info_node = new aiNode(AI_MDL_HL1_NODE_GLOBAL_INFO);
1257 rootnode_children_.push_back(global_info_node);
1258
1259 aiMetadata *md = global_info_node->mMetaData = aiMetadata::Alloc(import_settings_.read_misc_global_info ? 16 : 11);
1260 md->Set(0, "Version", AI_MDL_HL1_VERSION);
1261 md->Set(1, "NumBodyparts", header_->numbodyparts);
1262 md->Set(2, "NumModels", total_models_);
1263 md->Set(3, "NumBones", header_->numbones);
1264 md->Set(4, "NumAttachments", import_settings_.read_attachments ? header_->numattachments : 0);
1265 md->Set(5, "NumSkinFamilies", texture_header_->numskinfamilies);
1266 md->Set(6, "NumHitboxes", import_settings_.read_hitboxes ? header_->numhitboxes : 0);
1267 md->Set(7, "NumBoneControllers", import_settings_.read_bone_controllers ? header_->numbonecontrollers : 0);
1268 md->Set(8, "NumSequences", import_settings_.read_animations ? header_->numseq : 0);
1269 md->Set(9, "NumBlendControllers", import_settings_.read_blend_controllers ? num_blend_controllers_ : 0);
1270 md->Set(10, "NumTransitionNodes", import_settings_.read_sequence_transitions ? header_->numtransitions : 0);
1271
1272 if (import_settings_.read_misc_global_info) {
1273 md->Set(11, "EyePosition", aiVector3D(header_->eyeposition[0], header_->eyeposition[1], header_->eyeposition[2]));
1274 md->Set(12, "HullMin", aiVector3D(header_->min[0], header_->min[1], header_->min[2]));
1275 md->Set(13, "HullMax", aiVector3D(header_->max[0], header_->max[1], header_->max[2]));
1276 md->Set(14, "CollisionMin", aiVector3D(header_->bbmin[0], header_->bbmin[1], header_->bbmin[2]));
1277 md->Set(15, "CollisionMax", aiVector3D(header_->bbmax[0], header_->bbmax[1], header_->bbmax[2]));
1278 }
1279 }
1280
1281 // ------------------------------------------------------------------------------------------------
1282 /** @brief This method reads a compressed anim value.
1283 *
1284 * @note The structure of this method is taken from HL2 source code.
1285 * Although this is from HL2, it's implementation is almost identical
1286 * to code found in HL1 SDK. See HL1 and HL2 SDKs for more info.
1287 *
1288 * source:
1289 * HL1 source code.
1290 * file: studio_render.cpp
1291 * function(s): CalcBoneQuaternion and CalcBonePosition
1292 *
1293 * HL2 source code.
1294 * file: bone_setup.cpp
1295 * function(s): ExtractAnimValue
1296 */
extract_anim_value(const AnimValue_HL1 * panimvalue,int frame,float bone_scale,float & value)1297 void HL1MDLLoader::extract_anim_value(
1298 const AnimValue_HL1 *panimvalue,
1299 int frame, float bone_scale, float &value) {
1300 int k = frame;
1301
1302 // find span of values that includes the frame we want
1303 while (panimvalue->num.total <= k) {
1304 k -= panimvalue->num.total;
1305 panimvalue += panimvalue->num.valid + 1;
1306 }
1307
1308 // Bah, missing blend!
1309 if (panimvalue->num.valid > k)
1310 value = panimvalue[k + 1].value * bone_scale;
1311 else
1312 value = panimvalue[panimvalue->num.valid].value * bone_scale;
1313 }
1314
1315 // ------------------------------------------------------------------------------------------------
1316 // Get the number of blend controllers.
get_num_blend_controllers(const int num_blend_animations,int & num_blend_controllers)1317 bool HL1MDLLoader::get_num_blend_controllers(const int num_blend_animations, int &num_blend_controllers) {
1318
1319 switch (num_blend_animations) {
1320 case SequenceBlendMode_HL1::NoBlend:
1321 num_blend_controllers = 0;
1322 return true;
1323 case SequenceBlendMode_HL1::TwoWayBlending:
1324 num_blend_controllers = 1;
1325 return true;
1326 case SequenceBlendMode_HL1::FourWayBlending:
1327 num_blend_controllers = 2;
1328 return true;
1329 default:
1330 num_blend_controllers = 0;
1331 ASSIMP_LOG_WARN(MDL_HALFLIFE_LOG_HEADER "Unsupported number of blend animations (" + std::to_string(num_blend_animations) + ")");
1332 return false;
1333 }
1334 }
1335
1336 } // namespace HalfLife
1337 } // namespace MDL
1338 } // namespace Assimp
1339