Skip to content

ItemShirtAsset — Shirt Clothing Definition

Overview

ItemShirtAsset defines a wearable shirt/torso item in Unturned. It inherits from ItemBagAsset (storage dimensions), which inherits from ItemClothingAsset (armor, proof system, movement speed), which inherits from ItemAsset. The class is the most visually complex clothing asset: it supports 2D texture mapping (Shirt, Emission, Metallic), complete 1st-person and 3rd-person character mesh replacement, and full material override.

Unlike most clothing subclasses that load a 3D prefab (GameObject), ItemShirtAsset primarily loads 2D textures. These textures are applied to the player character's base body mesh unless the mesh override system replaces the body mesh entirely. The mesh override system allows shirts to fundamentally change the player's silhouette — bulky armor, prosthetic limbs, altered body proportions.

Shirts occupy the EItemType.SHIRT slot and are one of four slot types eligible for armor damage reduction.

Source code location: Unturned/Bundles/ItemShirtAsset.cs (209 lines), inheriting from ItemBagAsset.cs (52 lines), ItemClothingAsset.cs (304 lines), and ItemAsset.cs (base).

Inheritance Chain

ItemAsset
  └─ ItemClothingAsset (abstract) — armor, proof, movement speed, visuals
       └─ ItemBagAsset (abstract) — storage dimensions (Width, Height)
            └─ ItemShirtAsset — textures, mesh override, material override

Note: Shirts inherit from ItemBagAsset, not ItemGearAsset. The storage dimensions from ItemBagAsset give shirts an inventory grid (Width × Height). The gear-specific hair/beard override system is not available to shirts.


Class Definition

csharp
public class ItemShirtAsset : ItemBagAsset
{
    protected Texture2D _shirt;
    protected Texture2D _emission;
    protected Texture2D _metallic;
    protected bool _ignoreHand;
    public Mesh[] characterMeshOverride1pLODs;
    public Mesh[] characterMeshOverride3pLODs;
    public Material characterMaterialOverride;

    public Texture2D shirt => _shirt;
    public Texture2D emission => _emission;
    public Texture2D metallic => _metallic;
    public bool ignoreHand => _ignoreHand;
}

Texture Loading

Shirt Texture

The primary "Shirt" texture is loaded from the master bundle:

csharp
_shirt = loadRequiredAsset<Texture2D>(p.bundle, "Shirt");

loadRequiredAsset fails with an error if the texture is missing. The shirt texture is the base color/diffuse map applied to the character's torso mesh. It defines the visible pattern, color, and detail of the shirt.

When Assets.shouldValidateAssets is true, two checks run:

csharp
if (shirt.isReadable)
{
    Assets.ReportError(this, "texture 'Shirt' can save memory by disabling read/write");
}

if (shirt.format != TextureFormat.RGBA32 && shirt.format != TextureFormat.RGB24
    && (shirt.width <= 128 || shirt.height <= 128))
{
    Assets.ReportError(this, $"texture Shirt might look weird because it is relatively "
        + $"low resolution but has compression enabled ({shirt.format})");
}

Read/write check: Textures with read/write enabled consume double the memory (CPU-side + GPU-side). Most shirt textures don't need CPU read access — disable it to save memory.

Compression check: Low-resolution textures (≤128px in either dimension) should not use compressed formats (DXT1/DXT5). Compressed formats at low resolutions produce visible artifacts. This check warns when a small texture has compression enabled.

Emission Texture

csharp
_emission = p.bundle.load<Texture2D>("Emission");

The emission (glow) texture is optional — load returns null if absent. When present, the emission map controls which parts of the shirt glow independently of scene lighting. Emission maps are additive: white pixels glow at full intensity, black pixels emit nothing.

Validation:

