Jump to content

[1.19.2] Dynamic change of texture and model, item JSON


Luckydel

Recommended Posts

 

I would like to add a sight to the weapon, a silencer, as a layer (If possible) and change item texture.

As far as I understand, I need BakedModel. But it looks like I'm doing something wrong. ( I don't go to methods. )

Spoiler
public class Makarov extends ProjectileWeaponItem implements BakedModel {
	@Override
    public List<BakedQuad> getQuads(@Nullable BlockState p_235039_, @Nullable Direction p_235040_, RandomSource p_235041_) {
        return null;
    }

    @Override
    public boolean useAmbientOcclusion() {
        return false;
    }

    @Override
    public boolean isGui3d() {
        return false;
    }

    @Override
    public boolean usesBlockLight() {
        return false;
    }

    @Override
    public boolean isCustomRenderer() {
        return false;
    }

    @Override
    public TextureAtlasSprite getParticleIcon() {
        return null;
    }

    @Override
    public ItemOverrides getOverrides() {
        return null;
    }
    ...
}

 

Maybe I still need ItemOverrides

But, my json looks different (Blockbench), I don't have "model". I have vertices and groups of them in JSON
 

Spoiler
{
	"textures": {
		"0": "mod:item/makarov/main",
		"1": "mod:item/makarov/aim",
		"particle": "block/main"
	},
	"elements": [
		{
			"from": [6.4, 10.4, 7.1],
			"to": [14, 10.5, 8.6],
			"rotation": {"angle": 0, "axis": "y", "origin": [6, 4.7, 7.1]},
			"faces": {
				"north": {"uv": [1, 11, 4.8125, 11.0625], "texture": "#0"},
				"east": {"uv": [11.5, 12.5, 12.25, 12.5625], "texture": "#0"},
				"south": {"uv": [11, 2.5, 14.8125, 2.5625], "texture": "#0"},
				"west": {"uv": [12.5, 12.5, 13.25, 12.5625], "texture": "#0"},
				"up": {"uv": [3.8125, 0.75, 0, 0], "texture": "#0"},
				"down": {"uv": [3.8125, 1, 0, 1.75], "texture": "#0"}
			}
		},
	...

	"groups": [
		{
			"name": "group",
			"origin": [0, 0, 0],
			"color": 0,
			"nbt": "{}",
			"armAnimationEnabled": false,
			"children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33]
		},
		{
			"name": "aim",
			"origin": [8, 8, 8],
			"color": 0,
			"nbt": "{}",
			"armAnimationEnabled": false,
			"children": [37, 38, 39, 40]
		}
	]

 

 

Edited by Luckydel
Link to comment
Share on other sites

On 10/29/2022 at 11:19 AM, Luckydel said:

As far as I understand, I need BakedModel. But it looks like I'm doing something wrong. ( I don't go to methods. )

 

The baked model is necessary as it represents the model, but it doesn't do anything if you don't register an associated ModelLoader to read the JSONs as that format. This process is not well documented and has recently gotten a massive overhaul, so the best thing to do is look at other model loaders already present and mimic them. Also, if everything is null, things will crash.

On 10/29/2022 at 11:19 AM, Luckydel said:

Maybe I still need ItemOverrides

Yes, they are for the context of an ItemStack, getQuads is typically in the context of the world (so, blocks).

On 10/29/2022 at 11:19 AM, Luckydel said:

But, my json looks different (Blockbench), I don't have "model". I have vertices and groups of them in JSON

You can have those, though whether they'll be read depends on if the vanilla model accepts them (don't think they do for some of the group fields).

Link to comment
Share on other sites

On 10/31/2022 at 2:48 PM, ChampionAsh5357 said:
 

The baked model is necessary as it represents the model, but it doesn't do anything if you don't register an associated ModelLoader to read the JSONs as that format. This process is not well documented and has recently gotten a massive overhaul, so the best thing to do is look at other model loaders already present and mimic them. Also, if everything is null, things will crash.

