Jump to content

Mod Loader/Reference/Accessory fields

From Threadbox

An accessory pack uses mod.json plus mesh and image files. The loader creates static game objects from the pack, so a content pack does not need a DLL.

The Oiia Cat example ZIP has a placed accessory and a repeating trim. Use it to follow the file layout, then supply your own mesh and texture for the mod you want to share.

Pack fields

Field Meaning
id Unique pack ID, such as yourname.cat-pack. Use letters, numbers, dots, underscores, or hyphens. Keep it stable after release.
name Name shown in Mod Manager.
author Name of the pack author.
version Pack version. Increase it when updating a shared pack; keep existing IDs to update the same items.
icon Optional pack thumbnail filename.
attributions Optional credits and license entries, shown in Mod Manager.
accessories Array of accessory entries. A pack can contain several items.

Accessory fields

Field Meaning and use
id Unique within the pack. Together with the pack ID, identifies the saved item. A new ID makes a new item; keeping the ID updates the existing one.
name Name shown in the shop.
baseAccessory Existing item whose placement behavior and shader are copied. Use a single-item base, such as Applique Luna Moth, for a placed accessory; use a trim base, such as Trim Pearl Beads, for a path-following trim.
accessoryType Shop category, for example Other or Trim.
model Pack-relative path to an .obj or .mesh.json file. For a custom trim, this mesh repeats along the path.
texture PNG or JPEG image used on the mesh. PNG supports transparency.
icon Item thumbnail image.
cost Shop price, using the base item's normal purchase behavior.
unlockLevel Prestige level from 0 to 50. Zero makes the item available from the start.
tagWeights Array of {"tag":"Cute","weight":1} values describing the styles added to the dress. Use tags listed in BepInEx/config/DressmakerMods/accessory-reference.json.
notes Optional notes for the item.
beadSpacing For custom trims, distance between repeats in metres at the default width. Allowed range is 0.005–1. For example, 0.04 is 4 cm. The game's trim width control also scales spacing, and path sampling makes the spacing approximate. Omit it to keep the base trim spacing.

Set reuseAsset: true to copy the base accessory's original model instead of loading a custom model. This is useful for recoloring an existing item, such as the Luna Moth or Golden Scale Mail Chain. Leave the field out when providing your own mesh.

Material settings

Custom models use the base accessory's shader. The loader starts them with a matte material to avoid overly bright reflections. Add a material object to adjust properties supported by that shader:

"material": {
  "floats": {
    "_Smoothness": 0.2,
    "_SpecularLightStrength": 0.15
  }
}

Material maps can include color, floats, ints, colors, vectors, textures, textureScales, textureOffsets, keywords, and renderQueue. The accessory's texture remains its main color image; use material.textures for other maps. Shader changes are not supported for accessories.

Colour values accept hex strings or numeric objects such as { "r": 0.0776, "g": 0.871, "b": 2.118, "a": 1 }. Alpha defaults to 1; HDR components above 1 are preserved. See numeric colours and HDR for an example.

Mesh format and orientation

A JSON model contains flat arrays of positions, UV coordinates, and triangle indices. Positions use metres: X is left/right, Y is up, and +Z faces out from the garment. For trims, Y follows the path and +Z points away from the surface. UVs map the texture onto the mesh.

The supplied cat meshes put the nearest surface at Z=0, so the model projects out from the dress. To resize a model, scale positions only; leave UVs and triangle indices alone. If you resize a trim mesh, adjust beadSpacing to match.

Models can have up to 65,535 vertices and 8 MB per model file. The model field accepts OBJ and mesh JSON. For FBX files inside a Unity package, use Accessory Creator to import and bake the accessory. A single accessory needs a single-item base, and a repeating trim needs a trim-path base. Keep trim meshes small because the game draws the mesh once for every repeat.

Use an OBJ model

Put the OBJ in your pack and set model to its filename:

"model": "models/button.obj"

Export a polygon mesh with UVs and normals. Triangles, quads, and simple concave polygons are supported; triangulate faces with more than 256 corners in your modelling program. Each file becomes one mesh, even if it contains several objects or groups. Export each accessory to its own file.

Positions are read as metres, with Y up. The loader keeps the coordinates, UVs, and triangle winding as written: it does not resize, centre, mirror, or rotate the model. Match the game mesh you are replacing. If faces disappear when viewed from outside, check face orientation and your export axes. Test a small model before exporting the whole set.

Normals are kept when every face corner has one. Otherwise the loader recalculates them; OBJ smoothing groups are not used. Export normals to keep smooth shading across UV seams and sharp edges where you intended them.

