NeoForge 26.1.2에서 BlockEntityRenderer는 2-제네릭 인터페이스BlockEntityRenderer<T extends BlockEntity, S extends BlockEntityRenderState>로 바뀌었습니다. 구 render(be, partialTick, poseStack, buffer, light, overlay) 단일 메소드는 삭제되고, 렌더 로직이 다음 3단계로 분리됩니다.
createRenderState() — 프레임마다 재사용할 빈 렌더 상태 스냅샷을 생성합니다.
extractRenderState(...) — BlockEntity 상태를 스냅샷으로 추출합니다(회전각·스케일·아이템 해석).
submit(...) — 스냅샷만으로 렌더 노드를 제출합니다(BlockEntity 직접 접근 금지).
아이템은 Minecraft.getInstance().getItemRenderer().renderStatic(...) 대신 ItemModelResolver로 ItemStackRenderState를 해석한 뒤 제출합니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/client/renderer/CounterRenderer.javapackage com.example.examplemod.client.renderer;import com.example.examplemod.blockentity.CounterBlockEntity;import com.example.examplemod.client.renderer.state.CounterRenderState;import com.mojang.blaze3d.vertex.PoseStack;import com.mojang.math.Axis;import net.minecraft.client.renderer.SubmitNodeCollector;import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;import net.minecraft.client.renderer.feature.ModelFeatureRenderer;import net.minecraft.client.renderer.item.ItemModelResolver;import net.minecraft.client.renderer.state.level.CameraRenderState;import net.minecraft.client.renderer.texture.OverlayTexture;import net.minecraft.world.item.ItemDisplayContext;import net.minecraft.world.item.ItemStack;import net.minecraft.world.item.Items;import net.minecraft.world.level.Level;import net.minecraft.world.phys.Vec3;import org.jspecify.annotations.Nullable;public class CounterRenderer implements BlockEntityRenderer<CounterBlockEntity, CounterRenderState> { private final ItemModelResolver itemModelResolver; public CounterRenderer(BlockEntityRendererProvider.Context context) { // context 에서 아이템 모델 해석기를 획득 (다이아몬드 아이콘 렌더에 사용) this.itemModelResolver = context.itemModelResolver(); } // 프레임마다 재사용할 빈 RenderState 를 생성합니다. (필수) @Override public CounterRenderState createRenderState() { return new CounterRenderState(); } // 블록엔티티 상태를 RenderState 스냅샷으로 추출합니다. @Override public void extractRenderState( CounterBlockEntity be, CounterRenderState state, float partialTick, Vec3 cameraPosition, ModelFeatureRenderer.@Nullable CrumblingOverlay breakProgress) { BlockEntityRenderer.super.extractRenderState(be, state, partialTick, cameraPosition, breakProgress); Level level = be.getLevel(); // 시간 기반 Y축 회전 — partialTick 으로 보간해 끊김 없음 long time = level != null ? level.getGameTime() : 0L; state.spinDegrees = (time + partialTick) * 2.0f; // 카운트가 많을수록 살짝 크게 (최대 1.5×) state.scale = 0.5f + Math.min(be.getCount() * 0.05f, 1.0f); // 다이아몬드 아이콘을 미리 해석 (Vulkan 호환 추상화 API) // owner 인자는 @Nullable ItemOwner — BlockEntity 는 ItemOwner 가 아니므로 null 전달. ItemStack stack = new ItemStack(Items.DIAMOND); this.itemModelResolver.updateForTopItem( state.item, stack, ItemDisplayContext.GROUND, level, null, 0); } // 추출된 스냅샷만으로 렌더 노드를 제출합니다(블록엔티티 직접 접근 금지). @Override public void submit( CounterRenderState state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, CameraRenderState camera) { poseStack.pushPose(); // 블록 위 중앙 위로 이동 poseStack.translate(0.5, 1.5, 0.5); // 시간 기반 Y축 회전 poseStack.mulPose(Axis.YP.rotationDegrees(state.spinDegrees)); // 카운트 기반 스케일 poseStack.scale(state.scale, state.scale, state.scale); // 아이템 렌더 노드 제출 state.item.submit( poseStack, submitNodeCollector, state.lightCoords, OverlayTexture.NO_OVERLAY, 0); poseStack.popPose(); } @Override public boolean shouldRenderOffScreen() { return true; // 카메라 밖에서도 렌더 }}
CounterRenderState는 추출된 스냅샷을 담는 클래스입니다. BlockEntityRenderState를 상속하고, 회전각·스케일과 미리 해석한 아이템 상태를 보관합니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/client/renderer/state/CounterRenderState.javapackage com.example.examplemod.client.renderer.state;import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;import net.minecraft.client.renderer.item.ItemStackRenderState;public class CounterRenderState extends BlockEntityRenderState { /** 표시할 아이템(다이아몬드)의 사전 해석된 렌더 상태. */ public final ItemStackRenderState item = new ItemStackRenderState(); /** Y축 회전 각도(도). extractRenderState 에서 게임시간+부분틱으로 계산. */ public float spinDegrees; /** 카운트 기반 스케일(0.5~1.5). */ public float scale = 0.5f;}