FreeMinecraftModels Model Authoring Notes
These notes describe the model files, companion files and names that FreeMinecraftModels reads during import and at runtime.
Source Formats
FreeMinecraftModels currently accepts:
.bbmodelfiles for editable source imports.fmmodelfiles for stripped runtime-ready model data
For a loose .bbmodel without companion files:
- place it directly in
plugins/FreeMinecraftModels/imports, using the lowercase.bbmodelextension - run
/fmm reload - check that its
.fmmodelconversion appears in the active models folder and that reload succeeds
The loose importer leaves the original in imports, so later imports convert it again. It does not collect loose .json or .yml companions, and does not accept a loose .fmmodel. For authored model sets, keep the models and their companions together under the active models folder, or use a correctly structured content package. An arbitrary subfolder under imports without pack.meta is skipped. A rejected reload can leave the previous catalog active; check the console.
The loose conversion keeps only texture source, id and name, dropping animation dimension/timing metadata. It also expects a textures array. Use the active model folder or a package that preserves the original file when texture metadata matters; the runtime model reader itself accepts a missing textures array.
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' runtime prefers lowercase models when it exists, falling back to Models otherwise. On a case-insensitive filesystem these names resolve to the same directory. On a case-sensitive filesystem they can coexist, and the shared package importer currently prefers an existing uppercase Models destination while the runtime reads lowercase models. Back up and consolidate them into lowercase models while the server is stopped to avoid imports landing outside the active tree.
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 runtime models-folder extension match is case-insensitive, so
.BBModeland.FMModelare accepted there. The looseimportspath only matches lowercase.bbmodel
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 every file with that ID is byte-for-byte identical, as happens when two content packages ship the same model, FMM loads the one whose absolute path sorts first and skips the copies without a warning. When the files differ:
- 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. An already merged outliner can still work; raw unnamed v5 group nodes can fail model loading when the skeleton reader expects a name - 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 to define runtime collision. A nested
hitboxgoes through ordinary bone parsing but remains hidden and does not define the model's hitbox - should contain exactly one cube, which defines 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 group logshas a hitbox bone but no hitbox cube!and leaves zero dimensions; it does not behave like a missing hitbox. Cube offsets and rotations do not become a hitbox offset: the parser uses absolute side lengths and a zero model offset - 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 in the Java runtime. Use cube-free bones for structure: Bedrock geometry export does not filter the
b_prefix, so cubes on such a bone can remain visible there
- non-display bones in the Java runtime. Use cube-free bones for structure: Bedrock geometry export does not filter the
m_...- mount-point bones. Each bone with this prefix creates a seat position for a player, managed by
MountPointManager. Multiplem_bones create multiple seats. The manager selects the first free seat and uses a temporary Interaction entity while occupied; its public mount method accepts players, not arbitrary entities.
- mount-point bones. Each bone with this prefix creates a seat position for a player, managed by
The prefixes are case-sensitive; the hitbox name is compared without regard to case. These names 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.
Cube Shading And Light Emission
- A cube's
light_emissionvalue from Blockbench is kept when it is a whole number from0to15. Any other value is dropped with a console warning ending inwith invalid light_emission. Expected an integer from 0 to 15; ignoring this value. - A cube with shading turned off (
shade: false) also getsshade_direction_override: "up"in the generated geometry, so Minecraft 26.3 clients render it unshaded like older clients do. FMM applies the same rule to cubes in a sibling display model.json. A value you set yourself is kept
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. Locators can also anchor Blockbench particle effects; see Particles
A null object becomes an IK controller only when its .bbmodel entry carries both ik_source (source bone) and ik_target (target bone or locator). The importer searches the hierarchy, with sibling and downward search paths too. Root-level siblings produce a source-only chain. lock_ik_target_rotation is read and stored but is not applied by the current solver.
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 drive IK. Their offset is added to the target bone/locator's rest position, not the stored controller rest position. Rotation and scale tracks do not drive IK
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.
FMM reads the JSON beside the active model file, copies it to the resource pack and rewrites non-namespaced values in its textures map. Numeric texture keys map to the model's extracted texture indices; an unmatched key falls back to an available texture if one exists. Namespaced values stay unchanged. If no sibling JSON exists, the item uses its base material appearance: normally paper for a prop placement item, or the configured material for a held item. A malformed companion logs Failed to process display model JSON for ... and does not register that display model.
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. Leather materials accept an RGB dye suffix such as LEATHER_CHESTPLATE:ff8800; see Configuration. 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.
Other invalid definitions, such as a non-boolean isEnabled, malformed enchantment entries, duplicate enchantments or an invalid item filename, can reject the candidate catalog and the reload. Use lowercase item filenames containing only a-z, 0-9, ., _ and -, up to 128 characters. The authored-item scan reads .yml, not .yaml. Obtain a registered item with /fmm giveitem <item> or the admin browser; prop recipes and shop purchases produce paper placement items instead.
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_). Add atag_anchor only when the fallback nameplate position above the hitbox is unsuitable - use lowercase
spawn,idle,walk,attackanddeathfor the shared runtime states; attacks and death still need their runtime trigger, and other names need explicit playback (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 detects bow and crossbow display states automatically during resource-pack generation. Each required state needs both a loaded model file and a successfully processed sibling display .json; filenames alone are insufficient. Configure the held item under its base name with the appropriate BOW or CROSSBOW material.
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/models/my_weapons/
cool_bow_idle.bbmodel
cool_bow_idle.json
cool_bow_draw_start.bbmodel
cool_bow_draw_start.json
cool_bow_draw_half.bbmodel
cool_bow_draw_half.json
cool_bow_draw_full.bbmodel
cool_bow_draw_full.json
cool_bow.yml
For a crossbow, also add cool_bow_charged.bbmodel and cool_bow_charged.json. The item configuration is cool_bow.yml, using the base name without _idle.
How Detection Works
- Detection happens automatically when the resource pack is generated (on startup or
/fmm reload). - A complete display-state set gets one public item definition under the base name, such as
cool_bow.json. The individual_idle, draw and charged item definitions are removed; their geometry remains referenced inside the base definition - The display registry exposes the base ID and removes the state IDs. A complete set with
_chargedis treated as a crossbow; without it, as a bow. Missing display states prevent grouping - Use the base name for the YML config, such as
cool_bow.yml. The item catalog skips state-suffixed YML files; prop config lookup also strips the suffix
Display Model JSON
Each required state must have its own sibling .json display model, such as cool_bow_idle.json and cool_bow_draw_full.json. FMM connects them through the generated base item definition. See Resource Pack Output for its conditions and output path.
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.