csharp
if (emission != null && Assets.shouldValidateAssets)
{
    if (emission.isReadable)
        Assets.ReportError(this, "texture 'Emission' can save memory by disabling read/write");

    if (emission.width <= 128 || emission.height <= 128)
    {
        if (emission.format == TextureFormat.RGBA32)
            Assets.ReportError(this, $"texture Emission is relatively low resolution so RGB24 format is recommended");
        else if (emission.format != TextureFormat.RGB24)
            Assets.ReportError(this, $"texture Emission might look weird because it is relatively low resolution but has compression enabled ({emission.format})");
    }
}

For low-resolution emission textures, RGB24 format is recommended over RGBA32 (saves memory by dropping the unused alpha channel). Compressed formats (DXT1/DXT5) at low resolutions cause visible block artifacts in the emission output.

Metallic Texture

csharp
_metallic = p.bundle.load<Texture2D>("Metallic");

The metallic/smoothness texture is optional. In Unity's standard shader, the metallic map encodes metallic in the R channel and smoothness in the A channel. The metallic texture controls surface reflectivity and roughness.

Validation mirrors the emission validation pattern but for metallic-specific format concerns:

csharp
if (metallic != null && Assets.shouldValidateAssets)
{
    if (metallic.isReadable)
        Assets.ReportError(this, "texture 'Metallic' can save memory by disabling read/write");

    if (metallic.format != TextureFormat.RGBA32 && (metallic.width <= 128 || metallic.height <= 128))
        Assets.ReportError(this, $"texture Metallic might look weird because it is relatively low resolution but has compression enabled ({metallic.format})");
}

Unlike emission textures (where RGB24 is acceptable), metallic textures should prefer RGBA32 format because the alpha channel carries smoothness data. Compression on low-resolution metallic textures produces visible artifacts in surface reflectivity.

Texture Loading Conditions

ConditionShirtEmissionMetallic
RequiredYes (loadRequiredAsset)No (load, returns null)No (load, returns null)
Skip conditionsServer OR characterMaterialOverride != nullServer OR character material setServer OR character material set
Read/write warningYesYesYes
Resolution warningYesYesYes
Format-specific warningCompression on low-resRGBA32 vs RGB24Compression on low-res

Character Mesh Override System

The mesh override system is the most powerful visual feature of ItemShirtAsset. Instead of overlaying a 2D texture on the default character body, a shirt can replace the character's body mesh entirely. This enables:

  • Silhouette changes: Bulky armor, robes, trench coats, prosthetic limbs
  • Proportional changes: Altered shoulder width, different arm thickness
  • Full body transformations: Non-humanoid torso shapes

.dat Flags for Mesh Override

.dat KeyTypePurpose
Has_1P_Character_Mesh_OverrideboolEnables first-person mesh replacement
Character_Mesh_3P_Override_LODsushortNumber of third-person LOD meshes
Has_Character_Material_OverrideboolEnables full material replacement

First-Person Mesh Override

csharp
bool hasOverrideMesh1p = p.data.ParseBool("Has_1P_Character_Mesh_Override", defaultValue: false);
if (hasOverrideMesh1p)
{
    characterMeshOverride1pLODs = new Mesh[1];
    for (int index = 0; index < characterMeshOverride1pLODs.Length; ++index)
    {
        GameObject overrideMeshObject = p.bundle.load<GameObject>("Character_Mesh_1P_Override_" + index);
        if (overrideMeshObject == null)
        {
            overrideMeshObject = p.bundle.load<GameObject>("Character_Mesh_Override_" + index);
        }
        // Extract MeshFilter.sharedMesh
    }
}

Bundle objects: Character_Mesh_1P_Override_0, Character_Mesh_1P_Override_1, etc. Each is a GameObject with a MeshFilter component whose sharedMesh is a skinned mesh. The first-person override replaces the player's arms and torso as seen from first-person perspective.

Fallback naming: If Character_Mesh_1P_Override_# is not found, the loader tries Character_Mesh_Override_# (without the 1P_ prefix). This provides backward compatibility for older mods.

