Skip to content

Block Entity Effects Example

This comprehensive tutorial shows how to create advanced block entity effects, including custom geometry, animated textures, particle systems, and distance-based optimization, following patterns from Minecraft’s BeaconRenderer.

Before starting this tutorial, you should understand:

  • Block entity rendering basics
  • Custom geometry creation
  • Animation systems
  • Particle effects

Let’s create a sophisticated magical pedestal block entity with multiple effects:

// Example: Advanced magical pedestal block entity
public class MagicalPedestalBlockEntity extends BlockEntity {
private float rotation = 0.0f;
private float floatOffset = 0.0f;
private int energyLevel = 0;
private int maxEnergy = 1000;
private final List<ItemStack> inventory = NonNullList.withSize(1, ItemStack.EMPTY);
// Animation state
private boolean isActive = false;
private long activationTime = 0;
private final ParticleManager particleManager = new ParticleManager();
public MagicalPedestalBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntities.MAGICAL_PEDESTAL.get(), pos, state);
}
@Override
public void tick() {
if (level == null || level.isClientSide) return;
// Update rotation animation
if (isActive) {
rotation += 2.0f;
if (rotation >= 360.0f) {
rotation -= 360.0f;
}
// Update floating animation
floatOffset = Mth.sin((System.currentTimeMillis() - activationTime) * 0.003f) * 0.1f + 0.2f;
// Consume energy
if (energyLevel > 0 && !inventory.get(0).isEmpty()) {
energyLevel = Math.max(0, energyLevel - 1);
// Process item on pedestal
processItem();
}
} else {
// Gradual slowdown
rotation *= 0.98f;
floatOffset *= 0.95f;
}
// Update particles
particleManager.tick();
// Sync with client periodically
if (level.getGameTime() % 20 == 0) {
setChanged();
}
}
private void processItem() {
ItemStack item = inventory.get(0);
if (!item.isEmpty()) {
// Apply magical transformation
if (item.getItem() instanceof ItemEnchanted) {
// Enchantment animation
createEnchantmentParticles();
// Random chance to upgrade
if (level.random.nextInt(1000) == 0) {
upgradeItem(item);
}
}
}
}
private void createEnchantmentParticles() {
Vec3 center = Vec3.atCenterOf(worldPosition);
for (int i = 0; i < 3; i++) {
double angle = (i * 120.0 + rotation) * Math.PI / 180.0;
double radius = 0.5 + Math.random() * 0.3;
double x = center.x + Math.cos(angle) * radius;
double y = center.y + 1.0 + floatOffset;
double z = center.z + Math.sin(angle) * radius;
level.addParticle(
ParticleTypes.ENCHANT,
x, y, z,
(Math.random() - 0.5) * 0.1,
0.1,
(Math.random() - 0.5) * 0.1
);
}
}
private void upgradeItem(ItemStack item) {
// Upgrade logic (simplified)
if (item.isEnchanted()) {
Map<Enchantment, Integer> enchantments = EnchantmentHelper.getEnchantments(item);
for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
if (level.random.nextInt(3) == 0) {
int newLevel = Math.min(entry.getKey().getMaxLevel(), entry.getValue() + 1);
enchantments.put(entry.getKey(), newLevel);
}
}
EnchantmentHelper.setEnchantments(enchantments, item);
}
}
// Getters for renderer
public float getRotation() {
return rotation;
}
public float getFloatOffset() {
return floatOffset;
}
public boolean isActive() {
return isActive;
}
public void setActive(boolean active) {
this.isActive = active;
if (active && !isActive) {
activationTime = System.currentTimeMillis();
}
}
public int getEnergyLevel() {
return energyLevel;
}
public void setEnergyLevel(int energy) {
this.energyLevel = Math.max(0, Math.min(maxEnergy, energy));
}
public ItemStack getDisplayedItem() {
return inventory.get(0);
}
public void setDisplayedItem(ItemStack item) {
inventory.set(0, item);
setChanged();
}
@Override
public void load(CompoundTag nbt) {
super.load(nbt);
rotation = nbt.getFloat("rotation");
floatOffset = nbt.getFloat("floatOffset");
isActive = nbt.getBoolean("isActive");
energyLevel = nbt.getInt("energyLevel");
ContainerHelper.loadAllItems(nbt, inventory);
}
@Override
public void saveAdditional(CompoundTag nbt) {
super.saveAdditional(nbt);
nbt.putFloat("rotation", rotation);
nbt.putFloat("floatOffset", floatOffset);
nbt.putBoolean("isActive", isActive);
nbt.putInt("energyLevel", energyLevel);
ContainerHelper.saveAllItems(nbt, inventory);
}
}

