-
-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'neoforged:main' into main
- Loading branch information
Showing
152 changed files
with
15,920 additions
and
856 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
{ | ||
"label": "Advanced Topics" | ||
"label": "Advanced Topics", | ||
"position": 12 | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"label": "Block Entities", | ||
"position": 6 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,123 @@ | ||
# BlockEntityRenderer | ||
|
||
A `BlockEntityRenderer` or `BER` is used to render blocks in a way that cannot be represented with a static baked model (JSON, OBJ, B3D, others). A block entity renderer requires the block to have a `BlockEntity`. | ||
A `BlockEntityRenderer`, often abbreviated as BER, is used to render [blocks][block] in a way that cannot be represented with a [static baked model][model] (JSON, OBJ, others). For example, this could be used to dynamically render container contents of a chest-like block. A block entity renderer requires the block to have a [`BlockEntity`][blockentity], even if the block does not store any data otherwise. | ||
|
||
## Creating a BER | ||
To create a BER, create a class that inherits from `BlockEntityRenderer`. It takes a generic argument specifying the block's `BlockEntity` class, which is used as a parameter type in the BER's `render` method. | ||
|
||
To create a BER, create a class that inherits from `BlockEntityRenderer`. It takes a generic argument specifying the block's `BlockEntity` class. The generic argument is used in the BER's `render` method. | ||
```java | ||
// Assumes the existence of MyBlockEntity as a subclass of BlockEntity. | ||
public class MyBlockEntityRenderer implements BlockEntityRenderer<MyBlockEntity> { | ||
// Add the constructor parameter for the lambda below. You may also use it to get some context | ||
// to be stored in local fields, such as the entity renderer dispatcher, if needed. | ||
public MyBlockEntityRenderer(BlockEntityRendererProvider.Context context) { | ||
} | ||
|
||
// This method is called every frame in order to render the block entity. Parameters are: | ||
// - blockEntity: The block entity instance being rendered. Uses the generic type passed to the super interface. | ||
// - partialTick: The amount of time, in fractions of a tick (0.0 to 1.0), that has passed since the last tick. | ||
// - poseStack: The pose stack to render to. | ||
// - bufferSource: The buffer source to get vertex buffers from. | ||
// - packedLight: The light value of the block entity. | ||
// - packedOverlay: The current overlay value of the block entity, usually OverlayTexture.NO_OVERLAY. | ||
@Override | ||
public void render(MyBlockEntity blockEntity, float partialTick, PoseStack stack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { | ||
// Do the rendering here. | ||
} | ||
} | ||
``` | ||
|
||
Only one BER exists for a given `BlockEntityType`. Therefore, values that are specific to a single instance in the level should be stored in the block entity being passed to the renderer rather than in the BER itself. For example, an integer that increments every frame, if stored in the BER, will increment every frame for every block entity of this type in the level. | ||
Only one BER may exist for a given `BlockEntityType<?>`. Therefore, values that are specific to a single block entity instance should be stored in that block entity instance, rather than the BER itself. | ||
|
||
### `render` | ||
When you have created your BER, you must also register it to `EntityRenderersEvent.RegisterRenderers`, an [event] fired on the [mod event bus][eventbus]: | ||
|
||
This method is called every frame in order to render the block entity. | ||
```java | ||
@SubscribeEvent | ||
public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers event) { | ||
event.registerBlockEntityRenderer( | ||
// The block entity type to register the renderer for. | ||
MyBlockEntities.MY_BLOCK_ENTITY.get(), | ||
// A function of BlockEntityRendererProvider.Context to BlockEntityRenderer. | ||
MyBlockEntityRenderer::new | ||
); | ||
} | ||
``` | ||
|
||
#### Parameters | ||
- `blockEntity`: This is the instance of the block entity being rendered. | ||
- `partialTick`: The amount of time, in fractions of a tick, that has passed since the last full tick. | ||
- `poseStack`: A stack holding four-dimensional matrix entries offset to the current position of the block entity. | ||
- `bufferSource`: A rendering buffer able to access a vertex consumer. | ||
- `combinedLight`: An integer of the current light value on the block entity. | ||
- `combinedOverlay`: An integer set to the current overlay of the block entity, usually `OverlayTexture#NO_OVERLAY` or 655,360. | ||
In the event that you do not need the BER provider context in your BER, you can also remove the constructor: | ||
|
||
## Registering a BER | ||
```java | ||
public class MyBlockEntityRenderer implements BlockEntityRenderer<MyBlockEntity> { | ||
@Override | ||
public void render( /* ... */ ) { /* ... */ } | ||
} | ||
|
||
In order to register a BER, you must subscribe to the `EntityRenderersEvent.RegisterRenderers` [event on the mod event bus][event] and call `#registerBlockEntityRenderer`. | ||
// In your event handler class | ||
@SubscribeEvent | ||
public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers event) { | ||
event.registerBlockEntityRenderer(MyBlockEntities.MY_BLOCK_ENTITY.get(), | ||
// Pass the context to an empty (default) constructor call | ||
context -> new MyBlockEntityRenderer() | ||
); | ||
} | ||
``` | ||
|
||
```java | ||
public class MyBlockEntityRenderer implements BlockEntityRenderer { | ||
## `BlockEntityWithoutLevelRenderer` | ||
|
||
public MyBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { | ||
// Do things here | ||
`BlockEntityWithoutLevelRenderer`, colloquially known as BEWLR, is an adaptation of the regular `BlockEntityRenderer` for special [item] rendering (hence "without level", as items do not have level context). Its overall purpose is the same: do special rendering for cases where static models aren't enough. | ||
To add a BEWLR, create a class that extends `BlockEntityWithoutLevelRenderer` and overrides `#renderByItem`. It also requires some additional constructor setup: | ||
```java | ||
public class MyBlockEntityWithoutLevelRenderer extends BlockEntityWithoutLevelRenderer { | ||
// We need some boilerplate in the constructor, telling the superclass where to find the central block entity and entity renderers. | ||
public MyBlockEntityWithoutLevelRenderer() { | ||
super(Minecraft.getInstance().getBlockEntityRenderDispatcher(), Minecraft.getInstance().getEntityModels()); | ||
} | ||
@Override | ||
public void renderByItem(ItemStack stack, ItemDisplayContext transform, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { | ||
// Do the rendering here. | ||
} | ||
} | ||
``` | ||
Keep in mind that, like with BERs, there is only one instance of your BEWLR. Stack-specific properties should therefore be stored in the stack, not the BEWLR. | ||
Unlike BERs, we do not register BEWLRs directly. Instead, we register an instance of `IClientItemExtensions` to the `RegisterClientExtensionsEvent`. `IClientItemExtensions` is an interface that allows us to specify a number of rendering-related behaviors on items, such as (but not limited to) a BEWLR. As such, our implementation of that interface could look like so: | ||
// Implement #render method here and any other logic | ||
```java | ||
public class MyClientItemExtensions implements IClientItemExtensions { | ||
// Cache our BEWLR in a field. | ||
private final MyBlockEntityWithoutLevelRenderer myBEWLR = new MyBlockEntityWithoutLevelRenderer(); | ||
// Return our BEWLR here. | ||
@Override | ||
public BlockEntityWithoutLevelRenderer getCustomRenderer() { | ||
return myBEWLR; | ||
} | ||
} | ||
``` | ||
// In another class using some method to listen to this event | ||
And then, we can register our `IClientItemExtensions` to the event: | ||
```java | ||
@SubscribeEvent | ||
public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) { | ||
event.registerBlockEntityRenderer(MyBlockEntityTypes.MYBE.get(), MyBlockEntityRenderer::new); | ||
public static void registerClientExtensions(RegisterClientExtensionsEvent event) { | ||
event.registerItem( | ||
// The only instance of our IClientItemExtensions, and as such, the only instance of our BEWLR. | ||
new MyClientItemExtensions(), | ||
// A vararg list of items that use this BEWLR. | ||
MyItems.ITEM_1, MyItems.ITEM_2 | ||
); | ||
} | ||
``` | ||
:::info | ||
`IClientItemExtensions` are generally expected to be treated as singletons. Do not construct them outside `RegisterClientExtensionsEvent`! | ||
::: | ||
[block]: ../blocks/index.md | ||
[blockentity]: index.md | ||
[event]: ../concepts/events.md#registering-an-event-handler | ||
[eventbus]: ../concepts/events.md#event-buses | ||
[item]: ../items/index.md | ||
[model]: ../resources/client/models/index.md |
Oops, something went wrong.