User Tools

Site Tools


tutorial:blockentityrenderers

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:blockentityrenderers [2019/12/14 10:42] – fix link juuztutorial:blockentityrenderers [2023/02/09 13:14] (current) – "you wants" -> "you want" mschae23
Line 1: Line 1:
 ====== Rendering blocks and items dynamically using block entity renderers ====== ====== Rendering blocks and items dynamically using block entity renderers ======
  
-//This is the 1.15 version of this tutorial. For the latest version, see [[tutorial:1.14:blockentityrenderers|Rendering blocks and items dynamically using block entity renderers (1.14)]].//+//This is the 1.15 & 1.16 version of this tutorial. For the 1.14 version, see [[tutorial:1.14:blockentityrenderers|Rendering blocks and items dynamically using block entity renderers (1.14)]].//
  
 Make sure you [[tutorial:blockentity|added a block entity]] before reading this tutorial!  Make sure you [[tutorial:blockentity|added a block entity]] before reading this tutorial! 
 ===== Introduction ===== ===== Introduction =====
-Blocks by themselves aren't that interesting,  +Blocks by themselves aren't that interesting, they just stay static at a certain location and a certain size until broken. We can use block entity renderers to render items and blocks associated with a block entity far more dynamically - render multiple different items, at differing locations and sizes, and more.  
-they just stay static at a certain location and a certain size until broken. +
-We can use block entity renderers to render items and blocks associated with a block entity far more dynamically - render multiple different items,  +
-at differing locations and sizes, and more.  +
 ===== Example ===== ===== Example =====
-In this tutorial we'll build off the block entity we created by adding a ''BlockEntityRenderer'' to it.  +In this tutorial we'll build off the block entity we created by adding a ''BlockEntityRenderer'' to it. The renderer will display a jukebox floating above the block, going up and down and spinning.  
-The renderer will display a jukebox floating above the block, going up and down and spinning.  +
      
 The first thing we need to do is create our ''BlockEntityRenderer'' class: The first thing we need to do is create our ''BlockEntityRenderer'' class:
 <code java> <code java>
-public class MyBlockEntityRenderer extends BlockEntityRenderer<DemoBlockEntity> {+@Environment(EnvType.CLIENT) 
 +public class DemoBlockEntityRenderer implements BlockEntityRenderer<DemoBlockEntity > {
     // A jukebox itemstack     // A jukebox itemstack
     private static ItemStack stack = new ItemStack(Items.JUKEBOX, 1);     private static ItemStack stack = new ItemStack(Items.JUKEBOX, 1);
          
-    public MyBlockEntityRenderer(BlockEntityRenderDispatcher dispatcher) { +    public DemoBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) {} 
-        super(dispatcher); +
-    +
-    +
     @Override     @Override
     public void render(DemoBlockEntity blockEntity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {     public void render(DemoBlockEntity blockEntity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
Line 28: Line 23:
 } }
 </code> </code>
-We're going to need to register our ''BlockEntityRenderer'', but only for the client.  +We're going to need to register our ''BlockEntityRenderer'', but only for the client. This wouldn't matter in a single-player setting, since the server runs in the same process as the client. However, in a multiplayer setting, where the server runs in a different process than the client, the server code has no concept of a "BlockEntityRenderer", and as a result would not accept registering one. To run initialization code only for the client, we need to setup a ''client'' entrypoint.  
-This wouldn't matter in a single-player setting, since the server runs in the same process as the client.  +
-However, in a multiplayer setting, where the server runs in a different process than the client, the server code +
-has no concept of a "BlockEntityRenderer", and as a result would not accept registering one.  +
-To run initialization code only for the client, we need to setup a ''client'' entrypoint.  +
  
-Create a new class next to your main class that implements ''ClientModInitializer'':+Create a new class next to your main class that implements ''ClientModInitializer'' (in this tutorial assume that ExampleModClient is in the same folder as the former ExampleMod is):
 <code java> <code java>
 +@Environment(EnvType.CLIENT)
 public class ExampleModClient implements ClientModInitializer { public class ExampleModClient implements ClientModInitializer {
     @Override     @Override
Line 50: Line 42:
     "client": [     "client": [
       {       {
-        "value": "tutorial.path.to.ExampleModClient"+        "value": "net.fabricmc.example.ExampleModClient"
       }       }
     ]     ]
Line 60: Line 52:
 @Override @Override
 public void onInitializeClient() { public void onInitializeClient() {
-    BlockEntityRendererRegistry.INSTANCE.register(DemoBlockEntity.classMyBlockEntityRenderer::new);+    BlockEntityRendererRegistry.register(DEMO_BLOCK_ENTITYDemoBlockEntityRenderer::new);
 } }
 </code> </code>
Line 70: Line 62:
     }     }
 </code> </code>
-We then perform the movement of the jukebox (matrices.translate) and rotation (matrices.multiply). +We then perform the movement of the jukebox (matrices.translate) and rotation (matrices.multiply). There are two parts to the translation: we translate it to 0.5, 1.25, and 0.5 which is above the center of our block. The second part is the part that changes: the offset in the y value. The offset is the height of the item for any given frame. We recalculate this each time because we want it to be animating bouncing up and down. We calculate this by:
-There are two parts to the translation: we translate it to 0.5, 1.25, and 0.5 which is above the center of our block.  +
-The second part is the part that changes: the offset in the y value. The offset is the height of the item for any given frame. +
-We recalculate this each time because we want it to be animating bouncing up and down. We calculate this by:+
   * Getting the current world time, which changes over time.   * Getting the current world time, which changes over time.
   * Adding the partial ticks. (The partial ticks is a fractional value representing the amount of time that’s passed between the last full tick and now. We use this because otherwise the animation would be jittery because there are fewer ticks per second than frames per second.)   * Adding the partial ticks. (The partial ticks is a fractional value representing the amount of time that’s passed between the last full tick and now. We use this because otherwise the animation would be jittery because there are fewer ticks per second than frames per second.)
Line 88: Line 77:
  
         // Rotate the item         // Rotate the item
-        matrices.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion((blockEntity.getWorld().getTime() + tickDelta) * 4));+        matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees((blockEntity.getWorld().getTime() + tickDelta) * 4));
     }     }
 </code> </code>
