Skip to content
On this page
Common

EntityRenderer

top.katton.api.registrycommon/src/main/kotlin/top/katton/api/registry/EntityRenderer.kt
Registers a custom entity renderer for a script-registered entity type.

registerEntityRenderer ​

registerEntityRendererCommonFunction
kotlin
@ApiStatus.Experimental
@ApiStatus.Experimental fun <T : Entity> registerEntityRenderer(entityType: EntityType<T>, rendererFactory: EntityRendererProvider<T>)

Registers a custom entity renderer for a script-registered entity type.

This is the client-side counterpart to [registerNativeEntity]. After registering an entity type with [registerNativeEntity], call this on the client to give your entity a visual appearance.

The [rendererFactory] receives an [EntityRendererProvider.Context], which provides access to the entity render dispatcher, item renderer, resource manager, and entity model set - everything you need to construct a standard [EntityRenderer].

Parameters ​

ParameterDescription
entityTypethe entity type (obtained from [registerNativeEntity]'s return value)
rendererFactoryfactory that creates the [EntityRenderer] instance

registerEntityRenderer ​

registerEntityRendererCommonFunction
kotlin
@ApiStatus.Experimental
@ApiStatus.Experimental fun <T : Entity> registerEntityRenderer(entityTypeId: String, rendererFactory: EntityRendererProvider<T>)

Registers a custom entity renderer by entity type ID.

Convenience overload that resolves the [EntityType] from the built-in registry using the given [entityTypeId].

Parameters ​

ParameterDescription
entityTypeIdthe entity type identifier (e.g., "mymod:ghost")
rendererFactoryfactory that creates the [EntityRenderer] instance

Throws ​

ExceptionDescription
IllegalStateExceptionif the entity type is not registered

registerEntityModelLayer ​

registerEntityModelLayerCommonFunction
kotlin
@ApiStatus.Experimental
@ApiStatus.Experimental fun registerEntityModelLayer(layer: ModelLayerLocation, definition: () -> LayerDefinition)

Registers a [ModelLayerLocation] and its [LayerDefinition] for entity model rendering.

In Minecraft 1.21.11+, [net.minecraft.client.model.geom.EntityModelSet] uses an ImmutableMap internally, so model layers registered this way cannot be resolved via context.bakeLayer(). Instead, use [getBakedModelPart] to retrieve the pre-baked [ModelPart] and pass it directly to your model constructor.

Parameters ​

ParameterDescription
layerthe model layer location (e.g., ModelLayerLocation(id("mymod:ghost"), "main"))
definitionfactory that creates the layer definition

getBakedModelPart ​

getBakedModelPartCommonFunction
kotlin
@ApiStatus.Experimental
@ApiStatus.Experimental fun getBakedModelPart(layer: ModelLayerLocation): ModelPart

Returns the pre-baked [ModelPart] for a model layer registered with [registerEntityModelLayer].

Since Minecraft 1.21.11+ uses an ImmutableMap for model layers, context.bakeLayer() cannot resolve dynamically registered layers. Use this function instead to obtain the baked root directly.

KeyframeEvent ​

KeyframeEventCommonData Class
kotlin
data class KeyframeEvent( val animName: String, val timeSeconds: Float, val action: (entity: Mob, model: EntityModel<*>, state: LivingEntityRenderState, bakedAnims: Map<String, KeyframeAnimation>) -> Unit )

A time-stamped callback that fires at a specific point during an entity animation.

Created per-entity-type in [registerAnimatedEntityRenderer]'s [keyframeEvents] list. The callback receives the animated model, entity, render state, and pre-baked animations use [top.katton.api.createBoneExecution] to get an ExecutionContext at a bone position.

Properties ​

PropertyDescription
animNamethe animation name matching a key in [registerAnimatedEntityRenderer.animations]
timeSecondstime in seconds from animation start when the callback should fire
actionthe callback (entity, model, state, baked animations)

registerAnimatedEntityRenderer ​

registerAnimatedEntityRendererCommonFunction
kotlin
@ApiStatus.Experimental
@ApiStatus.Experimental fun <S : LivingEntityRenderState, M : EntityModel<S>> registerAnimatedEntityRenderer(entityTypeId: String, modelLayer: ModelLayerLocation, bodyLayer: () -> LayerDefinition, modelFactory: (ModelPart) -> M, texture: Identifier, renderStateFactory: () -> S = { @Suppress("UNCHECKED_CAST") (LivingEntityRenderState() as S) }, shadowRadius: Float = 0.5f, animations: Map<String, AnimationDefinition> = emptyMap(), animate: ((M, Mob, S, Map<String, KeyframeAnimation>) -> Unit)? = null, keyframeEvents: List<KeyframeEvent> = emptyList())

Simplified entity renderer registration with animation support.

One call handles model layer, renderer construction, and animation wiring. Uses [Mob] as entity type internally to avoid ClassCastException across script reloads. Animation state is shared through [KattonBridge].

Entity side - publish animation states in tick():

kotlin
KattonBridge["anim:$id:idle"] = idleAnimationState
KattonBridge["anim:$id:walk"] = walkAnimationState

Client side - one call:

kotlin
registerAnimatedEntityRenderer<Zombie1RenderState, Zombie1Model<Zombie1RenderState>>(
entityTypeId = "test:zombie1",
modelLayer = Zombie1Model.LAYER_LOCATION,
bodyLayer = { Zombie1Model.createBodyLayer() },
modelFactory = { root -> Zombie1Model(root) },
texture = id("test", "textures/entity/zombie1.png"),
renderStateFactory = { Zombie1RenderState() },
animations = mapOf(
"idle" to Zombie1Animation.idle,
"walk" to Zombie1Animation.walkforward
)
)

Custom animation logic - pass an animate callback. It receives the model, entity, render state, and a map of pre-baked animations:

kotlin
animate = { model, entity, state, baked ->
model.resetPose()
// your custom logic...
baked["walk"]?.apply(walkAnimState, state.ageInTicks)
}

Parameters ​

ParameterDescription
animationsmap of name -> AnimationDefinition. Default logic plays

"walk" when moving and "idle" otherwise. Animation states are read from
KattonBridge["anim:<entityId>:<name>"].