3 Your First Instance
PepperCode1 edited this page 2022-01-04 13:19:49 -08:00

This wiki is a work in progress! Hopefully it can lead you in the right direction.

Start by creating an empty subclass of BlockEntityInstance, and build it up from there.

public class MyBlockInstance extends BlockEntityInstance<MyBlockEntity> {

    public MyBlockInstance(MaterialManager materialManager, MyBlockEntity blockEntity) {
        super(materialManager, blockEntity);
    }

    @Override
    public void remove() {
        // You'll be doing some cleanup here later.
    }
}

And you have to register the Instance with Flywheel, so might as well get that out of the way now.

public class MyInstances {
    public static void init() {
        InstancedRenderRegistry.configure(MyBlockEntityTypes.MY_BLOCK_ENTITY)
                .alwaysSkipRender() // Completely skip the BlockEntityRenderer.
                .factory(MyBlockInstance::new) // Use our BlockEntityInstance instead.
                .apply(); // Apply the instancing configuration.
    }
}

And be sure to call MyInstances.init() somewhere!

But, let's get back to the fun stuff. As it currently stands, our Instance does nothing but sit there and take up memory. So, you might ask, how can I get it to render something?

In Flywheel, BlockEntityInstances don't technically render models on their own. Instead, they ask the MaterialManager to create a parameterized instance of a model. The BlockEntityInstance is then free to change the parameters, and Flywheel takes care of the rest, ensuring that your copy of the model is rendered in the right place, with the right light, etc.

So let's have your Instance ask the MaterialManager for a copy of some bricks, because why not?

public class MyBlockInstance extends BlockEntityInstance<MyBlockEntity> {
    private final ModelData model;

    public MyBlockInstance(MaterialManager materialManager, MyBlockEntity blockEntity) {
        super(materialManager, blockEntity);

        model = materialManager.defaultSolid()
                .material(Materials.TRANSFORMED)
                .getModel(Blocks.BRICKS.defaultBlockState()) // Feel free to use a different blockstate!
                .createInstance();

        // translate the model to make it appear at your BlockEntity
        model.loadIdentity()
                .translate(getInstancePosition());
    }

    ...
}

And now that you've acquired resources, you have to make sure to clean up after yourself!

public class MyBlockInstance extends BlockEntityInstance<MyBlockEntity> {
    ...

    @Override
    public void remove() {
        model.delete();
    }
}

If you don't call InstanceData#delete(), all the models you ask for will be left behind when your block entity is broken.

If you were to launch your game and place down your block entity now, you'd see bricks, but they'd be very dark!

That's because you never tell Flywheel how to light the bricks!

In Flywheel, there's no need to query block and sky light every frame (like what's done for BlockEntityRenderers). Instead, light data can be stored in the ModelData, and updated as necessary. Flywheel already does most of the heavy lifting here, it's just on you to make sure you update your parameters when Flywheel detects a light update. You do this by overriding (drum roll...) #updateLight.

public class MyBlockInstance extends BlockEntityInstance<MyBlockEntity> {
    ...

    @Override
    public void updateLight() {
        relight(getWorldPosition(), model);
    }
}

relight(...) is a helper method that takes a BlockPos and a series of model parameters, and updates the parameters with the light data at the given position.

And so now your class looks like this:

public class MyBlockInstance extends BlockEntityInstance<MyBlockEntity> {

    private final ModelData model;

    public MyBlockInstance(MaterialManager materialManager, MyBlockEntity blockEntity) {
        super(materialManager, blockEntity);

        model = materialManager.defaultSolid()
                .material(Materials.TRANSFORMED)
                .getModel(Blocks.BRICKS.defaultBlockState())
                .createInstance();

        model.loadIdentity()
                .translate(getInstancePosition());
    }

    @Override
    public void remove() {
        model.delete();
    }

    @Override
    public void updateLight() {
        relight(getWorldPosition(), model);
    }
}