From 86c09722a08df626c003abeec290b8036fa6c74d Mon Sep 17 00:00:00 2001 From: RainWarrior Date: Tue, 15 Dec 2015 21:49:19 +0300 Subject: [PATCH] IModel can now depend on a variant definition (ModelResourceLocation); added MultiLayerModel - simple model that'll render correctly in multiple layers + example of using it. --- .../client/model/IPerspectiveAwareModel.java | 23 +- .../client/model/ModelLoader.java | 10 +- .../client/model/ModelLoaderRegistry.java | 1 + .../client/model/MultiLayerModel.java | 261 ++++++++++++++++++ .../client/model/b3d/B3DLoader.java | 7 +- .../client/model/obj/OBJModel.java | 7 +- .../debug/MultiLayerModelDebug.java | 65 +++++ .../blockstates/test_layer_block.json | 18 ++ 8 files changed, 374 insertions(+), 18 deletions(-) create mode 100644 src/main/java/net/minecraftforge/client/model/MultiLayerModel.java create mode 100644 src/test/java/net/minecraftforge/debug/MultiLayerModelDebug.java create mode 100644 src/test/resources/assets/forgedebugmultilayermodel/blockstates/test_layer_block.json diff --git a/src/main/java/net/minecraftforge/client/model/IPerspectiveAwareModel.java b/src/main/java/net/minecraftforge/client/model/IPerspectiveAwareModel.java index 7856bb04c2..de7e797bb5 100644 --- a/src/main/java/net/minecraftforge/client/model/IPerspectiveAwareModel.java +++ b/src/main/java/net/minecraftforge/client/model/IPerspectiveAwareModel.java @@ -68,6 +68,24 @@ public interface IPerspectiveAwareModel extends IFlexibleBakedModel return builder.build(); } + public static Pair handlePerspective(IFlexibleBakedModel model, ImmutableMap transforms, TransformType cameraTransformType) + { + TRSRTransformation tr = transforms.get(cameraTransformType); + Matrix4f mat = null; + if(tr != null && tr != TRSRTransformation.identity()) mat = TRSRTransformation.blockCornerToCenter(tr).getMatrix(); + return Pair.of(model, mat); + } + + public static Pair handlePerspective(IFlexibleBakedModel model, IModelState state, TransformType cameraTransformType) + { + TRSRTransformation tr = state.apply(Optional.of(cameraTransformType)).or(TRSRTransformation.identity()); + if(tr != TRSRTransformation.identity()) + { + return Pair.of(model, TRSRTransformation.blockCornerToCenter(tr).getMatrix()); + } + return Pair.of(model, null); + } + public boolean isAmbientOcclusion() { return parent.isAmbientOcclusion(); } public boolean isGui3d() { return parent.isGui3d(); } public boolean isBuiltInRenderer() { return parent.isBuiltInRenderer(); } @@ -80,10 +98,7 @@ public interface IPerspectiveAwareModel extends IFlexibleBakedModel @Override public Pair handlePerspective(TransformType cameraTransformType) { - TRSRTransformation tr = transforms.get(cameraTransformType); - Matrix4f mat = null; - if(tr != null && tr != TRSRTransformation.identity()) mat = TRSRTransformation.blockCornerToCenter(tr).getMatrix(); - return Pair.of(this, mat); + return handlePerspective(this, transforms, cameraTransformType); } } } diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index fd93a3f4b8..f0b4850b9a 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -227,8 +227,14 @@ public class ModelLoader extends ModelBakery IModel model = ModelLoaderRegistry.getModel(location); for (ResourceLocation dep : model.getDependencies()) { - getModel(dep); - + if(dep instanceof ModelResourceLocation) + { + loadVariants(ImmutableList.of((ModelResourceLocation)dep)); + } + else + { + getModel(dep); + } } textures.addAll(model.getTextures()); } diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java index 6a8f73ee04..ed36a77b4d 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoaderRegistry.java @@ -33,6 +33,7 @@ public class ModelLoaderRegistry registerLoader(OBJLoader.instance); registerLoader(ModelFluid.FluidLoader.instance); registerLoader(ItemLayerModel.Loader.instance); + registerLoader(MultiLayerModel.Loader.instance); } /* diff --git a/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java b/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java new file mode 100644 index 0000000000..ede84210d8 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java @@ -0,0 +1,261 @@ +package net.minecraftforge.client.model; + +import java.util.Collection; +import java.util.List; + +import javax.vecmath.Matrix4f; + +import org.apache.commons.lang3.tuple.Pair; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.model.IBakedModel; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumWorldBlockLayer; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.MinecraftForgeClient; +import net.minecraftforge.client.model.ItemLayerModel.BakedModel; +import net.minecraftforge.fml.common.FMLLog; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +public class MultiLayerModel implements IModelCustomData +{ + public static final MultiLayerModel instance = new MultiLayerModel(ImmutableMap.of()); + + private final ImmutableMap, ModelResourceLocation> models; + + public MultiLayerModel(ImmutableMap, ModelResourceLocation> models) + { + this.models = models; + } + + @Override + public Collection getDependencies() + { + return ImmutableList.copyOf(models.values()); + } + + @Override + public Collection getTextures() + { + return ImmutableList.of(); + } + + @Override + public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter) + { + return new MultiLayerBakedModel(models, format, IPerspectiveAwareModel.MapWrapper.getTransforms(state)); + } + + @Override + public IModelState getDefaultState() + { + return TRSRTransformation.identity(); + } + + @Override + public IModel process(ImmutableMap customData) + { + ImmutableMap.Builder, ModelResourceLocation> builder = ImmutableMap.builder(); + for(String key : customData.keySet()) + { + if("base".equals(key)) + { + builder.put(Optional.absent(), getLocation(customData.get(key))); + } + for(EnumWorldBlockLayer layer : EnumWorldBlockLayer.values()) + { + if(layer.toString().equals(key)) + { + builder.put(Optional.of(layer), getLocation(customData.get(key))); + } + } + } + ImmutableMap, ModelResourceLocation> models = builder.build(); + if(models.isEmpty()) return instance; + return new MultiLayerModel(models); + } + + private ModelResourceLocation getLocation(String json) + { + JsonElement e = new JsonParser().parse(json); + if(e.isJsonPrimitive() && e.getAsJsonPrimitive().isString()) + { + return new ModelResourceLocation(e.getAsString()); + } + FMLLog.severe("Expect ModelResourceLocation, got: ", json); + return new ModelResourceLocation("builtin/missing", "missing"); + } + + public static class MultiLayerBakedModel implements IFlexibleBakedModel, ISmartBlockModel, IPerspectiveAwareModel + { + private final ImmutableMap, ModelResourceLocation> models; + private final VertexFormat format; + private final ImmutableMap cameraTransforms;; + private IBakedModel base; + private ImmutableMap bakedModels; + private ImmutableMap, ImmutableList> quads; + + public MultiLayerBakedModel(ImmutableMap, ModelResourceLocation> models, VertexFormat format, ImmutableMap cameraTransforms) + { + this.models = models; + this.format = format; + this.cameraTransforms = cameraTransforms; + } + + private void compute() + { + if(base == null) + { + ModelManager manager = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelManager(); + base = getModel(manager, Optional.absent()); + + ImmutableMap.Builder builder = ImmutableMap.builder(); + for(EnumWorldBlockLayer layer : EnumWorldBlockLayer.values()) + { + if(models.containsKey(Optional.of(layer))) + { + builder.put(layer, getModel(manager, Optional.of(layer))); + } + } + bakedModels = builder.build(); + + ImmutableMap.Builder, ImmutableList> quadBuilder = ImmutableMap.builder(); + quadBuilder.put(Optional.absent(), buildQuads(Optional.absent())); + for(EnumFacing side: EnumFacing.values()) + { + quadBuilder.put(Optional.of(side), buildQuads(Optional.of(side))); + } + quads = quadBuilder.build(); + } + } + + private IBakedModel getModel(ModelManager manager, Optional layer) + { + ModelResourceLocation loc = models.get(layer); + if(loc == null) + { + loc = new ModelResourceLocation("builtin/missing", "missing"); + } + return manager.getModel(loc); + } + + private ImmutableList buildQuads(Optional side) + { + ImmutableList.Builder builder = ImmutableList.builder(); + for(IBakedModel model : bakedModels.values()) + { + if(side.isPresent()) + { + builder.addAll(model.getFaceQuads(side.get())); + } + else + { + builder.addAll(model.getGeneralQuads()); + } + } + return builder.build(); + } + + @Override + public List getFaceQuads(EnumFacing side) + { + compute(); + return quads.get(Optional.of(side)); + } + + @Override + public List getGeneralQuads() + { + compute(); + return quads.get(Optional.absent()); + } + + @Override + public boolean isAmbientOcclusion() + { + compute(); + return base.isAmbientOcclusion(); + } + + @Override + public boolean isGui3d() + { + compute(); + return base.isGui3d(); + } + + @Override + public boolean isBuiltInRenderer() + { + compute(); + return base.isBuiltInRenderer(); + } + + @Override + public TextureAtlasSprite getTexture() + { + compute(); + return base.getTexture(); + } + + @Override + public ItemCameraTransforms getItemCameraTransforms() + { + return ItemCameraTransforms.DEFAULT; + } + + @Override + public IBakedModel handleBlockState(IBlockState state) + { + compute(); + return bakedModels.get(MinecraftForgeClient.getRenderLayer()); + } + + @Override + public VertexFormat getFormat() + { + return format; + } + + @Override + public Pair handlePerspective(TransformType cameraTransformType) + { + return IPerspectiveAwareModel.MapWrapper.handlePerspective(this, cameraTransforms, cameraTransformType); + } + } + + public static enum Loader implements ICustomModelLoader + { + instance; + + public void onResourceManagerReload(IResourceManager resourceManager) {} + + public boolean accepts(ResourceLocation modelLocation) + { + return modelLocation.getResourceDomain().equals("forge") && ( + modelLocation.getResourcePath().equals("multi-layer") || + modelLocation.getResourcePath().equals("models/block/multi-layer") || + modelLocation.getResourcePath().equals("models/item/multi-layer")); + } + + public IModel loadModel(ResourceLocation modelLocation) + { + return MultiLayerModel.instance; + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java index c06eacba26..5be50b080b 100644 --- a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java +++ b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java @@ -995,12 +995,7 @@ public class B3DLoader implements ICustomModelLoader public Pair handlePerspective(TransformType cameraTransformType) { - TRSRTransformation tr = state.apply(Optional.of(cameraTransformType)).or(TRSRTransformation.identity()); - if(tr != TRSRTransformation.identity()) - { - return Pair.of(this, TRSRTransformation.blockCornerToCenter(tr).getMatrix()); - } - return Pair.of(this, null); + return IPerspectiveAwareModel.MapWrapper.handlePerspective(this, state, cameraTransformType); } } } diff --git a/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java b/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java index f882cb3696..2d2ab24ea2 100644 --- a/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java +++ b/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java @@ -1575,12 +1575,7 @@ public class OBJModel implements IRetexturableModel, IModelCustomData @Override public Pair handlePerspective(TransformType cameraTransformType) { - TRSRTransformation tr = state.apply(Optional.of(cameraTransformType)).or(TRSRTransformation.identity()); - if(tr != TRSRTransformation.identity()) - { - return Pair.of(this, TRSRTransformation.blockCornerToCenter(tr).getMatrix()); - } - return Pair.of(this, null); + return IPerspectiveAwareModel.MapWrapper.handlePerspective(this, state, cameraTransformType); } @Override diff --git a/src/test/java/net/minecraftforge/debug/MultiLayerModelDebug.java b/src/test/java/net/minecraftforge/debug/MultiLayerModelDebug.java new file mode 100644 index 0000000000..2c4d178158 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/MultiLayerModelDebug.java @@ -0,0 +1,65 @@ +package net.minecraftforge.debug; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.Item; +import net.minecraft.util.EnumWorldBlockLayer; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.SidedProxy; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.registry.GameRegistry; + +@Mod(modid = MultiLayerModelDebug.MODID, version = MultiLayerModelDebug.VERSION) +public class MultiLayerModelDebug +{ + public static final String MODID = "forgedebugmultilayermodel"; + public static final String VERSION = "0.0"; + + public static String blockName = "test_layer_block"; + + @SidedProxy(serverSide = "net.minecraftforge.debug.MultiLayerModelDebug$CommonProxy", clientSide = "net.minecraftforge.debug.MultiLayerModelDebug$ClientProxy") + public static CommonProxy proxy; + + public static class CommonProxy + { + public void preInit(FMLPreInitializationEvent event) + { + GameRegistry.registerBlock(new Block(Material.wood) + { + { + setCreativeTab(CreativeTabs.tabBlock); + setUnlocalizedName(MODID + "." + blockName); + } + + @Override + public boolean isOpaqueCube() { return false; } + + @Override + public boolean isFullCube() { return false; } + + @Override + public boolean canRenderInLayer(EnumWorldBlockLayer layer) + { + return layer == EnumWorldBlockLayer.SOLID || layer == EnumWorldBlockLayer.TRANSLUCENT; + } + }, blockName); + } + } + + public static class ClientProxy extends CommonProxy + { + @Override + public void preInit(FMLPreInitializationEvent event) + { + super.preInit(event); + ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(GameRegistry.findBlock(MODID, blockName)), 0, new ModelResourceLocation(MODID.toLowerCase() + ":" + blockName, "inventory")); + } + } + + @EventHandler + public void preInit(FMLPreInitializationEvent event) { proxy.preInit(event); } +} diff --git a/src/test/resources/assets/forgedebugmultilayermodel/blockstates/test_layer_block.json b/src/test/resources/assets/forgedebugmultilayermodel/blockstates/test_layer_block.json new file mode 100644 index 0000000000..50a1efe266 --- /dev/null +++ b/src/test/resources/assets/forgedebugmultilayermodel/blockstates/test_layer_block.json @@ -0,0 +1,18 @@ +{ + "forge_marker": 1, + "defaults": { + "model": "forge:multi-layer", + "custom": { + // base is used for model properties - camera transforms, isGui3d, e.t.c. + "base": "minecraft:stone_slab#half=bottom", + // per-layer models + "Solid": "minecraft:stone_slab#half=bottom", + "Translucent": "minecraft:pink_stained_glass_pane#east=true,north=false,south=false,west=true" + }, + "transform": "forge:default-block" + }, + "variants": { + "normal": [{}], + "inventory": [{}] + } +}