Add a model loader that lets mods specify different models for different perspectives.

Allow custom models to reference vanilla item models as child models.
This commit is contained in:
David Quintana 2020-06-28 00:32:50 +02:00
parent af9bb9641b
commit 5f1a7326c7
4 changed files with 217 additions and 1 deletions

View file

@ -56,6 +56,7 @@ public class ModelLoaderRegistry
{ {
public static final String WHITE_TEXTURE = "forge:white"; public static final String WHITE_TEXTURE = "forge:white";
private static final ItemModelGenerator ITEM_MODEL_GENERATOR = new ItemModelGenerator();
private static final Map<ResourceLocation, IModelLoader<?>> loaders = Maps.newHashMap(); private static final Map<ResourceLocation, IModelLoader<?>> loaders = Maps.newHashMap();
private static volatile boolean registryFrozen = false; 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","composite"), CompositeModel.Loader.INSTANCE);
registerLoader(new ResourceLocation("forge","multi-layer"), MultiLayerModel.Loader.INSTANCE); registerLoader(new ResourceLocation("forge","multi-layer"), MultiLayerModel.Loader.INSTANCE);
registerLoader(new ResourceLocation("forge","item-layers"), ItemLayerModel.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 // TODO: Implement as new model loaders
//registerLoader(new ResourceLocation("forge:b3d"), new ModelLoaderAdapter(B3DLoader.INSTANCE)); //registerLoader(new ResourceLocation("forge:b3d"), new ModelLoaderAdapter(B3DLoader.INSTANCE));
@ -255,7 +257,16 @@ public class ModelLoaderRegistry
if (customModel != null) if (customModel != null)
model = customModel.bake(blockModel.customData, modelBakery, spriteGetter, modelTransform, blockModel.getOverrides(modelBakery, otherModel, spriteGetter), modelLocation); model = customModel.bake(blockModel.customData, modelBakery, spriteGetter, modelTransform, blockModel.getOverrides(modelBakery, otherModel, spriteGetter), modelLocation);
else 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()) if (customModelState != null && !model.doesHandlePerspectives())
model = new PerspectiveMapWrapper(model, customModelState); model = new PerspectiveMapWrapper(model, customModelState);

View file

@ -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<SeparatePerspectiveModel>
{
private final BlockModel baseModel;
private final ImmutableMap<ItemCameraTransforms.TransformType, BlockModel> perspectives;
public SeparatePerspectiveModel(BlockModel baseModel, ImmutableMap<ItemCameraTransforms.TransformType, BlockModel> perspectives)
{
this.baseModel = baseModel;
this.perspectives = perspectives;
}
@Override
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<RenderMaterial, TextureAtlasSprite> 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<RenderMaterial> getTextures(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors)
{
Set<RenderMaterial> 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<ItemCameraTransforms.TransformType, IBakedModel> perspectives;
public BakedModel(boolean isAmbientOcclusion, boolean isGui3d, boolean isSideLit, TextureAtlasSprite particle, ItemOverrideList overrides, IBakedModel baseModel, ImmutableMap<ItemCameraTransforms.TransformType, IBakedModel> 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<BakedQuad> 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<SeparatePerspectiveModel>
{
public static final Loader INSTANCE = new Loader();
private static final ImmutableMap<String, ItemCameraTransforms.TransformType> PERSPECTIVES = ImmutableMap.<String, ItemCameraTransforms.TransformType>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<ItemCameraTransforms.TransformType, BlockModel> perspectives = ImmutableMap.builder();
for(Map.Entry<String, ItemCameraTransforms.TransformType> 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());
}
}
}

View file

@ -123,6 +123,10 @@ public class NewModelLoaderTest
new Item(new Item.Properties().group(ItemGroup.MISC)) new Item(new Item.Properties().group(ItemGroup.MISC))
); );
public static RegistryObject<Item> separate_perspective = ITEMS.register("separate_perspective", () ->
new Item(new Item.Properties().group(ItemGroup.MISC))
);
public NewModelLoaderTest() public NewModelLoaderTest()
{ {
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();

View file

@ -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"
}
}
}