- Add optional "creator" JSON field to display author name in tooltip - Show body regions, movement style, lock status, and escape difficulty - Show pose priority and item ID in advanced mode (F3+H) - Update ARTIST_GUIDE.md field reference and test JSON
63 KiB
TiedUp! — 3D Item Creation Guide
Everything you need to create custom bondage items for TiedUp! using Blender. No Java required — just a GLB file and a JSON definition.
Table of Contents
- How It Works
- The Skeleton
- Body Regions
- Modeling Your Item
- Weight Painting
- Tint Channels — dynamic per-zone coloring
- Animations — item poses, fallback chain, variants, context animations
- Animation Templates
- Exporting from Blender
- The JSON Definition
- Packaging as a Resource Pack
- Common Mistakes
- Examples
- Furniture Creation — interactive furniture (cross, pillory, cage, etc.)
- Quick Reference Cards — body items + furniture cheat sheets
How It Works
TiedUp! uses a 2-layer animation system to render 3D items on players:
Layer 1 — CONTEXT (provided by the mod, or by your GLB)
Controls the player's overall posture: standing, sitting, kneeling, sneaking, walking.
Affects body + legs. Never touches head (vanilla head tracking is preserved).
Can be replaced by artist-made GLB files (see Context Animations).
Layer 2 — ITEM (provided by YOUR GLB file)
Controls the bones your item owns (based on its body regions).
Can ALSO animate free bones (not owned by any other equipped item).
Overrides Layer 1 for those bones. Everything else stays vanilla.
What this means for you:
- You animate the bones your item affects (e.g., arms for handcuffs).
- You can also animate free bones — bones no other item claims (e.g., legs if no ankle cuffs are worn). See Free Bones.
- The mod handles the rest: legs walk, body leans when sneaking, head follows the mouse.
- Your 3D mesh is skinned to the player skeleton and follows it in real-time.
- You can create context animation GLBs — standalone animation packs that replace or extend the mod's default postures (walk cycles, sit poses, etc.). See Context Animations.
The Skeleton
Your GLB must use this exact skeleton. Names are case-sensitive.
PlayerArmature ← armature root object (never keyframe this)
└─ body ← context layer by default; animate if you own TORSO/WAIST or if free
├─ torso ← maps to same body part as 'body'; prefer using 'body' instead
│ ├─ head
│ ├─ leftUpperArm
│ │ └─ leftLowerArm
│ └─ rightUpperArm
│ └─ rightLowerArm
├─ leftUpperLeg
│ └─ leftLowerLeg
└─ rightUpperLeg
└─ rightLowerLeg
11 skinned joints (body through rightLowerLeg). PlayerArmature is the armature root object, not a skinned joint — it must never be keyframed.
| Bone | What it controls | Type |
|---|---|---|
PlayerArmature |
Root transform | Never animate |
body |
Torso position/rotation | Full rotation — context layer by default, animate if owned or free |
torso |
Same as body (alias) |
Full rotation — prefer body instead |
head |
Head | Full rotation (pitch/yaw/roll) |
leftUpperArm |
Left shoulder to elbow | Full rotation |
leftLowerArm |
Left elbow to wrist | Bend (angle + direction) |
rightUpperArm |
Right shoulder to elbow | Full rotation |
rightLowerArm |
Right elbow to wrist | Bend (angle + direction) |
leftUpperLeg |
Left hip to knee | Full rotation |
leftLowerLeg |
Left knee to ankle | Bend (angle + direction) |
rightUpperLeg |
Right hip to knee | Full rotation |
rightLowerLeg |
Right knee to ankle | Bend (angle + direction) |
Upper vs Lower Bones
- Upper bones (head, body, upperArm, upperLeg) support full 3-axis rotation (pitch, yaw, roll).
- Lower bones (lowerArm, lowerLeg) support bend angle + bend direction — the mod extracts both values from your Blender pose. You can pose them freely in any direction; the mod faithfully reproduces both the magnitude and direction of the bend.
What You Can and Cannot Animate
Never animate: PlayerArmature — it's the armature root object, not a bone.
Everything else follows this rule:
- Your item always controls bones in its declared regions.
- Your item can also animate free bones (not owned by any other equipped item).
- Your item cannot override bones owned by another equipped item.
| Bone | Who Controls It |
|---|---|
body / torso |
Context layer by default. Your item if it owns TORSO or WAIST, or if body is free. |
head |
Vanilla head tracking by default. Your item if it owns HEAD, EYES, EARS, or MOUTH. |
Arms (*UpperArm, *LowerArm) |
Vanilla by default. Your item if it owns ARMS or HANDS. |
Legs (*UpperLeg, *LowerLeg) |
Context layer by default. Your item if it owns LEGS or FEET. |
Note: torso and body both map to the same internal part. Prefer animating body — using torso produces the same result but is less intuitive.
Body Regions
Every item declares which body regions it occupies. This determines two things:
- Gameplay — which equipment slots are taken (conflict resolution)
- Animation — which bones your item layer controls
Region Table
| Region | GLB Bones Controlled | Typical Items |
|---|---|---|
HEAD |
head |
Hood, helmet, head harness |
EYES |
head |
Blindfold |
EARS |
head |
Earplugs |
MOUTH |
head |
Gag, muzzle |
NECK |
(none) | Collar, choker |
TORSO |
body (+ torso) |
Straitjacket, harness |
ARMS |
rightUpperArm, rightLowerArm, leftUpperArm, leftLowerArm |
Handcuffs, armbinder |
HANDS |
rightUpperArm, rightLowerArm, leftUpperArm, leftLowerArm |
Mittens, fist mitts |
FINGERS |
(none) | Finger cuffs |
WAIST |
body (+ torso) |
Belt, chastity belt |
LEGS |
rightUpperLeg, rightLowerLeg, leftUpperLeg, leftLowerLeg |
Ankle cuffs, leg binder |
FEET |
rightUpperLeg, rightLowerLeg, leftUpperLeg, leftLowerLeg |
Forced shoes, foot cuffs |
TAIL |
(none) | Tail plug (pet play) |
WINGS |
(none) | Decorative wings |
Global vs Sub Regions
Three regions are global — they encompass sub-regions:
| Global | Sub-regions |
|---|---|
HEAD |
EYES, EARS, MOUTH |
ARMS |
HANDS, FINGERS |
LEGS |
FEET |
Important: Blocking is not automatic. A hood that occupies HEAD does not automatically block EYES. You must explicitly declare blocked regions in your JSON definition. This gives you full control — a tiara occupies HEAD but blocks nothing.
Regions Without Bones
NECK, FINGERS, TAIL, and WINGS don't control any animation bones. Items in these regions are purely cosmetic from an animation standpoint — they render as a mesh on the skeleton but don't change the player's pose. They still participate in gameplay (slot blocking, escape difficulty, etc.).
Modeling Your Item
Starting Point
Use the TiedUp! Blender Template (provided with the mod). It contains:
- The correct
PlayerArmatureskeleton with all 11 bones - A reference player mesh (Steve + Alex) for scale — toggle visibility as needed
- Pre-named Action slots
Guidelines
-
Model in rest pose. The template skeleton is in the Minecraft rest pose (arms down, legs straight). Model your item to fit the player in this pose.
-
Keep it tight. Your mesh will deform with the skeleton. Loose geometry that isn't weight-painted to any bone will stay frozen in space while the player moves.
-
Mind the polygon count. Minecraft renders every player in range every frame. A 500-triangle mesh is ideal. 2000 is acceptable. 10,000 is going to cause lag in multiplayer.
-
Textures are baked into the GLB. Apply your materials and textures in Blender. The mod reads them directly from the GLB file. No separate texture file needed (unless you want server-side color variants).
-
Slim model support. Minecraft has two player models: Steve (4px arms) and Alex (3px arms). If your item wraps tightly around the arms, consider providing a slim variant. Otherwise, the Steve-width mesh works for both (minor clipping on Alex is usually acceptable).
Weight Painting
Weight paint your mesh to the skeleton bones it should follow.
Rules
- Only paint to the 11 standard bones. Any other bones in your Blender file will be ignored by the mod.
- Paint to the bones of your regions. Handcuffs (ARMS region) should be weighted to
rightUpperArm,rightLowerArm,leftUpperArm,leftLowerArm. - You can paint to bones outside your regions for smooth deformation. For example, handcuffs might have small weights on
bodynear the shoulder area for smoother bending. This is fine — the weight painting is about mesh deformation, not animation control. - Normalize your weights. Each vertex's total weights across all bones must sum to 1.0. Blender does this by default.
Tips
- For rigid items (metal cuffs), use hard weights — each vertex fully assigned to one bone.
- For flexible items (rope, leather), blend weights between adjacent bones for smooth bending.
- The chain between handcuffs? Weight it 50/50 to both arms, or use a separate mesh element weighted to
body.
Tint Channels (Dynamic Colors)
Tint channels let players change the color of specific parts of your item without replacing the texture. A ball gag with a red ball can become blue, green, purple — while the black strap stays black.
How It Works
Your item's texture detail (highlights, shadows, stitching, scratches) is fully preserved. The mod multiplies the texture color by a tint color per-zone. Grayscale textures + color tint = any hue with full detail.
Blender Workflow
Use separate materials for fixed and colorable zones:
- Fixed zones — Name the material anything (e.g.,
strap,buckle,metal). These render as-is. - Colorable zones — Name the material
tintable_1,tintable_2, etc. These get tinted by the mod.
Example: Ball Gag
- Select the strap faces → assign material
strap→ texture with leather detail (full color) - Select the ball faces → assign material
tintable_1→ texture with grayscale surface detail (highlights, reflections, micro-scratches) - Export GLB
The grayscale texture on tintable_1 preserves all the surface detail. The mod multiplies it by the tint color: gray detail × red = detailed red ball.
Multiple Tint Channels
You can have as many independently-colorable zones as you want:
Material "strap" → fixed (leather, never changes)
Material "tintable_1" → ball (player picks color)
Material "tintable_2" → ring (player picks a different color)
Material "buckle" → fixed (chrome metal)
Each tintable_N channel gets its own color. Players can customize each zone independently.
Texturing Tips for Tintable Zones
- Use grayscale textures for tintable zones. The tint color replaces the hue entirely.
- Keep all the detail (bumps, scratches, reflections) in the luminance channel.
- A mid-gray base (not pure white) produces more natural-looking results.
- Dark areas in your grayscale texture stay dark regardless of tint — good for crevices and shadows.
What happens: finalPixelColor = textureGray × tintColor
- Gray (128,128,128) × Red (255,0,0) = Dark Red (128,0,0)
- White (255,255,255) × Red (255,0,0) = Bright Red (255,0,0)
- Black (0,0,0) × Red (255,0,0) = Black (0,0,0) — shadows stay dark
JSON Definition
Declare default colors per channel in your item JSON:
{
"type": "tiedup:bondage_item",
"display_name": "Ball Gag",
"model": "mycreator:models/gltf/ball_gag.glb",
"regions": ["MOUTH"],
"supports_color": true,
"tint_channels": {
"tintable_1": "#FF0000",
"tintable_2": "#C0C0C0"
},
"animation_bones": {
"idle": []
},
"pose_priority": 10,
"escape_difficulty": 3
}
Colors are hex RGB (#RRGGBB). These are the default colors — players can override them in-game via dyeing.
Material Naming Rules
| Name Pattern | Behavior | Example |
|---|---|---|
tintable_1, tintable_2, ... |
Colorable zone, numbered | Ball, ring, strap |
| Anything else | Fixed, rendered as-is | metal, buckle, leather |
- Names are case-sensitive and must start with
tintable_(lowercase). - Numbers don't need to be sequential —
tintable_1andtintable_5is fine. - The number is part of the channel name:
tintable_1in JSON matchestintable_1in Blender.
Items Without Tint Channels
If your item has no tintable_ materials, everything renders as-is. No tint_channels field needed in the JSON. Existing items are completely unaffected.
Animations
Animations define how the player's body is posed when wearing your item.
The Only Required Animation: Idle
Every item needs at least one animation named Idle. This is the default pose — what the player looks like while standing still with your item equipped.
In Blender: Create an Action named PlayerArmature|Idle.
The PlayerArmature| prefix is Blender's convention for armature-scoped actions. The mod strips it automatically — the resolved name is just Idle.
What to Animate in Idle
Only keyframe the bones your item affects. Leave everything else untouched.
| Item Type | Bones to Keyframe | Example Pose |
|---|---|---|
| Handcuffs (ARMS) | Upper + lower arms | Arms behind back, wrists together |
| Ankle cuffs (LEGS) | Upper + lower legs | Legs closer together, slightly bent |
| Blindfold (EYES) | Head | Slight head tilt down |
| Gag (MOUTH) | (none) | No pose change — mesh only |
| Straitjacket (ARMS+TORSO) | Arms + body (+ legs if free) | Arms crossed, slight forward lean, optional waddle |
Why only your bones? The mod's 2-layer system activates your keyframes for bones in your declared regions. But there's a nuance: free bones (bones not owned by any equipped item) can also be animated by your item.
For example: if a player wears only a straitjacket (ARMS+TORSO), the legs are "free" — no item claims them. If your straitjacket's GLB has leg keyframes (e.g., a waddle walk), the mod will use them. But if the player also wears ankle cuffs (LEGS), those leg keyframes are ignored — the ankle cuffs take over.
The rule: Your item always controls its own bones. It can also animate free bones if your GLB has keyframes for them. It can never override another item's bones.
Idle is a Single-Frame Pose
Idle should be a static pose — one keyframe at frame 0. The mod loops it as a held position.
Frame 0: Pose all owned bones → done.
Optional Animations
Beyond Idle, you can provide animations for specific contexts. All are optional — if missing, the mod falls back through a chain (see below).
| Animation Name | Context | Notes |
|---|---|---|
Idle |
Standing still | Required. Single-frame pose. |
Struggle |
Player is struggling | Multi-frame loop. 20-40 frames recommended. |
Walk |
Player is walking | Multi-frame loop synced to walk speed. |
Sneak |
Player is sneaking | Single-frame or short loop. |
SitIdle |
Sitting (chair, minecart) | Single-frame pose. |
SitStruggle |
Sitting + struggling | Multi-frame loop. |
KneelIdle |
Kneeling | Single-frame pose. |
KneelStruggle |
Kneeling + struggling | Multi-frame loop. |
Crawl |
Crawling (dog pose) | Multi-frame loop. |
Naming in Blender: Always prefix with PlayerArmature|. Examples:
PlayerArmature|IdlePlayerArmature|StrugglePlayerArmature|SitIdle
Fallback Chain
If an animation doesn't exist in your GLB, the mod looks for alternatives. At each step, Full variants are tried first:
SIT + STRUGGLE:
FullSitStruggle → SitStruggle → FullStruggle → Struggle
→ FullSit → Sit → FullStruggle → Struggle → FullIdle → Idle
KNEEL + STRUGGLE:
FullKneelStruggle → KneelStruggle → FullStruggle → Struggle
→ FullKneel → Kneel → FullStruggle → Struggle → FullIdle → Idle
SIT + IDLE: FullSitIdle → SitIdle → FullSit → Sit → FullIdle → Idle
KNEEL + IDLE: FullKneelIdle → KneelIdle → FullKneel → Kneel → FullIdle → Idle
SNEAK: FullSneak → Sneak → FullIdle → Idle
WALK: FullWalk → Walk → FullIdle → Idle
STAND STRUGGLE: FullStruggle → Struggle → FullIdle → Idle
STAND IDLE: FullIdle → Idle
In practice, most items only need Idle. Add FullWalk or FullStruggle when your item changes how the whole body moves.
Practical impact: If you only provide Idle, your item works in every context. The player will hold the Idle pose while sitting, kneeling, sneaking, etc. It won't look perfect, but it will work. Add more animations over time to polish the experience.
Animation Variants (Random Selection)
For variety, you can provide multiple versions of the same animation. The mod picks one at random each time the animation triggers.
Convention: Append .1, .2, .3, etc.
PlayerArmature|Struggle.1 ← variant 1
PlayerArmature|Struggle.2 ← variant 2
PlayerArmature|Struggle.3 ← variant 3
Works for any animation name: Idle.1/Idle.2, SitIdle.1/SitIdle.2, etc.
Rules:
- Number sequentially starting from
.1. The mod stops scanning at the first gap after.1(missing.1does not stop the scan — it continues checking.2,.3, etc.). - If only one version exists, don't number it — just use
Struggle, notStruggle.1. - The base name (e.g.,
Struggle) is included in the random pool alongside numbered variants if it exists. SoStruggle+Struggle.1+Struggle.2= three candidates in the pool.
Full-Body Animations (Naming Convention)
Some items affect the entire body — not just their declared regions. A straitjacket makes the player waddle, a full-body bind forces hopping. For these, you want to animate all bones, including free ones like legs and body.
Convention: Prefix the animation name with Full.
| Standard Name | Full-Body Name | What Changes |
|---|---|---|
Idle |
FullIdle |
Owned bones + body lean, leg stance |
Walk |
FullWalk |
Owned bones + leg waddle, body sway |
Struggle |
FullStruggle |
Owned bones + full-body thrashing |
Sneak |
FullSneak |
Owned bones + custom sneak posture |
In Blender:
PlayerArmature|Idle ← region-only: just arms for handcuffs
PlayerArmature|FullWalk ← full-body: arms + legs waddle + body bob
PlayerArmature|FullStruggle ← full-body: everything moves
How the mod resolves this:
- Checks for
Fullvariant first (e.g.,FullWalk) - Falls back to standard name (e.g.,
Walk) - Follows the normal fallback chain (
Walk→Idle)
When to use Full vs standard:
| Animation | Use Standard | Use Full |
|---|---|---|
Idle |
Your item only poses its own bones | Your item changes the whole resting posture |
Walk |
Legs should use vanilla/context walk | Your item needs a custom walk cycle (waddle, hop, shuffle) |
Struggle |
Only owned bones move | Whole body thrashes and writhes |
Sneak |
Default sneak lean is fine | Your item changes how sneaking looks |
Key points:
Fullanimations include keyframes for ALL bones you want to control (owned + free).- Free bones in
Fullanimations are only used when no other item owns them. - You can provide BOTH:
Idle(region-only) andFullWalk(full-body). The mod picks the right one per context. FullIdleis rarely needed — most items only need a full-body version for movement animations.
Context Animations
Context animations are separate from item animations. They control the player's overall body posture — how they walk, sit, sneak, kneel, etc. They are the foundation that item animations build on top of.
The Two-Layer System
Layer 1 — CONTEXT (body posture) ← walk cycle, sit pose, sneak lean...
Controls: body, legs (and any unowned bones)
Bones owned by items are disabled on this layer.
Layer 2 — ITEM (your item's pose) ← arms behind back, legs bound...
Controls: only the bones in the item's declared regions.
Overrides Layer 1 for those bones.
The mod ships with default context animations (currently as internal JSON files). These provide basic postures: a static standing pose, a simple walk, a sneak lean, sitting/kneeling positions.
Artists Can Replace or Extend Context Animations
This is where it gets interesting. You can create GLB files that replace or add to the mod's context animations. These are standalone GLBs — they are NOT tied to any specific item.
Use cases:
- Replace the default walk cycle with a smoother, more natural one
- Add a new "hogtied" context the mod doesn't ship with
- Create an animation overhaul pack that improves every default posture
- Provide themed context packs (e.g., "petplay contexts" with crawl animations)
Context GLB Format
A context GLB uses the same PlayerArmature skeleton as item GLBs. The difference is what it contains:
- Item GLB: mesh + animations for a specific item's bones
- Context GLB: no mesh — animations only, for all bones (body, legs, etc.)
The filename determines which context the GLB replaces. It must match one of the context suffixes:
| Filename | Context It Replaces |
|---|---|
stand_idle.glb |
Standing still |
stand_walk.glb |
Walking |
stand_sneak.glb |
Sneaking |
stand_struggle.glb |
Standing + struggling |
sit_idle.glb |
Sitting |
sit_struggle.glb |
Sitting + struggling |
kneel_idle.glb |
Kneeling |
kneel_struggle.glb |
Kneeling + struggling |
shuffle_idle.glb |
Shuffle style — standing still (legs close together) |
shuffle_walk.glb |
Shuffle style — tiny dragging steps |
hop_idle.glb |
Hop style — standing with feet bound together |
hop_walk.glb |
Hop style — small bunny hops |
waddle_idle.glb |
Waddle style — standing with slight sway |
waddle_walk.glb |
Waddle style — side-to-side waddling gait |
crawl_idle.glb |
Crawl style — on all fours, resting (petplay/dogwalk pose) |
crawl_move.glb |
Crawl style — on all fours, crawling forward |
Names are exact and case-sensitive. The mod strips the .glb extension and looks up the suffix.
You don't need to provide all of them. Missing contexts fall back to the mod's builtin defaults. The GLB itself should contain at least one animation clip — the mod uses the first clip found.
What to Animate in Context GLBs
Context animations should animate body and legs — the "posture" bones. Do NOT animate bones that items will control (arms, head), as your context keyframes would be overridden by any equipped item anyway.
| Bone | Recommended? | Why |
|---|---|---|
body |
Yes | Lean, sway, bob |
leftUpperLeg / rightUpperLeg |
Yes | Walk stride, sit angle |
leftLowerLeg / rightLowerLeg |
Yes | Knee bend |
head |
Usually no | Overridden by any item owning HEAD/EYES/EARS/MOUTH, and by vanilla head tracking |
leftUpperArm / rightUpperArm |
Usually no | Overridden by any item owning ARMS/HANDS. Only useful as a fallback when arms are free |
leftLowerArm / rightLowerArm |
Usually no | Same as upper arms |
torso |
Rarely | Same effect as body; prefer body |
Example: Smooth Walk Cycle
The mod's default walk is basic. You want a nicer one:
In Blender:
- Use the TiedUp! template (same skeleton)
- Delete the reference player mesh — context GLBs have no mesh
- Create
PlayerArmature|Walk: a 20-frame walk cycle animatingbody(bob),leftUpperLeg/rightUpperLeg(stride),leftLowerLeg/rightLowerLeg(knee bend) - Export as GLB (armature only, no mesh)
Place it in a resource pack:
assets/mycreator/tiedup_contexts/stand_walk.glb
The filename stand_walk.glb matches the stand_walk context suffix. The mod discovers it at startup (or on F3+T reload) and uses your walk cycle instead of the builtin one. Players wearing handcuffs will have your smooth walk cycle on their legs/body, with arms still locked behind their back by the item.
Example: Custom Kneeling Context
The mod ships a basic kneel. You want a more expressive one with the body leaned slightly forward and legs tucked tighter:
In Blender:
- Create
PlayerArmature|KneelIdle: single-frame pose with body pitched forward 15°, legs folded tight - Create
PlayerArmature|KneelStruggle: multi-frame animation with body rocking side to side while kneeling - Export as GLB
Replacing vs Extending
- Replace: Provide a context animation with the same name as a default. Your animation is used instead.
- Extend: Provide a context animation for a situation the mod doesn't cover yet (e.g., a "Hogtied" context). Items can then reference this new context.
Context Packs
Since context GLBs are loaded from resource packs, the community can create context animation packs — collections of improved or themed postures. Players install them like any resource pack. No code changes, no item modifications.
BetterAnimations Pack/
assets/betteranims/tiedup_contexts/
stand_walk.glb ← replaces default walk
stand_sneak.glb ← replaces default sneak
sit_idle.glb ← replaces default sit
kneel_idle.glb ← replaces default kneel
kneel_struggle.glb ← replaces default kneel struggle
crawl_idle.glb ← replaces default crawl idle
crawl_move.glb ← replaces default crawl movement
Movement Style Contexts
The mod includes 8 movement style contexts used when a player wears leg-restraining items with a movement_style field. These control how the player animates while moving in a restricted way.
The crawl style should animate the player on all fours in a petplay/dogwalk pose — NOT a belly-down swimming pose. Think of a pet being walked on a leash, with knees and hands on the ground, body tilted forward. The vanilla SWIMMING hitbox is used for the 0.6-block height, but the animation completely overrides the vanilla swimming visuals.
| Style | Idle | Walk/Move | Visual |
|---|---|---|---|
| Shuffle | Legs close together, slight sway | Tiny dragging steps, short stride | Feet hobbled with a short chain |
| Hop | Feet together, standing | Small bunny hops, feet never separate | Feet bound together, forced to hop |
| Waddle | Slight lateral sway | Body rocks left-right, wide stance | Legs hobbled mid-thigh |
| Crawl | On all fours, resting | On all fours, crawling forward | Petplay/dogwalk, hands and knees |
These are excellent candidates for artist GLB replacements — the default animations are basic placeholders. A well-made walk cycle for shuffle_walk.glb or crawl_move.glb will dramatically improve the feel of the restraint system.
Animation Templates
Not every item needs custom animations. Many items in the same category share identical poses — all handcuffs put arms behind the back, all ankle cuffs restrict leg movement.
TiedUp! provides animation template GLBs with pre-made animations:
assets/tiedup/models/gltf/templates/
handcuff_anims.glb ← Idle, Struggle, SitIdle for ARMS items
leg_restraint_anims.glb ← Idle, Walk, SitIdle for LEGS items
full_body_anims.glb ← Idle, Walk, Struggle, SitIdle for full-body items
How to Use a Template
In your JSON definition, separate the mesh from the animations:
{
"model": "mycreator:models/gltf/my_fancy_cuffs.glb",
"animation_source": "tiedup:models/gltf/templates/handcuff_anims.glb"
}
model— your GLB with the 3D mesh (no animations needed)animation_source— the template GLB with animations- If
animation_sourceis omitted, animations come frommodel
Artist workflow: Model your item, weight-paint it, skip all animation work, reference a template. Done.
When NOT to Use a Template
- Your item has a unique silhouette that would clip with template poses
- You want a distinctive struggle animation
- Your item affects bones differently (e.g., arms in front vs behind back)
- You want to ship a premium, polished item
Exporting from Blender
Export Settings
File > Export > glTF 2.0 (.glb)
| Setting | Value | Why |
|---|---|---|
| Format | glb (binary) |
Single file, faster loading |
| Include > Limit to | Selected Objects | Export only armature + item mesh |
| Transform > +Y Up | Checked | glTF standard |
| Mesh > Apply Modifiers | Checked | Bake subdivision, mirror, etc. |
| Mesh > Normals | Checked | Needed for lighting |
| Mesh > Vertex Colors | Checked (if used) | For tintable items |
| Animation > Export Actions | Checked | Include all named actions |
| Animation > Group by NLA Track | Unchecked | Avoids duplicate animations |
| Animation > Sampling Rate | 1 | One sample per frame |
Pre-Export Checklist
- Armature is named
PlayerArmature - All 11 bones have correct names (case-sensitive)
- Actions are named
PlayerArmature|Idle,PlayerArmature|Struggle, etc. - Mesh is weight-painted to skeleton bones only
- Weights are normalized
- No orphan bones (extra bones not in the standard 11 are ignored but add file size)
- Materials/textures are applied (the GLB bakes them in)
- Scale is correct (1 Blender unit = 1 Minecraft block = 16 pixels)
The JSON Definition
Every item needs a JSON file that declares its gameplay properties. The mod scans assets/<namespace>/tiedup_items/*.json at startup and on resource reload (F3+T).
Minimal Example — Rope Gag
{
"type": "tiedup:bondage_item",
"display_name": "Rope Gag",
"model": "mycreator:models/gltf/rope_gag.glb",
"regions": ["MOUTH"],
"animation_bones": {
"idle": []
},
"pose_priority": 10,
"escape_difficulty": 2,
"lockable": false
}
Standard Example — Iron Handcuffs
{
"type": "tiedup:bondage_item",
"display_name": "Iron Handcuffs",
"model": "mycreator:models/gltf/iron_cuffs.glb",
"slim_model": "mycreator:models/gltf/iron_cuffs_slim.glb",
"animation_source": "tiedup:models/gltf/templates/handcuff_anims.glb",
"regions": ["ARMS"],
"animation_bones": {
"idle": ["rightArm", "leftArm"],
"struggle": ["rightArm", "leftArm"]
},
"pose_priority": 30,
"escape_difficulty": 5,
"lockable": true
}
Complex Example — Straitjacket
{
"type": "tiedup:bondage_item",
"display_name": "Leather Straitjacket",
"model": "mycreator:models/gltf/straitjacket.glb",
"slim_model": "mycreator:models/gltf/straitjacket_slim.glb",
"regions": ["ARMS", "HANDS", "TORSO"],
"blocked_regions": ["ARMS", "HANDS", "TORSO", "FINGERS"],
"animation_bones": {
"idle": ["rightArm", "leftArm", "body"],
"struggle": ["rightArm", "leftArm", "body"]
},
"pose_priority": 50,
"escape_difficulty": 7,
"lockable": true
}
Complex Example — Ankle Chains (with movement style)
{
"type": "tiedup:bondage_item",
"display_name": "Ankle Chains",
"model": "mycreator:models/gltf/ankle_chains.glb",
"regions": ["FEET"],
"animation_bones": {
"idle": ["rightLeg", "leftLeg"],
"walk": ["rightLeg", "leftLeg"]
},
"pose_priority": 30,
"escape_difficulty": 5,
"lockable": true,
"movement_style": "shuffle"
}
The movement_style changes how the player physically moves — slower speed, different walking animation, and potentially disabled jumping. See Movement Styles below.
Complex Example — Hogtie (crawl style)
{
"type": "tiedup:bondage_item",
"display_name": "Hogtie Harness",
"model": "mycreator:models/gltf/hogtie.glb",
"regions": ["ARMS", "HANDS", "LEGS", "FEET"],
"blocked_regions": ["ARMS", "HANDS", "LEGS", "FEET", "FINGERS", "WAIST"],
"animation_bones": {
"idle": ["rightArm", "leftArm", "rightLeg", "leftLeg", "body"],
"struggle": ["rightArm", "leftArm", "rightLeg", "leftLeg", "body"]
},
"pose_priority": 90,
"escape_difficulty": 9,
"lockable": true,
"movement_style": "crawl",
"movement_modifier": {
"speed_multiplier": 0.15
}
}
Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | Always "tiedup:bondage_item" |
display_name |
string | Yes | Name shown in-game |
model |
string | Yes | ResourceLocation of the GLB mesh |
slim_model |
string | No | GLB for Alex-model players (3px arms) |
texture |
string | No | Override texture (if not baked in GLB) |
animation_source |
string | No | GLB to read animations from (defaults to model) |
regions |
string[] | Yes | Body regions this item occupies |
blocked_regions |
string[] | No | Regions blocked for other items (defaults to regions) |
pose_priority |
int | Yes | Higher = overrides lower-priority items (see below) |
escape_difficulty |
int | Yes | 1-10 scale. Higher = harder to struggle free |
lockable |
bool | No | Can a padlock be applied? Default: true |
supports_color |
bool | No | Whether this item has tintable zones. Default: false |
tint_channels |
object | No | Default colors per tintable zone: {"tintable_1": "#FF0000"} |
icon |
string | No | Inventory sprite model (see Inventory Icons below) |
animations |
string/object | No | "auto" (default) or explicit name mapping |
movement_style |
string | No | Movement restriction: "waddle", "shuffle", "hop", or "crawl" |
movement_modifier |
object | No | Override speed/jump for the movement style (requires movement_style) |
creator |
string | No | Author/creator name, shown in the item tooltip |
animation_bones |
object | Yes | Per-animation bone whitelist (see below) |
animation_bones (required)
Declares which bones each named animation is allowed to control for this item. This enables fine-grained per-animation bone filtering: an item might own body via its regions but only want the "idle" animation to affect the arms.
Format: A JSON object where each key is an animation name (matching the GLB animation names) and each value is an array of bone names.
Valid bone names: head, body, rightArm, leftArm, rightLeg, leftLeg
Example:
"animation_bones": {
"idle": ["rightArm", "leftArm"],
"struggle": ["rightArm", "leftArm", "body"]
}
At runtime, the effective bones for a given animation clip are computed as the intersection of animation_bones[clipName] and the item's owned parts (from region conflict resolution). If the clip name is not listed in animation_bones, the item falls back to using all its owned parts.
This field is required. Items without animation_bones will be rejected by the parser.
Pose Priority
When multiple items affect the same bones, the highest pose_priority wins.
| Priority Range | Item Type | Example |
|---|---|---|
| 1–10 | Light cosmetic | Collar, blindfold, gag |
| 20–40 | Standard restraint | Handcuffs, ankle cuffs |
| 40–60 | Heavy restraint | Armbinder, straitjacket |
| 60–80 | Full-body restraint | Leg binder + armbinder combo |
| 80–100 | Total restraint | Sleep sack, full bind |
Blocked Regions
blocked_regions prevents other items from being equipped on those regions while your item is worn.
Examples:
- Hood →
regions: ["HEAD"],blocked_regions: ["HEAD", "EYES", "EARS"]— covers head, eyes, ears. Mouth is still accessible (for a gag underneath). - Handcuffs →
regions: ["ARMS"], noblocked_regionsneeded — only blocks ARMS by default. - Straitjacket →
regions: ["ARMS", "HANDS", "TORSO"],blocked_regions: ["ARMS", "HANDS", "TORSO", "FINGERS"]— also blocks finger accessories.
If blocked_regions is omitted, it defaults to the same as regions.
Movement Styles
Items that restrict the player's legs can declare a movement_style to change how the player physically moves. This affects both server-side movement (speed, jumping) and client-side animation.
Available styles:
| Style | Speed | Jump | Animation | Typical Use |
|---|---|---|---|---|
waddle |
0.6× | allowed | Side-to-side sway | Hobble skirt, thigh cuffs |
shuffle |
0.4× | disabled | Tiny dragging steps | Short ankle chain, leg binder |
hop |
0.35× (between hops) | auto-hop | Small bunny hops | Bound feet (rope, tape) |
crawl |
0.2× | disabled | On all fours (petplay) | Hogtie, pet harness |
Resolution: If multiple equipped items have different styles, the most constraining one wins (crawl > hop > shuffle > waddle).
Override defaults: Use movement_modifier to fine-tune speed for a specific item:
{
"movement_style": "shuffle",
"movement_modifier": {
"speed_multiplier": 0.3,
"jump_disabled": true
}
}
movement_modifier only works when movement_style is set. Without a style, the modifier is ignored.
For artists: Each style has idle and walk/move context animations that can be replaced with GLB files (see Movement Style Contexts above). The default animations are basic placeholders — custom GLBs will dramatically improve the feel.
Inventory Icons
By default, data-driven items show a generic placeholder icon in the inventory. To give your item a custom inventory sprite, use the icon field.
How it works: The icon field points to a Minecraft item model (not a raw texture). The mod resolves this model at render time and displays it as the inventory sprite.
Step 1: Create a 16x16 PNG texture
Place it in your resource pack:
assets/<namespace>/textures/item/my_armbinder_icon.png
Step 2: Create a model JSON
Create a simple item/generated model that references your texture:
assets/<namespace>/models/item/my_armbinder_icon.json
Contents:
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "<namespace>:item/my_armbinder_icon"
}
}
Step 3: Reference it in your item/furniture JSON
{
"display_name": "Leather Armbinder",
"icon": "<namespace>:item/my_armbinder_icon",
...
}
The icon value is the model's ResourceLocation: <namespace>:item/<filename> (without the .json extension, matching how Minecraft references models).
Shortcut: You can also reference any existing vanilla or mod item model. For example, "icon": "minecraft:item/chain" displays the vanilla chain icon. Useful for testing.
If icon is omitted: The item displays the default generic sprite. No crash, no error.
Packaging as a Resource Pack
File Structure
MyItemPack/
pack.mcmeta
assets/
mycreator/
tiedup_items/ ← JSON definitions (scanned at startup + F3+T)
rope_gag.json
iron_cuffs.json
straitjacket.json
models/gltf/ ← GLB mesh files
rope_gag.glb
iron_cuffs.glb
straitjacket.glb
models/item/ ← Icon model JSONs (for inventory sprites)
rope_gag_icon.json
iron_cuffs_icon.json
textures/item/ ← Icon textures (16x16 PNG)
rope_gag_icon.png
iron_cuffs_icon.png
tiedup_contexts/ ← Context GLBs (optional, replaces default postures)
stand_walk.glb
stand_sneak.glb
pack.mcmeta
{
"pack": {
"pack_format": 15,
"description": "My Custom TiedUp! Items"
}
}
Namespace
Use your own namespace (e.g., mycreator) to avoid conflicts with the base mod or other packs. The mod scans all namespaces for tiedup_items/ and tiedup_contexts/ directories.
Common Mistakes
Skeleton Issues
| Mistake | Symptom | Fix |
|---|---|---|
Bone name typo (RightUpperArm instead of rightUpperArm) |
Mesh doesn't follow that bone | Names are camelCase, not PascalCase. Check exact spelling. |
| Extra bones in the armature | No visible issue (ignored), larger file | Delete non-standard bones before export |
Missing PlayerArmature root |
Mesh renders at wrong position | Rename your armature root to PlayerArmature |
Animating body bone without TORSO region |
Body keyframes used only if body is free (no other item owns it) |
Declare TORSO/WAIST region if you always want to control body, or use Full animations for free-bone effects |
Animation Issues
| Mistake | Symptom | Fix |
|---|---|---|
Action not prefixed with PlayerArmature| |
Animation not found, falls back to first clip | Rename: Idle → PlayerArmature|Idle |
Wrong case (idle instead of Idle) |
Animation not found | Use exact PascalCase: Idle, SitIdle, KneelStruggle |
Variant gap (.1, .2, .4 — missing .3) |
Only .1 and .2 are used | Number sequentially with no gaps |
| Animating bones outside your regions | Keyframes silently ignored | Only animate bones in your declared regions |
| Multi-frame Idle | Works but wastes resources | Idle should be a single keyframe at frame 0 |
Weight Painting Issues
| Mistake | Symptom | Fix |
|---|---|---|
| Vertices not weighted to any bone | Part of mesh stays frozen in space | Weight paint everything to at least one bone |
| Weights not normalized | Mesh stretches or compresses oddly | Blender > Weights > Normalize All |
| Weighted to a non-standard bone | That part of mesh stays frozen | Only weight to the 11 standard bones |
JSON Issues
| Mistake | Symptom | Fix |
|---|---|---|
| Wrong model path | Item invisible | Check ResourceLocation format: namespace:path/to/file.glb |
Missing regions |
Item can't be equipped | Every item needs at least one region |
pose_priority: 0 |
Other items always override yours | Use at least 1. See priority guide above. |
blocked_regions too broad |
Players can't equip combinations you intended | Only block what your item physically covers |
Examples
Example 1: Simple Collar (No Animation)
A collar sits on the neck. It doesn't change the player's pose.
Blender:
- Model a ring mesh around the neck area
- Weight paint to
bodybone (so it follows torso movement) - Create
PlayerArmature|Idlewith a single keyframe — don't move any bones (identity pose) - Export GLB
JSON:
{
"type": "tiedup:bondage_item",
"display_name": "Leather Collar",
"model": "mycreator:models/gltf/leather_collar.glb",
"regions": ["NECK"],
"animation_bones": {
"idle": []
},
"pose_priority": 5,
"escape_difficulty": 3,
"lockable": true
}
The collar renders on the player's neck, follows body movement, and takes the NECK slot. No bones are animated — the player moves normally.
Example 2: Handcuffs (Arms Behind Back)
Blender:
- Model cuff meshes on both wrists + a chain between them
- Weight paint cuffs to
rightLowerArmandleftLowerArm, chain tobody - Create
PlayerArmature|Idle: pose both arms behind the back - Optionally create
PlayerArmature|Struggle: multi-frame animation of pulling against cuffs - Export GLB
JSON:
{
"type": "tiedup:bondage_item",
"display_name": "Iron Handcuffs",
"model": "mycreator:models/gltf/iron_cuffs.glb",
"animation_source": "tiedup:models/gltf/templates/handcuff_anims.glb",
"regions": ["ARMS"],
"animation_bones": {
"idle": ["rightArm", "leftArm"],
"struggle": ["rightArm", "leftArm"]
},
"pose_priority": 30,
"escape_difficulty": 5,
"lockable": true
}
Using animation_source means the cuffs mesh comes from your GLB but animations come from the official handcuff template. The arms go behind the back, the cuffs mesh follows. The player can still walk, look around, sit, and sneak — only the arms are locked.
Example 3: Blindfold (Head Region, Cosmetic Pose)
Blender:
- Model a strip of cloth across the eyes
- Weight paint to
headbone - Create
PlayerArmature|Idle: tilt head down ~10 degrees (subtle "I can't see" pose) - Export GLB
JSON:
{
"type": "tiedup:bondage_item",
"display_name": "Silk Blindfold",
"model": "mycreator:models/gltf/silk_blindfold.glb",
"regions": ["EYES"],
"animation_bones": {
"idle": ["head"]
},
"pose_priority": 10,
"escape_difficulty": 1,
"lockable": false
}
The blindfold occupies EYES (a sub-region of HEAD). A hood (HEAD) would block it, but a gag (MOUTH) wouldn't. The head tilt is subtle and combines with vanilla head tracking.
Furniture Creation
Create interactive furniture (St. Andrew's Cross, pillory, cage, etc.) using a single GLB file containing both the furniture model and player seat skeletons.
How Furniture Works
Unlike body items (which are skinned onto the player's skeleton), furniture is a standalone entity in the world. It has its own mesh, its own skeleton, and its own animations. Players "sit" on it via the riding system, and the mod forces a pose on the player from the furniture's GLB.
One GLB file contains everything:
Furniture_Armature ← The furniture mesh (cross, pillory, cage...)
│ Mesh: wood_frame, chains, locks...
│ Animations: Idle, Occupied, LockClose, Shake...
│
Player_main ← Player skeleton #1 (positioned on the furniture)
│ Standard 11 bones (same as body items)
│ Animations: main:Idle, main:Struggle, main:Enter...
│
Player_left (optional) ← Player skeleton #2 (for multi-seat furniture)
│ Same 11 bones
│ Animations: left:Idle, left:Struggle...
│
Player_right (optional) ← Player skeleton #3
...
The artist sees the exact result in Blender — the player posed on the furniture. No guessing offsets in JSON.
Note: There is currently no ghost preview when placing furniture in-game. The entity spawns immediately on right-click. Use Blender to check positioning.
Furniture Skeleton
The furniture armature can have any bones you want. Unlike body items (which must use the standard 11-bone player skeleton), furniture bones are custom. Name them whatever makes sense: door_hinge, chain_left, lock_bolt, etc. The name Furniture_Armature used in this guide is a convention — any name works as long as it does NOT start with Player_.
Furniture_Armature ← armature root (required, any name NOT starting with "Player_")
└─ frame ← main body of the furniture
├─ door_hinge ← animated: opens/closes
├─ chain_left ← animated: tightens when occupied
├─ chain_right
└─ lock_bolt ← animated: rotates when locked
Rules:
- The furniture armature name must NOT start with
Player_(that prefix is reserved for seat skeletons) - All bones are kept — no filtering (unlike body items which filter through
isKnownBone) - Weight paint your furniture mesh to these bones normally
Seat Skeletons (Player_*)
Each seat is a standard player skeleton (the same 11 bones from body items) positioned on the furniture. The armature name determines the seat ID.
Player_main → seat ID: "main"
Player_left → seat ID: "left"
Player_right → seat ID: "right"
Player_grab → seat ID: "grab" (for monsters)
How to set up a seat in Blender:
- Duplicate the standard player armature (from a body item template or the skeleton reference above)
- Rename it to
Player_{seatId}(e.g.,Player_main) - Position it on the furniture exactly where you want the player to appear
- Pose it in the
Idleposition (arms spread on a cross, bent over a pillory, etc.) - The root position + rotation of this armature = where the player sits in-game
Important:
- The seat skeleton uses the EXACT same 11 bone names as body items (
body,head,leftUpperArm, etc.) - You do NOT need a mesh for the seat skeleton — it's data-only (positioning + animation)
- The seat skeleton's world-space position relative to the furniture origin = the player's offset in-game
- Multiple seats = multiple
Player_*armatures (each positioned differently on the furniture)
Furniture Animations
Furniture uses a naming convention to separate furniture animations from player animations.
Furniture Mesh Animations
Target the Furniture_Armature. Blender exports them as Furniture_Armature|AnimName.
| Animation Name | When Played | Required? |
|---|---|---|
Idle |
Default state, no passengers | Recommended |
Occupied |
At least one player is seated | Optional (falls back to Idle) |
LockClose |
A seat gets locked (one-shot) | Optional |
LockOpen |
A seat gets unlocked (one-shot) | Optional |
Shake |
Player is struggling (loops during struggle) | Optional |
Example in Blender's Action Editor:
Furniture_Armature|Idle ← chains hanging loose
Furniture_Armature|Occupied ← chains pulled taut
Furniture_Armature|Shake ← whole frame vibrates
Player Seat Animations
Target the Player_* armatures. Blender exports them as Player_main|AnimName.
The mod resolves them as {seatId}:{AnimName}.
| Animation Name | When Played | Required? |
|---|---|---|
Idle |
Default seated pose | Yes (no fallback) |
Struggle |
Player struggling to escape | Optional (stays in Idle) |
Enter |
Mount transition (one-shot, 1 second) | Optional (snaps to Idle if absent) |
Exit |
Dismount transition (one-shot, 1 second) | Optional (snaps to vanilla if absent) |
Example in Blender's Action Editor:
Player_main|Idle → resolved as "main:Idle" ← arms spread, legs apart
Player_main|Struggle → resolved as "main:Struggle" ← pulling against restraints
Player_left|Idle → resolved as "left:Idle" ← head and arms through pillory
Player_right|Idle → resolved as "right:Idle" ← same pose, other side
Key difference from body items: Furniture player animations control ALL 11 bones, not just region-owned bones. The furniture overrides the player's entire pose for the blocked regions, and the remaining regions still show body item effects (gag, blindfold, etc.).
The JSON Definition
Furniture JSON goes in data/<namespace>/tiedup_furniture/. Unlike body items, furniture definitions are server-authoritative and synced to clients automatically.
Full Format
{
"id": "mynamespace:my_cross",
"display_name": "Custom Cross",
"translation_key": "furniture.mynamespace.my_cross",
"model": "mynamespace:models/gltf/furniture/my_cross.glb",
"icon": "mynamespace:item/my_cross_icon",
"tint_channels": {
"tintable_0": "#8B4513",
"tintable_1": "#1A1A1A"
},
"supports_color": true,
"hitbox": { "width": 1.2, "height": 2.4 },
"placement": {
"snap_to_wall": true,
"floor_only": true
},
"lockable": true,
"break_resistance": 100,
"drop_on_break": true,
"seats": [
{
"id": "main",
"armature": "Player_main",
"blocked_regions": ["ARMS", "HANDS", "LEGS", "FEET"],
"lockable": true,
"locked_difficulty": 150,
"item_difficulty_bonus": true
}
],
"category": "restraint"
}
JSON Field Guide
| Field | Required? | Description |
|---|---|---|
id |
Yes | Unique ID, e.g., mynamespace:wooden_cross |
display_name |
Yes | Fallback name if no translation key |
translation_key |
No | i18n key for localized name |
model |
Yes | Path to GLB file in assets |
icon |
No | Inventory sprite model (same system as body items — see Inventory Icons) |
tint_channels |
No | Default tint colors (hex #RRGGBB) per channel |
supports_color |
No | Planned — player recoloring via dye (default: false) |
hitbox.width |
No | Entity collision width (0.1–5.0, default: 1.0) |
hitbox.height |
No | Entity collision height (0.1–5.0, default: 1.0) |
placement.snap_to_wall |
No | Align to nearest wall on placement (default: false) |
placement.floor_only |
No | Can only be placed on solid ground (default: true) |
lockable |
No | Can seats be locked with a key? (default: false) |
break_resistance |
No | Cumulative damage to break (1–10000, default: 100) |
drop_on_break |
No | Drop placer item when broken? (default: true) |
seats |
Yes | 1–8 seat definitions (see below) |
feedback.mount_sound |
No | Sound when a player is force-mounted |
feedback.lock_sound |
No | Sound when a seat is locked |
feedback.unlock_sound |
No | Sound when a seat is unlocked |
feedback.struggle_loop_sound |
No | Sound played when struggle starts |
feedback.escape_sound |
No | Sound on successful escape (default: chain break) |
feedback.denied_sound |
No | Sound when action is denied (locked dismount, no seat) |
category |
No | Creative tab grouping (default: "furniture") |
Seat Definition Fields
| Field | Required? | Description |
|---|---|---|
id |
Yes | Seat identifier (must match Player_{id} armature name, no : allowed) |
armature |
Yes | GLB armature name (e.g., Player_main) |
blocked_regions |
No | Body regions the furniture controls (default: empty — no blocking) |
lockable |
No | Can this seat be locked? (inherits from top-level lockable) |
locked_difficulty |
Yes if lockable | Struggle difficulty when locked (1–10000) |
item_difficulty_bonus |
No | Body items on free regions add to escape difficulty? (default: false) |
Blocked Regions Explained
When a player sits in a seat with blocked_regions: ["ARMS", "HANDS"]:
- Animation: The furniture's player pose controls arm + hand bones. Body items on ARMS/HANDS are ignored.
- Rendering: Body items on ARMS/HANDS are hidden (the furniture pose replaces them visually).
- Gameplay: The master cannot equip/unequip items on ARMS/HANDS while the player is seated.
- Other regions (HEAD, MOUTH, NECK, etc.) work normally — items render, can be changed, and contribute to escape difficulty if
item_difficulty_bonus: true.
Note: The struggle minigame for escaping locked furniture seats uses the same continuous struggle system as body items. The player holds directional keys to drain resistance. The total difficulty =
locked_difficulty+ bonus from equipped body items on non-blocked regions (ifitem_difficulty_bonus: true), capped at 600.
Tint Channels (Same as Body Items)
Furniture supports the exact same tint channel system as body items. See Tint Channels above — everything applies identically.
Name your Blender materials tintable_0, tintable_1, etc., use grayscale textures, and define defaults in the JSON tint_channels field.
Exporting Furniture from Blender
Same export settings as body items, with one extra consideration:
- Select ALL armatures — Furniture_Armature AND all Player_* armatures
- File → Export → glTF 2.0 (.glb)
- Settings:
- Format: glTF Binary (.glb)
- Include: Selected Objects
- Mesh: Apply Modifiers
- Animation: Export all actions (NLA strips or all actions)
- Important: Do NOT merge armatures — each must remain separate
Packaging as a Resource/Data Pack
Furniture needs files in two locations:
my_resource_pack/
├── assets/mynamespace/
│ ├── models/gltf/furniture/
│ │ └── my_cross.glb ← The GLB model (client resource)
│ ├── models/item/
│ │ └── my_cross_icon.json ← Icon model (inventory sprite)
│ └── textures/item/
│ └── my_cross_icon.png ← Icon texture (16x16)
│
└── data/mynamespace/
└── tiedup_furniture/
└── my_cross.json ← The JSON definition (server data)
The GLB goes in assets/ (it's a client resource for rendering). The JSON goes in data/ (it's server-authoritative gameplay data, synced to clients via packet).
Common Furniture Mistakes
| Mistake | Symptom | Fix |
|---|---|---|
Seat armature named Main instead of Player_main |
No seat detected, furniture is decoration only | Prefix MUST be Player_ |
Seat ID contains : (e.g., Player_seat:left) |
JSON rejected by parser | Use only alphanumeric + underscore in seat IDs |
| Player skeleton uses wrong bone names | Player not posed correctly on furniture | Use the exact 11 standard bone names (camelCase) |
| Armatures merged into one on export | Parser can't separate furniture from seats | Export with separate armatures, don't merge |
No Idle animation for a seat |
Player has no pose on furniture | Every seat MUST have at least `{armature} |
| Furniture animation named `Player_main | Idle` | Parsed as a seat animation, not furniture |
| More than 8 seats | JSON rejected by parser | Maximum 8 seats per furniture piece |
blocked_regions references unknown region |
JSON rejected by parser | Use exact names from the Region Table above |
Furniture Examples
Example 1: Simple Chair (No Restraint)
A decorative chair anyone can sit on. No locking, no blocked regions.
Blender:
- Model a chair mesh, weight paint to a single
framebone - Add
Player_mainskeleton sitting on the chair - Create
Player_main|Idlewith the player seated (legs bent, arms on armrests) - Create
Furniture_Armature|Idle(static, single keyframe)
JSON:
{
"id": "mycreator:wooden_chair",
"display_name": "Wooden Chair",
"model": "mycreator:models/gltf/furniture/wooden_chair.glb",
"hitbox": { "width": 0.8, "height": 1.0 },
"seats": [
{
"id": "main",
"armature": "Player_main",
"blocked_regions": []
}
]
}
No locking, no blocked regions. The player sits freely and can stand up anytime.
Example 2: St. Andrew's Cross (Single Seat)
Blender:
- Model the X-frame with chains at wrist and ankle positions
- Add bones for animated parts:
chain_left,chain_right,lock_mechanism - Add
Player_mainskeleton with arms and legs spread in an X pose - Create animations:
Furniture_Armature|Idle— chains hanging looseFurniture_Armature|Occupied— chains pulled tautFurniture_Armature|Shake— frame vibrating (for struggle)Player_main|Idle— arms spread, legs apart, flush against the crossPlayer_main|Struggle— pulling against restraints, body twisting
JSON:
{
"id": "mycreator:saint_andrews_cross",
"display_name": "St. Andrew's Cross",
"model": "mycreator:models/gltf/furniture/cross.glb",
"tint_channels": { "tintable_0": "#8B4513" },
"supports_color": true,
"hitbox": { "width": 1.2, "height": 2.4 },
"placement": { "snap_to_wall": true },
"lockable": true,
"break_resistance": 150,
"seats": [
{
"id": "main",
"armature": "Player_main",
"blocked_regions": ["ARMS", "HANDS", "LEGS", "FEET"],
"lockable": true,
"locked_difficulty": 150,
"item_difficulty_bonus": true
}
],
"category": "restraint"
}
The player is locked with arms and legs controlled by the cross. A master can still equip a gag (MOUTH), blindfold (EYES), or collar (NECK) on the mounted player. The gag adds to escape difficulty because item_difficulty_bonus: true.
Example 3: Double Pillory (Two Seats)
Blender:
- Model a long wooden pillory with two holes
- Add
Player_leftandPlayer_rightskeletons, each bent forward with head and arms through the pillory holes - Position them side by side on the furniture
JSON:
{
"id": "mycreator:double_pillory",
"display_name": "Double Pillory",
"model": "mycreator:models/gltf/furniture/pillory.glb",
"hitbox": { "width": 2.0, "height": 1.5 },
"lockable": true,
"seats": [
{
"id": "left",
"armature": "Player_left",
"blocked_regions": ["ARMS", "HANDS", "HEAD"],
"lockable": true,
"locked_difficulty": 120
},
{
"id": "right",
"armature": "Player_right",
"blocked_regions": ["ARMS", "HANDS", "HEAD"],
"lockable": true,
"locked_difficulty": 120
}
],
"category": "restraint"
}
Two players can be locked side by side. The mod picks the seat nearest to where you're looking when you sit down.
Monster Seat System (Planned)
The furniture system is built on a universal ISeatProvider interface that is not limited to static furniture. Any living entity (monster, NPC) can implement the same interface to hold players in constrained poses using the same mechanics: blocked regions, forced animations, lock/escape.
Example use case: A tentacle monster that grabs a player on attack — the player "rides" the monster, gets a forced pose (arms restrained), and must struggle to escape. The monster's GLB would contain a Player_grab armature with Player_grab|Idle and Player_grab|Struggle animations, following the exact same convention as furniture seats.
What this means for artists: If you create a monster model, you can include Player_* armatures in the GLB using the same workflow as furniture. The seat animations, blocked regions, and escape mechanics will work identically.
Current status: The ISeatProvider interface and all downstream systems (animation, rendering, packets, escape) are implemented and ready. No monster entity or AI has been created yet — this requires its own design phase (AI goals, behaviors, spawn conditions, etc.).
Quick Reference Cards
Body Items
REQUIRED:
✓ Skeleton: PlayerArmature with 11 named bones (camelCase, exact)
✓ At least one animation: PlayerArmature|Idle
✓ Weight painting to standard bones
✓ JSON in assets/<namespace>/tiedup_items/ with type, display_name, model, regions
NEVER DO:
✗ Animate PlayerArmature (armature root, not a bone)
✗ Use wrong case for bone names or animation names
✗ Leave vertices unweighted
✗ Use pose_priority 0
GOOD TO KNOW:
→ Only Idle is required. Everything else has fallbacks.
→ Templates let you skip animation entirely.
→ Free bones (not owned by any item) CAN be animated by your GLB.
→ Bones owned by another equipped item are always ignored.
→ The mod handles sitting, sneaking, walking — you don't have to.
→ Context GLBs in tiedup_contexts/ replace default postures.
→ Slim model is optional. Steve mesh works on Alex (minor clipping).
→ Textures bake into the GLB. No separate file needed.
Furniture
REQUIRED:
✓ Furniture_Armature (any name NOT starting with "Player_")
✓ At least one Player_{seatId} armature with 11 standard player bones
✓ At least one animation per seat: Player_{seatId}|Idle
✓ GLB in assets/<namespace>/models/gltf/furniture/
✓ JSON in data/<namespace>/tiedup_furniture/ with id, display_name, model, seats
NEVER DO:
✗ Name furniture armature starting with "Player_"
✗ Use ":" in seat IDs
✗ Merge armatures on export (each must stay separate)
✗ Use wrong bone names in seat skeletons
✗ More than 8 seats per furniture
GOOD TO KNOW:
→ Furniture bones can be anything (not limited to player skeleton)
→ Seat position = Player_* armature position in Blender (no JSON offset)
→ Player animations on blocked regions override body items
→ Body items on non-blocked regions still render normally
→ Furniture mesh animations (Idle, Occupied, Shake) are optional
→ Tint channels work the same as body items
→ JSON is server-authoritative, synced to clients automatically