Skip to main content

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:

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

The normal import flow is:

  1. place the model in plugins/FreeMinecraftModels/imports
  2. run /fmm reload
  3. 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
  • 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 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 .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 extension match is case-insensitive, so .BBModel and .FMModel are accepted

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 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_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, so its bones lose their names and the reserved prefixes (tag_, h_, b_, m_, hitbox) stop being recognized
    • 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. The importer only checks the root level for it; a hitbox group 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 logs has a hitbox bone but no hitbox cube! and no hitbox is generated
    • 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 (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 by MountPointManager.

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 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 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 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_) — and remember that any model meant to show a name needs a tag_ bone or it will be silently nameless
  • name state animations spawn, idle, walk, attack and death if you want them to fire automatically; everything else is a custom animation you trigger yourself (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 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

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/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 _idle model 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, not cool_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 .bbmodel quirk described in older local README material

Those details change faster than the verified runtime contract above.