diff --git a/src/main/java/net/neoforged/neoforge/common/data/vanilla/ModelTemplateWithCustomData.java b/src/main/java/net/neoforged/neoforge/common/data/vanilla/ModelTemplateWithCustomData.java index 4ceb897c6c..2e99e17381 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/vanilla/ModelTemplateWithCustomData.java +++ b/src/main/java/net/neoforged/neoforge/common/data/vanilla/ModelTemplateWithCustomData.java @@ -5,14 +5,19 @@ package net.neoforged.neoforge.common.data.vanilla; +import com.google.common.collect.Maps; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.Map; import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.data.models.model.ModelTemplate; import net.minecraft.data.models.model.TextureSlot; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemDisplayContext; import net.neoforged.neoforge.common.data.ExistingFileHelper; import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; public class ModelTemplateWithCustomData extends ModelTemplate { @Nullable @@ -21,6 +26,7 @@ public class ModelTemplateWithCustomData extends ModelTemplate { public Boolean ambientOcclusion = null; // BlockModel.DEFAULT_AMBIENT_OCCLUSION @Nullable public BlockModel.GuiLight guiLight = null; + public Map transforms = Maps.newHashMap(); public ModelTemplateWithCustomData(ModelTemplate template) { super(template.model, template.suffix, template.requiredSlots.toArray(TextureSlot[]::new)); @@ -29,6 +35,7 @@ public ModelTemplateWithCustomData(ModelTemplate template) { renderType = customData.renderType; ambientOcclusion = customData.ambientOcclusion; guiLight = customData.guiLight; + transforms = Maps.newHashMap(customData.transforms); } } @@ -41,11 +48,17 @@ public JsonObject createBaseTemplate(ResourceLocation modelPath, Map textureMap) { return createBaseTemplate(modelPath, textureMap, null); } + + public static JsonObject toJson(ItemTransform transform) { + var json = new JsonObject(); + var hasRightRotation = !transform.rightRotation.equals(ItemTransform.Deserializer.DEFAULT_ROTATION); + + if (!transform.translation.equals(ItemTransform.Deserializer.DEFAULT_TRANSLATION)) + json.add("translation", toJson(transform.translation)); + if (!transform.rotation.equals(ItemTransform.Deserializer.DEFAULT_ROTATION)) + json.add(hasRightRotation ? "left_rotation" : "rotation", toJson(transform.rotation)); + if (!transform.scale.equals(ItemTransform.Deserializer.DEFAULT_SCALE)) + json.add("scale", toJson(transform.scale)); + if (hasRightRotation) + json.add("right_rotation", toJson(transform.rightRotation)); + + return json; + } + + public static JsonObject toJson(Map transforms) { + var json = new JsonObject(); + + transforms.forEach((context, transform) -> { + if (transform.equals(ItemTransform.NO_TRANSFORM)) + return; + + var transformJson = toJson(transform); + + if (!transformJson.isEmpty()) + json.add(context.getSerializedName(), transformJson); + }); + + return json; + } + + public static JsonArray toJson(Vector3f vec) { + var array = new JsonArray(); + array.add(vec.x()); + array.add(vec.y()); + array.add(vec.z()); + return array; + } } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IModelTemplateExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IModelTemplateExtension.java index d40ec2fa02..a29be62ccc 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IModelTemplateExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IModelTemplateExtension.java @@ -7,8 +7,10 @@ import java.util.function.Consumer; import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.data.models.model.ModelTemplate; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemDisplayContext; import net.neoforged.neoforge.common.data.vanilla.ModelTemplateWithCustomData; public interface IModelTemplateExtension { @@ -28,11 +30,15 @@ default ModelTemplate withGuiLight(BlockModel.GuiLight guiLight) { return withCustomData(customData -> customData.guiLight = guiLight); } + default ModelTemplate withItemTransform(ItemDisplayContext displayContext, ItemTransform transform) { + return withCustomData(customData -> customData.transforms.put(displayContext, transform)); + } + private ModelTemplate self() { return (ModelTemplate) this; } - default ModelTemplate withCustomData(Consumer mutator) { + private ModelTemplate withCustomData(Consumer mutator) { var template = new ModelTemplateWithCustomData(self()); mutator.accept(template); return template; diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/ITexturedModelExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/ITexturedModelExtension.java index 941f6326b4..7d587cb46b 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/ITexturedModelExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/ITexturedModelExtension.java @@ -6,8 +6,10 @@ package net.neoforged.neoforge.common.extensions; import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.data.models.model.TexturedModel; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemDisplayContext; public interface ITexturedModelExtension { default TexturedModel withRenderType(String renderType) { @@ -26,6 +28,10 @@ default TexturedModel withGuiLight(BlockModel.GuiLight guiLight) { return new TexturedModel(self().getMapping(), self().getTemplate().withGuiLight(guiLight)); } + default TexturedModel withItemTransform(ItemDisplayContext displayContext, ItemTransform transform) { + return new TexturedModel(self().getMapping(), self().getTemplate().withItemTransform(displayContext, transform)); + } + private TexturedModel self() { return (TexturedModel) this; } @@ -47,6 +53,10 @@ default TexturedModel.Provider withGuiLight(BlockModel.GuiLight guiLight) { return block -> self().get(block).withGuiLight(guiLight); } + default TexturedModel.Provider withItemTransform(ItemDisplayContext displayContext, ItemTransform transform) { + return block -> self().get(block).withItemTransform(displayContext, transform); + } + private TexturedModel.Provider self() { return (TexturedModel.Provider) this; } diff --git a/tests/src/generated/resources/assets/neotests_test_model_generators/models/custom_model_generation.json b/tests/src/generated/resources/assets/neotests_test_model_generators/models/custom_model_generation.json new file mode 100644 index 0000000000..060569a52c --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_test_model_generators/models/custom_model_generation.json @@ -0,0 +1,127 @@ +{ + "parent": "minecraft:item/generated", + "ambientocclusion": true, + "display": { + "firstperson_lefthand": { + "rotation": [ + 0.0, + 135.0, + 0.0 + ], + "scale": [ + 0.4, + 0.4, + 0.4 + ], + "translation": [ + 0.0, + 7.0, + 4.0 + ] + }, + "firstperson_righthand": { + "rotation": [ + 0.0, + 135.0, + 0.0 + ], + "scale": [ + 0.4, + 0.4, + 0.4 + ], + "translation": [ + 0.0, + 7.0, + 4.0 + ] + }, + "fixed": { + "rotation": [ + -90.0, + 0.0, + 0.0 + ], + "translation": [ + 0.0, + 0.0, + -23.0 + ] + }, + "ground": { + "scale": [ + 0.25, + 0.25, + 0.25 + ], + "translation": [ + 0.0, + 6.0, + 4.0 + ] + }, + "gui": { + "rotation": [ + 30.0, + -135.0, + 0.0 + ], + "scale": [ + 0.5, + 0.5, + 0.5 + ], + "translation": [ + 0.0, + 3.0, + 0.0 + ] + }, + "head": { + "translation": [ + 0.0, + 30.0, + 4.0 + ] + }, + "thirdperson_lefthand": { + "rotation": [ + 75.0, + 45.0, + 0.0 + ], + "scale": [ + 0.375, + 0.375, + 0.375 + ], + "translation": [ + 0.0, + 3.0, + 4.0 + ] + }, + "thirdperson_righthand": { + "rotation": [ + 75.0, + 45.0, + 0.0 + ], + "scale": [ + 0.375, + 0.375, + 0.375 + ], + "translation": [ + 0.0, + 3.0, + 4.0 + ] + } + }, + "gui_light": "front", + "render_type": "minecraft:cutout", + "textures": { + "layer0": "minecraft:item/diamond" + } +} \ No newline at end of file diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/VanillaDataGenTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/VanillaDataGenTest.java index 6e876046b8..d4b0d6fec2 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/VanillaDataGenTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/VanillaDataGenTest.java @@ -6,17 +6,22 @@ package net.neoforged.neoforge.debug; import java.util.function.BiConsumer; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.core.component.DataComponents; import net.minecraft.data.models.BlockModelGenerators; import net.minecraft.data.models.EquipmentModelProvider; import net.minecraft.data.models.ItemModelGenerators; import net.minecraft.data.models.ModelProvider; +import net.minecraft.data.models.model.ModelTemplates; import net.minecraft.data.models.model.TextureMapping; import net.minecraft.data.models.model.TextureSlot; import net.minecraft.data.models.model.TexturedModel; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.Items; import net.minecraft.world.item.equipment.EquipmentModel; import net.minecraft.world.item.equipment.Equippable; import net.minecraft.world.level.block.Blocks; @@ -25,6 +30,7 @@ import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; import net.neoforged.testframework.registration.RegistrationHelper; +import org.joml.Vector3f; @ForEachTest(groups = "vanilla_data_gen") public interface VanillaDataGenTest { @@ -65,6 +71,24 @@ protected void registerModels(BlockModelGenerators blockModels, ItemModelGenerat // this allows generating your own custom Item model for your BlockItem // blockModels.skipAutoItemBlock(block.value()); // itemModels.generateFlatItem(blockItem.value(), ModelTemplates.FLAT_HANDHELD_ITEM); + + // custom model generation + // output should have all the below custom properties + // parent 'item/generated' + // and have texture slot 'layer0' set to 'item/diamond' + ModelTemplates.FLAT_ITEM + .withItemTransform(ItemDisplayContext.THIRD_PERSON_RIGHT_HAND, new ItemTransform(new Vector3f(75F, 45F, 0F), new Vector3f(0F, 3F, 4F), new Vector3f(.375F))) + .withItemTransform(ItemDisplayContext.THIRD_PERSON_LEFT_HAND, new ItemTransform(new Vector3f(75F, 45F, 0F), new Vector3f(0F, 3F, 4F), new Vector3f(.375F))) + .withItemTransform(ItemDisplayContext.FIRST_PERSON_RIGHT_HAND, new ItemTransform(new Vector3f(0F, 135F, 0F), new Vector3f(0F, 7F, 4F), new Vector3f(.4F))) + .withItemTransform(ItemDisplayContext.FIRST_PERSON_LEFT_HAND, new ItemTransform(new Vector3f(0F, 135F, 0F), new Vector3f(0F, 7F, 4F), new Vector3f(.4F))) + .withItemTransform(ItemDisplayContext.HEAD, new ItemTransform(new Vector3f(0F), new Vector3f(0F, 30F, 4F), new Vector3f(1F))) + .withItemTransform(ItemDisplayContext.GROUND, new ItemTransform(new Vector3f(0F), new Vector3f(0F, 6F, 4F), new Vector3f(.25F))) + .withItemTransform(ItemDisplayContext.FIXED, new ItemTransform(new Vector3f(-90F, 0F, 0F), new Vector3f(0F, 0F, -23F), new Vector3f(1F))) + .withItemTransform(ItemDisplayContext.GUI, new ItemTransform(new Vector3f(30F, -135F, 0F), new Vector3f(0F, 3F, 0F), new Vector3f(.5F))) + .withRenderType("cutout") + .withAmbientOcclusion(true) + .withGuiLight(BlockModel.GuiLight.FRONT) + .create(itemModels.modLocation("custom_model_generation"), TextureMapping.layer0(Items.DIAMOND), itemModels.output, fileHelper); } });