-
Notifications
You must be signed in to change notification settings - Fork 0
Developer Guide
This page is for mod developers who want to render their own entities or block entities through Easy Model Entities. EME handles profile loading, model baking, basic animation, and rendering; your mod keeps ownership of its gameplay logic.
Artifacts are split by loader. Use the artifact that matches your Minecraft and EME version.
Common:
compileOnly "de.markusbordihn.easymodelentities:easy_model_entities-common-${minecraft_version}:1.0.0"Fabric:
modImplementation "de.markusbordihn.easymodelentities:easy_model_entities-fabric-${minecraft_version}:1.0.0"Forge:
implementation fg.deobf("de.markusbordihn.easymodelentities:easy_model_entities-forge-${minecraft_version}:1.0.0")Custom entities or block entities implement EasyModelRenderable.
public class MyBlockEntity extends BlockEntity implements EasyModelRenderable {
private static final ResourceLocation PROFILE_ID =
new ResourceLocation("my_mod", "disguised_chest");
@Override
public ResourceLocation getEasyModelProfileId() {
return PROFILE_ID;
}
}If the server profile and render profile use the same ID,
getEasyModelProfileId() is enough. Override getEasyModelRenderProfileId(),
getEasyModelVersion(), or getEasyModelAnimationState() only when the runtime
state differs from the server profile.
Use the entity render delegate for custom entity renderers:
public class MyEntityRenderer extends EntityRenderer<MyEntity> {
private final EasyModelEntityRenderDelegate<MyEntity> delegate =
EasyModelEntitiesClientApi.createRenderDelegate();
@Override
public void render(
MyEntity entity,
float entityYaw,
float partialTick,
PoseStack poseStack,
MultiBufferSource buffer,
int packedLight) {
this.delegate.render(entity, entityYaw, partialTick, poseStack, buffer, packedLight);
super.render(entity, entityYaw, partialTick, poseStack, buffer, packedLight);
}
@Override
public ResourceLocation getTextureLocation(MyEntity entity) {
return this.delegate.getTextureLocation(entity);
}
}Pack-side setup and command examples are in Entities.
Current EME host entities only provide static and generic ground behavior. Mods that need swimming, flying, projectiles, vehicles, or custom AI should keep their own entity class and use EME as the render/profile layer.
Use the block entity render delegate for custom block entity renderers:
public class MyBlockEntityRenderer implements BlockEntityRenderer<MyBlockEntity> {
private final EasyModelBlockEntityRenderDelegate<MyBlockEntity> delegate =
EasyModelEntitiesClientApi.createBlockEntityRenderDelegate();
@Override
public void render(
MyBlockEntity blockEntity,
float partialTick,
PoseStack poseStack,
MultiBufferSource buffer,
int packedLight,
int packedOverlay) {
EasyModelBlockEntityRenderOptions options =
EasyModelBlockEntityRenderOptions.DEFAULT.withYawDegrees(180.0f);
this.delegate.render(blockEntity, partialTick, poseStack, buffer, packedLight, options);
}
}Pack-side setup and command examples are in Block Entities.
For EME host block entities, use animated_randomly in the server profile:
{
"model_type": "block_entity",
"preset_type": "animated_randomly",
"client": {
"render_profile": "my_mod:shrine"
}
}For render-only integrations, use random_idle in the render profile:
{
"preset_type": "static",
"model": "my_mod:easy_model_entities/models/disguised_chest",
"texture": "my_mod:textures/block/disguised_chest.png",
"animation": {
"mode": "random_idle"
}
}The block entity delegate schedules the idle animation client-side when
animation.mode is random_idle and no explicit animationTicks override is
provided.
Mods can return transforms per model part. The baked model is unchanged; the transform is applied only for the current render call.
EasyModelPartAnimator animator =
context -> {
if ("crystal".equals(context.partName())) {
float spin = context.ageInTicks() * 0.04f;
return new EasyModelPartTransform(0.0f, spin, 0.0f);
}
return EasyModelPartTransform.NONE;
};
EasyModelBlockEntityRenderOptions options =
EasyModelBlockEntityRenderOptions.DEFAULT.withPartAnimator(animator);
this.delegate.render(blockEntity, partialTick, poseStack, buffer, packedLight, options);The animator receives an EasyModelPartAnimationContext with part name, body
type, limbSwing, limbSwingAmount, ageInTicks, and automaticTransform.
Use automaticTransform when your transform should build on EME's automatic
animation.
EasyModelPartAnimationMode.ADD is the default. The animator result is added to
the automatic EME transform.
EasyModelBlockEntityRenderOptions options =
EasyModelBlockEntityRenderOptions.DEFAULT
.withPartAnimator(animator)
.withPartAnimationMode(EasyModelPartAnimationMode.ADD);EasyModelPartAnimationMode.REPLACE replaces the automatic transform for that
part. context.automaticTransform() is still available if the animator wants to
reuse it manually.
EasyModelBlockEntityRenderOptions options =
EasyModelBlockEntityRenderOptions.DEFAULT
.withPartAnimator(animator)
.withPartAnimationMode(EasyModelPartAnimationMode.REPLACE);For entities, use the same animator through EasyModelEntityRenderOptions:
EasyModelEntityRenderOptions options =
EasyModelEntityRenderOptions.DEFAULT
.withPartAnimator(animator)
.withPartAnimationMode(EasyModelPartAnimationMode.REPLACE);
this.delegate.render(entity, entityYaw, partialTick, poseStack, buffer, packedLight, options);Set animation.mode to none in the render profile when EME should not create
automatic transforms at all.
Renderers can read the public, read-only part structure:
List<EasyModelPartDefinition> roots = this.delegate.rootModelParts(blockEntity);
List<EasyModelPartDefinition> parts = this.delegate.modelParts(blockEntity);Entity delegates expose the same methods for entity models. Each part exposes name, offset, rotation, and children. Cube and renderer internals stay hidden.
Server-side helpers:
boolean known = EasyModelEntitiesApi.hasProfile(profileId);
Optional<EasyModelEntityProfile> profile = EasyModelEntitiesApi.getProfile(profileId);
Optional<Entity> entity = EasyModelEntitiesApi.createEntity(level, profileId, position);
Optional<ResourceLocation> currentProfile = EasyModelBlockEntitiesApi.getProfileId(blockEntity);
boolean changed = EasyModelBlockEntitiesApi.setProfileId(blockEntity, profileId);For custom entities and block entities, EasyModelRenderable plus the render
delegate is usually the only integration needed.
external_owner and several non-ground presets are currently WIP from a host
behavior perspective. They are useful for schemas, render defaults, and
mod-owned integrations, but should not be documented as complete built-in mob
behavior yet.