Jump to content

[SOLVED][1.10.2] Changing Item Texture at Runtime


Recommended Posts

Posted

Hello,

 

I'm working on a mod that dynamically creates armor from registered blocks and I'm running into a bit of a snag (again). Currently I am creating, registering, naming, etc. the armor pieces that I want for different blocks dynamically. I've been trying to have each armor piece's inventory icon change to a different texture (that I have) depending on the block that the armor is for, but I haven't made much progress. Normally, an item's inventory texture is assigned in its models/item json, but I don't have the textures until runtime (I think after texture stitching, to be exact), so I can't assign the textures that way.

 

Stuff I've tried:

 

 

  • I've looked into DynamicTextures by studying the vanilla map code, but I'm not sure how to properly use them or if they would work properly for this.
  • I tried experimenting with adding property overrides to each item to see if I could redirect the texture it gets there, but it looks like that only affects the models/item json instead of allowing me to just specify a texture.
  • I've also tried using the TextureStitchEvent.Post to change texture of the armor pieces, but haven't been successful. I haven't found much documentation or many examples of how to use the event properly, so it's possible that I'm doing something wrong.
  • I tried seeing if it's possible to change an item's TextureAtlasSprite from its IBakedModel, but haven't had any luck with that.

 

 

Of everything I've tried, using the TextureStitchEvent seems the most promising.

 

Does anyone have any idea how I can assign textures to my armor pieces at runtime?

 

Thanks in advance!

Posted

You'll probably need to generate textures and models at runtime like

ModelDynBucket

does.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

You'll probably need to generate textures and models at runtime like

ModelDynBucket

does.

 

After a long day of tracing code and trying to understand it, I finally managed to do it using a class similar to ModelDynBucket. Thank you!

 

For anyone else trying to do something similar, I can give you the highlights of how I did it (probably not the best way, but it works):

 

 

How I registered items that I wanted to change texture:

@SubscribeEvent
public void modelBake(ModelBakeEvent event)
{
	for (Item item : items) { //list of items to register
		ModelLoader.setCustomMeshDefinition(item, new ItemMeshDefinition()
		{
			@Override
			public ModelResourceLocation getModelLocation(ItemStack stack)
			{
				return ModelDynBlockArmor.LOCATION;
			}
		});
		ModelBakery.registerItemVariants(item, ModelDynBlockArmor.LOCATION);
	}
}

 

My dynamic model class:

public final class ModelDynBlockArmor implements IModel, IModelCustomData, IRetexturableModel
{
public static final ModelResourceLocation LOCATION = new ModelResourceLocation(BlockArmor.MODID+":block_armor", "inventory");

// minimal Z offset to prevent depth-fighting
private static final float NORTH_Z_BASE = 7.496f / 16f;
private static final float SOUTH_Z_BASE = 8.503f / 16f;
private static final float NORTH_Z_FLUID = 7.498f / 16f;
private static final float SOUTH_Z_FLUID = 8.502f / 16f;

public static final IModel MODEL = new ModelDynBlockArmor();

public ModelDynBlockArmor()
{

}

@Override
public Collection<ResourceLocation> getDependencies()
{
	return ImmutableList.of();
}

@Override
public Collection<ResourceLocation> getTextures()
{
	ImmutableSet.Builder<ResourceLocation> builder = ImmutableSet.builder();
	return builder.build();
}

@Override
public IBakedModel bake(IModelState state, VertexFormat format,	Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
	return new BakedDynBlockArmor(format);
}

@Override
public IModelState getDefaultState()
{
	return TRSRTransformation.identity();
}

@Override
public ModelDynBlockArmor process(ImmutableMap<String, String> customData)
{
	return new ModelDynBlockArmor();
}

@Override
public ModelDynBlockArmor retexture(ImmutableMap<String, String> textures)
{
	return new ModelDynBlockArmor();
}

public enum LoaderDynBlockArmor implements ICustomModelLoader
{
	INSTANCE;

