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

From Valve Developer Community
Jump to navigation Jump to search
(Replaced content with "{{APH|MDL}}")
Tag: Replaced
Line 1: Line 1:
{{LanguageBar}}
{{APH|MDL}}
{{tabs|MDL|quake=1|goldsrc=1|source=1|custom1=VMDL|custom1alt=Source 2}}
 
'''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.
 
== 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;    // Same, but for bounding box,
    Vector      view_bbmax;    // which is used for view culling
    int        flags;          // Binary flags in little-endian order.
                                // ex (0x010000C0) 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) in kilograms
 
    int        contents;    // contents flag, as defined in bspflags.h
                            // not all content types are valid; see
                            // documentation on $contents QC command
    // 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>
 
=== Model flags ===
{| class="wikitable"
! Bit
! Name
! Dev comment
! Description
|-
| 0x00000001
| STUDIOHDR_FLAGS_AUTOGENERATED_HITBOX
| This flag is set if no hitbox information was specified
| Set by StudioMDL if no [[$hbox]] QC commands were used
|-
| 0x00000002
| STUDIOHDR_FLAGS_USES_ENV_CUBEMAP
| NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild models when we change materials.
| Set by game engine if VMTs used for the model have {{cmd|$envmap|env_cubemap}}.
|-
| 0x00000004
| STUDIOHDR_FLAGS_FORCE_OPAQUE
| Use this when there are translucent parts to the model but we're not going to sort it
| Set by StudioMDL if the [[$opaque]] QC command is used
|-
| 0x00000008
| STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS
| Use this when we want to render the opaque parts during the opaque pass and the translucent parts during the translucent pass
| Set by StudioMDL if the [[$mostlyopaque]] QC command is used
|-
| 0x00000010
| STUDIOHDR_FLAGS_STATIC_PROP
| This is set any time the .qc files has $staticprop in it
 
Means there's no bones and no transforms
| Set by StudioMDL if the [[$staticprop]] QC command is used. Required for [[prop_static]] and [[prop_physics]].
|-
| 0x00000020
| STUDIOHDR_FLAGS_USES_FB_TEXTURE
| NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild models when we change materials.
| {{confirm|Does "FB" mean fullbright, or framebuffer?}}
|-
| 0x00000040
| STUDIOHDR_FLAGS_HASSHADOWLOD
| 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)
| Set by StudioMDL if the [[$shadowlod]] QC command is used.
|-
| 0x00000080
| STUDIOHDR_FLAGS_USES_BUMPMAPPING
| NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild models when we change materials.
| Set by game engine if [[$bumpmap]] or [[$normalmap]] are present in any of the models's VMTs.
|-
| 0x00000100
| STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS
| NOTE:  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)
| Set by StudioMDL if the [[$shadowlod]] command has the {{mono|use_shadowlod_materials}} parameter
|-
| 0x00000200
| STUDIOHDR_FLAGS_OBSOLETE
| NOTE:  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)
| Set by StudioMDL if the [[$obsolete]] QC command is used.
|-
| 0x00000400
| STUDIOHDR_FLAGS_UNUSED
| N/A
|
|-
| 0x00000800
| STUDIOHDR_FLAGS_NO_FORCED_FADE
| NOTE:  This flag is set at mdl build time
| Set by StudioMDL if the [[$noforcedfade]] QC command is used.
|-
| 0x00001000
| STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE
| NOTE:  The npc will lengthen the viseme check to always include two phonemes
| Set by StudioMDL if the [[$forcephonemecrossfade]] QC command is used.
|-
| 0x00002000
| STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT
| 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
| Set by StudioMDL if the [[$constantdirectionallight]] QC command is used.
|-
| 0x00004000
| STUDIOHDR_FLAGS_FLEXES_CONVERTED
| Flag to mark delta flexes as already converted from disk format to memory format
| Used by engine at runtime
|-
| 0x00008000
| STUDIOHDR_FLAGS_BUILT_IN_PREVIEW_MODE
| Indicates the studiomdl was built in preview mode
| Model has not been optimized into tristrips, and DMX quads have not been triangulated.
 
Set by running [[StudioMDL]] with {{code|-preview}} parameter.
|-
| 0x00010000
| STUDIOHDR_FLAGS_AMBIENT_BOOST
| Ambient boost (runtime flag)
| Contrary to the dev comment, this can be set by StudioMDL using the [[$ambientboost]] QC command.
|-
| 0x00020000
| STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS
| Don't cast shadows from this model (useful on first-person models)
| Set by StudioMDL if the [[$donotcastshadows]] QC command is used.
|-
| 0x00040000
| STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS
| alpha textures should cast shadows in vrad on this model (ONLY prop_static!)
| Set by StudioMDL if the [[$casttextureshadows]] QC command is used.
|-
| 0x00080000
| N/A
| N/A (undefined)
|
|-
| 0x00100000
| N/A
| N/A (undefined)
|
|-
| 0x00200000
| STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE
| flagged on load to indicate no animation events on this model
|
|}
 
===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 ==
* [[3D Model]]
* [[Model Viewer]]
* [[Model Creation Overview]]
{{Source topicon}} <!-- topicon automatically adds Category:Source. -->
[[Category:Modeling]]
[[Category:File formats]]

Revision as of 01:29, 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.