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 overrideNote: 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
| Condition | Shirt | Emission | Metallic |
|---|---|---|---|
| Required | Yes (loadRequiredAsset) | No (load, returns null) | No (load, returns null) |
| Skip conditions | Server OR characterMaterialOverride != null | Server OR character material set | Server OR character material set |
| Read/write warning | Yes | Yes | Yes |
| Resolution warning | Yes | Yes | Yes |
| Format-specific warning | Compression on low-res | RGBA32 vs RGB24 | Compression 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 Key | Type | Purpose |
|---|---|---|
Has_1P_Character_Mesh_Override | bool | Enables first-person mesh replacement |
Character_Mesh_3P_Override_LODs | ushort | Number of third-person LOD meshes |
Has_Character_Material_Override | bool | Enables 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 Key | Type | Default | Purpose |
|---|---|---|---|
Width | byte | 0 | Storage grid columns |
Height | byte | 0 | Storage 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 flagPRO vs Non-PRO Behavior
| Feature | Non-PRO Shirt | PRO Shirt |
|---|---|---|
| Armor values | Parsed from .dat | Forced to 1.0 |
| Storage dimensions | Parsed from .dat | Forced to 0 |
| Texture loading | Normal | Normal |
| Mesh override | Normal | Normal |
| Material override | Normal | Normal |
| Cosmetic preview | — | CosmeticPreviewOverride 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)
| Column | Source |
|---|---|
GUID | PK, 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 Key | Type | Default | Category |
|---|---|---|---|
Armor | float | 1.0 | Damage mitigation (SHIRT slot applies) |
Armor_Explosion | float | equals Armor | Explosion-specific armor |
Falling_Damage_Multiplier | float | 1.0 | Fall damage scalar |
Proof_Water | flag | — | Water breathing |
Proof_Fire | flag | — | Fire immunity |
Proof_Radiation | flag | — | Radiation immunity |
Prevents_Falling_Broken_Bones | bool | false | No fall bone breaks |
Movement_Speed_Multiplier | float | 1.0 | Movement speed modifier |
Hair_Visible | bool | true | Show character hair |
Beard_Visible | bool | true | Show character beard |
Width | byte | 0 | Storage columns |
Height | byte | 0 | Storage rows |
Has_1P_Character_Mesh_Override | bool | false | Enable 1P mesh replacement |
Character_Mesh_3P_Override_LODs | ushort | 0 | Count of 3P override meshes |
Has_Character_Material_Override | bool | false | Enable full material replacement |
Ignore_Hand | flag | — | Skip hand model modification |
Master Bundle Asset Requirements
| Bundle Key | Type | Required | Purpose |
|---|---|---|---|
"Shirt" | Texture2D | Required (unless material override) | Base color/diffuse texture |
"Emission" | Texture2D | Optional | Glow/emission map |
"Metallic" | Texture2D | Optional | Metallic/smoothness map |
"Character_Mesh_1P_Override_0" | GameObject | Optional (requires flag) | 1P body mesh replacement |
"Character_Mesh_3P_Override_0" | GameObject | Optional (requires LOD count) | 3P body mesh LOD 0 |
"Character_Mesh_3P_Override_1" | GameObject | Optional | 3P body mesh LOD 1 |
"Character_Mesh_Override_0" | GameObject | Optional (legacy fallback) | Legacy naming fallback |
"Character_Material_Override" | Material | Optional (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 falseBundle 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 falseBundle 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.05Bundle 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
Shirt texture not applying: Verify the
"Shirt"key matches exactly (case-sensitive) and the texture is aTexture2Dtype. If usingHas_Character_Material_Override, the shirt texture is skipped — the material handles all rendering.Mesh override doesn't appear: Ensure
Has_1P_Character_Mesh_Override trueis set in the.datAND theCharacter_Mesh_1P_Override_0GameObject exists in the bundle. The override GameObject must have aMeshFiltercomponent.Missing MeshFilter error: The override GameObject must have a
MeshFiltercomponent with a validsharedMesh. Without it, the asset reports:"missing MeshFilter on character mesh 1P override 0".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
RGBA32orRGB24for small textures.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).
Hair/beard clipping through mesh override: When using mesh overrides, remember to set
Hair_Visible falseandBeard_Visible false. The mesh override replaces the body but the hair/beard are separate head meshes that won't be covered.Emission texture alpha channel: For low-resolution emission textures, use
RGB24format instead ofRGBA32to save memory — the alpha channel is unused for emission.Metallic texture format: Metallic textures should use
RGBA32format because the alpha channel carries smoothness data. UsingRGB24would lose the smoothness information.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.Hand model mismatch: If hands look wrong with a mesh override, set
Ignore_Handto preserve the default hand model.
Document history
| Version | Date | Author | Notes |
|---|---|---|---|
| 1.0 | 2026-07-28 | 57 Studios | Initial publication. Full shirt asset documentation including mesh override system, texture validation, storage dimensions, and modding examples. |