	@Override
	public boolean accepts(ResourceLocation modelLocation)
	{
		return (modelLocation.getResourceDomain().equals(BlockArmor.MODID) && (modelLocation.getResourcePath().contains("helmet") 
				|| modelLocation.getResourcePath().contains("chestplate") || modelLocation.getResourcePath().contains("leggings") ||
				modelLocation.getResourcePath().contains("boots")));
	}

	@Override
	public IModel loadModel(ResourceLocation modelLocation)
	{
		return MODEL;
	}

	@Override
	public void onResourceManagerReload(IResourceManager resourceManager)
	{

	}
}

private static final class BakedDynBlockArmorOverrideHandler extends ItemOverrideList
{
	public static final BakedDynBlockArmorOverrideHandler INSTANCE = new BakedDynBlockArmorOverrideHandler();
	private BakedDynBlockArmorOverrideHandler()
	{
		super(ImmutableList.<ItemOverride>of());
	}

	@Override
	public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, World world, EntityLivingBase entity)
	{
		if (originalModel instanceof BakedDynBlockArmor && ArmorSet.getInventoryTextureLocation((ItemBlockArmor) stack.getItem()) != null) {
			ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
			IModelState state = new SimpleModelState(((BakedDynBlockArmor)originalModel).transforms);
			TRSRTransformation transform = TRSRTransformation.identity();
			state = new ModelStateComposition(state, transform);
			VertexFormat format = ((BakedDynBlockArmor)originalModel).format;
			//Full block texture
			ResourceLocation textureLocation = ArmorSet.getInventoryTextureLocation((ItemBlockArmor) stack.getItem());
			TextureAtlasSprite blockTexture = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(textureLocation.toString());
			//builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, NORTH_Z_BASE, blockTexture, EnumFacing.NORTH, 0xffffffff));
			//builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, SOUTH_Z_BASE, blockTexture, EnumFacing.SOUTH, 0xffffffff));	            
			String armorType = "";
			EntityEquipmentSlot slot = ((ItemBlockArmor) stack.getItem()).getEquipmentSlot();
			if (slot == EntityEquipmentSlot.HEAD)
				armorType = "helmet";
			else if (slot == EntityEquipmentSlot.CHEST)
				armorType = "chestplate";
			else if (slot == EntityEquipmentSlot.LEGS)
				armorType = "leggings";
			else if (slot == EntityEquipmentSlot.FEET)
				armorType = "boots";
			//Base texture and model
			ResourceLocation baseLocation = new ResourceLocation("blockarmor:items/block_armor_"+armorType+"_base");
			IBakedModel model = (new ItemLayerModel(ImmutableList.of(baseLocation))).bake(state, format, new Function<ResourceLocation, TextureAtlasSprite>() {
				public TextureAtlasSprite apply(ResourceLocation location)
				{
					return Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(location.toString());
				}
			});
			builder.addAll(model.getQuads(null, null, 0));
			//Template texture for left half
			String templateLocation = new ResourceLocation("blockarmor:items/block_armor_"+armorType+"1_template").toString();
			TextureAtlasSprite templateTexture = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(templateLocation);
			builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, templateTexture, blockTexture, NORTH_Z_FLUID, EnumFacing.NORTH, 0xffffffff));
			builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, templateTexture, blockTexture, SOUTH_Z_FLUID, EnumFacing.SOUTH, 0xffffffff));
			//Template texture for right half
			templateLocation = new ResourceLocation("blockarmor:items/block_armor_"+armorType+"2_template").toString();
			templateTexture = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(templateLocation);
			builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, templateTexture, blockTexture, NORTH_Z_FLUID, EnumFacing.NORTH, 0xffffffff));
			builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, templateTexture, blockTexture, SOUTH_Z_FLUID, EnumFacing.SOUTH, 0xffffffff));
			//Cover texture
			String coverLocation = new ResourceLocation("blockarmor:items/block_armor_"+armorType+"_cover").toString();
			TextureAtlasSprite coverTexture = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(coverLocation);
			builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, NORTH_Z_BASE, coverTexture, EnumFacing.NORTH, 0xffffffff));
			builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, SOUTH_Z_BASE, coverTexture, EnumFacing.SOUTH, 0xffffffff));
			((BakedDynBlockArmor)originalModel).quads = builder.build();
		}

		return originalModel;
	}
}

