Skip to content

Geckolib Armor (Geckolib4)

Tslat edited this page Sep 29, 2024 · 6 revisions

Pre-word

When making your armor model in Blockbench, ensure you have selected the Armor model type in the Geckolib Model Settings panel in the File menu. This is critical to ensure that Blockbench generates the correct project template and output files for you when finished.

Steps

Creating a GeckoLib armor requires the following steps:

  1. Creating your Blockbench Model
  2. Creating your Geo Model
  3. Creating your item display json
  4. Creating your Item class
  5. Creating and providing your renderer

Steps #1 and #2 will not be covered on this page, instead visit their respective links for info. This page will focus on steps #3, #4, and #5

Full Video Guides

The Item Class

GeckoLib4->4.4
Forge

Armor item classes are created the exact same way normal animatable item classes are. Because of this, you'll want to look at the Geckolib Items page for instructions on creating the class.

The only difference to normal items is that we normally extend ArmorItem instead. This handles the equip functionality of armor for you. If for some reason you're unable to extend ArmorItem, you will need to instead implement the Equipable interface and fill out its methods.

Example Armor Item Class

public final class ExampleArmorItem extends ArmorItem implements GeoItem {
    private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);

    public ExampleArmorItem(ArmorMaterial armorMaterial, Type type, Properties properties) {
        super(armorMaterial, type, properties);
    }

    // Create our armor model/renderer for forge and return it
    @Override
    public void initializeClient(Consumer<IClientItemExtensions> consumer) {
        consumer.accept(new IClientItemExtensions() {
            private GeoArmorRenderer<?> renderer;

            @Override
            public @NotNull HumanoidModel<?> getHumanoidArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, HumanoidModel<?> original) {
                if (this.renderer == null)
                    this.renderer = new ExampleArmorRenderer();

                // This prepares our GeoArmorRenderer for the current render frame.
                // These parameters may be null however, so we don't do anything further with them
                this.renderer.prepForRender(livingEntity, itemStack, equipmentSlot, original);

                return this.renderer;
            }
        });
    }

    // Let's add our animation controller
    @Override
    public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
        controllers.add(new AnimationController<>(this, 20, state -> {
            // Apply our generic idle animation.
            // Whether it plays or not is decided down below.
            state.setAnimation(DefaultAnimations.IDLE);

            // Let's gather some data from the state to use below
            // This is the entity that is currently wearing/holding the item
            Entity entity = state.getData(DataTickets.ENTITY);

            // We'll just have ArmorStands always animate, so we can return here
            if (entity instanceof ArmorStand)
                return PlayState.CONTINUE;

            // For this example, we only want the animation to play if the entity is wearing all pieces of the armor
            // Let's collect the armor pieces the entity is currently wearing
            Set<Item> wornArmor = new ObjectOpenHashSet<>();

            for (ItemStack stack : entity.getArmorSlots()) {
                // We can stop immediately if any of the slots are empty
                if (stack.isEmpty())
                    return PlayState.STOP;

                wornArmor.add(stack.getItem());
            }

            // Check each of the pieces match our set
            boolean isFullSet = wornArmor.containsAll(ObjectArrayList.of(
                    ItemRegistry.EXAMPLE_ARMOR_BOOTS.get(),
                    ItemRegistry.EXAMPLE_ARMOR_LEGGINGS.get(),
                    ItemRegistry.EXAMPLE_ARMOR_CHESTPLATE.get(),
                    ItemRegistry.EXAMPLE_ARMOR_HELMET.get()));

            // Play the animation if the full set is being worn, otherwise stop
            return isFullSet ? PlayState.CONTINUE : PlayState.STOP;
        }));
    }

    @Override
    public AnimatableInstanceCache getAnimatableInstanceCache() {
        return this.cache;
    }
}
Fabric

Armor item classes are created the exact same way normal animatable item classes are. Because of this, you'll want to look at the Geckolib Items page for instructions on creating the class.