Third-Person Mesh Override

csharp
ushort lodCount = p.data.ParseUInt16("Character_Mesh_3P_Override_LODs");
if (lodCount > 0)
{
    characterMeshOverride3pLODs = new Mesh[lodCount];
    for (int index = 0; index < characterMeshOverride3pLODs.Length; ++index)
    {
        GameObject overrideMeshObject = p.bundle.load<GameObject>("Character_Mesh_3P_Override_" + index);
        if (overrideMeshObject == null)
        {
            overrideMeshObject = p.bundle.load<GameObject>("Character_Mesh_Override_" + index);
        }
        // Extract MeshFilter.sharedMesh
    }
}

The third-person system supports multiple LOD (Level of Detail) levels via Character_Mesh_3P_Override_LODs. Each LOD index provides progressively simpler meshes: Character_Mesh_3P_Override_0 (highest detail), Character_Mesh_3P_Override_1 (medium), Character_Mesh_3P_Override_2 (low), etc.

Fallback naming: Same as 1P — tries Character_Mesh_Override_# if the 3P_ variant is not found.

Character Material Override

csharp
bool hasOverrideMaterial = p.data.ParseBool("Has_Character_Material_Override", defaultValue: false);
if (hasOverrideMaterial)
{
    characterMaterialOverride = p.bundle.load<Material>("Character_Material_Override");
    if (characterMaterialOverride == null)
        Assets.ReportError(this, "missing 'Character_Material_Override' Material");
}

When Has_Character_Material_Override is true, the entire character material is replaced — not just the shirt texture. The "Character_Material_Override" Material is loaded from the bundle. This allows complete control over the character's rendering: custom shaders, custom texture maps (including normal maps, ambient occlusion, etc.), and completely custom material properties.

When a character material override is set, the texture loading step (_shirt, _emission, _metallic) is skipped — the material handles all rendering.

Server Skip

All mesh override data is nulled on the dedicated server:

csharp
if (Dedicator.IsDedicatedServer)
{
    characterMeshOverride1pLODs = null;
    characterMeshOverride3pLODs = null;
    characterMaterialOverride = null;
}

Mesh overrides are purely visual and irrelevant to the server.


Ignore Hand Flag

csharp
_ignoreHand = p.data.ContainsKey("Ignore_Hand");

When the Ignore_Hand key is present in the .dat file, the shirt tells the character clothing system to skip hand model modification. Normally, shirts modify the character's hand model to match the shirt's style. Setting Ignore_Hand preserves the default hand appearance.

This is useful for shirts that:

  • Only modify the torso (hands should remain default)
  • Use mesh overrides that already include hand geometry
  • Are designed to be worn with specific glove/gauntlet combinations

Storage Dimensions (Inherited from ItemBagAsset)

Shirts inherit inventory storage from ItemBagAsset:

.dat KeyTypeDefaultPurpose
Widthbyte0Storage grid columns
Heightbyte0Storage grid rows

Storage dimensions are forced to 0 for PRO items:

csharp
if (!isPro)
{
    _width = p.data.ParseUInt8("Width");
    _height = p.data.ParseUInt8("Height");
}

Only non-zero dimensions are displayed in the tooltip:

csharp
if (width > 0 && height > 0)
{
    builder.Append(localization.format("ItemDescription_StorageDimensions", width, height), DescSort_Important);
}

Inherited Behavior: ItemClothingAsset

Shirts inherit the full clothing behavior suite. This section covers shirt-specific considerations.

Armor System (Shirts Are Eligible)

Shirts are one of four slot types that apply armor. Armor from a shirt multiplies with armor from hats, pants, and vests.

Proof System and Movement Speed

All proof flags and movement speed modifiers apply to shirts identically to other clothing types. See the ItemHatAsset documentation for the full clothing inheritance behavior.

Hair and Beard Visibility

