FreeMinecraftModels Model Authoring Notes
This page documents the current authoring details that are visible in the FreeMinecraftModels codebase. It is intentionally conservative: it focuses on the import/runtime contract, not on every Blockbench workflow preference.
Source Formats
FreeMinecraftModels currently accepts:
.bbmodelfiles for editable source imports.fmmodelfiles for stripped runtime-ready model data
The normal import flow is:
- place the model in
plugins/FreeMinecraftModels/imports - run
/fmm reload - let FreeMinecraftModels import the model into the active model set and rebuild the generated resource pack
Folder Roles
plugins/FreeMinecraftModels/imports
plugins/FreeMinecraftModels/models
plugins/FreeMinecraftModels/models_disabled
importsis the intake folder for manual model imports and official package downloads before processingmodelscontains active installed model contentmodels_disabledcontains downloaded or installed package content that is currently disabled
Older installs may have an uppercase Models folder instead. FreeMinecraftModels resolves this by preferring the canonical lowercase models when it exists, and only falling back to the legacy Models when it does not. On Windows and macOS the two names are the same directory anyway; on a case-sensitive Linux filesystem an old install keeps working, but if both directories somehow exist, only lowercase models is read. Consolidate into models if you find both.
Model IDs
- Runtime model IDs come from the file name, without the
.bbmodelor.fmmodelextension - Use stable, unique file names because the ID is what commands and API calls resolve against
- Blockbench animation references are name-based, so duplicate or unclear naming inside the model is more likely to cause trouble than a clean, explicit naming scheme
- The extension match is case-insensitive, so
.BBModeland.FMModelare accepted
ID Normalization And Collisions
The file name is normalized before it becomes the runtime model ID and the resource-pack filename:
- lowercased
- every character outside
a-z,0-9,.,_, and-is replaced with_
So My Table.bbmodel, my table.bbmodel, and my_table.bbmodel all normalize to the same ID, my_table.
Before any conversion runs, FreeMinecraftModels sweeps the whole models tree and checks for files that normalize to the same ID. When it finds a collision:
- every colliding file is rejected — none of them is loaded, so there is no silent "last one wins"
- the model ID is blocked for the rest of that load pass
- rejected models are also excluded from the Bedrock custom-entity bundle export
- the console gets:
[FMM Models] Rejected normalized model ID collision '<id>'. These files normalize to the same ID: <paths>. Rename the files so every normalized model ID is unique; no colliding model was loaded.
The fix is always to rename the files so the normalized IDs are distinct. The safest habit is to name model files in lowercase with underscores from the start (stone_table.bbmodel), which makes the file name and the runtime ID identical and removes any chance of a surprise collision.
Blockbench Compatibility
- FreeMinecraftModels reads
meta.format_versionfrom the.bbmodeland branches on its major number - A missing
metablock, a missingformat_version, or a value it cannot parse all fall back to version4, with an info/warning line naming the model - A missing
texturesarray is accepted and treated as an empty texture list instead of crashing the importer. The Bedrock custom-entity exporter then skips that model because Bedrock export requires at least one texture - Extracted texture filenames are normalized to one
.pngextension. A source name such asbody.jpg,body.PNG, orbodyis written and referenced asbody.png format_version4.xand older: bones are read straight from theoutlinertree, which carries the bone names inlineformat_version5.xand newer: the outliner schema changed.outlineris now a nested tree of bare UUID strings and{uuid, isOpen, children}dicts with no name key, while a separate flatgroupsarray holds the actual bone data (includingname). FMM joins the two by UUID — each outliner node is replaced by its matchinggroupsentry, then children are merged recursively — so bone names, origins and rotations resolve normally- Consequences worth knowing when hand-editing or generating
.bbmodelfiles:- A v5 file whose
groupsarray is missing is passed through unmerged, so its bones lose their names and the reserved prefixes (tag_,h_,b_,m_,hitbox) stop being recognized - UUIDs must match exactly between
outlinerandgroups; an outliner node with no matching group is kept as-is rather than being dropped - Do not mix a v5
format_versionwith a v4-shaped outliner, or vice versa — the branch is chosen from the declared version, not from the actual shape
- A v5 file whose
- If import logs say the model format is not compatible with FreeMinecraftModels, treat that as a model-format issue first, not a wiki or command issue
Runtime-Significant Bone Conventions
The current converter and skeleton pipeline recognize a few naming conventions:
hitbox- reserved for hitbox generation
- should define the model hitbox cleanly instead of being used as a visual bone
- must be a top-level bone in the outliner. The importer only checks the root level for it; a
hitboxgroup nested inside another bone is treated as an ordinary bone - must contain exactly one cube, which defines the runtime width (x), depth (z), and height (y). Extra cubes log
has more than one value defining a hitbox! Only the first cube will be used; an empty hitbox bone logshas a hitbox bone but no hitbox cube!and no hitbox is generated - never animated (animation tracks targeting
hitboxare skipped), never rendered, and excluded from the Bedrock geometry export. On Bedrock, a model with nohitboxbone falls back to1.0x2.0
tag_...- an optional authored nameplate anchor. See Nameplate Position below
h_...- treated as head bones
b_...- non-display bones (hidden at runtime). Use these for structural or organizational bones that should not render in-game.
m_...- mount-point bones. Each bone with this prefix creates a rideable seat position on the model. Players or entities can be mounted onto these positions at runtime. Multiple
m_bones create multiple seats. Managed internally byMountPointManager.
- mount-point bones. Each bone with this prefix creates a rideable seat position on the model. Players or entities can be mounted onto these positions at runtime. Multiple
These are not just style conventions; they affect conversion and runtime behavior.
Nameplate Position
A tag_ bone is now optional. FMM uses the alphabetically first named tag_ bone as the model's nameplate anchor. With no such bone, it places the nameplate above the scaled hitbox, so an untagged model can still display its name.
Use a cube-free tag_name bone when the fallback is not the right position. Multiple tag bones no longer mean multiple copies of the same nameplate.
The API supports multiline names, scale, line gap, and visibility. Lines are ordered top-to-bottom with the last line at the anchor. See the API guide.
Freefloating Cubes
Cubes declared at the top of the outliner without a containing group arrive as bare UUID strings rather than bone entries. FreeMinecraftModels attaches them to the auto-generated root bone (freeminecraftmodels_autogenerated_root) so they still render. They are not addressable by animations, so put anything you intend to animate inside a real group.
IK, Null Objects, And Locators
Current code confirms support for:
- Blockbench null objects as IK controllers
- IK chain blueprints and runtime IK solving (FABRIK)
- locator parsing
A null object becomes an IK controller only when its .bbmodel entry carries both ik_source (the bone the chain starts at) and ik_target (the bone or locator the chain reaches for). lock_ik_target_rotation is read as well. The chain is discovered by walking up the bone hierarchy from the target to the source, so the two must actually be connected.
Important practical constraints:
- The source/target link is by UUID, so it survives renames — but if either UUID is missing from the model, the console logs
IK chain in model <model>: Could not find source bone with UUID .../Could not find target with UUID ...and that chain is skipped - If the walk finds no path from source to target, you get
Could not find path from source to targetand the chain is skipped - Animation lookup for a controller is name-based, so controller naming needs to stay stable between the model structure and the animation data
- Only position keyframes on a null object matter — they become the IK goal offset. Rotation and scale tracks on a null object are ignored
See Animations for how IK is driven per frame.
1.21.4+ Output Split
FreeMinecraftModels declares api-version: 1.21.4, so 1.21.4 is the minimum server version and the modern item-model definition layout is the one you will always get:
plugins/FreeMinecraftModels/output/FreeMinecraftModels/assets/freeminecraftmodels/items
Legacy (pre-1.21.4) leather-horse-armor override branches still exist in the code, but no server that can load the current plugin reaches them. If you are reading old notes or an old output folder, that is the difference you are seeing.
Display Model JSON (1.21.4+)
Admins can place a sibling .json file next to a .bbmodel or .fmmodel file with the same base name (for example, table.bbmodel + table.json). This JSON should be exported from Blockbench as a "Java Block/Item" model and defines how the item looks when held in-hand or shown in inventory.
During import, FMM copies the JSON into the resource pack output and automatically rewrites any bare texture references inside it to point to the model's extracted textures. If no sibling JSON exists, the item displays as plain paper in-game.
Custom Item Config in YML
Set material in the model's sibling YAML to register a held item:
isEnabled: true
material: DIAMOND_SWORD
name: "&6Flame Blade"
lore:
- "&7A blade with a fire enchantment"
enchantments:
- "minecraft:fire_aspect,2"
Use lowercase namespaced enchantment IDs. An item with an unsupported material, an unnamespaced enchantment, or a nonempty scripts: list is unavailable; the console explains why and the original file is preserved. scripts: remains a prop-only feature.
Item effects now belong to enchantment definitions. Wands and staves use magic weapon configuration. Neither uses the former equipped-item Lua runtime.
Bedrock And Render-Path Notes
- Bedrock support depends on
sendCustomModelsToBedrockClientsV2(defaults totrue; replaces the oldersendCustomModelsToBedrockClientskey) and the surrounding Floodgate/Geyser/resource-pack path - Java clients on supported versions can use display-entity rendering when
useDisplayEntitiesWhenPossibleis enabled - Do not assume a model that looks correct on a Java client is automatically safe for your Bedrock path
Practical Authoring Advice
- keep file names stable because they become runtime IDs
- keep controller and animation naming explicit because Blockbench animation lookup is name-driven
- use the reserved virtual-bone names intentionally (
hitbox,tag_,h_,b_,m_) — and remember that any model meant to show a name needs atag_bone or it will be silently nameless - name state animations
spawn,idle,walk,attackanddeathif you want them to fire automatically; everything else is a custom animation you trigger yourself (see Animations) - a model intended for
/fmm disguiseshould also shipidleat minimum, and may addsneakandjump, which only the disguise controller recognizes (see Player Disguises) - validate imported output after
/fmm reload, not just inside Blockbench - verify the generated pack contents for your target Minecraft line, especially on
1.21.4+
Bow And Crossbow State Models
FMM supports automatic pull-animation states for custom bow and crossbow items. No configuration is needed -- just name your model files with the correct suffixes and FMM detects the state set automatically during resource pack generation.
Naming Convention
| Suffix | Purpose | Bow | Crossbow |
|---|---|---|---|
_idle | Item in hand, not drawing or charged | required | required |
_draw_start | Just started pulling | required | required |
_draw_half | Halfway drawn | required | required |
_draw_full | Fully drawn | required | required |
_charged | Loaded crossbow (arrow or rocket) | -- | required |
A bow needs four models (all except _charged). A crossbow needs all five.
Example File Layout
plugins/FreeMinecraftModels/imports/
cool_bow_idle.bbmodel
cool_bow_draw_start.bbmodel
cool_bow_draw_half.bbmodel
cool_bow_draw_full.bbmodel
cool_bow.yml <-- config uses the base name, not _idle
For a crossbow you would add a fifth file, cool_bow_charged.bbmodel.
How Detection Works
- Detection happens automatically when the resource pack is generated (on startup or
/fmm reload). - Only the
_idlemodel gets an item definition JSON in the output pack. The draw and charged states are referenced as conditional entries inside that definition. - Only the base name gets a YML config file. For the example above, the config is
cool_bow.yml, notcool_bow_idle.yml.
Display Model JSON
Each state model can have its own sibling .json display model (e.g. cool_bow_idle.json, cool_bow_draw_full.json). FMM wires them into the generated item definition automatically. See Resource Pack Output for the exact JSON structure that gets generated.
Out Of Scope
This page does not try to guarantee:
- exact Blockbench UI steps
- artistic workflow preferences
- every legacy
.bbmodelquirk described in older local README material
Those details change faster than the verified runtime contract above.