-Finally, we will get the Minecraft 'ItemRenderer' and render the jukebox item by using ''renderItem'' +Finally, we will get the Minecraft 'ItemRenderer' and render the jukebox item by using ''renderItem''. We also pass ''ModelTransformation.Type.GROUND'' to ''renderItem'' because we want a similiar effect to an item lying on the ground. Try experimenting with this value and see what happens (it's an enum). We also need to call ''matrices.pop();'' after these GL calls:
-We also pass ''ModelTransformation.Type.GROUND'' to ''renderItem'' because we want a similiar effect to  +
-an item lying on the ground. Try experimenting with this value and see what happens (it's an enum).  +
-We also need to call ''matrices.pop();'' after these GL calls:+
 <code java> <code java>
     public void render(DemoBlockEntity blockEntity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {     public void render(DemoBlockEntity blockEntity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
         [...]         [...]
-        MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformation.Type.GROUND, light, overlay, matrices, vertexConsumers);+        MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformation.Mode.GROUND, light, overlay, matrices, vertexConsumers, 0);
  
         // Mandatory call after GL calls         // Mandatory call after GL calls
Line 105: Line 91:
 </code> </code>
  
-You can try your newly created block entity renderer right now.  +You can try your newly created block entity renderer right now. However, if you didn't make your block transparent, you will notice something is amiss - the floating block, the jukebox, is pitch black! This is because by default, //whatever you render in the block entity, will receive light as if it's in the same position as the block entity//. So the floating block receives light from //inside// our opaque block, which means it receives no light! To fix this, we will tell Minecraft to receive light from //one block above// the location of the block entity.  
-However, if you didn't make your block transparent, you will notice something is amiss - the floating block, the jukebox, is pitch black!   +
-This is because by default, //whatever you render in the block entity, will receive light as if it's in the same position as the block entity//. +
-So the floating block receives light from //inside// our opaque block, which means it receives no light!  +
-To fix this, we will tell Minecraft to receive light from //one block above// the location of the block entity.  +
  
 To get the light, we call ''WorldRenderer#getLightmapCoordinates()'' on the position above our block entity,  To get the light, we call ''WorldRenderer#getLightmapCoordinates()'' on the position above our block entity, 
Line 119: Line 101:
                  
         int lightAbove = WorldRenderer.getLightmapCoordinates(blockEntity.getWorld(), blockEntity.getPos().up());         int lightAbove = WorldRenderer.getLightmapCoordinates(blockEntity.getWorld(), blockEntity.getPos().up());
-        MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformation.Type.GROUND, lightAbove, OverlayTexture.DEFAULT_UV, matrices, vertexConsumers);+        MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformation.Mode.GROUND, lightAbove, OverlayTexture.DEFAULT_UV, matrices, vertexConsumers, 0);
                  
         [...]         [...]
Line 126: Line 108:
  
 The jukebox should now have the proper lighting.  The jukebox should now have the proper lighting. 
 +
 +===== Rendering according to block entity data =====
 +Sometimes you want to render according to the block entity data (nbt), and you find they are all empty, even if you can access the data through ''/data get block'' command. That's because you did not sync data from server to client. See [[tutorial:blockentity#Sync data from server to client]].
tutorial/blockentityrenderers.1576320176.txt.gz · Last modified: 2019/12/14 10:42 by juuz