Template:Archived Page History/MDL (Source): Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(standardizing file type page name format)
Tag: Removed redirect
 
(31 intermediate revisions by 7 users not shown)
Line 1: Line 1:
'''MDL''' is the extension for [[Source]]'s proprietary model format. It defines the structure of the model along with animation, bounding box, hit box, materials, mesh and [[LOD]] information. It does not, however, contain ''all'' the information needed for the model.  Additional data is stored in [[PHY]], [[ANI]], [[VTX]] and [[VVD]] files, and sometimes, usually for shared animations, other .mdl files.
{{APH|MDL}}
 
== File format ==
Some details of the file format may be gleaned from the source code in Valve's <code>studio.h</code>, specifically the struct <code>studiohdr_t</code>. The early header defines a series of offsets and lengths for various sub-sections within the file, along with some key scalar information. The MDL also contains the names of materials ([[VMT]]), which may be used and referenced in various ways.
 
=== Main header ===
To get the latest header for specific game, please use the <code>studio.h</code> file in the Valve's SDK instead.
<source lang="cpp">struct studiohdr_t
{
    int        id;            // Model format ID, such as "IDST" (0x49 0x44 0x53 0x54)
    int        version;        // Format version number, such as 48 (0x30,0x00,0x00,0x00)
    int        checksum;      // This has to be the same in the phy and vtx files to load!
    char        name[64];      // The internal name of the model, padding with null bytes.
                                // Typically "my_model.mdl" will have an internal name of "my_model"
    int        dataLength;    // Data size of MDL file in bytes.
    // A vector is 12 bytes, three 4-byte float-values in a row.
    Vector      eyeposition;    // Position of player viewpoint relative to model origin
    Vector      illumposition;  // Position (relative to model origin) used to calculate ambient light contribution and cubemap reflections for the entire model.
    Vector      hull_min;      // Corner of model hull box with the least X/Y/Z values
    Vector      hull_max;        // Opposite corner of model hull box
    Vector      view_bbmin;    // TODO: what's this, how is it different from hull_min/max?
    Vector      view_bbmax;
    int        flags;          // Binary flags in little-endian order.
                                // ex (00000001,00000000,00000000,11000000) means flags for position 0, 30, and 31 are set.
                                // Set model flags section for more information
    /*
    * After this point, the header contains many references to offsets
    * within the MDL file and the number of items at those offsets.
    *
    * Offsets are from the very beginning of the file.
    *
    * Note that indexes/counts are not always paired and ordered consistently.
    */   
    // mstudiobone_t
    int        bone_count;    // Number of data sections (of type mstudiobone_t)
    int        bone_offset;  // Offset of first data section
    // mstudiobonecontroller_t
    int        bonecontroller_count;
    int        bonecontroller_offset;
    // mstudiohitboxset_t
    int        hitbox_count;
    int        hitbox_offset;
    // mstudioanimdesc_t
    int        localanim_count;
    int        localanim_offset;
    // mstudioseqdesc_t
    int        localseq_count;
    int        localseq_offset;
    int        activitylistversion; // ??
    int        eventsindexed;      // ??
    // VMT texture filenames
    // mstudiotexture_t
    int        texture_count;
    int        texture_offset;
    // This offset points to a series of ints.
    // Each int value, in turn, is an offset relative to the start of this header/the-file,
    // At which there is a null-terminated string.
    int        texturedir_count;
    int        texturedir_offset;
    // Each skin-family assigns a texture-id to a skin location
    int        skinreference_count;
    int        skinrfamily_count;
    int        skinreference_index;
    // mstudiobodyparts_t
    int        bodypart_count;
    int        bodypart_offset;
    // Local attachment points       
    // mstudioattachment_t
    int        attachment_count;
    int        attachment_offset;
    // Node values appear to be single bytes, while their names are null-terminated strings.
    int        localnode_count;
    int        localnode_index;
    int        localnode_name_index;
    // mstudioflexdesc_t
    int        flexdesc_count;
    int        flexdesc_index;
    // mstudioflexcontroller_t
    int        flexcontroller_count;
    int        flexcontroller_index;
    // mstudioflexrule_t
    int        flexrules_count;
    int        flexrules_index;
    // IK probably referse to inverse kinematics
    // mstudioikchain_t
    int        ikchain_count;
    int        ikchain_index;
    // Information about any "mouth" on the model for speech animation
    // More than one sounds pretty creepy.
    // mstudiomouth_t
    int        mouths_count;
    int        mouths_index;
    // mstudioposeparamdesc_t
    int        localposeparam_count;
    int        localposeparam_index;
    /*
    * For anyone trying to follow along, as of this writing,
    * the next "surfaceprop_index" value is at position 0x0134 (308)
    * from the start of the file.
    */
    // Surface property value (single null-terminated string)
    int        surfaceprop_index;
    // Unusual: In this one index comes first, then count.
    // Key-value data is a series of strings. If you can't find
    // what you're interested in, check the associated PHY file as well.
    int        keyvalue_index;
    int        keyvalue_count;   
    // More inverse-kinematics
    // mstudioiklock_t
    int        iklock_count;
    int        iklock_index;
    float      mass;      // Mass of object (4-bytes)
    int        contents;    // ??
    // Other models can be referenced for re-used sequences and animations
    // (See also: The $includemodel QC option.)
    // mstudiomodelgroup_t
    int        includemodel_count;
    int        includemodel_index;
   
    int        virtualModel;    // Placeholder for mutable-void*
    // Note that the SDK only compiles as 32-bit, so an int and a pointer are the same size (4 bytes)
    // mstudioanimblock_t
    int        animblocks_name_index;
    int        animblocks_count;
    int        animblocks_index;
   
    int        animblockModel; // Placeholder for mutable-void*
 
    // Points to a series of bytes?
    int        bonetablename_index;
   
    int        vertex_base;    // Placeholder for void*
    int        offset_base;    // Placeholder for void*
   
    // Used with $constantdirectionallight from the QC
    // Model should have flag #13 set if enabled
    byte        directionaldotproduct;
   
    byte        rootLod;    // Preferred rather than clamped
   
    // 0 means any allowed, N means Lod 0 -> (N-1)
    byte        numAllowedRootLods;   
   
    byte        unused0; // ??
    int        unused1; // ??
   
    // mstudioflexcontrollerui_t
    int        flexcontrollerui_count;
    int        flexcontrollerui_index;
 
    float      vertAnimFixedPointScale; // ??
    int        unused2;
   
    /**
    * Offset for additional header information.
    * May be zero if not present, or also 408 if it immediately
    * follows this studiohdr_t
    */
    // studiohdr2_t
    int        studiohdr2index;
   
    int        unused3; // ??
   
    /**
    * As of this writing, the header is 408 bytes long in total
    */
};
</source>
 