The only difference to normal items is that we normally extend ArmorItem instead. This handles the equip functionality of armor for you. If for some reason you're unable to extend ArmorItem, you will need to instead implement the Equipable interface and fill out its methods.

Example Armor Item Class

public final class ExampleArmorItem extends ArmorItem implements GeoItem {
    private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
    private final Supplier<Object> renderProvider = GeoItem.makeRenderer(this);

    public ExampleArmorItem(ArmorMaterial armorMaterial, ArmorItem.Type type, Properties properties) {
        super(armorMaterial, type, properties);
    }

    // Create our armor model/renderer for Fabric and return it
    @Override
    public void createRenderer(Consumer<Object> consumer) {
        consumer.accept(new RenderProvider() {
            private GeoArmorRenderer<?> renderer;

            @Override
            public HumanoidModel<LivingEntity> getHumanoidArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, HumanoidModel<LivingEntity> original) {
                if(this.renderer == null)
                    this.renderer = new ExampleArmorRenderer();

                // This prepares our GeoArmorRenderer for the current render frame.
                // These parameters may be null however, so we don't do anything further with them
                this.renderer.prepForRender(livingEntity, itemStack, equipmentSlot, original);

                return this.renderer;
            }
        });
    }

    @Override
    public Supplier<Object> getRenderProvider() {
        return this.renderProvider;
    }

    // Let's add our animation controller
    @Override
    public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
        controllers.add(new AnimationController<>(this, 20, state -> {
            // Apply our generic idle animation.
            // Whether it plays or not is decided down below.
            state.getController().setAnimation(DefaultAnimations.IDLE);

            // Let's gather some data from the state to use below
            // This is the entity that is currently wearing/holding the item
            Entity entity = state.getData(DataTickets.ENTITY);

	    // We'll just have ArmorStands always animate, so we can return here
            if (entity instanceof ArmorStand)
                return PlayState.CONTINUE;

            // For this example, we only want the animation to play if the entity is wearing all pieces of the armor
            // Let's collect the armor pieces the entity is currently wearing
            Set<Item> wornArmor = new ObjectOpenHashSet<>();

            for (ItemStack stack : entity.getArmorSlots()) {
                // We can stop immediately if any of the slots are empty
                if (stack.isEmpty())
                    return PlayState.STOP;

                wornArmor.add(stack.getItem());
            }

            // Check each of the pieces match our set
            boolean isFullSet = wornArmor.containsAll(ObjectArrayList.of(
                    ItemRegistry.EXAMPLE_ARMOR_BOOTS,
                    ItemRegistry.EXAMPLE_ARMOR_LEGGINGS,
                    ItemRegistry.EXAMPLE_ARMOR_CHESTPLATE,
                    ItemRegistry.EXAMPLE_ARMOR_HELMET));

            // Play the animation if the full set is being worn, otherwise stop
            return isFullSet ? PlayState.CONTINUE : PlayState.STOP;
        }));
    }

    @Override
    public AnimatableInstanceCache getAnimatableInstanceCache() {
        return this.cache;
    }
}
GeckoLib4.5+

Armor item classes are created the exact same way normal animatable item classes are. Because of this, you'll want to look at the Geckolib Items page for instructions on creating the class.

The only difference to normal items is that we normally extend ArmorItem instead. This handles the equip functionality of armor for you.

Example Armor Item Class

