Jump to content

Dynamically Generated Item Textures


Recommended Posts

Hi all,

I have an item where the color of every pixel is determined by what is used to craft it (I use a custom crafting table with a 16x16 grid). The item has an array of hexadecimal values, 256 values long, for a 16x16 texture. These values will be determined at the time of crafting, and will stay the same for the remainder of the item's lifetime. I've looked at the MapItemRenderer class, and DynamicTexture seems like the way to go about this, but I can't seem to figure out how the puzzle pieces fit together. Do I have to write my own ItemRenderer for this, or is there a way to apply the texture to the item after the mod's initialization? If I need to write a renderer, how do I tell forge to bind it to this item and provide it with the data it needs to generate the DynamicTexture? Any help would be appreciated. If you need code samples let me know, but I'm not sure that they'd help any more than the info I've given.

Edited by chaseoqueso
Link to comment
Share on other sites

Ok so progress report:

I've started going in the direction of a ICustomModelLoader based on ModelDynBucket, but I'm still struggling to put the pieces together. It's clear that this model loader is registered properly, since I'm no longer getting errors about missing blockstate or item model .json files. I can also confirm that it accepts exactly 1 resource location (which I assume with relative certainty to be the item in question), and that it gets loadModel called, constructs a new model, and that the model then gets bake called, which constructs a new baked model

All that is well and good, but my custom ItemOverrideList doesn't seem to be getting utilized; neither getOverrides nor handleItemState get called. I don't see any place to register it, and I don't see anything different between my class and the ModelDynBucket class. If anyone has any insight on how to fix this, it would be greatly appreciated.

(Sidenote: I acknowledge that my current code for creating and storing the DynamicTexture in the process method is garbage, but one step at a time y'know)

package com.chaseoqueso.bitcrafting.rendering;

import com.chaseoqueso.bitcrafting.BitCraftingMod;
import com.chaseoqueso.bitcrafting.items.ItemBitSword;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.*;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.model.*;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;

import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Function;

public class ModelBitSword implements IModel
    public static final ModelResourceLocation LOCATION = new ModelResourceLocation(new ResourceLocation(BitCraftingMod.MODID, "itembitsword"), "inventory");

    private DynamicTexture tempTexture;
    private ResourceLocation textureLocation;

    public ModelBitSword()

    public ModelBitSword(@Nullable ResourceLocation textureLocation)
        this.textureLocation = textureLocation;

    public Collection<ResourceLocation> getTextures()
        ImmutableSet.Builder<ResourceLocation> builder = ImmutableSet.builder();

        if (textureLocation != null)

        return builder.build();

    public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
        ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transformMap = PerspectiveMapWrapper.getTransforms(state);

        TRSRTransformation transform = state.apply(Optional.empty()).orElse(TRSRTransformation.identity());
        TextureAtlasSprite fluidSprite = null;
        TextureAtlasSprite particleSprite = null;
        ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
        if (textureLocation != null)
            IBakedModel model = (new ItemLayerModel(ImmutableList.of(textureLocation))).bake(state, format, bakedTextureGetter);
            builder.addAll(model.getQuads(null, null, 0));
            particleSprite = model.getParticleTexture();

        return new ModelBitSword.BakedBitSword(this, builder.build(), particleSprite, format, Maps.immutableEnumMap(transformMap), Maps.newHashMap(), transform.isIdentity());

    public ModelBitSword process(ImmutableMap<String, String> customData)
        String colorArrayString = customData.get("colorArrayString");
        String[] items = colorArrayString.split(",");

        tempTexture = new DynamicTexture(16, 16);
        int[] textureArray = tempTexture.getTextureData();

        for (int i = 0; i < items.length; i++) {
            try {
                textureArray[i] = Integer.parseInt(items[i]);
            } catch (NumberFormatException nfe) {
                System.out.println("ERROR: Something went wrong with converting the sword's color data from string to int array");


        textureLocation = Minecraft.getMinecraft().getTextureManager().getDynamicTextureLocation("itembitsword/" + colorArrayString, tempTexture);

        return new ModelBitSword(textureLocation);
    public ModelBitSword retexture(ImmutableMap<String, String> textures)
        ResourceLocation base = textureLocation;

        if (textures.containsKey("base"))
            base = new ResourceLocation(textures.get("base"));

        return new ModelBitSword(base);

    public enum LoaderBitSword implements ICustomModelLoader

        public boolean accepts(ResourceLocation modelLocation)
            if(modelLocation.getResourceDomain().equals(BitCraftingMod.MODID) && modelLocation.getResourcePath().contains("itembitsword")) System.out.println("Bit Sword Accepts");
            return modelLocation.getResourceDomain().equals(BitCraftingMod.MODID) && modelLocation.getResourcePath().contains("itembitsword");

        public IModel loadModel(ResourceLocation modelLocation)
            return new ModelBitSword();

        public void onResourceManagerReload(IResourceManager resourceManager)
            //I think I need to clear the cache here but idk what that means or how to do it (probably something to do with BakedBitSword.cache but that's private so idk)

    private static final class BakedBitSwordOverrideHandler extends ItemOverrideList
        public static final ModelBitSword.BakedBitSwordOverrideHandler INSTANCE = new ModelBitSword.BakedBitSwordOverrideHandler();
        private BakedBitSwordOverrideHandler()

        public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)

            if (!(stack.getItem() instanceof ItemBitSword))
                return originalModel;

            ModelBitSword.BakedBitSword model = (ModelBitSword.BakedBitSword)originalModel;

            String arrayString = ItemBitSword.getColorArrayAsString(stack);

            if (!model.cache.containsKey(arrayString))
                ModelBitSword parent = model.parent.process(ImmutableMap.of("colorArrayString", arrayString));
                Function<ResourceLocation, TextureAtlasSprite> textureGetter;
                textureGetter = location -> Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(location.toString());

                IBakedModel bakedModel = parent.bake(new SimpleModelState(model.transforms), model.format, textureGetter);
                model.cache.put(arrayString, bakedModel);
                return bakedModel;

            return model.cache.get(arrayString);

    private static final class BakedBitSword extends BakedItemModel
        private final ModelBitSword parent;
        private final Map<String, IBakedModel> cache;
        private final VertexFormat format;
        private final ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms;

        BakedBitSword(ModelBitSword parent,
                       ImmutableList<BakedQuad> quads,
                       TextureAtlasSprite particle,
                       VertexFormat format,
                       ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms,
                       Map<String, IBakedModel> cache,
                       boolean untransformed)
            super(quads, particle, transforms, BakedBitSwordOverrideHandler.INSTANCE, untransformed);
            this.transforms = transforms;
            this.format = format;
            this.parent = parent;
            this.cache = cache;


Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...

Important Information

By using this site, you agree to our Terms of Use.