NeoForge icon indicating copy to clipboard operation
NeoForge copied to clipboard

Create Dynamic Armor Trim System

Open GizmoTheMoonPig opened this issue 7 months ago • 12 comments

Armor trims!

A feature of recent MC I absolutely adore. When they first came out, I knew I had to add support for them in every single one of my projects. I quickly ran into an issue however: armor trims are NOT mod friendly at all.

Problems:

There are 2 main problems with armor trims and mod compatibility:

  1. Atlas shenanigans. While defining atlas stuff via JSON is normally very resource pack friendly, for paletted permutations it is not. When registering either a new trim pattern or a new trim material, you need to define all materials/patterns for your pattern/material. Lets look at an example. Mod A adds a new trim pattern. This pattern has a large smiley face on the front of the chestplate and some other small decorations around the rest of the armor model. We'll call this the Smiley Trim. Mod A would create their armor_trims.json atlas as such:
{
    "sources": [
        {
            "type": "paletted_permutations",
            "textures": [
                "mod_a:trims/entity/humanoid/smiley",
                "mod_a:trims/entity/humanoid_leggings/smiley"
            ],
            "palette_key": "trims/color_palettes/trim_palette",
            "permutations": {
                "quartz": "trims/color_palettes/quartz",
                "iron": "trims/color_palettes/iron",
                "gold": "trims/color_palettes/gold",
                "diamond": "trims/color_palettes/diamond",
                "netherite": "trims/color_palettes/netherite",
                "redstone": "trims/color_palettes/redstone",
                "copper": "trims/color_palettes/copper",
                "emerald": "trims/color_palettes/emerald",
                "lapis": "trims/color_palettes/lapis",
                "amethyst": "trims/color_palettes/amethyst",
                "iron_darker": "trims/color_palettes/iron_darker",
                "gold_darker": "trims/color_palettes/gold_darker",
                "diamond_darker": "trims/color_palettes/diamond_darker",
                "netherite_darker": "trims/color_palettes/netherite_darker",
                "resin": "trims/color_palettes/resin"
            }
        }
    ]
}

Mod B adds some new trim materials. I'm going to shamelessly use my own project, Twilight Forest, for this. We add 6 new materials, for all of our resources. Ironwood, Steeleaf, Knightmetal, Fiery, Carminite, and Naga Scale. Our JSON looks like the following: https://github.com/TeamTwilight/twilightforest/blob/1.21.x/src/main/resources/assets/minecraft/atlases/armor_trims.json Now, lets say a user installs these 2 mods together. This user wants to combine the Smiley Trim from Mod A with the Carminite material from Mod B. What do they get? A missing texture armor model! Why is this? It has to do with how the permutations are set up on the atlas. Each pattern defined will only be assigned to all the materials defined in that same JSON. Because Mod A doesn't list Mod B's materials in their JSON, and Mod B doesn't list Mod A's pattern in their JSON, the 2 don't work together.

  1. Item models. These have always been the worst part, but they got much worse with 1.21.4! In 1.21.1 and below, all you needed to do was set your trim material's index to the closest vanilla color and you were "fine". Granted, the color wouldn't exactly match, but something would render. In 1.21.4, custom trims do not render on vanilla armor items AT ALL thanks to the selection property checking for exact trim materials. The only way to fix this is to replace the vanilla armor model, but multiple mods obviously cant do this as only one will actually do that in the end.

The Proposed Solution:

This PR aims to fix these 2 issues listed. I wanted to take the least invasive, most compatible route of doing things while also keeping the change count small.

  1. A new Directory-based paletted permutations sprite source (neoforge:directory_paletted_permutations). All trim materials, patterns, and even item model layers will automatically be pulled from their proper directories across ALL resource packs and processed, meaning mods not only don't need to add to the atlas themselves anymore but also don't need to worry about missing textures on their armor models anymore!

  2. A new dynamic item model loader for trimmed armor. The idea behind this is that instead of having a selection list like vanilla currently does, the trim layer will dynamically be rendered on the armor based on what trim is applied. This also comes with the bonus of allowing mods to define their own base trim texture to be applied to the armor, meaning armors don't need to be a "one size fits all" to look good with trims. This system is very easily to utilize and only requires a few lines in the item json instead of hundreds of cases. Here's an example:

