Mod Loader/Reference/Garment fields
Add tags under garmentTags and garment pieces under garmentComponents in mod.json. To make your first pack, follow Start with a game garment. The Side Placket Top file notes cover the unfinished mesh example.
Start with a game pattern
A custom piece copies a supported game garment. The base keeps its garment type, fit rules, compatibility, materials, and any panel geometry you leave alone. Dressmaker does not support adding a new garment category.
Use the exact component ID from BepInEx/config/DressmakerMods/garment-reference.json. The file is written when the loader starts. Bodices, skirts, sleeves, and collars are supported bases.
Pack fields
| Field | Use |
|---|---|
id
|
Stable pack namespace. Combined with each garment's local ID. |
name, author, version
|
Pack details shown in Mod Manager. |
icon
|
Optional pack thumbnail. Separate from each garment's selection icon. |
garmentTags
|
Custom tag definitions, each with id and name.
|
garmentComponents
|
Garment entries described below. |
dressTemplates
|
Optional combinations of bodice, skirt, sleeve and collar. See Dress templates. |
attributions
|
Optional credits and licenses for included assets. |
Side Placket Top defines one garment with four panel files. For a pack using OBJ panels and surface maps, see the bodice example.
{
"id": "yuuma.side-placket-top",
"name": "Side Placket Top",
"author": "Yuuma",
"version": "0.1.0",
"icon": "icon.png",
"garmentTags": [
{
"id": "SidePlacket",
"name": "Side placket"
}
],
"garmentComponents": [
{
"id": "top",
"name": "Side Placket Top",
"baseComponent": "Split Crew Neck Bodice",
"icon": "icon.png",
"sketch": "sketch.png",
"replaceTags": true,
"tagWeights": [
{
"tag": "SidePlacket",
"weight": 12
},
{
"tag": "Casual",
"weight": 3
}
],
"variants": [
{
"baseVariant": 0,
"panels": [
{
"basePanel": 1,
"name": "Back",
"model": "pieces/back.mesh.json"
},
{
"basePanel": 2,
"name": "Front Right",
"model": "pieces/front-right.mesh.json"
},
{
"basePanel": 3,
"name": "Front Left",
"model": "pieces/front-left.mesh.json"
},
{
"basePanel": 0,
"name": "Placket",
"model": "pieces/placket.mesh.json"
}
]
}
]
}
]
}
The loader saves the piece as yuuma.side-placket-top.top and the tag as yuuma.side-placket-top.SidePlacket. Keep the pack ID and local IDs stable after release; saved dresses refer to them.
Garment component fields
| Field | Required | Meaning |
|---|---|---|
id
|
Yes | Local ID for this piece. The loader combines it with the pack ID. |
name
|
Yes | Name shown in the game. |
pieceType
|
No | Category ID registered by a plugin, or Bodice, Skirt, Sleeve or Collar. Omit to inherit the base category. This changes the category property; it does not convert the base C# class or add game slots. |
baseComponent
|
Yes | Exact ID of a supported game garment. The base supplies type and fit behavior. |
icon
|
No | Pack-relative PNG or JPG used in selection UI. Omit it to keep the base icon. |
sketch
|
No | Pack-relative PNG or JPG used as the sketch for variants that do not set their own. Omit it to keep each base sketch. |
variants
|
No | Variants to copy or customize. If omitted, every base variant is copied. If present, list each variant to keep. |
replaceTags
|
No | When true, start with no base tag weights. Otherwise inherit the base weights. Default is false.
|
tagWeights
|
No | Tag IDs and additive weights. A listed weight adds a tag or replaces that tag's inherited weight. |
notes
|
No | Notes copied to the garment component; when omitted, the base notes remain. |
unlockByDefault
|
No | Boolean, default true. False waits for an unlock such as a friendship reward. Does not revoke patterns already earned.
|
compatibility
|
No | Optional object controlling sleeve, collar, waist and exclusion rules. See Compatibility fields. Omitted properties keep base values. |
normalMap
|
No | Pack-relative PNG normal map. Used by variants and panels without an override. Omit or leave blank to keep the base map. |
ambientOcclusionMap
|
No | Pack-relative PNG ambient occlusion map. Uses the same inheritance as normalMap.
|
flipNormalGreen
|
No | Optional boolean, default false. Invert the green channel of a supplied normal map. Variant and panel values can override it.
|
Tags and weights
Each garmentTags entry has an id and player-facing name. A tagWeights entry uses a tag ID and numeric weight. You can name a tag from this pack, another pack, or the game (such as Elegant). The weight contributes to the game's tag score; it is not a percentage.
Without replaceTags, the loader copies the base weights, then adds listed tags or changes the matching tag's weight. With replaceTags: true, only listed weights are used. Zero contributes no score. An unknown or duplicate tag makes the piece fail to load; check BepInEx/LogOutput.log.
A pack may contain tags without garment pieces. The loader retains garment and tag data referenced by saved dresses in its local cache after a pack is removed. Keep the pack installed if you want to continue editing its pieces.
Variant fields
| Field | Meaning |
|---|---|
baseVariant
|
Zero-based index of the variant to copy from the base; default 0. Must refer to an existing variant. |
sketch
|
Optional pack-relative PNG or JPG for this variant. Overrides the component-level sketch. |
panels
|
Optional replacement panel list. If omitted, the variant copies every panel from its base variant. |
normalMap
|
Optional PNG normal map for this variant. Omitted or blank uses the garment map. |
ambientOcclusionMap
|
Optional PNG ambient occlusion map for this variant. Omitted or blank uses the garment map. |
flipNormalGreen
|
Optional boolean overriding the garment setting. An explicit false disables flipping for this variant unless a panel overrides it.
|
When you provide variants, list every base variant you want to keep. A garment can have up to 16 variants.
Panel fields
A panel list replaces the base variant's panel list. Copy an existing panel with basePanel, or supply model and zones to define one directly.
| Field | Meaning |
|---|---|
zones
|
Optional nonempty list of body regions. Overrides inherited zones. Without basePanel, model is required. See accepted zones.
|
basePanel
|
Optional source panel index in the base variant, not the new panel's number. Several entries can copy the same source. Omit when supplying model and zones. Older entries without zones still default to source panel 0. |
model
|
Optional pack-relative .obj or .mesh.json file. See Panel OBJ files. Omit it to copy the base panel mesh.
|
name
|
Optional panel name shown by the game. Omit it to keep the base panel name. |
grainDirection
|
Optional three-number vector, such as [0, 1, 0]. Must be finite and nonzero. Omit to inherit the source direction, or use [0, 1, 0] for a new panel.
|
isBiasCut
|
Optional boolean. Omit to inherit the source setting, or use false for a new panel. |
normalMap
|
Optional PNG normal map for this panel. Omitted or blank uses the variant, then garment, then base material. |
ambientOcclusionMap
|
Optional PNG ambient occlusion map for this panel. Uses the same inheritance as normalMap.
|
flipNormalGreen
|
Optional boolean overriding the variant and garment settings for a supplied normal map. |
transform
|
Optional object with scale, rotation and offset arrays. Applies to an imported or copied mesh. See Transform fields.
|
This creates one variant with one panel. Add entries to panels for the other pieces you want to keep:
{
"variants": [{
"baseVariant": 0,
"panels": [{
"basePanel": 0,
"name": "Moonlit front",
"model": "front.mesh.json",
"grainDirection": [0, 1, 0],
"isBiasCut": false
}]
}]
}
Panel OBJ files
Set a panel's model to a pack-relative OBJ path, such as pieces/back.obj. Each file becomes one panel mesh; OBJ objects and groups are combined. Export each sewing panel separately.
Use metres with Y up. Coordinates are imported as written unless the panel supplies a transform. Include UVs for the cutting pattern and normals for shading. Normals are recalculated if any face corner has none; smoothing groups are ignored.
Triangles, quads, and simple concave polygons are supported. Faces may have at most 256 corners. Each file may be up to 8 MB, with 65,535 imported vertices and 131,070 triangles. UV and normal seams can increase the imported vertex count.
Material names and MTL files are ignored. Garments use the fabrics chosen in game. Skeletons and animation are not imported. Zones come from the source panel unless you supply zones. The base garment retains its compatibility rules. To replace a panel with an OBJ, follow the garment guide.
Panel mesh JSON
Mesh JSON is supported alongside OBJ. A garment panel cannot use a raw FBX file or the accessory Unity package importer. A JSON mesh has positions, uvs, and triangles arrays:
positionscontains X, Y, Z triples in metres.uvscontains one U, V pair for each vertex.trianglescontains zero-based vertex indices in groups of three.
Each panel needs at least one triangle, UV area, and 3D surface area. A file can have at most 65,535 vertices and 8 MB. Keep the base panel's orientation and seam zones in mind: a replacement mesh does not create new sewing joins or fit rules. Test by cutting and sewing the panel; the mannequin preview alone does not show whether it works.
Dress templates
Put dressTemplates beside garmentComponents at the top level of mod.json. Each entry adds a Template: [name] choice based on its bodice. Selecting it also selects the specified skirt, sleeves, collar and variants. It does not combine the meshes into one piece.
| Field | Required/default | Use |
|---|---|---|
id
|
Required | Local template ID. The saved bodice alias is packId.template.id.
|
name
|
Required | Displayed after Template:. |
icon
|
Optional | Pack-relative PNG or JPEG. Omit to use the bodice icon. |
bodice, skirt, sleeve, collar
|
All required | Exact native IDs or full modded IDs. Use native none/no-collar entries where appropriate, not an empty string. |
bodiceVariant, skirtVariant, sleeveVariant, collarVariant
|
Each defaults to 0 | Zero-based variant index for the corresponding piece. |
All four pieces must already exist and be compatible with the bodice. The loader checks waist type, collar type, attached-sleeve rules and excluded components. Look up IDs and variant counts in garment-reference.json.
C# garment API
A BepInEx plugin can register the same garment definitions. Reference DressmakerMods.dll, BepInEx, the game assemblies, and the Unity assemblies. Declare a dependency on yuu.dressmaker.mods and register inside WhenReady.
using System.Collections.Generic;
using System.IO;
using BepInEx;
using DressmakerMods;
[BepInPlugin("yourname.moon-clothes", "Moon Clothes", "1.0.0")]
[BepInDependency("yuu.dressmaker.mods", "0.6.67")]
public sealed class MoonClothesPlugin : BaseUnityPlugin
{
private void Awake()
{
GarmentModApi.WhenReady(() =>
{
string art = Path.Combine(Paths.GameRootPath, "Mods", "moon-clothes");
ItemTag tag = GarmentModApi.RegisterTag(
"yourname.moon-clothes", "Moonlit", "Moonlit");
GarmentModApi.RegisterFromBase(
"Split Crew Neck Bodice", "yourname.moon-clothes",
"moon-bodice", "Moon Bodice", draft =>
{
draft.icon = GarmentModApi.LoadIcon(art, "icon.png");
draft.variations[0].sketchedSprite =
GarmentModApi.LoadSketch(art, "sketch.png");
draft.additiveTags = new List<TagWeight> {
new TagWeight { tag = tag, weight = 8f }
};
});
});
}
}
| Method or type | Use |
|---|---|
bool IsReadyvoid WhenReady(Action callback)
|
Run after the loader and native garment data are ready. If already ready, the callback runs immediately. |
void LoadAssets(GarmentComponent component)
|
Load a pack garment's meshes, sketches and surface maps before reading or editing its variations field. Call on the main thread. Throws if the assets cannot be loaded.
|
GarmentComponent FindComponent(string id)GarmentComponent[] GetComponents()
|
Look up registered garment pieces and load their assets if needed. |
ItemTag FindTag(string id)ItemTag[] GetTags()
|
Look up native and custom tags. |
ItemTag RegisterTag(string modId, string localId, string displayName)
|
Register a namespaced garment tag. |
GarmentComponent RegisterComponent(string modId, ModGarmentDefinition definition, string assetDirectory)
|
Register a ModGarmentDefinition using the same fields as the JSON pack.
|
GarmentComponent RegisterFromBase(string baseComponentId, string modId, string localId, string displayName, Action<GarmentComponent> configure)
|
Copy a base piece, edit the copy in the callback, and register it. |
Sprite LoadIcon(string assetDirectory, string file)Texture2D LoadSketch(string assetDirectory, string file)Mesh LoadPanelMesh(string assetDirectory, string file)
|
Load an icon, sketch, or panel mesh from the supplied asset directory. |
ModGarmentDefinition, ModGarmentVariant, ModGarmentPanel, GarmentTagWeight, ModGarmentTag
|
Public types for constructing a garment definition in C#. |
To replace a panel mesh in the callback, assign GarmentModApi.LoadPanelMesh(...) to that panel's mannequinMesh. The API copies the base component and its variants before the callback, so edits do not change the native piece. The loader checks custom mesh triangles and UVs. Assets loaded through the API stay available for the session; leave the plugin installed so it can register the piece on the next launch.
Pack garments load their meshes and sketches when the game needs them. FindComponent and GetComponents load assets before returning. If you get a component from the game directly, call GarmentModApi.LoadAssets(component) before editing its variants. Garments registered through C# are loaded during registration.
Check a garment
- Back up the save, install the pack, and restart Dressmaker.
- Check the piece icon and each sketchbook variant.
- Check tag scoring on a commission.
- Cut and sew each panel, then reopen the saved dress.
- If a piece is missing, read
BepInEx/LogOutput.logfor its ID and validation error.
Unlock a garment through friendship
Set unlockByDefault to false in the garment definition to keep it out of the player's starting patterns. This defaults to true when omitted. Add a client reward using friendship rewards. Existing earned patterns stay unlocked.
Add a panel without copying one
Give the panel a model and a list of zones, and leave out basePanel. For example, this adds a back belt panel:
{
"name": "Belt Back",
"model": "pieces/test-belt-b.obj",
"zones": ["BodiceBack"],
"grainDirection": [0, 1, 0],
"isBiasCut": false
}
Put this entry in a variant's panels list. The list replaces all panels, so include the other pieces you want to keep. You can mix new panels and entries that copy a basePanel, up to 32 panels per variant.
zones identifies body regions. Use more than one when needed, such as ["BodiceBack", "BodiceRight"]. These labels don't move or reshape the OBJ. Check its position, UVs and seams in game.
For new panels, grainDirection defaults to [0, 1, 0] and isBiasCut to false. The garment still needs baseComponent and a valid baseVariant for its category, materials and compatibility rules.
Accepted zones: BodiceFront, BodiceBack, BodiceLeft, BodiceRight, SkirtFront, SkirtBack, SkirtLeft, SkirtRight, Collar, ArmLeft, ArmRight, CuffLeft, CuffRight. Names are case-insensitive. Empty lists, None, numbers and unknown names are rejected.
Create panels in C#
Use these helpers on Unity's main thread. This example goes inside the RegisterFromBase callback, after GarmentModApi.WhenReady:
var mesh = GarmentModApi.LoadPanelMesh(assets, "pieces/test-belt-b.obj");
var panel = GarmentModApi.CreatePanel(
mesh, "Belt Back", GarmentModApi.ParsePanelZones("BodiceBack"));
draft.variations[0].panels.Add(panel);
This adds to the copied panel list. Call draft.variations[0].panels.Clear() first if you are replacing all of its panels.
string[] GetPanelZoneNames()returns the accepted individual zone names.Mannequin.Zones ParsePanelZones(params string[] names)combines names into flags and rejects invalid entries.PatternPanelDefinition CreatePanel(Mesh mesh, string name, Mannequin.Zones zones, Vector3? grainDirection = null, bool isBiasCut = false)validates the mesh, zones and grain direction. It defaults to upward grain and no bias cut. The mesh is shared; the caller must keep it alive while the garment uses it.
RegisterComponent accepts ModGarmentPanel.Zones as a List<string>, using the JSON rules. Leave BasePanel unset for a new panel. Setting it to 0 explicitly selects source panel 0.
Normal and ambient occlusion maps
Add normalMap and ambientOcclusionMap to a garment entry. Both are PNG paths relative to the pack folder. The maps use the garment mesh's UVs and stay in place when the player changes fabric.
"normalMap": "images/bodice-normal.png", "ambientOcclusionMap": "images/bodice-ao.png"
Use a tangent-space RGB normal map, with a flat normal around RGB (128, 128, 255). Alpha is ignored. For ambient occlusion, use a grayscale image: white leaves the surface unoccluded and darker values add shading. These files are loaded as linear data. Keep each image under 25 MB and no larger than 8192 pixels on either side.
Each entry in variants can set the same two fields to override the garment's maps. An omitted or blank variant field uses the garment-level setting; if neither provides a map, the base garment's map is kept. Variant maps apply to each panel unless that panel overrides them.
C# mods use ModGarmentDefinition.NormalMap and AmbientOcclusionMap, or the corresponding properties on ModGarmentVariant, with GarmentModApi.RegisterComponent. Retained garment data and dress exports include the referenced files.
Choose compatible pieces
Add compatibility to a garment entry. Omitted fields keep the base piece's settings. These rules change which pieces can be combined; they do not reshape the mesh.
"compatibility": {
"usesSleeves": false,
"collarType": "None",
"waistType": "NaturalWaist",
"excludedComponents": ["another-pack.oversized-skirt"]
}
| Field | Applies to | Values |
|---|---|---|
| usesSleeves | Bodices | true or false |
| collarType | Bodices and collars | Round, None, VNeck, Detached |
| waistType | Bodices and skirts | NaturalWaist, EmpireWaist |
| detachedSleeves | Sleeves | true or false |
| excludedComponents | Bodices | Up to 256 exact garment IDs. Modded IDs include the pack ID. An empty array clears inherited exclusions. |
Excluded pieces may come from packs that load later. Missing IDs are reported in the log and resolved if that piece registers later. An exclusion does not make the other pack required or add its unused pieces to a dress export.
Give a panel its own maps
Set normalMap or ambientOcclusionMap inside a panel entry to override that variant's map. Unset or blank paths fall back to the variant, then the garment, then the base material.
"panels": [{
"basePanel": 0,
"normalMap": "images/front-normal.png",
"ambientOcclusionMap": "images/front-ao.png",
"flipNormalGreen": true
}]
flipNormalGreen inverts the green channel of a supplied normal PNG. Use it when your normal-map export uses the opposite Y direction. It can be set on the garment, variant or panel; the closest explicit value wins. It does not convert a normal map inherited from a native material.
Adjust an imported mesh
Each panel accepts a transform. Scale and rotate the mesh around its origin, then move it by the offset. Rotation is in degrees, using Unity's Euler rotation convention. Offsets use game-space units. UV coordinates are unchanged.
| Field | Default | Use |
|---|---|---|
scale
|
[1, 1, 1]
|
X/Y/Z multipliers. Negative values mirror; an absolute value below 0.000001 on any axis is rejected. |
rotation
|
[0, 0, 0]
|
Unity Euler angles in degrees, around the model's origin. |
offset
|
[0, 0, 0]
|
X/Y/Z translation in metres, after scaling and rotation. |
Each array must contain exactly three finite numbers.
"transform": {
"scale": [0.01, 0.01, 0.01],
"rotation": [0, 180, 0],
"offset": [0, 0, 0]
}
The defaults are scale [1, 1, 1], rotation [0, 0, 0] and offset [0, 0, 0]. Negative scales mirror the mesh; zero scales are rejected. The loader copies inherited meshes before transforming them and generates tangents for imported or transformed panels. Check grainDirection after rotating a model: that field stays in garment space.
The loader generates missing tangents before checking the mesh. The log reports any remaining problems, including degenerate or overlapping UV triangles and unusually small or large bounds. Check the fit by cutting and sewing the garment. Very dense UV layouts have a bounded overlap check, and the log says when that check was incomplete.
C# helpers
void GarmentModApi.ConfigureCompatibility(GarmentComponent component,
GarmentCompatibilityOptions options)
GarmentMeshDiagnostic[] GarmentModApi.InspectPanelMesh(Mesh mesh)
Mesh GarmentModApi.TransformPanelMesh(Mesh mesh, GarmentMeshTransform transform)
Call these on Unity's main thread. Compatibility settings use the same fields as JSON; omitted values preserve the current settings. Changes to a registered JSON garment are recorded for dress export. Mesh inspection returns Code and Message for each concern. TransformPanelMesh returns a new mesh which your plugin owns; destroy it when you no longer need it.
React to garment changes
ModContext context = ModLoaderApi.Register(this);
IDisposable subscription = GarmentEventsApi.Subscribe(context,
GarmentEvent.FabricChanged, change => {
Logger.LogInfo("Fabric changed on " + change.Garment?.name);
});
Keep the context for your plugin's lifetime. Dispose the subscription to stop receiving it, or dispose the context when your plugin unloads. A callback exception is logged without stopping other callbacks.
| Event | When it runs | Context |
|---|---|---|
| Selected | The sketchbook update observes a different selected garment or variant. Includes the first observed selection. | Garment and Variant. Garment may be null when a slot is cleared. |
| PanelCreated | Dress.CreatePanel returns, after choosing that panel's material. | Garment and Panel. The dress may still be creating its other panels. |
| FabricChanged | An active panel's update observes a changed fabric. Includes its first non-null fabric. | Garment, Panel, PreviousFabric and Fabric. |
Selection and fabric events report the state observed during an update; they do not report every intermediate assignment within a frame. Inactive panels report a change when they next update. Variant is -1 for events that do not supply it.
Register a piece type
Call this in your plugin's Awake, after installing the patches that support your category. Add a BepInEx dependency on yuu.dressmaker.mods. Packs waiting for a category are retried in Mod Loader's Start, after plugin Awake methods have run.
GarmentModApi.RegisterPieceType(
"yourmod.overskirt",
yourOverskirtType,
"Overskirt");
yourOverskirtType is the GarmentComponent.Type value used by your plugin. Keep that value stable across launches and coordinate it with other category plugins. Mod Loader does not allocate enum values or implement the category's gameplay. IDs must be namespaced, such as yourmod.overskirt; duplicate IDs are rejected. Registrations last for the game session.
void GarmentModApi.RegisterPieceType(string id,
GarmentComponent.Type value, string displayName)
void GarmentModApi.SetPieceType(GarmentComponent component,
string pieceType)
Use SetPieceType inside a RegisterFromBase callback to assign a registered category. JSON imports use ModGarmentDefinition.PieceType. Call these methods on Unity's main thread. A skirt base remains a SkirtDefinition, including its fitting fields, even when its category is overridden.