Jump to content

Mod Loader/Reference/Garment fields

From Threadbox

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:

  • positions contains X, Y, Z triples in metres.
  • uvs contains one U, V pair for each vertex.
  • triangles contains 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 IsReady
void 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

  1. Back up the save, install the pack, and restart Dressmaker.
  2. Check the piece icon and each sketchbook variant.
  3. Check tag scoring on a commission.
  4. Cut and sew each panel, then reopen the saved dress.
  5. If a piece is missing, read BepInEx/LogOutput.log for 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.

Back to the Mod Loader guide