{| class="standard-table"
|+Known flags
! Name
! Position
! Details
|-
|STUDIOHDR_FLAGS_AUTOGENERATED_HITBOX
|0
|This flag is set if no hitbox information was specified
|-
|STUDIOHDR_FLAGS_USES_ENV_CUBEMAP
|1
|This flag is set at loadtime, not mdl build time so that we don't have to rebuild models when we change materials.
|-
|STUDIOHDR_FLAGS_FORCE_OPAQUE
|2
|Use this when there are translucent parts to the model but we're not going to sort it.
|-
|STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS
|3
|Use this when we want to render the opaque parts during the opaque pass and the translucent parts during the translucent pass. Added using $mostlyopaque to the QC.
|-
|STUDIOHDR_FLAGS_STATIC_PROP
|4
|This is set any time the .qc files has $staticprop in it. Means there's no bones and no transforms.
|-
|STUDIOHDR_FLAGS_USES_FB_TEXTURE
|5
|This flag is set at loadtime, not mdl build time so that we don't have to rebuild models when we change materials.
|-
|STUDIOHDR_FLAGS_HASSHADOWLOD
|6
|This flag is set by studiomdl.exe if a separate "$shadowlod" entry was present for the .mdl (the shadow lod is the last entry in the lod list if present).
|-
|STUDIOHDR_FLAGS_USES_BUMPMAPPING
|7
|This flag is set at loadtime, not mdl build time so that we don't have to rebuild models when we change materials.
|-
|STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS
|8
|This flag is set when we should use the actual materials on the shadow LOD instead of overriding them with the default one (necessary for translucent shadows).
|-
|STUDIOHDR_FLAGS_OBSOLETE
|9
|This flag is set when we should use the actual materials on the shadow LOD instead of overriding them with the default one (necessary for translucent shadows).
|-
|STUDIOHDR_FLAGS_UNUSED
|10
|
|-
|STUDIOHDR_FLAGS_NO_FORCED_FADE
|11
|This flag is set at mdl build time.
|-
|STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE
|12
|The npc will lengthen the viseme check to always include two phonemes.
|-
|STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT
|13
|This flag is set when the .qc has $constantdirectionallight in it. If set, we use constantdirectionallightdot to calculate light intensity rather than the normal directional dot product. Only valid if STUDIOHDR_FLAGS_STATIC_PROP is also set.
|-
|STUDIOHDR_FLAGS_FLEXES_CONVERTED
|14
|Flag to mark delta flexes as already converted from disk format to memory format.
|-
|STUDIOHDR_FLAGS_BUILT_IN_PREVIEW_MODE
|15
|Indicates the studiomdl was built in preview mode (added with the -preview flag).
|-
|STUDIOHDR_FLAGS_AMBIENT_BOOST
|16
|Ambient boost (runtime flag).
|-
|STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS
|17
|Don't cast shadows from this model (useful on first-person models).
|-
|STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS
|18
|Alpha textures should cast shadows in vrad on this model (ONLY prop_static!). Requires setup in the lights.rad file.
|}
 