private static final class BakedDynBlockArmor implements IPerspectiveAwareModel
{
	private final ImmutableMap<TransformType, TRSRTransformation> transforms;
	private ImmutableList<BakedQuad> quads;
	private final VertexFormat format;

	public BakedDynBlockArmor(VertexFormat format)
	{
		this.format = format;

		ImmutableMap.Builder<TransformType, TRSRTransformation> builder = ImmutableMap.builder();
		builder.put(TransformType.GROUND, new TRSRTransformation(new Vector3f(0.25f, 0.375f, 0.25f), new Quat4f(0.0f, 0.0f, 0.0f, 1.0f), new Vector3f(0.5f, 0.5f, 0.5f), new Quat4f(0.0f, 0.0f, 0.0f, 1.0f)));
		builder.put(TransformType.HEAD, new TRSRTransformation(new Vector3f(1.0f, 0.8125f, 1.4375f), new Quat4f(0.0f, 1.0f, 0.0f, -4.371139E-8f), new Vector3f(1.0f, 1.0f, 1.0f), new Quat4f(0.0f, 0.0f, 0.0f, 1.0f)));
		builder.put(TransformType.FIRST_PERSON_RIGHT_HAND, new TRSRTransformation(new Vector3f(0.910625f, 0.24816513f, 0.40617055f), new Quat4f(-0.15304594f, -0.6903456f, 0.15304594f, 0.6903456f), new Vector3f(0.68000007f, 0.68000007f, 0.68f), new Quat4f(0.0f, 0.0f, 0.0f, 1.0f)));
		builder.put(TransformType.FIRST_PERSON_LEFT_HAND, new TRSRTransformation(new Vector3f(0.910625f, 0.24816513f, 0.40617055f), new Quat4f(-0.15304594f, -0.6903456f, 0.15304594f, 0.6903456f), new Vector3f(0.68000007f, 0.68000007f, 0.68f), new Quat4f(0.0f, 0.0f, 0.0f, 1.0f)));
		builder.put(TransformType.THIRD_PERSON_RIGHT_HAND, new TRSRTransformation(new Vector3f(0.225f, 0.4125f, 0.2875f), new Quat4f(0.0f, 0.0f, 0.0f, 0.99999994f), new Vector3f(0.55f, 0.55f, 0.55f), new Quat4f(0.0f, 0.0f, 0.0f, 1.0f)));
		builder.put(TransformType.THIRD_PERSON_LEFT_HAND, new TRSRTransformation(new Vector3f(0.225f, 0.4125f, 0.2875f), new Quat4f(0.0f, 0.0f, 0.0f, 0.99999994f), new Vector3f(0.55f, 0.55f, 0.55f), new Quat4f(0.0f, 0.0f, 0.0f, 1.0f)));
		ImmutableMap<TransformType, TRSRTransformation> transformMap = builder.build();
		this.transforms = Maps.immutableEnumMap(transformMap);
	}

	@Override
	public ItemOverrideList getOverrides()
	{
		return BakedDynBlockArmorOverrideHandler.INSTANCE;
	}

	@Override
	public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
	{

		return IPerspectiveAwareModel.MapWrapper.handlePerspective(this, transforms, cameraTransformType);
	}

	@Override
	public List<BakedQuad> getQuads(IBlockState state, EnumFacing side, long rand)
	{
		if(side == null) return quads;
		return ImmutableList.of();
	}

	public boolean isAmbientOcclusion() { return true;  }
	public boolean isGui3d() { return false; }
	public boolean isBuiltInRenderer() { return false; }
	public TextureAtlasSprite getParticleTexture() { return null; }
	public ItemCameraTransforms getItemCameraTransforms() { return ItemCameraTransforms.DEFAULT; }
}
}

 

 

Some of the code is specific to the mod I'm working on, but hopefully it still helps other people. If you want to see it in context, the mod (currently WIP) is on github: https://github.com/2piradians/Block-Armor

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.

Guest
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.

Announcements



×
×
  • Create New...

Important Information

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