Skip to main content

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:

  • .bbmodel files for editable source imports
  • .fmmodel files for stripped runtime-ready model data

For a loose .bbmodel without companion files:

  1. place it directly in plugins/FreeMinecraftModels/imports, using the lowercase .bbmodel extension
  2. run /fmm reload
  3. check that its .fmmodel conversion 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
  • imports is the intake folder for manual model imports and official package downloads before processing
  • models contains active installed model content
  • models_disabled contains 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 .bbmodel or .fmmodel extension
  • 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 .BBModel and .FMModel are accepted there. The loose imports path 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:

  1. lowercased
  2. 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_version from the .bbmodel and branches on its major number
  • A missing meta block, a missing format_version, or a value it cannot parse all fall back to version 4, with an info/warning line naming the model
  • A missing textures array 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 .png extension. A source name such as body.jpg, body.PNG, or body is written and referenced as body.png
  • format_version 4.x and older: bones are read straight from the outliner tree, which carries the bone names inline
  • format_version 5.x and newer: the outliner schema changed. outliner is now a nested tree of bare UUID strings and {uuid, isOpen, children} dicts with no name key, while a separate flat groups array holds the actual bone data (including name). FMM joins the two by UUID — each outliner node is replaced by its matching groups entry, then children are merged recursively — so bone names, origins and rotations resolve normally
  • Consequences worth knowing when hand-editing or generating .bbmodel files:
    • A v5 file whose groups array 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 outliner and groups; an outliner node with no matching group is kept as-is rather than being dropped
    • Do not mix a v5 format_version with a v4-shaped outliner, or vice versa — the branch is chosen from the declared version, not from the actual shape
  • 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 hitbox goes 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 logs has 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 hitbox are skipped), never rendered, and excluded from the Bedrock geometry export. On Bedrock, a model with no hitbox bone falls back to 1.0 x 2.0
  • tag_...
  • 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
  • m_...
    • mount-point bones. Each bone with this prefix creates a seat position for a player, managed by MountPointManager. Multiple m_ 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.

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_emission value from Blockbench is kept when it is a whole number from 0 to 15. Any other value is dropped with a console warning ending in with invalid light_emission. Expected an integer from 0 to 15; ignoring this value.
  • A cube with shading turned off (shade: false) also gets shade_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 target and 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 to true; replaces the older sendCustomModelsToBedrockClients key) and the surrounding Floodgate/Geyser/resource-pack path
  • Java clients on supported versions can use display-entity rendering when useDisplayEntitiesWhenPossible is 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 a tag_ anchor only when the fallback nameplate position above the hitbox is unsuitable
  • use lowercase spawn, idle, walk, attack and death for 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 disguise should also ship idle at minimum, and may add sneak and jump, 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​

SuffixPurposeBowCrossbow
_idleItem in hand, not drawing or chargedrequiredrequired
_draw_startJust started pullingrequiredrequired
_draw_halfHalfway drawnrequiredrequired
_draw_fullFully drawnrequiredrequired
_chargedLoaded 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 _charged is 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 .bbmodel quirk described in older local README material

Those details change faster than the verified runtime contract above.