diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java index 264fd312c..91dbafc2c 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java @@ -56,6 +56,7 @@ public class ModelLoaderRegistry { public static final String WHITE_TEXTURE = "forge:white"; + private static final ItemModelGenerator ITEM_MODEL_GENERATOR = new ItemModelGenerator(); private static final Map> loaders = Maps.newHashMap(); private static volatile boolean registryFrozen = false; @@ -68,6 +69,7 @@ public class ModelLoaderRegistry registerLoader(new ResourceLocation("forge","composite"), CompositeModel.Loader.INSTANCE); registerLoader(new ResourceLocation("forge","multi-layer"), MultiLayerModel.Loader.INSTANCE); registerLoader(new ResourceLocation("forge","item-layers"), ItemLayerModel.Loader.INSTANCE); + registerLoader(new ResourceLocation("forge", "separate-perspective"), SeparatePerspectiveModel.Loader.INSTANCE); // TODO: Implement as new model loaders //registerLoader(new ResourceLocation("forge:b3d"), new ModelLoaderAdapter(B3DLoader.INSTANCE)); @@ -255,7 +257,16 @@ public class ModelLoaderRegistry if (customModel != null) model = customModel.bake(blockModel.customData, modelBakery, spriteGetter, modelTransform, blockModel.getOverrides(modelBakery, otherModel, spriteGetter), modelLocation); else - model = blockModel.bakeVanilla(modelBakery, otherModel, spriteGetter, modelTransform, modelLocation, guiLight3d); + { + // handle vanilla item models here, since vanilla has a shortcut for them + if (blockModel.getRootModel() == ModelBakery.MODEL_GENERATED) { + model = ITEM_MODEL_GENERATOR.makeItemModel(spriteGetter, blockModel).bakeModel(modelBakery, blockModel, spriteGetter, modelTransform, modelLocation, guiLight3d); + } + else + { + model = blockModel.bakeVanilla(modelBakery, otherModel, spriteGetter, modelTransform, modelLocation, guiLight3d); + } + } if (customModelState != null && !model.doesHandlePerspectives()) model = new PerspectiveMapWrapper(model, customModelState); diff --git a/src/main/java/net/minecraftforge/client/model/SeparatePerspectiveModel.java b/src/main/java/net/minecraftforge/client/model/SeparatePerspectiveModel.java new file mode 100644 index 000000000..8b3e049bc --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/SeparatePerspectiveModel.java @@ -0,0 +1,186 @@ +package net.minecraftforge.client.model; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.datafixers.util.Pair; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.model.*; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.resources.IResourceManager; +import net.minecraft.util.Direction; +import net.minecraft.util.JSONUtils; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.geometry.IModelGeometry; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Function; + +public class SeparatePerspectiveModel implements IModelGeometry +{ + private final BlockModel baseModel; + private final ImmutableMap perspectives; + + public SeparatePerspectiveModel(BlockModel baseModel, ImmutableMap perspectives) + { + this.baseModel = baseModel; + this.perspectives = perspectives; + } + + @Override + public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function spriteGetter, IModelTransform modelTransform, ItemOverrideList overrides, ResourceLocation modelLocation) + { + return new BakedModel( + owner.useSmoothLighting(), owner.isShadedInGui(), owner.isSideLit(), + spriteGetter.apply(owner.resolveTexture("particle")), overrides, + baseModel.bakeModel(bakery, baseModel, spriteGetter, modelTransform, modelLocation, owner.isSideLit()), + ImmutableMap.copyOf(Maps.transformValues(perspectives, value -> { + return value.bakeModel(bakery, value, spriteGetter, modelTransform, modelLocation, owner.isSideLit()); + })) + ); + } + + @Override + public Collection getTextures(IModelConfiguration owner, Function modelGetter, Set> missingTextureErrors) + { + Set textures = Sets.newHashSet(); + textures.addAll(baseModel.getTextures(modelGetter, missingTextureErrors)); + for(BlockModel model : perspectives.values()) + textures.addAll(model.getTextures(modelGetter, missingTextureErrors)); + return textures; + } + + public static class BakedModel implements IBakedModel + { + private final boolean isAmbientOcclusion; + private final boolean isGui3d; + private final boolean isSideLit; + private final TextureAtlasSprite particle; + private final ItemOverrideList overrides; + private final IBakedModel baseModel; + private final ImmutableMap perspectives; + + public BakedModel(boolean isAmbientOcclusion, boolean isGui3d, boolean isSideLit, TextureAtlasSprite particle, ItemOverrideList overrides, IBakedModel baseModel, ImmutableMap perspectives) + { + this.isAmbientOcclusion = isAmbientOcclusion; + this.isGui3d = isGui3d; + this.isSideLit = isSideLit; + this.particle = particle; + this.overrides = overrides; + this.baseModel = baseModel; + this.perspectives = perspectives; + } + + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) + { + return Collections.emptyList(); + } + + @Override + public boolean isAmbientOcclusion() + { + return isAmbientOcclusion; + } + + @Override + public boolean isGui3d() + { + return isGui3d; + } + + @Override + public boolean func_230044_c_() + { + return isSideLit; + } + + @Override + public boolean isBuiltInRenderer() + { + return false; + } + + @Override + public TextureAtlasSprite getParticleTexture() + { + return particle; + } + + @Override + public ItemOverrideList getOverrides() + { + return overrides; + } + + @Override + public boolean doesHandlePerspectives() + { + return true; + } + + @Override + public ItemCameraTransforms getItemCameraTransforms() + { + return ItemCameraTransforms.DEFAULT; + } + + @Override + public IBakedModel handlePerspective(ItemCameraTransforms.TransformType cameraTransformType, MatrixStack mat) + { + if (perspectives.containsKey(cameraTransformType)) + { + IBakedModel p = perspectives.get(cameraTransformType); + return p.handlePerspective(cameraTransformType, mat); + } + return baseModel.handlePerspective(cameraTransformType, mat); + } + } + + public static class Loader implements IModelLoader + { + public static final Loader INSTANCE = new Loader(); + + private static final ImmutableMap PERSPECTIVES = ImmutableMap.builder() + .put("none", ItemCameraTransforms.TransformType.NONE) + .put("third_person_left_hand", ItemCameraTransforms.TransformType.THIRD_PERSON_LEFT_HAND) + .put("third_person_right_hand", ItemCameraTransforms.TransformType.THIRD_PERSON_RIGHT_HAND) + .put("first_person_left_hand", ItemCameraTransforms.TransformType.FIRST_PERSON_LEFT_HAND) + .put("first_person_right_hand", ItemCameraTransforms.TransformType.FIRST_PERSON_RIGHT_HAND) + .put("head", ItemCameraTransforms.TransformType.HEAD) + .put("gui", ItemCameraTransforms.TransformType.GUI) + .put("ground", ItemCameraTransforms.TransformType.GROUND) + .put("fixed", ItemCameraTransforms.TransformType.FIXED) + .build(); + + @Override + public void onResourceManagerReload(IResourceManager resourceManager) + { + // Not used + } + + @Override + public SeparatePerspectiveModel read(JsonDeserializationContext deserializationContext, JsonObject modelContents) + { + BlockModel baseModel = deserializationContext.deserialize(JSONUtils.getJsonObject(modelContents, "base"), BlockModel.class); + + JsonObject perspectiveData = JSONUtils.getJsonObject(modelContents, "perspectives"); + + ImmutableMap.Builder perspectives = ImmutableMap.builder(); + for(Map.Entry perspective : PERSPECTIVES.entrySet()) + { + if (perspectiveData.has(perspective.getKey())) + { + BlockModel perspectiveModel = deserializationContext.deserialize(JSONUtils.getJsonObject(perspectiveData, perspective.getKey()), BlockModel.class); + perspectives.put(perspective.getValue(), perspectiveModel); + } + } + + return new SeparatePerspectiveModel(baseModel, perspectives.build()); + } + } +} diff --git a/src/test/java/net/minecraftforge/debug/client/model/NewModelLoaderTest.java b/src/test/java/net/minecraftforge/debug/client/model/NewModelLoaderTest.java index 492646f8d..dff522848 100644 --- a/src/test/java/net/minecraftforge/debug/client/model/NewModelLoaderTest.java +++ b/src/test/java/net/minecraftforge/debug/client/model/NewModelLoaderTest.java @@ -123,6 +123,10 @@ public class NewModelLoaderTest new Item(new Item.Properties().group(ItemGroup.MISC)) ); + public static RegistryObject separate_perspective = ITEMS.register("separate_perspective", () -> + new Item(new Item.Properties().group(ItemGroup.MISC)) + ); + public NewModelLoaderTest() { IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); diff --git a/src/test/resources/assets/new_model_loader_test/models/item/separate_perspective.json b/src/test/resources/assets/new_model_loader_test/models/item/separate_perspective.json new file mode 100644 index 000000000..df94f4e58 --- /dev/null +++ b/src/test/resources/assets/new_model_loader_test/models/item/separate_perspective.json @@ -0,0 +1,15 @@ +{ + "parent":"forge:item/default", + "loader":"forge:separate-perspective", + "base": { + "parent": "minecraft:item/coal" + }, + "perspectives": { + "gui": { + "parent": "minecraft:item/snowball" + }, + "first_person_left_hand": { + "parent": "minecraft:item/bone" + } + } +} \ No newline at end of file