BlockEntityRenderer
A BlockEntityRenderer, often abbreviated as BER, is used to 'render' blocks in a way that cannot be represented with a static baked model (JSON, OBJ, others). For example, this could be used to dynamically render container contents of a chest-like block. A block entity renderer requires the block to have a BlockEntity, even if the block does not store any data otherwise.
BERs directly implements the BlockEntityRenderer, which submits its features for rendering:
// The generic type in the superinterface should be set to what block entity
// you are trying to render, along with its extracted render state. More on this below.
public class MyBlockEntityRenderer implements BlockEntityRenderer<MyBlockEntity, MyBlockEntityRenderState> {
public MyBlockEntityRenderer(BlockEntityRendererProvider.Context context) {
// Get whatever is necessary from the context
}
// Tell the renderer how to create a new render state.
@Override
public MyBlockEntityRenderState createRenderState() {
return new MyBlockEntityRenderState();
}
// Update the render state by copying the needed values from the passed block entity
// to the passed render state.
// The block entity and render state are the generic types passed to the renderer
@Override
public void extractRenderState(MyBlockEntity blockEntity, MyBlockEntityRenderState renderState, float partialTick, Vec3 cameraPos, @Nullable ModelFeatureRenderer.CrumblingOverlay crumblingOverlay) {
// Always call super or `BlockEntityRenderState#extractBase`
super.extractRenderState(blockEntity, renderState, partialTick, cameraPos, crumblingOverlay);
// Extract and store any additional values in the state here.
renderState.value = blockEntity.getValue();
}
// Actually submit the features of the block entity to render.
// The first parameter matches the render state's generic type.
@Override
public void submit(MyBlockEntityRenderState renderState, PoseStack poseStack, SubmitNodeCollector collector, CameraRenderState cameraState) {
// Submit using the collector here.
}
}
Now that we have our BER, we also need to register and connect it to its owning block entity. This is done in EntityRenderersEvent.RegisterRenderers like so:
@SubscribeEvent // on the mod event bus only on the physical client
public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers event) {
event.registerBlockEntityRenderer(
// The block entity type to register the renderer for.
MyBlockEntities.MY_BLOCK_ENTITY.get(),
// A function of BlockEntityRendererProvider.Context to BlockEntityRenderer.
MyBlockEntityRenderer::new
);
}
note
In the event that you do not need the provider context in your BER, you can also remove the constructor:
public class MyBlockEntityRenderer implements BlockEntityRenderer<MyBlockEntity, MyBlockEntityRenderState> {
// ...
}
// In some event handler class
@SubscribeEvent // on the mod event bus only on the physical client
public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers event) {
event.registerBlockEntityRenderer(MyBlockEntities.MY_BLOCK_ENTITY.get(),
// Pass the context to an empty (default) constructor call
context -> new MyBlockEntityRenderer()
);
}