{
  "model": {
    "type": "neoforge:trimmed_armor",
    "base_model": {
      "model": "minecraft:item/diamond_helmet"
    },
    "base_trim_texture": "minecraft:trims/items/helmet_trim",
    "darker_trim_override": "minecraft:diamond"
  }
}

Downsides:

My system isn't perfect. There are a couple small issues that I'm not 100% sure how to address, or even if I should at all.

  • The base_model can only use basic item models. That means its not compatible with composite models or conditional models. The reason for this is because I need to have a way of getting the actual mode itself for things like item transforms. There are other ways to have your armor items be composite or whatever, but you cant have them inside the base model block.
  • This system only supports one layer for the trim across all trim patterns/materials. Mods will need to create their own loader if they want to do something like have unique trim textures per pattern. I felt like that sort of thing was out of scope for this as most mods will probably just be looking to keep their trims looking like vanilla.
  • Resource Packs that change how vanilla's models will no longer work if they don't modify the item JSON as each version of a trimmed armor piece is no longer its own JSON. I don't how much people really care about this as packs can revert it back if they so desire, but figured I would bring it up regardless.

Please let me know if you would like a test mod written. I wasn't too sure how to approach that as these are all purely visual changes, but I can figure something out if needed. I would also like to backport this to 1.21.4 but I would need to do it manually due to item model changes.

Thanks for reading!

GizmoTheMoonPig avatar May 12 '25 21:05 GizmoTheMoonPig

  • [ ] Publish PR to GitHub Packages

To fix some of the downsides, could you preserve the existing selection behaviour? Keep the selection property for vanilla trims, then in the fallback check if the trim component is present. If so use the dynamic model, otherwise the untrimmed model. This way vanilla/known trims behave exactly the same as before, with the downsides only occurring with modded trims. I guess mod / pack authors could do this anyway, but it'd provide an example and compatibility with vanilla resource packs.

For the base_model, couldn't you use two properties? One with the full model definition, another with only a basic model, used for the item transforms. One of the properties could be optional, if only basic models are being used.

TeamSpen210 avatar May 13 '25 00:05 TeamSpen210

To fix some of the downsides, could you preserve the existing selection behaviour? Keep the selection property for vanilla trims, then in the fallback check if the trim component is present. If so use the dynamic model, otherwise the untrimmed model. This way vanilla/known trims behave exactly the same as before, with the downsides only occurring with modded trims. I guess mod / pack authors could do this anyway, but it'd provide an example and compatibility with vanilla resource packs.

Would not mind doing this, would probably make sense to have it for vanilla armor models anyway. Mods can just fully rely on the custom item model part. The only thing thats unfortunate about going back to vanilla's system is the fact that vanilla trims will have Z-fighting on the sides of the item again but modded ones wont, which is an inconsistency I'm not a fan of but its not the end of the world. Would like to hear some more thoughts on this.

For the base_model, couldn't you use two properties? One with the full model definition, another with only a basic model, used for the item transforms. One of the properties could be optional, if only basic models are being used.

I'm honestly not a fan of this. I want the trims to always align with the armor transforms so it looks like a multi-layered item, and having to change multiple files to achieve this is a bit annoying. I don't really imagine anyone will be using anything other than item/generated transforms but just in case I would rather fetch whatever the base model has to say

GizmoTheMoonPig avatar May 14 '25 04:05 GizmoTheMoonPig

I think @onlyin is being deprecated maybe it's not wise to use it?

Thunderrock424242 avatar Jul 05 '25 18:07 Thunderrock424242

I think @onlyin is being deprecated maybe it's not wise to use it?