public final class ExampleArmorItem extends ArmorItem implements GeoItem {
    private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);

    public ExampleArmorItem(Holder<ArmorMaterial> armorMaterial, ArmorItem.Type type, Properties properties) {
        super(armorMaterial, type, properties);
    }

    // Create our armor model/renderer for Fabric and return it
    @Override
    public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
        consumer.accept(new GeoRenderProvider() {
            private GeoArmorRenderer<?> renderer;

            @Override
            public <T extends LivingEntity> HumanoidModel<?> getGeoArmorRenderer(@Nullable T livingEntity, ItemStack itemStack, @Nullable EquipmentSlot equipmentSlot, @Nullable HumanoidModel<T> original) {
                if(this.renderer == null)
                    this.renderer = new ExampleArmorRenderer();

                return this.renderer;
            }
        });
    }

    // Let's add our animation controller
    @Override
    public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
        controllers.add(new AnimationController<>(this, 20, state -> {
            // Apply our generic idle animation.
            // Whether it plays or not is decided down below.
            state.getController().setAnimation(DefaultAnimations.IDLE);

            // Let's gather some data from the state to use below
            // This is the entity that is currently wearing/holding the item
            Entity entity = state.getData(DataTickets.ENTITY);

	    // We'll just have ArmorStands always animate, so we can return here
            if (entity instanceof ArmorStand)
                return PlayState.CONTINUE;

            // For this example, we only want the animation to play if the entity is wearing all pieces of the armor
            // Let's collect the armor pieces the entity is currently wearing
            Set<Item> wornArmor = new ObjectOpenHashSet<>();

            for (ItemStack stack : entity.getArmorSlots()) {
                // We can stop immediately if any of the slots are empty
                if (stack.isEmpty())
                    return PlayState.STOP;

                wornArmor.add(stack.getItem());
            }

            // Check each of the pieces match our set
            boolean isFullSet = wornArmor.containsAll(ObjectArrayList.of(
                    ItemRegistry.EXAMPLE_ARMOR_BOOTS,
                    ItemRegistry.EXAMPLE_ARMOR_LEGGINGS,
                    ItemRegistry.EXAMPLE_ARMOR_CHESTPLATE,
                    ItemRegistry.EXAMPLE_ARMOR_HELMET));

            // Play the animation if the full set is being worn, otherwise stop
            return isFullSet ? PlayState.CONTINUE : PlayState.STOP;
        }));
    }

    @Override
    public AnimatableInstanceCache getAnimatableInstanceCache() {
        return this.cache;
    }
}

The Renderer

Creating the Class

By default, Geckolib armors operate on a humanoid model basis. This means that the expected format for an armour roughly resembles a humanoid in that it has two arms, two legs, two feet, a torso, and a head. The default template for GeckoLib armour in Blockbench assumes this and automatically names all of your bones and positions them correctly. If you need to make adjustments, you will need to override the relevant method(s) in the renderer to do so

To create the renderer, we need to create a class that extends GeoArmorRenderer, and provide it with a relevant GeoModel instance.

We do this by assigning the name of the bone (found in our geo.json, or as the name of the bone group in Blockbench) to each respective part of the renderer. See below for example usage of this, with generic placeholder bone names.

Example Renderer Class

public final class ExampleArmorRenderer extends GeoArmorRenderer<ExampleArmorItem> {
    public ExampleArmorRenderer() {
        super(new DefaultedItemGeoModel<>(new ResourceLocation(ExampleMod.MOD_ID, "armor/example_armor"))); // Using DefaultedItemGeoModel like this puts our 'location' as item/armor/example armor in the assets folders.
    }
}

Registering the Renderer

GeckoLib4->4.4
Forge

To register your GeckoLib armor renderer, we utilise the existing Forge render provider system for our class. This render provider allows for dynamic handling of the renderer and its associated details, providing a very powerful access point for custom rendering.

We do this by overriding initializeClient in our item class and instantiate our renderer, and then return it in getHumanoidArmorModel. You can copy the below example code to make it easier, if you want.

Example Renderer Registration

@Override
public void initializeClient(Consumer<IClientItemExtensions> consumer) {
    consumer.accept(new IClientItemExtensions() {
        private GeoArmorRenderer<?> renderer;

        @Override
        public @NotNull HumanoidModel<?> getHumanoidArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, HumanoidModel<?> original) {
            if (this.renderer == null)
                this.renderer = new ExampleArmorRenderer();

            // This prepares our GeoArmorRenderer for the current render frame.
            // These parameters may be null however, so we don't do anything further with them
            this.renderer.prepForRender(livingEntity, itemStack, equipmentSlot, original);

            return this.renderer;
        }
    });
}
Fabric