===Secondary header===
This header section is optional, and is found via the studiohdr2index value.
 
<source lang="cpp">struct studiohdr2_t
{
        // ??
        int    srcbonetransform_count;
        int    srcbonetransform_index;
 
        int    illumpositionattachmentindex;
 
        float  flMaxEyeDeflection;    //  If set to 0, then equivalent to cos(30)
 
        // mstudiolinearbone_t
        int    linearbone_index;
 
        int    unknown[64];
};
</source>
 
===Texture data===
<source lang="cpp">struct mstudiotexture_t
{
        // Number of bytes past the beginning of this structure
        // where the first character of the texture name can be found.
        int    name_offset; // Offset for null-terminated string
        int    flags;
 
        int    used;        // Padding?
        int    unused;      // Padding.
 
        int    material;        // Placeholder for IMaterial
        int    client_material; // Placeholder for void*
        int    unused2[10]; // Final padding
        // Struct is 64 bytes long
};
</source>
 
=== Skin replacement tables ===
 
Each "skin" that a model has (as seen in the [[Model Viewer]] and choose-able for [[Prop_Types_Overview|prop entities]]) is actually referred to as a skin "family" in the MDL code. (Additional skins may be created with the [[$texturegroup]] compile option.) For the purposes of this section, a "skin" an area where a single material (aka texture) may be applied, while a skin-family defines a list of materials to use on skin zones 0,1,2, etc.
 
The skin-family section of the MDL is a sequence of <code>short</code> (2-byte) values, which should be broken up into a table. To illustrate how it works, we will consider a model and then examine how it would be represented on the byte-level. For this example, imagine a crate-model with three skin-families labeled (fam0,fam1,fam2) two skins-zones (mainbody,trimming) and three textures (lightwood,darkwood,metal).
 
{| class="standard-table"
|+Skin table
!
! mainbody
! trimming
|-
!fam0
|lightwood
|metal
|-
!fam1
|darkwood
|metal
|-
!fam2
|metal
|metal
|}
 
Let's assume that the various textures are given ID values in the MDL like so:
 
{| class="standard-table"
|+Texture IDs
!ID
!Name
|-
|0
|lightwood
|-
|1
|darkwood
|-
|2
|metal
|}
 
{{note|This ordering frequently matches the "VMTs Loaded" display of [[HLMV]]}}
 
In the MDL data, this table relationship is broken down into a stream of bytes, with two-byte texture IDs replacing the literal names. Thus the final series of bytes that corresponds to these skin-family relationships would (as little-endian short values) be:
 
0'''0''' 00  0'''2''' 00
0'''1''' 00  0'''2''' 00
0'''2''' 00  0'''2''' 00
 
The total number of bytes is <code>numskinfamilies*numskins*2</code>.
 
{{note|MDLs may often have a texture-replacement table which is larger than necessary, with additional columns which are never used.}}
{{todo|Discover how to accurately detect which columns are meaningful in too-large replacement tables.}}
 
== See also ==
* [[Model Viewer]]
* [[Model Creation Overview]]
* [[Model]]
 
[[Category:Glossary]]
[[Category:Modeling]]
[[Category:File formats]]

Latest revision as of 01:30, 3 October 2024

Note.pngNote:Archived rest of page history of page MDL
Icon-Important.pngImportant:These pages are linked often from page history logs so Special:WhatLinksHere will not show anything.