Yes, they are for the context of an ItemStack, getQuads is typically in the context of the world (so, blocks).

You can have those, though whether they'll be read depends on if the vanilla model accepts them (don't think they do for some of the group fields).

Then, for regular json, do I need to use UnbakedModel? Something does not work.

Link to comment
Share on other sites

18 hours ago, Luckydel said:

Then, for regular json, do I need to use UnbakedModel? Something does not work.

That's not what I said? You can bake the models in using the normal procedure and just have your custom model loader consume the baked models and render them all at specific offsets. Once again, take a look at existing custom model loaders (the composite model loader comes to mind here).

Link to comment
Share on other sites

7 hours ago, ChampionAsh5357 said:

That's not what I said? You can bake the models in using the normal procedure and just have your custom model loader consume the baked models and render them all at specific offsets. Once again, take a look at existing custom model loaders (the composite model loader comes to mind here).

Thank you for your patience.
Okay, I've created a custom model loader, how do I tell the item to use it?

Link to comment
Share on other sites

I am reluctant to get involved in this thread since I know virtually nothing about custom model loaders. 🙂

I just want to suggest you look at what ForgeClientMod ClientForgeMod does.

This is just another mod, the only thing special about it, is that it is preinstalled with forge.

e.g. it creates the composite loader mentioned above, along with others.

 

You might also want to look at: https://docs.minecraftforge.net/en/1.19.x/rendering/modelloaders/#custom-model-loaders

Edited by warjort

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

2 hours ago, Luckydel said:

Okay, I've created a custom model loader, how do I tell the item to use it?

You'll need to register it with `ModelEvent$RegisterGeometryLoaders` if you haven't already, which will register your loader with the name `modid:name`. Then you supply that within the main model of your item with the `loader` key.

Link to comment
Share on other sites

13 hours ago, ChampionAsh5357 said:

You'll need to register it with `ModelEvent$RegisterGeometryLoaders` if you haven't already, which will register your loader with the name `modid:name`. Then you supply that within the main model of your item with the `loader` key.

It looks like I misunderstood something

Custom model loader is CompositeModelBuilder or CompositeModel$Loader ?
EDIT:

Can you give more details about the `loader` key, or a link to the documentation

Edited by Luckydel
Link to comment
Share on other sites

2 hours ago, Luckydel said:

Custom model loader is CompositeModelBuilder or CompositeModel$Loader ?

`CompositeModel$Loader`, the builder is used to make the baked model from quads, which can be obtained from other baked models.

2 hours ago, Luckydel said:

Can you give more details about the `loader` key, or a link to the documentation

It's just a key that points to the name you registered the loader under. There is currently no actual documentation on this yet.

{
  "loader": "modid:loader_name",
  // ... Rest of the model json
}
Link to comment
Share on other sites

3 hours ago, ChampionAsh5357 said:

`CompositeModel$Loader`, the builder is used to make the baked model from quads, which can be obtained from other baked models.

It's just a key that points to the name you registered the loader under. There is currently no actual documentation on this yet.

{
  "loader": "modid:loader_name",
  // ... Rest of the model json
}

I'm doing something wrong. Doesn't go to registration event.
 

Spoiler
public class modSubscribeEvents {
    public static final String ModelLoader = "CustomModelLoader";
	//work
    @SubscribeEvent
    public static void test(RenderGuiOverlayEvent.Pre event){
        int d=3;
    }
	//don't work
    @SubscribeEvent
    public static void registryModelLoader(ModelEvent.RegisterGeometryLoaders event){
        event.register(Main.MODID+":"+ModelLoader, ExampleModelBuilder.Loader.INSTANCE);
    }

}

 

Model loader class, as a basis I used ElementsModel.

Spoiler
public class ExampleModelBuilder extends SimpleUnbakedGeometry<ExampleModelBuilder> {
    private static final Logger LOGGER = LogManager.getLogger();

    private final List<BlockElement> elements;
    private final boolean deprecatedLoader;