Set textures and material properties in mod.json. OBJ material names and .mtl files are ignored. Vertex colours, curves, skeletons, and animation are not imported. Keep the file under 8 MB, with no more than 65,535 imported vertices and 131,070 triangles. UV and normal seams can increase the imported vertex count.

Exporting a mesh from Blender

Import an OBJ, FBX, or glTF model into Blender. Check its UV map and texture in Material Preview. The loader uses one color texture for the whole accessory, so combine multiple images into an atlas and update the UVs if the model needs them.

Select only the mesh objects to include. Blender uses Z as up; point the model's front toward −Y. The script below applies object transforms and evaluated modifiers, rotates Blender coordinates into Dressmaker coordinates, centers the model, and writes a static mesh. It does not export skeletons or animation. Open Blender's Scripting workspace. In the Text Editor, click New and paste the script below. Change OUTPUT to a file in an existing folder and set HEIGHT_CM. Keep your mesh objects selected, then click Run Script (the triangle button in the Text Editor). The JSON file appears at the path you entered.

import bpy
import json
from pathlib import Path

OUTPUT = Path(r"C:\Users\YourName\Downloads\cat-single.mesh.json")
HEIGHT_CM = 6.0

if not 0 < HEIGHT_CM <= 100:
    raise ValueError("Choose a height above 0 and at most 100 cm.")
objects = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH']
if not objects:
    raise ValueError("Select your accessory mesh first.")

positions, uvs, triangles = [], [], []
graph = bpy.context.evaluated_depsgraph_get()
for obj in objects:
    evaluated = obj.evaluated_get(graph)
    mesh = evaluated.to_mesh()
    try:
        if not mesh.uv_layers.active:
            raise ValueError(obj.name + " needs a UV map.")
        mesh.calc_loop_triangles()
        lookup = {}
        mirrored_object = evaluated.matrix_world.determinant() < 0
        for triangle in mesh.loop_triangles:
            corners = []
            for loop_index in triangle.loops:
                vertex_index = mesh.loops[loop_index].vertex_index
                uv = mesh.uv_layers.active.data[loop_index].uv
                point = evaluated.matrix_world @ mesh.vertices[vertex_index].co
                position = (-point.x, point.z, -point.y)
                key = (vertex_index, float(uv.x), float(uv.y))
                if key not in lookup:
                    lookup[key] = len(positions)
                    positions.append(position)
                    uvs.append((float(uv.x), float(uv.y)))
                corners.append(lookup[key])
            triangles.extend(corners if mirrored_object else corners[::-1])
    finally:
        evaluated.to_mesh_clear()

if not positions or not triangles:
    raise ValueError("The selected meshes have no triangles.")
low = [min(point[axis] for point in positions) for axis in range(3)]
high = [max(point[axis] for point in positions) for axis in range(3)]
height = high[1] - low[1]
if height <= 0:
    raise ValueError("The model needs some height. Check its orientation.")
scale = HEIGHT_CM / 100.0 / height
origin = ((low[0] + high[0]) / 2, (low[1] + high[1]) / 2, low[2])
result = {
    "positions": [round((point[axis] - origin[axis]) * scale, 7)
                  for point in positions for axis in range(3)],
    "uvs": [value for pair in uvs for value in pair],
    "triangles": triangles
}
if len(positions) > 65535 or len(triangles) > 393210:
    raise ValueError("Too much geometry for the loader. Simplify the model first.")
content = json.dumps(result, separators=(',', ':'), allow_nan=False)
if len(content.encode('utf-8')) > 8 * 1024 * 1024:
    raise ValueError("Mesh JSON is over 8 MB. Simplify the model first.")
OUTPUT.write_text(content, encoding='utf-8')
print("Exported:", OUTPUT, "Vertices:", len(positions), "Triangles:", len(triangles) // 3)

Run the script once for the detailed single accessory (about 6 cm high), then again for a simplified trim mesh (about 3 cm high). The Oiia Cat example ZIP has 15,774 triangles in the single cat and 3,494 in each trim cat. The single one is closer to a detailed prop than an ordinary small bow or bead. The trim cat is also much more complex than the simple shapes usually repeated along a trim, and its cost multiplies along the path.

Finish the pack

Put mod.json, the mesh JSON, texture, and optional icon in the pack folder. Point each accessory to its model, texture, and icon. Use a single-item base for the placed item and a trim base for the repeating one. For a trim, set beadSpacing to match the model size.

Change the pack ID, item IDs, names, author, and version for your own work. Keep IDs stable when updating an item so saved dresses can still find it. Remove an entry from the accessories array if you do not want to include that item. ZIP the pack folder with its contents inside and install it from Mod Manager, or place the folder in Dressmaker/Mods. Restart the game to load it.

Back to the Mod Loader guide