ItemClothingAsset reads Hair_Visible and Beard_Visible from the .dat file, defaulting to true. The code comment notes: "Added late in development for mesh override shirts, so most items do not have these set."

A mesh-override shirt that covers the character's head should set Hair_Visible false and Beard_Visible false to hide the hair and beard that would otherwise clip through the replacement mesh.

Wear Audio

Shirts default to the sleeve rustle sound (not the zipper sound used by backpacks and vests):

csharp
wearAudio = new AudioReference("core.masterbundle", "Sounds/Sleeve.mp3");

PopulateAsset Call Chain

ItemAsset.PopulateAsset
  └─ ItemClothingAsset.PopulateAsset  — armor, proof, movement, visuals
       └─ ItemBagAsset.PopulateAsset  — Width, Height
            └─ ItemShirtAsset.PopulateAsset
                 ├─ Server check (null mesh/material override)
                 ├─ 1P mesh override loading
                 ├─ 3P mesh override loading
                 ├─ Material override loading
                 ├─ Shirt texture loading (+ validation)
                 ├─ Emission texture loading (+ validation)
                 ├─ Metallic texture loading (+ validation)
                 └─ Ignore_Hand flag

PRO vs Non-PRO Behavior

FeatureNon-PRO ShirtPRO Shirt
Armor valuesParsed from .datForced to 1.0
Storage dimensionsParsed from .datForced to 0
Texture loadingNormalNormal
Mesh overrideNormalNormal
Material overrideNormalNormal
Cosmetic previewCosmeticPreviewOverride loaded

PRO shirts provide no gameplay benefit — no armor, no storage. They are visual-only items for the cosmetic loadout system.


BuildCargoData — Wiki Export

Shirts contribute to two Cargo database tables:

Clothing table (from ItemClothingAsset)

Standard clothing columns: GUID, Armor, Armor_Explosion, Falling_Damage_Multiplier, Proof_Water, Proof_Fire, Proof_Radiation, Prevents_Falling_Broken_Bones, Movement_Speed_Multiplier, Mirror_Left_Handed_Model, Priority_Over_Cosmetic.

Bag table (from ItemBagAsset)

ColumnSource
GUIDPK, FK to Clothing
Width_width
Height_height

Note: Shirts have no dedicated Shirt Cargo table — there is no exporter beyond Clothing and Bag.


.dat File Reference — Shirt-Specific

.dat KeyTypeDefaultCategory
Armorfloat1.0Damage mitigation (SHIRT slot applies)
Armor_Explosionfloatequals ArmorExplosion-specific armor
Falling_Damage_Multiplierfloat1.0Fall damage scalar
Proof_WaterflagWater breathing
Proof_FireflagFire immunity
Proof_RadiationflagRadiation immunity
Prevents_Falling_Broken_BonesboolfalseNo fall bone breaks
Movement_Speed_Multiplierfloat1.0Movement speed modifier
Hair_VisiblebooltrueShow character hair
Beard_VisiblebooltrueShow character beard
Widthbyte0Storage columns
Heightbyte0Storage rows
Has_1P_Character_Mesh_OverrideboolfalseEnable 1P mesh replacement
Character_Mesh_3P_Override_LODsushort0Count of 3P override meshes
Has_Character_Material_OverrideboolfalseEnable full material replacement
Ignore_HandflagSkip hand model modification

Master Bundle Asset Requirements

Bundle KeyTypeRequiredPurpose
"Shirt"Texture2DRequired (unless material override)Base color/diffuse texture
"Emission"Texture2DOptionalGlow/emission map
"Metallic"Texture2DOptionalMetallic/smoothness map
"Character_Mesh_1P_Override_0"GameObjectOptional (requires flag)1P body mesh replacement
"Character_Mesh_3P_Override_0"GameObjectOptional (requires LOD count)3P body mesh LOD 0
"Character_Mesh_3P_Override_1"GameObjectOptional3P body mesh LOD 1
"Character_Mesh_Override_0"GameObjectOptional (legacy fallback)Legacy naming fallback
"Character_Material_Override"MaterialOptional (requires flag)Full material replacement