    public ExampleModelBuilder(List<BlockElement> elements)
    {
        this(elements, false);
    }

    private ExampleModelBuilder(List<BlockElement> elements, boolean deprecatedLoader)
    {
        this.elements = elements;
        this.deprecatedLoader = deprecatedLoader;
    }

    @Override
    protected void addQuads(IGeometryBakingContext context, IModelBuilder<?> modelBuilder, ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, ModelState modelState, ResourceLocation modelLocation)
    {
        if (deprecatedLoader)
            LOGGER.warn("Model \"" + modelLocation + "\" is using the deprecated loader \"minecraft:elements\" instead of \"forge:elements\". This loader will be removed in 1.20.");

        var rootTransform = context.getRootTransform();
        if (!rootTransform.isIdentity())
            modelState = new SimpleModelState(modelState.getRotation().compose(rootTransform), modelState.isUvLocked());

        for (BlockElement element : elements)
        {
            for (Direction direction : element.faces.keySet())
            {
                var face = element.faces.get(direction);
                var sprite = spriteGetter.apply(context.getMaterial(face.texture));
                var quad = BlockModel.bakeFace(element, face, sprite, direction, modelState, modelLocation);

                if (face.cullForDirection == null)
                    modelBuilder.addUnculledFace(quad);
                else
                    modelBuilder.addCulledFace(modelState.getRotation().rotateTransform(face.cullForDirection), quad);
            }
        }
    }

    @Override
    public Collection<Material> getMaterials(IGeometryBakingContext context, Function<ResourceLocation, UnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors)
    {
        Set<Material> textures = Sets.newHashSet();
        if (context.hasMaterial("particle"))
            textures.add(context.getMaterial("particle"));
        for (BlockElement part : elements)
        {
            for (BlockElementFace face : part.faces.values())
            {
                Material texture = context.getMaterial(face.texture);
                if (texture.texture().equals(MissingTextureAtlasSprite.getLocation()))
                    missingTextureErrors.add(Pair.of(face.texture, context.getModelName()));
                textures.add(texture);
            }
        }

        return textures;
    }

    public static final class Loader implements IGeometryLoader<ExampleModelBuilder>
    {
        public static final ExampleModelBuilder.Loader INSTANCE = new ExampleModelBuilder.Loader(false);
        @Deprecated(forRemoval = true, since = "1.19")
        public static final ExampleModelBuilder.Loader INSTANCE_DEPRECATED = new ExampleModelBuilder.Loader(true);

        private final boolean deprecated;

        private Loader(boolean deprecated)
        {
            this.deprecated = deprecated;
        }

        @Override
        public ExampleModelBuilder read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) throws JsonParseException
        {
            if (!jsonObject.has("elements"))
                throw new JsonParseException("An element model must have an \"elements\" member.");

            List<BlockElement> elements = new ArrayList<>();
            for (JsonElement element : GsonHelper.getAsJsonArray(jsonObject, "elements"))
            {
                elements.add(deserializationContext.deserialize(element, BlockElement.class));
            }

            return new ExampleModelBuilder(elements, deprecated);
        }
    }
}

 

 

 

I have another question, after I register it and set "loader". I will be able to interact with the model via IUnbakedGeometry#bake in item  class or am I misunderstanding something?

Link to comment
Share on other sites

ModelEvent$RegisterGeometryLoaders is on the mod event bus as the event says on its javadocs.

Do not copy-paste the raw loader. The reason why I mentioned using the composite loader is because it does something similar to what you want but requires you to thing about what to implement.

19 hours ago, Luckydel said:

I have another question, after I register it and set "loader". I will be able to interact with the model via IUnbakedGeometry#bake in item  class or am I misunderstanding something?

You read it into an unbaked geometry and then bake it into a model after everything has been loaded. You should not be able to access the geometry from the item class. If you want to provide overrides depending on the stack, you need to provide your own custom ItemOverrides to and specifying that for this stack use this model.

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.

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.

×
×
  • Create New...

Important Information

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