To register your GeckoLib armor renderer, we need to set up our custom render provider for our class. This render provider allows for dynamic handling of the renderer and its associated details, providing a very powerful access point for custom rendering.

To do this, we first cache a renderer for our class, storing the variable in the item class and return it in the getter GeoItem provides:

private final Supplier<Object> renderProvider = GeoItem.makeRenderer(this);

@Override
public Supplier<Object> getRenderProvider() {
    return this.renderProvider;
}

Then, we override createRenderer in our item class and instantiate our renderer, and then return it in getHumanoidArmorModel. You can copy the below example code to make it easier, if you want.

    @Override
    public void createRenderer(Consumer<Object> consumer) {
        consumer.accept(new RenderProvider() {
            private GeoArmorRenderer<?> renderer;

            @Override
            public HumanoidModel<LivingEntity> getHumanoidArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, HumanoidModel<LivingEntity> original) {
                if(this.renderer == null) // Important that we do this. If we just instantiate  it directly in the field it can cause incompatibilities with some mods.
                    this.renderer = new ExampleArmorRenderer();

                // This prepares our GeoArmorRenderer for the current render frame.
                // These parameters may be null however, so we don't do anything further with them
                this.renderer.prepForRender(livingEntity, itemStack, equipmentSlot, original);

                return this.renderer;
            }
        });
    }

Example Renderer Registration

    private final Supplier<Object> renderProvider = GeoItem.makeRenderer(this);

    @Override
    public void createRenderer(Consumer<Object> consumer) {
        consumer.accept(new RenderProvider() {
            private GeoArmorRenderer<?> renderer;

            @Override
            public HumanoidModel<LivingEntity> getHumanoidArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, HumanoidModel<LivingEntity> original) {
                if(this.renderer == null) // Important that we do this. If we just instantiate  it directly in the field it can cause incompatibilities with some mods.
                    this.renderer = new ExampleArmorRenderer();

                // This prepares our GeoArmorRenderer for the current render frame.
                // These parameters may be null however, so we don't do anything further with them
                this.renderer.prepForRender(livingEntity, itemStack, equipmentSlot, original);

                return this.renderer;
            }
        });
    }

    @Override
    public Supplier<Object> getRenderProvider() {
        return this.renderProvider;
    }
GeckoLib4.5+

To register your GeckoLib armor renderer, we need to set up our custom geo render provider for our class. This render provider allows for dynamic handling of the renderer and its associated details, providing a very powerful access point for custom rendering.

To do this, override createGeoRenderer in your item class, providing the consumer a new instance of GeoRenderProvider. Then inside that, override getGeoArmorRenderer and cache-return your armor renderer instance.

    @Override
    public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
        consumer.accept(new GeoRenderProvider() {
            private GeoArmorRenderer<?> renderer;

            @Override
            public <T extends LivingEntity> HumanoidModel<?> getGeoArmorRenderer(@Nullable T livingEntity, ItemStack itemStack, @Nullable EquipmentSlot equipmentSlot, @Nullable HumanoidModel<T> original) {
                if(this.renderer == null) // Important that we do this. If we just instantiate  it directly in the field it can cause incompatibilities with some mods.
                    this.renderer = new ExampleArmorRenderer();

                return this.renderer;
            }
        });
    }

Table of Contents

Geckolib 3
Geckolib 4

Hosted By: Cloudsmith

Package repository hosting is graciously provided by Cloudsmith.

Cloudsmith is the only fully hosted, cloud-native, universal package management solution that enables your organization to create, store and share packages in any format, to any place, with total confidence.

Clone this wiki locally