Custom Render Type

A render type controls how a bone behavior is rendered to clients. Every BoneBehaviorType carries an IRenderType, which is asked to produce a BehaviorRenderer for each ActiveModel that uses the behavior. The built-in DefaultRenderType values (ANY, NONE, MODEL) are markers that only gate which bones may use a behavior; a custom render type lets you attach your own renderer.

Creating a render type

IRenderType is a functional interface with a single method that returns a BehaviorRenderer, or null if the behavior should not render anything for the given model.

public class CustomRenderType implements IRenderType {

	@Override
	@Nullable
	public BehaviorRenderer createBehaviorRenderer(ActiveModel activeModel) {
		// Return null if this model should not render the behavior,
		// otherwise return your renderer instance.
		return new CustomRenderer(activeModel);
	}

}

Because IRenderType is functional, a renderer whose constructor takes an ActiveModel can be supplied directly as a constructor reference (this is how the built-in behaviors do it, e.g. CustomRenderer::new).

Implementing the renderer

The actual rendering work lives in a BehaviorRenderer. It extends DataIO, so load / save are already given default no-op implementations you only override if your renderer needs persistence.

public class CustomRenderer implements BehaviorRenderer {

	private final ActiveModel activeModel;
	private ModelRenderer modelRenderer;

	public CustomRenderer(ActiveModel activeModel) {
		this.activeModel = activeModel;
	}

	@Override
	public ModelRenderer getModelRenderer() {
		return modelRenderer;
	}

	@Override
	public void setModelRenderer(ModelRenderer renderer) {
		this.modelRenderer = renderer;
	}

	@Override
	public ActiveModel getActiveModel() {
		return activeModel;
	}

	@Override
	public void initialize() {
		// Set up state once when the renderer is created
	}

	@Override
	public void readBoneData() {
		// Pull the latest transforms / values from the bone each tick
	}

	@Override
	public void sendToClient(RenderParsers parsers) {
		// Build and send packets through the supplied parsers
	}

	@Override
	public void destroy(RenderParsers parsers) {
		// Tear down / remove any spawned entities
	}

}

The RenderParsers passed into sendToClient and destroy is the version-specific NMS bridge used to actually emit packets.

Using the render type

A render type is attached to a behavior through the BoneBehaviorType.Builder. Pass your IRenderType (or a renderer constructor reference) to renderType(...) when building the type.

BoneBehaviorType<CustomBehavior> type = BoneBehaviorType.Builder
		.of(CustomBehavior::new, null, "custom")
		.renderType(new CustomRenderType())
		// or, equivalently, .renderType(CustomRenderer::new)
		.build();

When the behavior is active on a model, Model Engine lazily calls createBehaviorRenderer(activeModel) and caches the result; you can retrieve it via ActiveModel#getBehaviorRenderer(BoneBehaviorType). Returning null from the render type means no renderer is created for that model.

Updated Aug 19, 2026