Now let’s create a sophisticated renderer following BeaconRenderer patterns:

// Example: Advanced magical pedestal renderer
public class MagicalPedestalRenderer implements BlockEntityRenderer<MagicalPedestalBlockEntity> {
private static final ResourceLocation PEDESTAL_TEXTURE =
new ResourceLocation("modid", "textures/block/magical_pedestal.png");
private static final ResourceLocation ENERGY_TEXTURE =
new ResourceLocation("modid", "textures/block/energy_field.png");
private static final ResourceLocation RUNE_TEXTURE =
new ResourceLocation("modid", "textures/block/magic_runes.png");
private static final RenderType PEDESTAL_TYPE =
RenderType.entityCutout(PEDESTAL_TEXTURE);
private static final RenderType ENERGY_TYPE =
RenderType.entityTranslucent(ENERGY_TEXTURE);
private static final RenderType RUNE_TYPE =
RenderType.entityTranslucent(RUNE_TEXTURE);
private final Model pedestalModel;
private final ItemRenderer itemRenderer;
public MagicalPedestalRenderer(BlockEntityRendererProvider.Context context) {
this.pedestalModel = context.bakeLayer(PedestalModel.LAYER_LOCATION);
this.itemRenderer = context.getItemRenderer();
}
@Override
public void render(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight, int packedOverlay) {
poseStack.pushPose();
// Translate to block center
poseStack.translate(0.5, 0.5, 0.5);
// Render pedestal base
renderPedestalBase(pedestal, partialTick, poseStack, bufferSource, packedLight);
// Render energy effects
if (pedestal.isActive()) {
renderEnergyEffects(pedestal, partialTick, poseStack, bufferSource, packedLight);
renderFloatingRune(pedestal, partialTick, poseStack, bufferSource, packedLight);
}
// Render floating item
ItemStack displayedItem = pedestal.getDisplayedItem();
if (!displayedItem.isEmpty()) {
renderFloatingItem(pedestal, displayedItem, partialTick, poseStack, bufferSource, packedLight);
}
// Render energy beams
if (pedestal.isActive() && pedestal.getEnergyLevel() > 500) {
renderEnergyBeams(pedestal, partialTick, poseStack, bufferSource, packedLight);
}
poseStack.popPose();
}
private void renderPedestalBase(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
VertexConsumer vertexConsumer = bufferSource.getBuffer(PEDESTAL_TYPE);
// Apply subtle rotation to base
poseStack.pushPose();
poseStack.mulPose(Axis.YP.rotationDegrees(pedestal.getRotation() * 0.2f));
// Render base model
pedestalModel.renderToBuffer(poseStack, vertexConsumer, packedLight, packedOverlay,
1.0f, 1.0f, 1.0f, 1.0f);
poseStack.popPose();
}
private void renderEnergyEffects(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
VertexConsumer energyConsumer = bufferSource.getBuffer(ENERGY_TYPE);
Matrix4f matrix = poseStack.last().pose();
float time = System.currentTimeMillis() * 0.001f;
float energyIntensity = (float) pedestal.getEnergyLevel() / pedestal.getMaxEnergy();
// Render rotating energy ring
renderEnergyRing(matrix, energyConsumer, time, energyIntensity);
// Render pulsing energy orb
renderEnergyOrb(matrix, energyConsumer, time, energyIntensity);
// Render energy particles
renderEnergyParticles(matrix, energyConsumer, time, energyIntensity);
}
private void renderEnergyRing(Matrix4f matrix, VertexConsumer energyConsumer,
float time, float intensity) {
int segments = 32;
float radius = 0.8f;
float height = pedestal.getFloatOffset() + 0.3f;
// Create ring vertices
for (int i = 0; i < segments; i++) {
float angle1 = (i * 2.0f * (float) Math.PI) / segments;
float angle2 = ((i + 1) * 2.0f * (float) Math.PI) / segments;
float x1 = Mth.cos(angle1) * radius;
float z1 = Mth.sin(angle1) * radius;
float x2 = Mth.cos(angle2) * radius;
float z2 = Mth.sin(angle2) * radius;
// Add wave effect
float wave1 = Mth.sin(angle1 * 3.0f + time * 2.0f) * 0.1f;
float wave2 = Mth.sin(angle2 * 3.0f + time * 2.0f) * 0.1f;
// Calculate colors with gradient
float r1 = 0.3f + 0.2f * Mth.sin(time + angle1);
float g1 = 0.5f + 0.3f * Mth.sin(time + angle1 + 2.094f);
float b1 = 0.8f + 0.2f * Mth.sin(time + angle1 + 4.189f);
float r2 = 0.3f + 0.2f * Mth.sin(time + angle2);
float g2 = 0.5f + 0.3f * Mth.sin(time + angle2 + 2.094f);
float b2 = 0.8f + 0.2f * Mth.sin(time + angle2 + 4.189f);
// Add quad vertices
addEnergyVertex(matrix, energyConsumer, x1, height + wave1, z1,
0.0f, 0.0f, r1, g1, b1, intensity);
addEnergyVertex(matrix, energyConsumer, x2, height + wave2, z2,
1.0f, 0.0f, r2, g2, b2, intensity);
addEnergyVertex(matrix, energyConsumer, x2, height + wave2 + 0.05f, z2,
1.0f, 1.0f, r2, g2, b2, intensity);
addEnergyVertex(matrix, energyConsumer, x1, height + wave1 + 0.05f, z1,
0.0f, 1.0f, r1, g1, b1, intensity);
}
}
private void renderEnergyOrb(Matrix4f matrix, VertexConsumer energyConsumer,
float time, float intensity) {
float height = pedestal.getFloatOffset() + 0.6f;
float baseRadius = 0.2f + Mth.sin(time * 3.0f) * 0.05f;
float pulse = Mth.sin(time * 2.0f) * 0.3f + 0.7f;
// Render glowing orb using octahedron
renderOctahedron(matrix, energyConsumer, 0.0f, height, 0.0f,
baseRadius, 1.0f, 0.8f, 1.0f, intensity * pulse);
}
private void renderEnergyParticles(Matrix4f matrix, VertexConsumer energyConsumer,
float time, float intensity) {
int particleCount = (int) (8 * intensity);
for (int i = 0; i < particleCount; i++) {
float angle = (i * 360.0f / particleCount + time * 50.0f) * (float) Math.PI / 180.0f;
float radius = 0.6f + Mth.sin(time * 2.0f + i) * 0.2f;
float height = pedestal.getFloatOffset() + 0.4f + Mth.sin(time * 3.0f + i * 0.5f) * 0.2f;
float x = Mth.cos(angle) * radius;
float z = Mth.sin(angle) * radius;
float size = 0.02f + Mth.sin(time * 4.0f + i) * 0.01f;
// Small glowing particle
renderSmallOrb(matrix, energyConsumer, x, height, z, size,
0.9f, 0.7f, 1.0f, intensity);
}
}
private void renderFloatingRune(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
VertexConsumer runeConsumer = bufferSource.getBuffer(RUNE_TYPE);
poseStack.pushPose();
// Position rune above pedestal
poseStack.translate(0.0, pedestal.getFloatOffset() + 0.8f, 0.0);
// Rotate rune independently
poseStack.mulPose(Axis.YP.rotationDegrees(-pedestal.getRotation()));
poseStack.mulPose(Axis.XP.rotationDegrees(30.0f));
// Scale based on energy level
float energyScale = 0.5f + (float) pedestal.getEnergyLevel() / pedestal.getMaxEnergy() * 0.3f;
poseStack.scale(energyScale, energyScale, energyScale);
// Render rune quad with custom vertex submission
submitRuneQuad(poseStack.last().pose(), runeConsumer, 0xF000F0);
poseStack.popPose();
}
private void submitRuneQuad(Matrix4f matrix, VertexConsumer consumer, int lightColor) {
float size = 0.3f;
// Add quad vertices for floating rune
addRuneVertex(matrix, consumer, -size, -size, 0.0f, 0.0f, 0.0f, lightColor);
addRuneVertex(matrix, consumer, size, -size, 0.0f, 1.0f, 0.0f, lightColor);
addRuneVertex(matrix, consumer, size, size, 0.0f, 1.0f, 1.0f, lightColor);
addRuneVertex(matrix, consumer, -size, size, 0.0f, 0.0f, 1.0f, lightColor);
}
private void renderFloatingItem(MagicalPedestalBlockEntity pedestal, ItemStack item,
float partialTick, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight) {
poseStack.pushPose();
// Position item above pedestal
poseStack.translate(0.0, pedestal.getFloatOffset() + 1.0f, 0.0);
// Rotate and scale item
poseStack.mulPose(Axis.YP.rotationDegrees(pedestal.getRotation()));
float scale = 0.5f + Mth.sin(System.currentTimeMillis() * 0.001f) * 0.05f;
poseStack.scale(scale, scale, scale);
// Add glow effect if active
if (pedestal.isActive()) {
poseStack.mulPose(Axis.XP.rotationDegrees(15.0f));
}
// Render item
this.itemRenderer.renderStatic(item, ItemDisplayContext.FIXED,
packedLight, OverlayTexture.NO_OVERLAY,
poseStack, bufferSource, pedestal.getLevel(), 0);
poseStack.popPose();
}
private void renderEnergyBeams(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
VertexConsumer beamConsumer = bufferSource.getBuffer(RenderType.lightning());
Matrix4f matrix = poseStack.last().pose();
float time = System.currentTimeMillis() * 0.001f;
float beamIntensity = (float) pedestal.getEnergyLevel() / pedestal.getMaxEnergy();
// Create energy beams from pedestal to nearby positions
for (int i = 0; i < 4; i++) {
float angle = (i * 90.0f + time * 20.0f) * (float) Math.PI / 180.0f;
float distance = 1.5f;
float endX = Mth.cos(angle) * distance;
float endY = 0.5f + Mth.sin(time * 2.0f + i) * 0.3f;
float endZ = Mth.sin(angle) * distance;
// Create lightning-like beam
submitLightningBeam(matrix, beamConsumer, 0.0f, pedestal.getFloatOffset() + 0.5f, 0.0f,
endX, endY, endZ, beamIntensity);
}
}
private void submitLightningBeam(Matrix4f matrix, VertexConsumer consumer,
float x1, float y1, float z1, float x2, float y2, float z2,
float intensity) {
int segments = 10;
float segmentLength = Mth.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1)) / segments;
for (int i = 0; i < segments; i++) {
float t1 = (float) i / segments;
float t2 = (float) (i + 1) / segments;
// Calculate positions with lightning jitter
float jitter1 = (Math.random() - 0.5) * 0.1f * (1.0f - t1);
float jitter2 = (Math.random() - 0.5) * 0.1f * (1.0f - t2);
float px1 = x1 + (x2-x1) * t1 + jitter1;
float py1 = y1 + (y2-y1) * t1 + jitter1;
float pz1 = z1 + (z2-z1) * t1 + jitter1;
float px2 = x1 + (x2-x1) * t2 + jitter2;
float py2 = y1 + (y2-y1) * t2 + jitter2;
float pz2 = z1 + (z2-z1) * t2 + jitter2;
// Add line segment with glow
float alpha = intensity * (1.0f - t1 * 0.5f);
addLightningVertex(matrix, consumer, px1, py1, pz1, 0.8f, 0.9f, 1.0f, alpha);
addLightningVertex(matrix, consumer, px2, py2, pz2, 0.8f, 0.9f, 1.0f, alpha);
}
}
// Helper methods for vertex submission
private void addEnergyVertex(Matrix4f matrix, VertexConsumer consumer,
float x, float y, float z, float u, float v,
float r, float g, float b, float intensity) {
consumer.addVertex(matrix, x, y, z)
.setColor(r * intensity, g * intensity, b * intensity, 0.7f * intensity)
.setUv(u, v)
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(0xF000F0) // Full brightness
.setNormal(0.0f, 1.0f, 0.0f);
}
private void addRuneVertex(Matrix4f matrix, VertexConsumer consumer,
float x, float y, float z, float u, float v, int lightColor) {
consumer.addVertex(matrix, x, y, z)
.setColor(0.8f, 0.7f, 1.0f, 0.8f)
.setUv(u, v)
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(lightColor)
.setNormal(0.0f, 1.0f, 0.0f);
}
private void addLightningVertex(Matrix4f matrix, VertexConsumer consumer,
float x, float y, float z, float r, float g, float b,
float alpha) {
consumer.addVertex(matrix, x, y, z)
.setColor(r, g, b, alpha)
.setUv(0.5f, 0.5f)
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(0xF000F0)
.setNormal(0.0f, 1.0f, 0.0f);
}
private void renderOctahedron(Matrix4f matrix, VertexConsumer consumer,
float x, float y, float z, float size,
float r, float g, float b, float alpha) {
// Octahedron vertices (8 faces)
float[][] vertices = {
{0, size, 0}, // Top
{ size, 0, 0}, // Right
{0, 0, size}, // Front
{-size, 0, 0}, // Left
{0, 0, -size}, // Back
{0, -size, 0} // Bottom
};
int[][] faces = {
{0, 1, 2}, {0, 2, 3}, {0, 3, 4}, {0, 4, 1}, // Top pyramid
{5, 2, 1}, {5, 3, 2}, {5, 4, 3}, {5, 1, 4} // Bottom pyramid
};
for (int[] face : faces) {
int i1 = face[0], i2 = face[1], i3 = face[2];
addEnergyVertex(matrix, consumer,
x + vertices[i1][0], y + vertices[i1][1], z + vertices[i1][2],
0.5f, 0.5f, r, g, b, alpha);
addEnergyVertex(matrix, consumer,
x + vertices[i2][0], y + vertices[i2][1], z + vertices[i2][2],
0.5f, 0.5f, r, g, b, alpha);
addEnergyVertex(matrix, consumer,
x + vertices[i3][0], y + vertices[i3][1], z + vertices[i3][2],
0.5f, 0.5f, r, g, b, alpha);
}
}
private void renderSmallOrb(Matrix4f matrix, VertexConsumer consumer,
float x, float y, float z, float size,
float r, float g, float b, float alpha) {
// Simple quad for small orb
addEnergyVertex(matrix, consumer, x - size, y - size, z, 0.0f, 0.0f, r, g, b, alpha);
addEnergyVertex(matrix, consumer, x + size, y - size, z, 1.0f, 0.0f, r, g, b, alpha);
addEnergyVertex(matrix, consumer, x + size, y + size, z, 1.0f, 1.0f, r, g, b, alpha);
addEnergyVertex(matrix, consumer, x - size, y + size, z, 0.0f, 1.0f, r, g, b, alpha);
}
}
// Example: Pedestal model
public class PedestalModel extends EntityModel<MagicalPedestalBlockEntity> {
public static final ModelLayerLocation LAYER_LOCATION =
new ModelLayerLocation(new ResourceLocation("modid", "magical_pedestal"), "main");
private final ModelPart base;
private final ModelPart pillar;
private final ModelPart top;
private final ModelPart[] decorativeRings = new ModelPart[3];
public PedestalModel(ModelPart root) {
this.base = root.getChild("base");
this.pillar = root.getChild("pillar");
this.top = root.getChild("top");
for (int i = 0; i < 3; i++) {
this.decorativeRings[i] = root.getChild("ring_" + i);
}
}
public static LayerDefinition createBodyLayer() {
MeshDefinition meshDefinition = new MeshDefinition();
PartDefinition partDefinition = meshDefinition.getRoot();
// Base platform
PartDefinition basePart = partDefinition.addOrReplaceChild("base",
CubeListBuilder.create()
.texOffs(0, 0)
.addBox(-6.0f, -2.0f, -6.0f, 12.0f, 2.0f, 12.0f),
PartPose.ZERO);
// Central pillar
PartDefinition pillarPart = partDefinition.addOrReplaceChild("pillar",
CubeListBuilder.create()
.texOffs(0, 4)
.addBox(-2.0f, -10.0f, -2.0f, 4.0f, 10.0f, 4.0f),
PartPose.ZERO);
// Top platform
PartDefinition topPart = partDefinition.addOrReplaceChild("top",
CubeListBuilder.create()
.texOffs(0, 18)
.addBox(-4.0f, -12.0f, -4.0f, 8.0f, 2.0f, 8.0f),
PartPose.ZERO);
// Decorative rings
for (int i = 0; i < 3; i++) {
float y = -4.0f - i * 2.0f;
partDefinition.addOrReplaceChild("ring_" + i,
CubeListBuilder.create()
.texOffs(16, 0)
.addBox(-3.0f, y, -3.0f, 6.0f, 0.5f, 6.0f),
PartPose.ZERO);
}
return LayerDefinition.create(meshDefinition, 32, 32);
}
@Override
public void setupAnim(MagicalPedestalBlockEntity pedestal, float limbSwing,
float limbSwingAmount, float ageInTicks, float netHeadYaw,
float headPitch) {
// Animate decorative rings when active
if (pedestal.isActive()) {
for (int i = 0; i < decorativeRings.length; i++) {
decorativeRings[i].yRot = ageInTicks * 0.1f * (i % 2 == 0 ? 1 : -1);
decorativeRings[i].y = Mth.sin(ageInTicks * 0.2f + i) * 0.1f - 4.0f - i * 2.0f;
}
} else {
// Reset rings to default positions
for (int i = 0; i < decorativeRings.length; i++) {
decorativeRings[i].yRot = 0.0f;
decorativeRings[i].y = -4.0f - i * 2.0f;
}
}
}
@Override
public void renderToBuffer(PoseStack poseStack, VertexConsumer consumer,
int packedLight, int packedOverlay, float red,
float green, float blue, float alpha) {
base.render(poseStack, consumer, packedLight, packedOverlay, red, green, blue, alpha);
pillar.render(poseStack, consumer, packedLight, packedOverlay, red, green, blue, alpha);
top.render(poseStack, consumer, packedLight, packedOverlay, red, green, blue, alpha);
for (ModelPart ring : decorativeRings) {
ring.render(poseStack, consumer, packedLight, packedOverlay, red, green, blue, alpha);
}
}
}
// Example: LOD and optimization for block entity rendering
public class OptimizedBlockEntityRenderer implements BlockEntityRenderer<MagicalPedestalBlockEntity> {
private static final float[] LOD_DISTANCES = {16.0f, 32.0f, 64.0f, 128.0f};
private static final int[] PARTICLE_COUNTS = {12, 8, 4, 2, 0};
private static final int[] BEAM_COUNTS = {4, 3, 2, 1, 0};
@Override
public void render(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight, int packedOverlay) {
// Calculate distance to player
double distance = calculateDistanceToPlayer(pedestal);
int lodLevel = getLODLevel(distance);
poseStack.pushPose();
poseStack.translate(0.5, 0.5, 0.5);
// Always render base
renderPedestalBase(pedestal, partialTick, poseStack, bufferSource, packedLight);
// Render effects based on LOD
if (lodLevel < 4) { // Skip effects at maximum distance
renderLODEffects(pedestal, partialTick, poseStack, bufferSource, packedLight, lodLevel);
}
poseStack.popPose();
}
private double calculateDistanceToPlayer(MagicalPedestalBlockEntity pedestal) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null || minecraft.level == null) return Double.MAX_VALUE;
Vec3 playerPos = minecraft.player.getEyePosition();
Vec3 blockPos = Vec3.atCenterOf(pedestal.getBlockPos());
return playerPos.distanceToSqr(blockPos);
}
private int getLODLevel(double distance) {
for (int i = 0; i < LOD_DISTANCES.length; i++) {
if (distance < LOD_DISTANCES[i] * LOD_DISTANCES[i]) {
return i;
}
}
return LOD_DISTANCES.length; // Maximum LOD level
}
private void renderLODEffects(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight, int lodLevel) {
if (!pedestal.isActive()) return;
switch (lodLevel) {
case 0: // Ultra quality - full effects
renderFullEffects(pedestal, partialTick, poseStack, bufferSource, packedLight);
break;
case 1: // High quality - reduced particles
renderHighQualityEffects(pedestal, partialTick, poseStack, bufferSource, packedLight);
break;
case 2: // Medium quality - basic effects
renderMediumQualityEffects(pedestal, partialTick, poseStack, bufferSource, packedLight);
break;
case 3: // Low quality - minimal effects
renderLowQualityEffects(pedestal, partialTick, poseStack, bufferSource, packedLight);
break;
}
}
private void renderFullEffects(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
renderEnergyEffects(pedestal, partialTick, poseStack, bufferSource, packedLight,
PARTICLE_COUNTS[0], BEAM_COUNTS[0]);
renderFloatingItem(pedestal, partialTick, poseStack, bufferSource, packedLight);
renderEnergyBeams(pedestal, partialTick, poseStack, bufferSource, packedLight,
BEAM_COUNTS[0]);
}
private void renderHighQualityEffects(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
renderEnergyEffects(pedestal, partialTick, poseStack, bufferSource, packedLight,
PARTICLE_COUNTS[1], BEAM_COUNTS[1]);
renderFloatingItem(pedestal, partialTick, poseStack, bufferSource, packedLight);
renderEnergyBeams(pedestal, partialTick, poseStack, bufferSource, packedLight,
BEAM_COUNTS[1]);
}
private void renderMediumQualityEffects(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
renderEnergyEffects(pedestal, partialTick, poseStack, bufferSource, packedLight,
PARTICLE_COUNTS[2], BEAM_COUNTS[2]);
renderFloatingItem(pedestal, partialTick, poseStack, bufferSource, packedLight);
// Skip energy beams at medium distance
}
private void renderLowQualityEffects(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
renderEnergyEffects(pedestal, partialTick, poseStack, bufferSource, packedLight,
PARTICLE_COUNTS[3], 0);
renderFloatingItem(pedestal, partialTick, poseStack, bufferSource, packedLight);
}
// Modified renderEnergyEffects with particle/beam count parameters
private void renderEnergyEffects(MagicalPedestalBlockEntity pedestal, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight, int particleCount, int beamCount) {
VertexConsumer energyConsumer = bufferSource.getBuffer(ENERGY_TYPE);
Matrix4f matrix = poseStack.last().pose();
float time = System.currentTimeMillis() * 0.001f;
float energyIntensity = (float) pedestal.getEnergyLevel() / pedestal.getMaxEnergy();
// Always render energy ring and orb
renderEnergyRing(matrix, energyConsumer, time, energyIntensity);
renderEnergyOrb(matrix, energyConsumer, time, energyIntensity);
// Render limited number of particles
renderLimitedEnergyParticles(matrix, energyConsumer, time, energyIntensity, particleCount);
// Render limited number of beams
if (beamCount > 0) {
renderLimitedEnergyBeams(matrix, energyConsumer, time, energyIntensity, beamCount);
}
}
private void renderLimitedEnergyParticles(Matrix4f matrix, VertexConsumer energyConsumer,
float time, float intensity, int maxParticles) {
for (int i = 0; i < maxParticles; i++) {
float angle = (i * 360.0f / maxParticles + time * 50.0f) * (float) Math.PI / 180.0f;
float radius = 0.6f + Mth.sin(time * 2.0f + i) * 0.2f;
float height = pedestal.getFloatOffset() + 0.4f + Mth.sin(time * 3.0f + i * 0.5f) * 0.2f;
float x = Mth.cos(angle) * radius;
float z = Mth.sin(angle) * radius;
float size = 0.02f + Mth.sin(time * 4.0f + i) * 0.01f;
renderSmallOrb(matrix, energyConsumer, x, height, z, size,
0.9f, 0.7f, 1.0f, intensity);
}
}
private void renderLimitedEnergyBeams(Matrix4f matrix, VertexConsumer beamConsumer,
float time, float intensity, int beamCount) {
for (int i = 0; i < beamCount; i++) {
float angle = (i * 360.0f / beamCount + time * 20.0f) * (float) Math.PI / 180.0f;
float distance = 1.5f;
float endX = Mth.cos(angle) * distance;
float endY = 0.5f + Mth.sin(time * 2.0f + i) * 0.3f;
float endZ = Mth.sin(angle) * distance;
submitLightningBeam(matrix, beamConsumer, 0.0f, pedestal.getFloatOffset() + 0.5f, 0.0f,
endX, endY, endZ, intensity);
}
}
}
// Example: Configuration for block entity effects
public class BlockEntityEffectConfig {
private final Map<String, EffectSettings> effectSettings = new HashMap<>();
public static class EffectSettings {
public boolean enabled = true;
public float intensity = 1.0f;
public int particleCount = 12;
public int beamCount = 4;
public float maxRenderDistance = 64.0f;
public boolean useLOD = true;
}
public BlockEntityEffectConfig() {
// Default settings
EffectSettings pedestalSettings = new EffectSettings();
pedestalSettings.intensity = 1.0f;
pedestalSettings.particleCount = 12;
pedestalSettings.beamCount = 4;
effectSettings.put("magical_pedestal", pedestalSettings);
// Load from config file
loadFromConfig();
}
private void loadFromConfig() {
// Load settings from configuration file
// This would use Minecraft's config system
}
public EffectSettings getSettings(String effectName) {
return effectSettings.getOrDefault(effectName, new EffectSettings());
}
public void updateSettings(String effectName, EffectSettings settings) {
effectSettings.put(effectName, settings);
saveToConfig();
}
private void saveToConfig() {
// Save settings to configuration file
}
}
  1. Too Many Particles: Use LOD to reduce particle count at distance
  2. Complex Geometry: Simplify meshes for distant rendering
  3. Expensive Shaders: Use simpler render types for far objects
  4. Memory Allocations: Avoid creating objects in render loops
  1. Z-fighting: Adjust depth values or use appropriate blend modes
  2. Lighting Issues: Ensure proper light values and render types
  3. Animation Stuttering: Use partial ticks for smooth animations
  4. Texture Problems: Verify UV coordinates and texture binding
  1. Missing Textures: Ensure all textures are in correct locations
  2. Model Problems: Check model layer registration
  3. Render Type Errors: Verify render type configurations
  4. Registration Issues: Ensure proper block entity registration