Modding Example — Basic Shirt with Storage .dat

ini
GUID a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
Type Shirt
Rarity Common
Size_X 3
Size_Y 2
Armor 0.95
Width 3
Height 2
Hair_Visible false

Bundle contents: Shirt (Texture2D, RGBA32, 512×512), optional Emission, optional Metallic.


Modding Example — Mesh Override Armor .dat

ini
GUID f1e2d3c4b5a69788796a5b4c3d2e1f0a
Type Shirt
Rarity Epic
Size_X 4
Size_Y 3
Armor 0.75
Armor_Explosion 0.60
Has_1P_Character_Mesh_Override true
Character_Mesh_3P_Override_LODs 3
Has_Character_Material_Override true
Hair_Visible false
Beard_Visible false

Bundle contents: Character_Mesh_1P_Override_0, Character_Mesh_3P_Override_0, Character_Mesh_3P_Override_1, Character_Mesh_3P_Override_2, Character_Material_Override.

This creates bulky armor that:

  • Provides 25% general and 40% explosive damage reduction
  • Replaces the player character's body mesh at 1P and 3 LOD levels
  • Uses a completely custom material
  • Hides hair and beard to prevent clipping

Modding Example — Texture-Only Shirt .dat

ini
GUID d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
Type Shirt
Rarity Rare
Size_X 2
Size_Y 2
Armor 0.90
Width 4
Height 3
Movement_Speed_Multiplier 1.05

Bundle contents: Shirt (Texture2D), Emission (Texture2D, RGB24), Metallic (Texture2D, RGBA32).

This creates a lightweight combat shirt that:

  • Provides 10% damage reduction
  • Has a 4×3 storage grid (12 slots)
  • Slightly increases movement speed (5% bonus)
  • Includes emission and metallic maps for advanced rendering

Common Issues

  1. Shirt texture not applying: Verify the "Shirt" key matches exactly (case-sensitive) and the texture is a Texture2D type. If using Has_Character_Material_Override, the shirt texture is skipped — the material handles all rendering.

  2. Mesh override doesn't appear: Ensure Has_1P_Character_Mesh_Override true is set in the .dat AND the Character_Mesh_1P_Override_0 GameObject exists in the bundle. The override GameObject must have a MeshFilter component.

  3. Missing MeshFilter error: The override GameObject must have a MeshFilter component with a valid sharedMesh. Without it, the asset reports: "missing MeshFilter on character mesh 1P override 0".

  4. Low-resolution artifacts: If the shirt texture looks blocky or pixelated, it may be using a compressed format (DXT1/DXT5) at a low resolution (<128px). Switch to RGBA32 or RGB24 for small textures.

  5. Memory waste from read/write: Textures with read/write enabled consume double memory. Disable read/write on textures that don't need CPU-side access (most clothing textures).

  6. Hair/beard clipping through mesh override: When using mesh overrides, remember to set Hair_Visible false and Beard_Visible false. The mesh override replaces the body but the hair/beard are separate head meshes that won't be covered.

  7. Emission texture alpha channel: For low-resolution emission textures, use RGB24 format instead of RGBA32 to save memory — the alpha channel is unused for emission.

  8. Metallic texture format: Metallic textures should use RGBA32 format because the alpha channel carries smoothness data. Using RGB24 would lose the smoothness information.

  9. PRO shirt storage: PRO items have storage dimensions forced to 0. If you need storage on a cosmetic item, use a non-PRO shirt asset type.

  10. Hand model mismatch: If hands look wrong with a mesh override, set Ignore_Hand to preserve the default hand model.

Document history

VersionDateAuthorNotes
1.02026-07-2857 StudiosInitial publication. Full shirt asset documentation including mesh override system, texture validation, storage dimensions, and modding examples.