diff --git a/patches/minecraft/net/minecraft/block/Block.java.patch b/patches/minecraft/net/minecraft/block/Block.java.patch index 523a8df49..a946cbdbb 100644 --- a/patches/minecraft/net/minecraft/block/Block.java.patch +++ b/patches/minecraft/net/minecraft/block/Block.java.patch @@ -161,7 +161,7 @@ } protected ItemStack func_180643_i(IBlockState p_180643_1_) -@@ -971,6 +989,1011 @@ +@@ -971,6 +989,1019 @@ return Block.EnumOffsetType.NONE; } @@ -1145,6 +1145,14 @@ + return type != null && type.equals(getHarvestTool(state)); + } + ++ /** ++ * Can return IExtendedBlockState ++ */ ++ public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos) ++ { ++ return func_176221_a(state, world, pos); ++ } ++ + // For Internal use only to capture droped items inside getDrops + protected static ThreadLocal captureDrops = new ThreadLocal() + { diff --git a/patches/minecraft/net/minecraft/block/state/BlockState.java.patch b/patches/minecraft/net/minecraft/block/state/BlockState.java.patch new file mode 100644 index 000000000..90b08e76e --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/BlockState.java.patch @@ -0,0 +1,39 @@ +--- ../src-base/minecraft/net/minecraft/block/state/BlockState.java ++++ ../src-work/minecraft/net/minecraft/block/state/BlockState.java +@@ -47,6 +47,16 @@ + + public BlockState(Block p_i45663_1_, IProperty ... p_i45663_2_) + { ++ this(p_i45663_1_, p_i45663_2_, null); ++ } ++ ++ protected StateImplementation createState(Block block, ImmutableMap properties, ImmutableMap unlistedProperties) ++ { ++ return new StateImplementation(block, properties); ++ } ++ ++ protected BlockState(Block p_i45663_1_, IProperty[] p_i45663_2_, ImmutableMap unlistedProperties) ++ { + this.field_177627_c = p_i45663_1_; + Arrays.sort(p_i45663_2_, new Comparator() + { +@@ -70,7 +80,7 @@ + { + List list = (List)iterator.next(); + Map map = MapPopulator.func_179400_b(this.field_177624_d, list); +- BlockState.StateImplementation stateimplementation = new BlockState.StateImplementation(p_i45663_1_, ImmutableMap.copyOf(map), null); ++ BlockState.StateImplementation stateimplementation = createState(p_i45663_1_, ImmutableMap.copyOf(map), unlistedProperties); + linkedhashmap.put(map, stateimplementation); + arraylist.add(stateimplementation); + } +@@ -231,5 +241,10 @@ + { + this(p_i45661_1_, p_i45661_2_); + } ++ ++ public ImmutableTable getPropertyValueTable() ++ { ++ return field_177238_c; ++ } + } + } diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch new file mode 100644 index 000000000..a24be3d7e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.java +@@ -129,6 +129,12 @@ + ibakedmodel = ((WeightedBakedModel)ibakedmodel).func_177564_a(MathHelper.func_180186_a(p_175022_3_)); + } + ++ if(ibakedmodel instanceof net.minecraftforge.client.model.ISmartBlockModel) ++ { ++ IBlockState extendedState = block.getExtendedState(p_175022_1_, p_175022_2_, p_175022_3_); ++ ibakedmodel = ((net.minecraftforge.client.model.ISmartBlockModel)ibakedmodel).handleBlockState(extendedState); ++ } ++ + return ibakedmodel; + } + diff --git a/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.java.patch b/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.java.patch new file mode 100644 index 000000000..dbe123b28 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.java.patch @@ -0,0 +1,14 @@ +--- ../src-base/minecraft/net/minecraft/client/renderer/ItemModelMesher.java ++++ ../src-work/minecraft/net/minecraft/client/renderer/ItemModelMesher.java +@@ -52,6 +52,11 @@ + } + } + ++ if(ibakedmodel instanceof net.minecraftforge.client.model.ISmartItemModel) ++ { ++ ibakedmodel = ((net.minecraftforge.client.model.ISmartItemModel)ibakedmodel).handleItemState(p_178089_1_); ++ } ++ + if (ibakedmodel == null) + { + ibakedmodel = this.field_178090_d.func_174951_a(); diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.java.patch new file mode 100644 index 000000000..9b2da14ec --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.java.patch @@ -0,0 +1,10 @@ +--- ../src-base/minecraft/net/minecraft/client/resources/model/ModelManager.java ++++ ../src-work/minecraft/net/minecraft/client/resources/model/ModelManager.java +@@ -28,6 +28,7 @@ + ModelBakery modelbakery = new ModelBakery(p_110549_1_, this.field_174956_b, this.field_174957_c); + this.field_174958_a = modelbakery.func_177570_a(); + this.field_174955_d = (IBakedModel)this.field_174958_a.func_82594_a(ModelBakery.field_177604_a); ++ net.minecraftforge.client.ForgeHooksClient.onModelBake(this, this.field_174958_a, modelbakery); + this.field_174957_c.func_178124_c(); + } + diff --git a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java index 9b9b75e7a..d0126b678 100644 --- a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -2,41 +2,31 @@ package net.minecraftforge.client; import static net.minecraftforge.common.ForgeVersion.Status.BETA; import static net.minecraftforge.common.ForgeVersion.Status.BETA_OUTDATED; -import java.util.Random; -import javax.imageio.ImageIO; import net.minecraft.block.Block; -import net.minecraft.block.BlockBed; -import net.minecraft.block.BlockLiquid; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.ISound; import net.minecraft.client.audio.SoundEventAccessorComposite; import net.minecraft.client.audio.SoundManager; -import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.model.ModelBase; -import net.minecraft.client.model.ModelBiped; import net.minecraft.client.renderer.EntityRenderer; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderGlobal; -import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.resources.I18n; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.ModelManager; import net.minecraft.client.settings.GameSettings; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.util.BlockPos; -import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.MathHelper; +import net.minecraft.util.IRegistry; import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; @@ -45,6 +35,7 @@ import net.minecraftforge.client.event.DrawBlockHighlightEvent; import net.minecraftforge.client.event.EntityViewRenderEvent; import net.minecraftforge.client.event.FOVUpdateEvent; import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.MouseEvent; import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderWorldLastEvent; @@ -54,14 +45,9 @@ import net.minecraftforge.common.ForgeModContainer; import net.minecraftforge.common.ForgeVersion; import net.minecraftforge.common.ForgeVersion.Status; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fml.client.FMLClientHandler; -import net.minecraftforge.fml.client.registry.RenderingRegistry; -import net.minecraftforge.fml.common.FMLLog; -import org.lwjgl.LWJGLException; -import org.lwjgl.opengl.Display; + import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.PixelFormat; //import static net.minecraftforge.client.IItemRenderer.ItemRenderType.*; //import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.*; @@ -470,4 +456,9 @@ public class ForgeHooksClient } } */ + + public static void onModelBake(ModelManager modelManager, IRegistry modelRegistry, ModelBakery modelBakery) + { + MinecraftForge.EVENT_BUS.post(new ModelBakeEvent(modelManager, modelRegistry, modelBakery)); + } } diff --git a/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java b/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java new file mode 100644 index 000000000..502454f07 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java @@ -0,0 +1,24 @@ +package net.minecraftforge.client.event; + +import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.util.IRegistry; + +/** + * Fired when the ModelManager is notified of the resource manager reloading. + * Called after model registry is setup, but before it's passed to BlockModelShapes. + */ +public class ModelBakeEvent extends Event +{ + public final ModelManager modelManager; + public final IRegistry modelRegistry; + public final ModelBakery modelBakery; + + public ModelBakeEvent(ModelManager modelManager, IRegistry modelRegistry, ModelBakery modelBakery) + { + this.modelManager = modelManager; + this.modelRegistry = modelRegistry; + this.modelBakery = modelBakery; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/ISmartBlockModel.java b/src/main/java/net/minecraftforge/client/model/ISmartBlockModel.java new file mode 100644 index 000000000..37dee843e --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ISmartBlockModel.java @@ -0,0 +1,9 @@ +package net.minecraftforge.client.model; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.resources.model.IBakedModel; + +public interface ISmartBlockModel extends IBakedModel +{ + IBakedModel handleBlockState(IBlockState state); +} diff --git a/src/main/java/net/minecraftforge/client/model/ISmartItemModel.java b/src/main/java/net/minecraftforge/client/model/ISmartItemModel.java new file mode 100644 index 000000000..7973271d4 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ISmartItemModel.java @@ -0,0 +1,9 @@ +package net.minecraftforge.client.model; + +import net.minecraft.item.ItemStack; +import net.minecraft.client.resources.model.IBakedModel; + +public interface ISmartItemModel extends IBakedModel +{ + IBakedModel handleItemState(ItemStack stack); +} diff --git a/src/main/java/net/minecraftforge/common/property/ExtendedBlockState.java b/src/main/java/net/minecraftforge/common/property/ExtendedBlockState.java new file mode 100644 index 000000000..ef54140b2 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/property/ExtendedBlockState.java @@ -0,0 +1,138 @@ +package net.minecraftforge.common.property; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.block.Block; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.state.BlockState; +import net.minecraft.block.state.IBlockState; + +import com.google.common.base.Optional; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Iterables; + +public class ExtendedBlockState extends BlockState +{ + private final ImmutableSet> unlistedProperties; + + public ExtendedBlockState(Block blockIn, IProperty[] properties, IUnlistedProperty[] unlistedProperties) + { + super(blockIn, properties, buildUnlistedMap(unlistedProperties)); + ImmutableSet.Builder> builder = ImmutableSet.>builder(); + for(IUnlistedProperty property : unlistedProperties) + { + builder.add(property); + } + this.unlistedProperties = builder.build(); + } + + private static ImmutableMap, Optional> buildUnlistedMap(IUnlistedProperty[] unlistedProperties) + { + ImmutableMap.Builder, Optional> builder = ImmutableMap., Optional>builder(); + for(IUnlistedProperty p : unlistedProperties) + { + builder.put(p, Optional.absent()); + } + return builder.build(); + } + + @Override + protected StateImplementation createState(Block block, ImmutableMap properties, ImmutableMap unlistedProperties) + { + return new ExtendedStateImplementation(block, properties, unlistedProperties, null); + } + + protected static class ExtendedStateImplementation extends StateImplementation implements IExtendedBlockState + { + private final ImmutableMap, Optional> unlistedProperties; + private Map, IBlockState> normalMap; + + protected ExtendedStateImplementation(Block block, ImmutableMap properties, ImmutableMap, Optional> unlistedProperties, ImmutableTable table) + { + super(block, properties); + this.unlistedProperties = unlistedProperties; + this.propertyValueTable = table; + } + + @Override + public IBlockState withProperty(IProperty property, Comparable value) + { + if (!this.getProperties().containsKey(property)) + { + throw new IllegalArgumentException("Cannot set property " + property + " as it does not exist in " + getBlock().getBlockState()); + } + else if (!property.getAllowedValues().contains(value)) + { + throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on block " + Block.blockRegistry.getNameForObject(getBlock()) + ", it is not an allowed value"); + } + else + { + if(this.getProperties().get(property) == value) + { + return this; + } + if(Iterables.all(unlistedProperties.values(), Predicates.>equalTo(Optional.absent()))) + { // no dynamic properties present, looking up in the normal table + return super.withProperty(property, value); + } + Map map = new HashMap(getProperties()); + map.put(property, value); + ImmutableTable table = propertyValueTable; + table = ((StateImplementation)table.get(property, value)).getPropertyValueTable(); + return new ExtendedStateImplementation(getBlock(), ImmutableMap.copyOf(map), unlistedProperties, table); + } + } + + public IExtendedBlockState withProperty(IUnlistedProperty property, V value) + { + if(!this.unlistedProperties.containsKey(property)) + { + throw new IllegalArgumentException("Cannot set unlisted property " + property + " as it does not exist in " + getBlock().getBlockState()); + } + if(!property.isValid(value)) + { + throw new IllegalArgumentException("Cannot set unlisted property " + property + " to " + value + " on block " + Block.blockRegistry.getNameForObject(getBlock()) + ", it is not an allowed value"); + } + Map, Optional> newMap = new HashMap, Optional>(unlistedProperties); + newMap.put(property, Optional.fromNullable(value)); + if(Iterables.all(newMap.values(), Predicates.>equalTo(Optional.absent()))) + { // no dynamic properties, lookup normal state + return (IExtendedBlockState) normalMap.get(getProperties()); + } + return new ExtendedStateImplementation(getBlock(), getProperties(), ImmutableMap.copyOf(newMap), propertyValueTable); + } + + public Collection> getUnlistedNames() + { + return Collections.unmodifiableCollection(unlistedProperties.keySet()); + } + + public V getValue(IUnlistedProperty property) + { + if(!this.unlistedProperties.containsKey(property)) + { + throw new IllegalArgumentException("Cannot get unlisted property " + property + " as it does not exist in " + getBlock().getBlockState()); + } + return property.getType().cast(this.unlistedProperties.get(property).orNull()); + } + + public ImmutableMap, Optional> getUnlistedProperties() + { + return unlistedProperties; + } + + @Override + public void buildPropertyValueTable(Map map) + { + this.normalMap = map; + super.buildPropertyValueTable(map); + } + } +} diff --git a/src/main/java/net/minecraftforge/common/property/IExtendedBlockState.java b/src/main/java/net/minecraftforge/common/property/IExtendedBlockState.java new file mode 100644 index 000000000..218e42711 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/property/IExtendedBlockState.java @@ -0,0 +1,19 @@ +package net.minecraftforge.common.property; + +import java.util.Collection; + +import net.minecraft.block.state.IBlockState; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; + +public interface IExtendedBlockState extends IBlockState +{ + Collection> getUnlistedNames(); + + V getValue(IUnlistedProperty property); + + IExtendedBlockState withProperty(IUnlistedProperty property, V value); + + ImmutableMap, Optional> getUnlistedProperties(); +} diff --git a/src/main/java/net/minecraftforge/common/property/IUnlistedProperty.java b/src/main/java/net/minecraftforge/common/property/IUnlistedProperty.java new file mode 100644 index 000000000..da4b70219 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/property/IUnlistedProperty.java @@ -0,0 +1,12 @@ +package net.minecraftforge.common.property; + +public interface IUnlistedProperty +{ + String getName(); + + boolean isValid(V value); + + Class getType(); + + String valueToString(V value); +} diff --git a/src/main/java/net/minecraftforge/common/property/Properties.java b/src/main/java/net/minecraftforge/common/property/Properties.java new file mode 100644 index 000000000..b24e6beea --- /dev/null +++ b/src/main/java/net/minecraftforge/common/property/Properties.java @@ -0,0 +1,45 @@ +package net.minecraftforge.common.property; + +import java.lang.reflect.InvocationTargetException; + +import net.minecraft.block.properties.IProperty; + +import org.apache.commons.lang3.reflect.ConstructorUtils; + +public class Properties +{ + public static