// Example: Debug utilities for block entity rendering
public class BlockEntityDebugger {
private boolean debugMode = false;
private boolean showBoundingBoxes = false;
private boolean showRenderInfo = false;
public void renderDebugInfo(MagicalPedestalBlockEntity pedestal,
PoseStack poseStack, MultiBufferSource bufferSource) {
if (!debugMode) return;
// Render bounding box
if (showBoundingBoxes) {
renderBoundingBox(pedestal, poseStack, bufferSource);
}
// Render debug information
if (showRenderInfo) {
renderDebugText(pedestal, poseStack, bufferSource);
}
// Render effect indicators
renderEffectIndicators(pedestal, poseStack, bufferSource);
}
private void renderBoundingBox(MagicalPedestalBlockEntity pedestal,
PoseStack poseStack, MultiBufferSource bufferSource) {
VertexConsumer consumer = bufferSource.getBuffer(RenderType.lines());
Matrix4f matrix = poseStack.last().pose();
AABB bb = pedestal.getRenderBoundingBox();
renderWireframeBox(matrix, consumer, bb);
}
private void renderDebugText(MagicalPedestalBlockEntity pedestal,
PoseStack poseStack, MultiBufferSource bufferSource) {
// This would require GUI text rendering capabilities
// Display energy level, LOD level, animation state, etc.
}
private void renderEffectIndicators(MagicalPedestalBlockEntity pedestal,
PoseStack poseStack, MultiBufferSource bufferSource) {
// Visual indicators for active effects
if (pedestal.isActive()) {
// Render indicator that pedestal is active
}
// Show energy level visually
float energyRatio = (float) pedestal.getEnergyLevel() / pedestal.getMaxEnergy();
renderEnergyBar(poseStack, bufferSource, energyRatio);
}
}
assets/modid/
├── blockentities/
│ └── magical_pedestal.json
├── models/
│ └── block/
│ └── magical_pedestal.json
├── textures/
│ ├── block/
│ │ ├── magical_pedestal.png
│ │ ├── energy_field.png
│ │ └── magic_runes.png
│ └── item/
└── sounds/
└── (optional sound effects)

This comprehensive example demonstrates advanced block entity rendering with multiple visual effects, performance optimization through LOD systems, and extensive debugging capabilities, providing a complete reference for creating sophisticated block entity effects in Minecraft 26.1.