Im not using it anywhere, thats just part of a patch file where its already in there. Nothing I can do about that

GizmoTheMoonPig avatar Jul 06 '25 00:07 GizmoTheMoonPig

@neoforged/bots run applyFormatting

dhyces avatar Jul 10 '25 01:07 dhyces

@neoforged/bots run applyFormatting

dhyces avatar Jul 10 '25 01:07 dhyces

I tried to explain what it was and why it was needed for armor trims in the original description, but its essentially just a mashup of vanilla's DirectoryLister and PalettedPermutations sprite sources. It processes the whole directory instead of a set list of trim materials/patterns/textures. Otherwise, every single mod would need to define every single other mod's materials and patterns in their atlas jsons when adding anything. This obviously needed fixing if mods want their materials to show up on modded trim patterns, and vice versa

GizmoTheMoonPig avatar Jul 29 '25 04:07 GizmoTheMoonPig

Okay, after looking through the PR properly again I finally managed to grasp what the hell the custom sprite source is doing. It looks fine to me in theory. However, it exposes a rather severe flaw with the material suffixing (which is not exclusive to the sprite source): if two mods add a trim material with the same path then, of the two palettes for the "duplicate" material, the one that gets loaded last will win and the other one will be inaccessible, both in terms of specifying it in the TrimMaterial as well as the generation of the texture permutation. If we consider this an acceptable limitation (as far as I can tell this won't cause any user-visible issues beyond the materials being "forcefully deduplicated"), then I would say this is good to go (except for one nitpick).

XFactHD avatar Jul 29 '25 22:07 XFactHD

Okay, after looking through the PR properly again I finally managed to grasp what the hell the custom sprite source is doing. It looks fine to me in theory. However, it exposes a rather severe flaw with the material suffixing (which is not exclusive to the sprite source): if two mods add a trim material with the same path then, of the two palettes for the "duplicate" material, the one that gets loaded last will win and the other one will be inaccessible, both in terms of specifying it in the TrimMaterial as well as the generation of the texture permutation. If we consider this an acceptable limitation (as far as I can tell this won't cause any user-visible issues beyond the materials being "forcefully deduplicated"), then I would say this is good to go (except for one nitpick).

If its not gonna be a user facing issue I would say thats alright? As long as if both mods add a trim material for something like, say, tin, and it works fine with both of their ingots. I kinda like the idea of it auto deduplicating, the less space being taken up on atlases the better imo

GizmoTheMoonPig avatar Jul 30 '25 04:07 GizmoTheMoonPig

Okay, after looking through the PR properly again I finally managed to grasp what the hell the custom sprite source is doing. It looks fine to me in theory. However, it exposes a rather severe flaw with the material suffixing (which is not exclusive to the sprite source): if two mods add a trim material with the same path then, of the two palettes for the "duplicate" material, the one that gets loaded last will win and the other one will be inaccessible, both in terms of specifying it in the TrimMaterial as well as the generation of the texture permutation. If we consider this an acceptable limitation (as far as I can tell this won't cause any user-visible issues beyond the materials being "forcefully deduplicated"), then I would say this is good to go (except for one nitpick).

If its not gonna be a user facing issue I would say thats alright? As long as if both mods add a trim material for something like, say, tin, and it works fine with both of their ingots. I kinda like the idea of it auto deduplicating, the less space being taken up on atlases the better imo

Only as long as it's obvious and people know about it. We're likely going to get a lot of devs trying to figure out why their colors are different when they're testing in a pack or users complaining to mod authors about the colors being wrong.

dhyces avatar Jul 30 '25 04:07 dhyces

Documenting this limitation on TrimmedArmorModel is probably the best we'll get (and of course mention it in the Neo docs when somewrite writes those for this feature :D). People adding custom armor will need to opt-in to the special item model anyway, so they'll hopefully read that.

XFactHD avatar Aug 01 '25 22:08 XFactHD