IUnlistedProperty toUnlisted(P property) + { + return new PropertyAdapter(property); + } + + public static class PropertyAdapter implements IUnlistedProperty + { + private final IProperty parent; + + public PropertyAdapter(IProperty parent) + { + this.parent = parent; + } + + public String getName() + { + return parent.getName(); + } + + public boolean isValid(V value) + { + return parent.getAllowedValues().contains(value); + } + + public Class getType() + { + return parent.getValueClass(); + } + + public String valueToString(V value) + { + return parent.getName(value); + } + } +} diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index c2381c6b4..f59ef2681 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -26,4 +26,5 @@ net/minecraft/block/BlockFire.tryCatchFire(Lnet/minecraft/world/World;Lnet/minec net/minecraft/block/BlockSkull.getDrops(Lnet/minecraft/world/IBlockAccess;Lnet/minecraft/util/BlockPos;Lnet/minecraft/block/state/IBlockState;I)Ljava/util/List;=|p_180663_1_,p_180663_2_,p_180663_3_,fortune net/minecraft/item/ItemDye.applyBonemeal(Lnet/minecraft/item/ItemStack;Lnet/minecraft/world/World;Lnet/minecraft/util/BlockPos;Lnet/minecraft/entity/player/EntityPlayer;)Z=|p_179234_0_,p_179234_1_,p_179234_2_,player net/minecraft/server/management/ItemInWorldManager.removeBlock(Lnet/minecraft/util/BlockPos;Z)Z=|p_180235_1_,canHarvest -net/minecraft/client/gui/GuiScreen.drawHoveringText(Ljava/util/List;IILnet/minecraft/client/gui/FontRenderer;)V=|p_146283_1_,p_146283_2_,p_146283_3_,font \ No newline at end of file +net/minecraft/client/gui/GuiScreen.drawHoveringText(Ljava/util/List;IILnet/minecraft/client/gui/FontRenderer;)V=|p_146283_1_,p_146283_2_,p_146283_3_,font +net/minecraft/block/state/BlockState.(Lnet/minecraft/block/Block;[Lnet/minecraft/block/properties/IProperty;Lcom/google/common/collect/ImmutableMap;)V=|p_i45663_1_,p_i45663_2_,unlistedProperties diff --git a/src/main/resources/forge_at.cfg b/src/main/resources/forge_at.cfg index ff42e14a5..6c78cf234 100644 --- a/src/main/resources/forge_at.cfg +++ b/src/main/resources/forge_at.cfg @@ -119,4 +119,7 @@ public net.minecraft.world.gen.ChunkProviderServer field_73251_h # worldObj # RenderEntityItem protected net.minecraft.client.renderer.entity.RenderEntityItem func_177078_a(Lnet/minecraft/item/ItemStack;)I # getMiniItemCount public net.minecraft.item.crafting.RecipesBanners$RecipeAddPattern -public net.minecraft.item.crafting.RecipesBanners$RecipeDuplicatePattern \ No newline at end of file +public net.minecraft.item.crafting.RecipesBanners$RecipeDuplicatePattern +protected net.minecraft.block.state.BlockState$StateImplementation +protected net.minecraft.block.state.BlockState$StateImplementation (Lnet/minecraft/block/Block;Lcom/google/common/collect/ImmutableMap;)V +protected net.minecraft.block.state.BlockState$StateImplementation field_177238_c # propertyValueTable diff --git a/src/test/java/net/minecraftforge/debug/ModelBakeEventDebug.java b/src/test/java/net/minecraftforge/debug/ModelBakeEventDebug.java new file mode 100644 index 000000000..c8de9a48b --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/ModelBakeEventDebug.java @@ -0,0 +1,354 @@ +package net.minecraftforge.debug; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.properties.PropertyInteger; +import net.minecraft.block.state.BlockState; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemMeshDefinition; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.IBakedModel; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.Vec3; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.client.model.ISmartBlockModel; +import net.minecraftforge.client.model.ISmartItemModel; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.property.ExtendedBlockState; +import net.minecraftforge.common.property.IExtendedBlockState; +import net.minecraftforge.common.property.IUnlistedProperty; +import net.minecraftforge.common.property.Properties; +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.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.GameRegistry; + +import com.google.common.primitives.Ints; + +@Mod(modid = ModelBakeEventDebug.MODID, version = ModelBakeEventDebug.VERSION) +public class ModelBakeEventDebug +{ + public static final String MODID = "ForgeDebugModelBakeEvent"; + public static final String VERSION = "1.0"; + public static final int cubeSize = 3; + + private static String blockName = MODID.toLowerCase() + ":" + CustomModelBlock.name; + + public static final IUnlistedProperty[] properties = new IUnlistedProperty[6]; + + static + { + for(EnumFacing f : EnumFacing.values()) + { + properties[f.ordinal()] = Properties.toUnlisted(PropertyInteger.create(f.getName(), 0, (1 << (cubeSize * cubeSize)) - 1)); + } + } + + @SidedProxy(serverSide = "net.minecraftforge.debug.ModelBakeEventDebug$CommonProxy", clientSide = "net.minecraftforge.debug.ModelBakeEventDebug$ClientProxy") + public static CommonProxy proxy; + + @EventHandler + public void init(FMLInitializationEvent event) { proxy.init(event); } + + @EventHandler + public void postInit(FMLPostInitializationEvent event) { proxy.postInit(event); } + + public static class CommonProxy + { + public void init(FMLInitializationEvent event) + { + GameRegistry.registerBlock(CustomModelBlock.instance, CustomModelBlock.name); + GameRegistry.registerTileEntity(CustomTileEntity.class, MODID.toLowerCase() + ":custom_tile_entity"); + } + + public void postInit(FMLPostInitializationEvent event) {} + } + + public static class ClientProxy extends CommonProxy + { + private static ModelResourceLocation modelLocation = new ModelResourceLocation(blockName, null); + + @Override + public void init(FMLInitializationEvent event) + { + super.init(event); + MinecraftForge.EVENT_BUS.register(BakeEventHandler.instance); + } + + @Override + public void postInit(FMLPostInitializationEvent event) { + super.postInit(event); + Item item = Item.getItemFromBlock(CustomModelBlock.instance); + RenderItem renderItem = Minecraft.getMinecraft().getRenderItem(); + if(renderItem != null) + { + renderItem.getItemModelMesher().register(item, new ItemMeshDefinition() { + public ModelResourceLocation getModelLocation(ItemStack stack) + { + return modelLocation; + } + }); + } + } + } + + public static class BakeEventHandler + { + public static final BakeEventHandler instance = new BakeEventHandler(); + + private BakeEventHandler() {}; + + @SubscribeEvent + public void onModelBakeEvent(ModelBakeEvent event) + { + TextureAtlasSprite base = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite("minecraft:blocks/slime"); + TextureAtlasSprite overlay = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite("minecraft:blocks/redstone_block"); + event.modelRegistry.putObject(ClientProxy.modelLocation, new CustomModel(base, overlay)); + } + } + + public static class CustomModelBlock extends BlockContainer + { + public static final CustomModelBlock instance = new CustomModelBlock(); + public static final String name = "custom_model_block"; + + private CustomModelBlock() + { + super(Material.iron); + setCreativeTab(CreativeTabs.tabBlock); + setUnlocalizedName(MODID + ":" + name); + } + + @Override + public int getRenderType() { return 3; } + + @Override + public boolean isOpaqueCube() { return false; } + + @Override + public boolean isFullCube() { return false; } + + @Override + public boolean isVisuallyOpaque() { return false; } + + @Override + public TileEntity createNewTileEntity(World world, int meta) + { + return new CustomTileEntity(); + } + + @Override + public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumFacing side, float hitX, float hitY, float hitZ) + { + TileEntity te = world.getTileEntity(pos); + if(te instanceof CustomTileEntity) + { + CustomTileEntity cte = (CustomTileEntity) te; + Vec3 vec = revRotate(new Vec3(hitX - .5, hitY - .5, hitZ - .5), side).addVector(.5, .5, .5); + IUnlistedProperty property = properties[side.ordinal()]; + Integer value = (Integer)cte.getState().getValue(property); + if(value == null) value = 0; + value ^= (1 << ( cubeSize * ((int)(vec.xCoord * (cubeSize - .0001))) + ((int)(vec.zCoord * (cubeSize - .0001))) )); + cte.setState(cte.getState().withProperty(property, value)); + world.markBlockRangeForRenderUpdate(pos, pos); + } + return true; + } + + @Override + public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos) + { + TileEntity te = world.getTileEntity(pos); + if(te instanceof CustomTileEntity) + { + CustomTileEntity cte = (CustomTileEntity) te; + return cte.getState(); + } + return state; + } + + @Override + protected BlockState createBlockState() + { + return new ExtendedBlockState(this, new IProperty[0], properties); + } + } + + public static class CustomTileEntity extends TileEntity + { + private IExtendedBlockState state; + public CustomTileEntity() {} + + public IExtendedBlockState getState() + { + if(state == null) + { + state = (IExtendedBlockState)getBlockType().getDefaultState(); + } + return state; + } + + public void setState(IExtendedBlockState state) + { + this.state = state; + } + } + + public static class CustomModel implements IBakedModel, ISmartBlockModel, ISmartItemModel + { + private final TextureAtlasSprite base, overlay; + private boolean hasStateSet = false; + private final IExtendedBlockState state; + + public CustomModel(TextureAtlasSprite base, TextureAtlasSprite overlay) + { + this(base, overlay, null); + } + + public CustomModel(TextureAtlasSprite base, TextureAtlasSprite overlay, IExtendedBlockState state) + { + this.base = base; + this.overlay = overlay; + this.state = state; + } + + @Override + public List getFaceQuads(EnumFacing side) + { + return Collections.emptyList(); + } + + private int[] vertexToInts(float x, float y, float z, int color, TextureAtlasSprite texture, float u, float v) + { + return new int[] { + Float.floatToRawIntBits(x), + Float.floatToRawIntBits(y), + Float.floatToRawIntBits(z), + color, + Float.floatToRawIntBits(texture.getInterpolatedU(u)), + Float.floatToRawIntBits(texture.getInterpolatedV(v)), + 0 + }; + } + + private BakedQuad createSidedBakedQuad(float x1, float x2, float z1, float z2, float y, TextureAtlasSprite texture, EnumFacing side) + { + Vec3 v1 = rotate(new Vec3(x1 - .5, y - .5, z1 - .5), side).addVector(.5, .5, .5); + Vec3 v2 = rotate(new Vec3(x1 - .5, y - .5, z2 - .5), side).addVector(.5, .5, .5); + Vec3 v3 = rotate(new Vec3(x2 - .5, y - .5, z2 - .5), side).addVector(.5, .5, .5); + Vec3 v4 = rotate(new Vec3(x2 - .5, y - .5, z1 - .5), side).addVector(.5, .5, .5); + return new BakedQuad(Ints.concat( + vertexToInts((float)v1.xCoord, (float)v1.yCoord, (float)v1.zCoord, -1, texture, 0, 0), + vertexToInts((float)v2.xCoord, (float)v2.yCoord, (float)v2.zCoord, -1, texture, 0, 16), + vertexToInts((float)v3.xCoord, (float)v3.yCoord, (float)v3.zCoord, -1, texture, 16, 16), + vertexToInts((float)v4.xCoord, (float)v4.yCoord, (float)v4.zCoord, -1, texture, 16, 0) + ), -1, side); + } + + @Override + public List getGeneralQuads() + { + int len = cubeSize * 5 + 1; + List ret = new ArrayList(); + for(EnumFacing f : EnumFacing.values()) + { + ret.add(createSidedBakedQuad(0, 1, 0, 1, 1, base, f)); + for(int i = 0; i < cubeSize; i++) + { + for(int j = 0; j < cubeSize; j++) + { + if(state != null) + { + Integer value = (Integer)state.getValue(properties[f.ordinal()]); + if(value != null && (value & (1 << (i * cubeSize + j))) != 0) + { + ret.add(createSidedBakedQuad((float)(1 + i * 5) / len, (float)(5 + i * 5) / len, (float)(1 + j * 5) / len, (float)(5 + j * 5) / len, 1.0001f, overlay, f)); + } + } + } + } + } + return ret; + } + + @Override + public boolean isGui3d() { return true; } + + @Override + public boolean isAmbientOcclusion() { return true; } + + @Override + public boolean isBuiltInRenderer() { return false; } + + @Override + public TextureAtlasSprite getTexture() { return this.base; } + + @Override + public ItemCameraTransforms getItemCameraTransforms() + { + return ItemCameraTransforms.DEFAULT; + } + + @Override + public IBakedModel handleBlockState(IBlockState state) + { + return new CustomModel(base, overlay, (IExtendedBlockState)state); + } + + @Override + public IBakedModel handleItemState(ItemStack stack) + { + IExtendedBlockState itemState = ((IExtendedBlockState)CustomModelBlock.instance.getDefaultState()).withProperty(properties[1], (1 << (cubeSize * cubeSize)) - 1); + return new CustomModel(base, overlay, itemState); + } + } + + private static Vec3 rotate(Vec3 vec, EnumFacing side) + { + switch(side) + { + case DOWN: return new Vec3( vec.xCoord, -vec.yCoord, -vec.zCoord); + case UP: return new Vec3( vec.xCoord, vec.yCoord, vec.zCoord); + case NORTH: return new Vec3( vec.xCoord, vec.zCoord, -vec.yCoord); + case SOUTH: return new Vec3( vec.xCoord, -vec.zCoord, vec.yCoord); + case WEST: return new Vec3(-vec.yCoord, vec.xCoord, vec.zCoord); + case EAST: return new Vec3( vec.yCoord, -vec.xCoord, vec.zCoord); + } + return null; + } + + private static Vec3 revRotate(Vec3 vec, EnumFacing side) + { + switch(side) + { + case DOWN: return new Vec3( vec.xCoord, -vec.yCoord, -vec.zCoord); + case UP: return new Vec3( vec.xCoord, vec.yCoord, vec.zCoord); + case NORTH: return new Vec3( vec.xCoord, -vec.zCoord, vec.yCoord); + case SOUTH: return new Vec3( vec.xCoord, vec.zCoord, -vec.yCoord); + case WEST: return new Vec3( vec.yCoord, -vec.xCoord, vec.zCoord); + case EAST: return new Vec3(-vec.yCoord, vec.xCoord, vec.zCoord); + } + return null; + } +} diff --git a/src/test/resources/assets/forgedebugmodelbakeevent/blockstates/custom_model_block.json b/src/test/resources/assets/forgedebugmodelbakeevent/blockstates/custom_model_block.json new file mode 100644 index 000000000..c2352ff43 --- /dev/null +++ b/src/test/resources/assets/forgedebugmodelbakeevent/blockstates/custom_model_block.json @@ -0,0 +1,4 @@ +{ + "variants": { + } +} diff --git a/src/test/resources/assets/forgedebugmodelbakeevent/models/item/custom_model_block.json b/src/test/resources/assets/forgedebugmodelbakeevent/models/item/custom_model_block.json new file mode 100644 index 000000000..43a272a60 --- /dev/null +++ b/src/test/resources/assets/forgedebugmodelbakeevent/models/item/custom_model_block.json @@ -0,0 +1,3 @@ +{ + "parent": "builtin/entity" +}