Custom Importer

Model Engine reads model files through a ModelParser. By default only Blockbench .bbmodel files are supported, but you can register your own parser to import models stored in any other format. A parser is responsible for taking a File and producing a ModelBlueprint (transformation and animation data) paired with a ModelAssets (textures and item models) for that model.

Creating a parser class

A parser must implement the ModelParser interface, which has two methods. During import, Model Engine offers every file to each registered parser; the first parser whose validateFile returns true is asked to generate the model.

public class CustomParser implements ModelParser {

	@Override
	public boolean validateFile(File file) {
		// Return true if this parser can handle the file (e.g. by extension)
		return TFile.isExtension(file.getName(), "mymodel");
	}

	@Override
	public Pair<ModelBlueprint, ModelAssets> generate(File file, ErrorCollector collector) throws Exception {
		var name = TFile.removeExtension(file.getName()).toLowerCase(Locale.ENGLISH);

		// Read your own format and populate the blueprint
		var blueprint = new ModelBlueprint();
		blueprint.setName(name);
		// blueprint.getBones().put(...) for each BlueprintBone
		blueprint.constructFlatBoneMap(collector);
		blueprint.cacheBoneBehaviors(collector);

		// Populate the assets (textures and item models)
		var assets = new ModelAssets();
		assets.setName(name);
		// assets.getTextures().add(...) and assets.getModels().put(...)

		return Pair.of(blueprint, assets);
	}

}

Pair here is it.unimi.dsi.fastutil.Pair, the same type returned by the built-in BlockbenchParser.

validateFile

This is a cheap check that decides whether the file is yours to handle. It is called for every parser on every file, so keep it lightweight - matching on the file extension is the typical approach.

generate

This does the actual work. Build a ModelBlueprint from your format's bone hierarchy and animations, and a ModelAssets from its textures and models, then return them as a Pair. Any problem encountered during parsing should be reported through the supplied ErrorCollector rather than thrown silently; returning null causes the model to be skipped. After populating bones you should call constructFlatBoneMap and cacheBoneBehaviors (or finalizeModel) on the blueprint so its internal lookups and bone behaviors are prepared.

Registering the parser

Parsers are registered by listening for RegisterParserEvent and calling register with your parser instance. This is a normal Bukkit event, so register it the same way as any other listener.

public class ParserListener implements Listener {

	@EventHandler
	public void onRegisterParser(RegisterParserEvent event) {
		event.register(new CustomParser());
	}

}
// On plugin start-up
getServer().getPluginManager().registerEvents(new ParserListener(), this);

The built-in BlockbenchParser is always registered first, so your parser only receives files that it claims through validateFile.

Updated Aug 19, 2026