mirror of
https://github.com/GTNewHorizons/GTNHLib.git
synced 2026-11-08 12:34:52 +00:00
Add proper automatic model detection (#313)
Co-authored-by: Ruling-0 <47913168+Ruling-0@users.noreply.github.com>
This commit is contained in:
1 parent
43db33a076
commit
ae2de77b74
33 files changed
+641
-376
No files matched your search
@@ -5,6 +5,7 @@ plugins {
|
||||
|
||||
minecraft {
|
||||
//extraRunJvmArguments.add("-Dgtnhlib.dumpClass=true")
|
||||
//extraRunJvmArguments.addAll("-Dlegacy.debugClassLoading=true", "-Dlegacy.debugClassLoadingFiner=true", "-Dlegacy.debugClassLoadingSave=true")
|
||||
}
|
||||
|
||||
// Add a Java 17 sourceset for including code optimized for newer Java versions
|
||||
|
||||
@@ -2,15 +2,12 @@ package com.gtnewhorizon.gtnhlib;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.GTNHLib.MODID;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.SimpleReloadableResourceManager;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraftforge.client.ClientCommandHandler;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.ResourcePackUpdater.ResourcePackUpdateEventHandler;
|
||||
@@ -92,19 +89,6 @@ public class ClientProxy extends CommonProxy {
|
||||
final var resourceManager = ((SimpleReloadableResourceManager) Minecraft.getMinecraft().getResourceManager());
|
||||
resourceManager.registerReloadListener(new ModelRegistry.ReloadListener());
|
||||
MinecraftForge.EVENT_BUS.register(new ModelRegistry.EventHandler());
|
||||
|
||||
// Applied Item Render for Block Use ModelISBRH
|
||||
for (Object obj : Block.blockRegistry) {
|
||||
try {
|
||||
Block block = (Block) obj;
|
||||
if (block.getRenderType() == ModelISBRH.JSON_ISBRH_ID) {
|
||||
Item item = Item.getItemFromBlock(block);
|
||||
if (item != null) {
|
||||
MinecraftForgeClient.registerItemRenderer(item, ModelISBRH.INSTANCE);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {} // Some blocks may throw an Exception here, we can simply ignore it.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,24 +2,14 @@ package com.gtnewhorizon.gtnhlib;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.core.GTNHLibCore.isObf;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.command.BlockStateCommand;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockPropertyTrait;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.init.BlockPropertyInit;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.properties.DirectionBlockProperty;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.registry.BlockPropertyRegistry;
|
||||
import com.gtnewhorizon.gtnhlib.brigadier.BrigadierApi;
|
||||
import com.gtnewhorizon.gtnhlib.chat.ChatComponentCustomRegistry;
|
||||
import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentEnergy;
|
||||
@@ -39,7 +29,6 @@ import com.gtnewhorizon.gtnhlib.network.PacketViewDistance;
|
||||
import com.gtnewhorizon.gtnhlib.test.block.BlockTest;
|
||||
import com.gtnewhorizon.gtnhlib.test.block.BlockTestTint;
|
||||
import com.gtnewhorizon.gtnhlib.test.block.BlockTestTintMul;
|
||||
import com.gtnewhorizon.gtnhlib.test.block.TileTestTintMul;
|
||||
import com.gtnewhorizon.gtnhlib.test.item.TestItem;
|
||||
import com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatConfig;
|
||||
import com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatUtil;
|
||||
@@ -69,52 +58,9 @@ public class CommonProxy {
|
||||
GTNHLib.info("GTNHLib version " + Tags.VERSION + " loaded.");
|
||||
|
||||
if (GTNHLibConfig.enableTestBlocks) {
|
||||
GameRegistry.registerBlock(new BlockTest(), "model_test");
|
||||
GameRegistry.registerBlock(new BlockTestTint(), "model_test_tint");
|
||||
|
||||
Block block = new BlockTestTintMul();
|
||||
GameRegistry.registerBlock(block, "model_test_tint_mul");
|
||||
GameRegistry.registerTileEntity(TileTestTintMul.class, "tile_test_tint_mul");
|
||||
DirectionBlockProperty property = new DirectionBlockProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "facing";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTrait(BlockPropertyTrait trait) {
|
||||
return switch (trait) {
|
||||
case SupportsWorld, WorldMutable, StackMutable, SupportsStacks -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeDirection getValue(IBlockAccess world, int x, int y, int z) {
|
||||
TileEntity te = world.getTileEntity(x, y, z);
|
||||
if (te instanceof TileTestTintMul tile) {
|
||||
return tile.getFacing();
|
||||
}
|
||||
return ForgeDirection.NORTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(World world, int x, int y, int z, ForgeDirection value) {
|
||||
TileEntity te = world.getTileEntity(x, y, z);
|
||||
if (te instanceof TileTestTintMul tile) {
|
||||
tile.setFacing(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeDirection getValue(ItemStack stack) {
|
||||
return ForgeDirection.NORTH;
|
||||
}
|
||||
};
|
||||
|
||||
BlockPropertyRegistry.registerProperty(block, property);
|
||||
BlockPropertyRegistry.registerProperty(Item.getItemFromBlock(block), property);
|
||||
BlockTest.register();
|
||||
BlockTestTint.register();
|
||||
BlockTestTintMul.register();
|
||||
}
|
||||
|
||||
if (GTNHLibConfig.enableTestItems) {
|
||||
|
||||
@@ -19,15 +19,6 @@ public class GTNHLibConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static int modelCacheSize;
|
||||
|
||||
@Config.Comment("Automatically load model textures based on model files. Disabling may cause models to lose their textures.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean autoTextureLoading;
|
||||
|
||||
@Config.Comment("Ensures that blocks always return a valid icon for JSON model blocks, by overriding the block icon functions and passing the particle icon.")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean modelIconWrapperMixin;
|
||||
|
||||
@Config.Comment("Enables various mixins that allow blocks to dynamically change their sound.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.gtnewhorizon.gtnhlib.api;
|
||||
|
||||
/// This class is mixed into {@link net.minecraft.block.Block}, so the model loader can store model information on them.
|
||||
/// The data must *only* be mutated by the resource reload manager, but can be read by anyone. If your model can only
|
||||
/// be found by a custom locator, you can implement this yourself and override [#nhlib$isModeled()] to always be true.
|
||||
public interface BlockModelInfo {
|
||||
|
||||
boolean nhlib$isModeled();
|
||||
|
||||
void nhlib$setModeled(boolean modeled);
|
||||
}
|
||||
@@ -6,11 +6,8 @@ import com.gtnewhorizon.gtnhlib.client.model.BakedModelQuadContext;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.ModelISBRH;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.baked.BakedModel;
|
||||
|
||||
/**
|
||||
* An interface for {@link Block}s to allow providing custom models for {@link ModelISBRH}. Not necessary for standard
|
||||
* JSON models. This effectively allows for overwriting {@link ModelISBRH#getModel} without needing a child class, which
|
||||
* avoids needing a new render ID (and associated mixins).
|
||||
*/
|
||||
/// An interface for {@link Block}s to allow providing custom models for {@link ModelISBRH}. Not necessary for standard
|
||||
/// JSON models. This allows for custom model detection and baking logic.
|
||||
public interface IBlockModelProvider {
|
||||
|
||||
BakedModel getModel(BakedModelQuadContext context);
|
||||
|
||||
@@ -36,6 +36,51 @@ public class BlockPropertyRegistry {
|
||||
|
||||
private BlockPropertyRegistry() {}
|
||||
|
||||
/// Registers the property as normal (see [#registerProperty(Block, BlockProperty)]), and additionally adds it to
|
||||
/// the respective item (obtained from [Item#getItemFromBlock(Block)]). The item property is assigned irrespective
|
||||
/// of metadata, and has only one value - the default passed here.
|
||||
public static <T> void registerBlockItemProperty(Block block, BlockProperty<T> property, T defauld) {
|
||||
registerProperty(block, property);
|
||||
registerProperty(Item.getItemFromBlock(block), new BlockProperty<T>() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return property.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return property.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue(ItemStack stack) {
|
||||
return defauld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTrait(BlockPropertyTrait trait) {
|
||||
return trait == BlockPropertyTrait.SupportsStacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stringify(T t) {
|
||||
return property.stringify(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Registers the property on both the block given and its block item.
|
||||
///
|
||||
/// @throws IllegalArgumentException if the property doesn't support itemstacks.
|
||||
public static <T> void registerBlockItemProperty(Block block, BlockProperty<T> property) {
|
||||
if (!property.hasTrait(BlockPropertyTrait.SupportsStacks))
|
||||
throw new IllegalArgumentException("BlockItem property should support ItemStacks!");
|
||||
|
||||
registerProperty(block, property);
|
||||
registerProperty(Item.getItemFromBlock(block), property);
|
||||
}
|
||||
|
||||
private static class PropertyMap<K> extends Object2ObjectOpenHashMap<K, Map<String, BlockProperty<?>>> {
|
||||
|
||||
public void add(K key, BlockProperty<?> prop) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import static net.minecraftforge.client.IItemRenderer.ItemRenderType.INVENTORY;
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -20,11 +21,12 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import net.minecraftforge.client.IItemRenderer;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.api.IBlockModelProvider;
|
||||
import com.gtnewhorizon.gtnhlib.api.BlockModelInfo;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.registry.BlockPropertyRegistry;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.baked.BakedModel;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.color.BlockColor;
|
||||
@@ -33,6 +35,7 @@ import com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuadView;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing;
|
||||
import com.gtnewhorizon.gtnhlib.core.fml.transformers.BlockIconTransformer;
|
||||
import com.gtnewhorizons.angelica.api.ThreadSafeISBRH;
|
||||
|
||||
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||
@@ -43,9 +46,9 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
|
||||
|
||||
public static final ModelISBRH INSTANCE = new ModelISBRH();
|
||||
|
||||
/**
|
||||
* Any blocks using a JSON model should return this for {@link Block#getRenderType()}.
|
||||
*/
|
||||
/// Any blocks using a JSON model may return this for [Block#getRenderType()]. However, models are primarily
|
||||
/// identified via the presence of a blockstate -> model map for the block. If you don't have such a file, you can
|
||||
/// implement {@link BlockModelInfo} instead, and simply always return true.
|
||||
public static final int JSON_ISBRH_ID = RenderingRegistry.getNextAvailableRenderId();
|
||||
|
||||
private final Random RAND = new Random();
|
||||
@@ -55,14 +58,6 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
|
||||
|
||||
public ModelISBRH() {}
|
||||
|
||||
/// For programmatic model selection, see {@link IBlockModelProvider}
|
||||
@SuppressWarnings("unused")
|
||||
public BakedModel getModel(BakedModelQuadContext context) {
|
||||
if (context.getBlockState().getBlock() instanceof IBlockModelProvider selector)
|
||||
return selector.getModel(context);
|
||||
return ModelRegistry.getBakedModel(context.getBlockState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderInventoryBlock(Block block, int meta, int modelId, RenderBlocks renderer) {}
|
||||
|
||||
@@ -82,7 +77,7 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
|
||||
worldContext.blockState = BlockPropertyRegistry.getBlockState(world, x, y, z);
|
||||
worldContext.random = random;
|
||||
|
||||
final BakedModel model = getModel(worldContext);
|
||||
final BakedModel model = ModelRegistry.getBakedModel(worldContext);
|
||||
|
||||
int color = model.getColor(world, x, y, z, block, meta, random);
|
||||
|
||||
@@ -243,7 +238,7 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
|
||||
itemContext.blockState = BlockPropertyRegistry.getBlockState(stack);
|
||||
itemContext.random = RAND;
|
||||
|
||||
final BakedModel model = getModel(itemContext);
|
||||
final BakedModel model = ModelRegistry.getBakedModel(itemContext);
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
@@ -287,11 +282,9 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
|
||||
itemContext.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: We need to find a good way to make it so the bound fields are not accounted for but I still want to use
|
||||
* {@link Block#shouldSideBeRendered}. This is so blocks can define custom culling behavior when needed, but the
|
||||
* bound fields are not relevant here because JSON models don't listen to them.
|
||||
*/
|
||||
/// TODO: We need to find a good way to make it so the bound fields are not accounted for but I still want to use
|
||||
/// [Block#shouldSideBeRendered]. This is so blocks can define custom culling behavior when needed, but the
|
||||
/// bound fields are not relevant here because JSON models don't listen to them.
|
||||
protected boolean shouldSideBeRendered(IBlockAccess world, int x, int y, int z, int side, Block block) {
|
||||
return block.shouldSideBeRendered(world, x, y, z, side);
|
||||
}
|
||||
@@ -418,14 +411,28 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
public IIcon getParticleIcon(Block block, @Nullable IBlockAccess world, int x, int y, int z, int meta) {
|
||||
/// Mirrors the default {@link Block#getIcon(IBlockAccess, int, int, int, int)}, with the exception that the block
|
||||
/// is also re-fetched. This is because the blockstate construction does world access anyway, overriding just the
|
||||
/// block is annoying and seems less correct.
|
||||
///
|
||||
/// Used in {@link BlockIconTransformer}
|
||||
public @NotNull IIcon getParticleIcon(@Nullable IBlockAccess world, int x, int y, int z) {
|
||||
worldContext.world = world;
|
||||
worldContext.x = x;
|
||||
worldContext.y = y;
|
||||
worldContext.z = z;
|
||||
worldContext.random = RAND;
|
||||
worldContext.blockState = BlockPropertyRegistry.getBlockState(world, x, y, z);
|
||||
final var model = getModel(worldContext);
|
||||
return model.getParticle(worldContext);
|
||||
final var model = ModelRegistry.getBakedModel(worldContext);
|
||||
final var ret = model.getParticle(worldContext);
|
||||
|
||||
worldContext.reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Used in {@link BlockIconTransformer}
|
||||
@SuppressWarnings("unused")
|
||||
public @NotNull IIcon getMissingIcon() {
|
||||
return Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite("missingno");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.gtnewhorizon.gtnhlib.client.model.loading;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.GTNHLibConfig.autoTextureLoading;
|
||||
import static com.gtnewhorizon.gtnhlib.GTNHLibConfig.modelCacheSize;
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.unbaked.MissingModel.MISSING_MODEL;
|
||||
import static com.gtnewhorizon.gtnhlib.core.GTNHLibCore.MODEL_LOGGER;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -11,14 +11,19 @@ import net.minecraft.client.resources.FallbackResourceManager;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.client.resources.IResourceManagerReloadListener;
|
||||
import net.minecraft.client.resources.IResourcePack;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.gtnewhorizon.gtnhlib.api.BlockModelInfo;
|
||||
import com.gtnewhorizon.gtnhlib.api.IBlockModelProvider;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockState;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.BakedModelQuadContext;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.ModelISBRH;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.baked.BakedModel;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.state.MissingState;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.state.StateDeserializer;
|
||||
@@ -28,6 +33,7 @@ import com.gtnewhorizon.gtnhlib.concurrent.ThreadsafeCache;
|
||||
|
||||
import cpw.mods.fml.common.FMLContainerHolder;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.registry.GameData;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
@@ -39,7 +45,32 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
/// beyond excessively complex models being loaded multiple times... add a counter if I'm wrong.
|
||||
public class ModelRegistry {
|
||||
|
||||
public static final Logger MODEL_LOGGER = LogManager.getLogger(ModelRegistry.class);
|
||||
/// Obtain a model, diverting to a custom pipeline if it exists. See {@link IBlockModelProvider} if you wish to
|
||||
/// implement said custom pipeline.
|
||||
public static BakedModel getBakedModel(BakedModelQuadContext context) {
|
||||
if (context.getBlockState().getBlock() instanceof IBlockModelProvider selector)
|
||||
return selector.getModel(context);
|
||||
return getBakedModel(context.getBlockState());
|
||||
}
|
||||
|
||||
/// Getter for {@link BakedModel}s, obtained purely from the default path. We don't want to publicly expose the
|
||||
/// cache, modders can't be trusted with it :P
|
||||
public static BakedModel getBakedModel(BlockState state) {
|
||||
return BLOCKSTATE_MODEL_CACHE.get(state.clone());
|
||||
}
|
||||
|
||||
/// Getter for {@link JSONModel}s - again, normal pipeline, but this grabs them pre-bake. See
|
||||
/// {@link ModelRegistry#getBakedModel(BlockState)}
|
||||
public static JSONModel getJSONModel(ResourceLoc.ModelLoc loc) {
|
||||
return JSON_MODEL_CACHE.get(loc);
|
||||
}
|
||||
|
||||
/// Registers the given mod ID for automatic texture loading. The resource pack attached to this mod will be scanned
|
||||
/// for model files, and textures from those files will be automatically loaded.
|
||||
public static void registerModid(String modid) {
|
||||
ReloadListener.PERMITTED_MODIDS.add(modid);
|
||||
}
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().registerTypeAdapter(StateModelMap.class, new StateDeserializer())
|
||||
.registerTypeAdapter(JSONModel.class, new ModelDeserializer()).create();
|
||||
|
||||
@@ -78,22 +109,6 @@ public class ModelRegistry {
|
||||
return dough.bake();
|
||||
}
|
||||
|
||||
/// Getter for {@link BakedModel}s. We don't want to publicly expose the cache, modders can't be trusted with it :P
|
||||
public static BakedModel getBakedModel(BlockState state) {
|
||||
return BLOCKSTATE_MODEL_CACHE.get(state.clone());
|
||||
}
|
||||
|
||||
/// Getter for {@link JSONModel}s. See {@link ModelRegistry#getBakedModel(BlockState)}
|
||||
public static JSONModel getJSONModel(ResourceLoc.ModelLoc loc) {
|
||||
return JSON_MODEL_CACHE.get(loc);
|
||||
}
|
||||
|
||||
/// Registers the given mod ID for automatic texture loading. The resource pack attached to this mod will be scanned
|
||||
/// for model files, and textures from those files will be automatically loaded.
|
||||
public static void registerModid(String modid) {
|
||||
ReloadListener.PERMITTED_MODIDS.add(modid);
|
||||
}
|
||||
|
||||
private static StateModelMap getStateModelMap(Block block) {
|
||||
final var name = BlockName.fromBlock(block);
|
||||
final var stateLocation = new ResourceLoc.StateLoc(name.domain, name.name);
|
||||
@@ -142,14 +157,15 @@ public class ModelRegistry {
|
||||
return;
|
||||
}
|
||||
|
||||
if (autoTextureLoading) detectAndLoadTextures(manager);
|
||||
loadModelInfo(manager);
|
||||
}
|
||||
|
||||
private void detectAndLoadTextures(GlobalResourceManager manager) {
|
||||
private boolean infoMixinFailed = false;
|
||||
|
||||
private void loadModelInfo(GlobalResourceManager manager) {
|
||||
// Scan resource packs for model files
|
||||
final var domains = manager.nhlib$getDomainResourceManagers();
|
||||
final var resourcePacks = new ObjectOpenHashSet<IResourcePack>();
|
||||
|
||||
for (var entry : domains.entrySet()) {
|
||||
var files = entry.getValue();
|
||||
if (files instanceof FallbackResourceManager) {
|
||||
@@ -157,6 +173,8 @@ public class ModelRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
// Gather the list of modeled blocks and their textures
|
||||
final var modeledBlocks = new ObjectOpenHashSet<String>();
|
||||
final var texturesToLoad = new ObjectArrayList<String>();
|
||||
for (var pack : resourcePacks) {
|
||||
if (!(pack instanceof ModelResourcePack mrp)) continue;
|
||||
@@ -166,17 +184,44 @@ public class ModelRegistry {
|
||||
&& !PERMITTED_MODIDS.contains(fmlch.getFMLContainer().getModId()))
|
||||
continue;
|
||||
|
||||
final var texture = mrp.nhlib$getReferencedTextures(reader -> GSON.fromJson(reader, JSONModel.class));
|
||||
texturesToLoad.addAll(texture);
|
||||
final var info = mrp.nhlib$gatherModelInfo(reader -> GSON.fromJson(reader, JSONModel.class));
|
||||
modeledBlocks.addAll(info.modeledBlocks());
|
||||
texturesToLoad.addAll(info.textureNames());
|
||||
}
|
||||
|
||||
GameData.getBlockRegistry().registryObjects.forEach((s, b) -> {
|
||||
if (!(s instanceof String name)) return;
|
||||
if (!(b instanceof Block block)) return;
|
||||
if (!(b instanceof BlockModelInfo modelInfo)) {
|
||||
if (!infoMixinFailed) {
|
||||
MODEL_LOGGER.error("Block registry contained a non-block or the info mixin failed!");
|
||||
MODEL_LOGGER.error("Either you won't notice anything, or all JSON models will stop loading...");
|
||||
infoMixinFailed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
modelInfo.nhlib$setModeled(modeledBlocks.contains(name));
|
||||
// We can't shortcut this, since some blocks may manually implement the interface.
|
||||
if (modelInfo.nhlib$isModeled()) {
|
||||
final var item = Item.getItemFromBlock(block);
|
||||
if (item == null) {
|
||||
MODEL_LOGGER.error(
|
||||
"Block {} has a null item! Unable to register item model.",
|
||||
block.getLocalizedName());
|
||||
return;
|
||||
}
|
||||
MinecraftForgeClient.registerItemRenderer(Item.getItemFromBlock(block), ModelISBRH.INSTANCE);
|
||||
}
|
||||
});
|
||||
|
||||
EventHandler.texturesToLoad = texturesToLoad;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EventHandler {
|
||||
|
||||
private static List<String> texturesToLoad = ObjectLists.emptyList();
|
||||
private static @NotNull List<String> texturesToLoad = ObjectLists.emptyList();
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
@@ -184,6 +229,7 @@ public class ModelRegistry {
|
||||
for (var texture : texturesToLoad) {
|
||||
event.map.registerIcon(texture.replaceFirst("^minecraft:", ""));
|
||||
}
|
||||
texturesToLoad = ObjectLists.emptyList(); // don't need it anymore
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import com.gtnewhorizon.gtnhlib.client.model.unbaked.JSONModel;
|
||||
public interface ModelResourcePack {
|
||||
|
||||
/// Returns a list of the texture names referenced by models in this resource pack.
|
||||
List<String> nhlib$getReferencedTextures(Function<Reader, JSONModel> jsonParser);
|
||||
RPInfo nhlib$gatherModelInfo(Function<Reader, JSONModel> jsonParser);
|
||||
|
||||
default List<String> nhlib$getReferencedTextures(Stream<? extends InputStream> files,
|
||||
Function<Reader, JSONModel> jsonParser) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.gtnewhorizon.gtnhlib.client.model.loading;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
|
||||
/// Tiny holder for data coming from the resourcepack scanning
|
||||
@Desugar
|
||||
public record RPInfo(@NotNull List<String> textureNames, @NotNull List<String> modeledBlocks) {}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizon.gtnhlib.client.model.loading;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry.MODEL_LOGGER;
|
||||
import static com.gtnewhorizon.gtnhlib.core.GTNHLibCore.MODEL_LOGGER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.gtnewhorizon.gtnhlib.client.model.unbaked;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.loading.ModelDeserializer.ModelElement.Rotation.NOOP;
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry.MODEL_LOGGER;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.NEG_Y;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.properties.ModelQuadFacing.POS_Y;
|
||||
import static com.gtnewhorizon.gtnhlib.core.GTNHLibCore.MODEL_LOGGER;
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
import static org.joml.Math.fma;
|
||||
|
||||
@@ -6,6 +6,8 @@ import java.util.Set;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.spongepowered.asm.launch.GlobalProperties;
|
||||
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
|
||||
|
||||
@@ -25,6 +27,8 @@ import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
"com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator" })
|
||||
public class GTNHLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
/// Lifted here so we can safely use it in Mixins. This class should be loaded by the time Mixins fire.
|
||||
public static final Logger MODEL_LOGGER = LogManager.getLogger("GTNHLib|Models");
|
||||
private static Boolean isObf;
|
||||
|
||||
public GTNHLibCore() {
|
||||
@@ -46,7 +50,8 @@ public class GTNHLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
return new String[] { "com.gtnewhorizon.gtnhlib.core.fml.transformers.EventBusSubTransformer" };
|
||||
return new String[] { "com.gtnewhorizon.gtnhlib.core.fml.transformers.EventBusSubTransformer",
|
||||
"com.gtnewhorizon.gtnhlib.core.fml.transformers.BlockIconTransformer" };
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package com.gtnewhorizon.gtnhlib.core.fml.transformers;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.core.fml.transformers.BlockIconTransformer.Blockness.BLOCK;
|
||||
import static com.gtnewhorizon.gtnhlib.core.fml.transformers.BlockIconTransformer.Blockness.NOT_A_BLOCK;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
|
||||
import static org.objectweb.asm.Opcodes.ALOAD;
|
||||
import static org.objectweb.asm.Opcodes.ARETURN;
|
||||
import static org.objectweb.asm.Opcodes.GETFIELD;
|
||||
import static org.objectweb.asm.Opcodes.GETSTATIC;
|
||||
import static org.objectweb.asm.Opcodes.IFEQ;
|
||||
import static org.objectweb.asm.Opcodes.ILOAD;
|
||||
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
|
||||
import static org.objectweb.asm.Type.BOOLEAN_TYPE;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.JumpInsnNode;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
|
||||
public class BlockIconTransformer implements IClassTransformer {
|
||||
|
||||
private static final String FIELD_NAME = "nhlib$isModeled";
|
||||
private static final String BOOL_DESC = BOOLEAN_TYPE.getDescriptor();
|
||||
private static final String BLOCK_CLASS = "net/minecraft/block/Block";
|
||||
private static final String ISBRH_CLASS = "com/gtnewhorizon/gtnhlib/client/model/ModelISBRH";
|
||||
private static final String ISBRH_DESC = "L" + ISBRH_CLASS + ";";
|
||||
private static final String NEW_WORLD_DESC = "(Lnet/minecraft/world/IBlockAccess;III)Lnet/minecraft/util/IIcon;";
|
||||
private static final String MISSINGNO_DESC = "()Lnet/minecraft/util/IIcon;";
|
||||
|
||||
// These are the targeted calls - a variety of getIcons declared by the Block class
|
||||
private static final String GI_WORLD = "getIcon(Lnet/minecraft/world/IBlockAccess;IIII)Lnet/minecraft/util/IIcon;";
|
||||
private static final String GI_SIDE_META = "getIcon(II)Lnet/minecraft/util/IIcon;";
|
||||
private static final String OBF_SIDE_META = "func_149735_b(II)Lnet/minecraft/util/IIcon;";
|
||||
private static final String GI_SIDE = "getBlockTextureFromSide(I)Lnet/minecraft/util/IIcon;";
|
||||
private static final HashSet<String> GETICON_SIGS = new HashSet<>();
|
||||
static {
|
||||
GETICON_SIGS.add(GI_WORLD);
|
||||
GETICON_SIGS.add(GI_SIDE_META);
|
||||
GETICON_SIGS.add(OBF_SIDE_META);
|
||||
GETICON_SIGS.add(GI_SIDE);
|
||||
}
|
||||
|
||||
private final ObjectOpenHashSet<String> blockFamily = new ObjectOpenHashSet<>();
|
||||
|
||||
public BlockIconTransformer() {
|
||||
blockFamily.add(BLOCK_CLASS);
|
||||
}
|
||||
|
||||
/// This is what our hook is injecting for the IBlockAccess variant:
|
||||
/// ```java
|
||||
/// if (this.nhlib$isModeled) return ModelISBRH.INSTANCE.getParticleIcon(world, x, y, z);
|
||||
/// ```
|
||||
/// This is what our hook is injecting for the side:meta and just side variants:
|
||||
/// ```java
|
||||
/// if (this.nhlib$isModeled) return ModelISBRH.INSTANCE.getMissingIcon();
|
||||
/// ```
|
||||
/// This is because modeled blocks *need* world access to properly get their particle icons. Returning missingno
|
||||
/// fails loudly, allowing us to find and patch out the incorrect usages.
|
||||
@Override
|
||||
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
||||
if (basicClass == null) return null;
|
||||
final var cr = new ClassReader(basicClass);
|
||||
final var blockness = checkForBlockChild(cr.getClassName(), cr.getSuperName());
|
||||
if (blockness == NOT_A_BLOCK) {
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
// Transform the class. We hit:
|
||||
// - getIcon and overloads (func_149735_b, getBlockTextureFromSide), inserting an early return if needed
|
||||
// TODO determine if the Block#blockIcon field needs to be redirected
|
||||
final var cn = new ClassNode();
|
||||
boolean transformed = false;
|
||||
if (blockness == BLOCK) {
|
||||
cn.fields.add(new FieldNode(ACC_PROTECTED, FIELD_NAME, BOOL_DESC, null, 0));
|
||||
transformed = true;
|
||||
}
|
||||
cr.accept(cn, 0);
|
||||
for (var methodNode : cn.methods) {
|
||||
transformed |= hookMethod(cn, methodNode);
|
||||
}
|
||||
|
||||
if (transformed) {
|
||||
final var cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
private boolean hookMethod(ClassNode cn, MethodNode mn) {
|
||||
final var signature = mn.name + mn.desc;
|
||||
if (!GETICON_SIGS.contains(signature)) return false;
|
||||
|
||||
final var injectedHook = new InsnList();
|
||||
final var endLabel = new LabelNode();
|
||||
injectedHook.add(new VarInsnNode(ALOAD, 0));
|
||||
injectedHook.add(new FieldInsnNode(GETFIELD, cn.name, FIELD_NAME, BOOL_DESC));
|
||||
injectedHook.add(new JumpInsnNode(IFEQ, endLabel));
|
||||
injectedHook.add(new FieldInsnNode(GETSTATIC, ISBRH_CLASS, "INSTANCE", ISBRH_DESC));
|
||||
|
||||
// This call varies based on the node we injected to.
|
||||
switch (signature) {
|
||||
case GI_WORLD -> {
|
||||
injectedHook.add(new VarInsnNode(ALOAD, 1));
|
||||
injectedHook.add(new VarInsnNode(ILOAD, 2));
|
||||
injectedHook.add(new VarInsnNode(ILOAD, 3));
|
||||
injectedHook.add(new VarInsnNode(ILOAD, 4));
|
||||
injectedHook
|
||||
.add(new MethodInsnNode(INVOKEVIRTUAL, ISBRH_CLASS, "getParticleIcon", NEW_WORLD_DESC, false));
|
||||
}
|
||||
case GI_SIDE_META, OBF_SIDE_META, GI_SIDE -> injectedHook
|
||||
.add(new MethodInsnNode(INVOKEVIRTUAL, ISBRH_CLASS, "getMissingIcon", MISSINGNO_DESC, false));
|
||||
default -> throw new RuntimeException("Attempted to hook non-icon method!");
|
||||
}
|
||||
|
||||
injectedHook.add(new InsnNode(ARETURN));
|
||||
injectedHook.add(endLabel);
|
||||
|
||||
mn.instructions.insert(injectedHook);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Blockness checkForBlockChild(String className, String superName) {
|
||||
if (BLOCK_CLASS.equals(className)) return BLOCK;
|
||||
if (blockFamily.contains(superName)) {
|
||||
blockFamily.add(className);
|
||||
return Blockness.BLOCK_SUBCLASS;
|
||||
}
|
||||
return NOT_A_BLOCK;
|
||||
}
|
||||
|
||||
enum Blockness {
|
||||
BLOCK,
|
||||
BLOCK_SUBCLASS,
|
||||
NOT_A_BLOCK
|
||||
}
|
||||
}
|
||||
@@ -23,19 +23,14 @@ public enum Mixins implements IMixins {
|
||||
FONT_RENDERER(new MixinBuilder("Font rendering replacements").addClientMixins("MixinFontRenderer")
|
||||
.setPhase(Phase.EARLY).setApplyIf(() -> GTNHLibConfig.enableFontRendererMixin)),
|
||||
BLOCK_PROPERTIES_ACCESSORS(Side.COMMON, "MixinTileEntitySkull"),
|
||||
MODEL_TEXTURE_LOADING(
|
||||
new MixinBuilder("Automatically load model textures")
|
||||
.addClientMixins(
|
||||
"models.FRMAccessor",
|
||||
"models.MixinFileResourcePack",
|
||||
"models.MixinFolderResourcePack",
|
||||
"models.SRRMAccessor")
|
||||
.setPhase(Phase.EARLY).setApplyIf(() -> GTNHLibConfig.autoTextureLoading)),
|
||||
MODEL_ICON_WRAPPER(new MixinBuilder(
|
||||
"Ensures that blocks always return a valid icon for JSON model blocks, by using the particle icon.")
|
||||
.addClientMixins("models.MixinBlock_IconWrapper")
|
||||
.addClientMixins("models.MixinEntityDiggingFX_IconWrapper").setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> GTNHLibConfig.modelIconWrapperMixin)),
|
||||
MODEL_TEXTURE_LOADING(new MixinBuilder("Automatically load models, textures, and metadata.").addClientMixins(
|
||||
"models.FRMAccessor",
|
||||
"models.MixinBlock_ModelInfo",
|
||||
"models.MixinEffectRenderer_FixParticleIcons",
|
||||
"models.MixinFileResourcePack",
|
||||
"models.MixinFolderResourcePack",
|
||||
"models.MixinRenderBlocks",
|
||||
"models.SRRMAccessor").setPhase(Phase.EARLY).setApplyIf(() -> true)),
|
||||
MODEL_ITEM_RENDERER(new MixinBuilder("Restore origin pivot before modifier").addClientMixins("models.MixinModelFHC")
|
||||
.setPhase(Phase.EARLY)),
|
||||
DYNAMIC_BLOCK_SOUNDS(new MixinBuilder("Dynamic block sounds")
|
||||
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early.models;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.model.ModelISBRH;
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
|
||||
import cpw.mods.fml.client.FMLClientHandler;
|
||||
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||
|
||||
@Mixin(Block.class)
|
||||
public abstract class MixinBlock_IconWrapper {
|
||||
|
||||
@Shadow
|
||||
public abstract int getRenderType();
|
||||
|
||||
@Shadow
|
||||
protected IIcon blockIcon;
|
||||
|
||||
@WrapMethod(method = "getIcon(Lnet/minecraft/world/IBlockAccess;IIII)Lnet/minecraft/util/IIcon;")
|
||||
private IIcon nhlib$wrapGetIcon(IBlockAccess world, int x, int y, int z, int side, Operation<IIcon> original) {
|
||||
return RenderingRegistry.instance().blockRenderers.get(getRenderType()) instanceof ModelISBRH
|
||||
? nhlib$getParticleIcon(world, x, y, z, world.getBlockMetadata(x, y, z))
|
||||
: original.call(world, x, y, z, side);
|
||||
}
|
||||
|
||||
@WrapMethod(method = "getIcon(II)Lnet/minecraft/util/IIcon;")
|
||||
private IIcon nhlib$wrapGetIcon(int side, int meta, Operation<IIcon> original) {
|
||||
return RenderingRegistry.instance().blockRenderers.get(getRenderType()) instanceof ModelISBRH
|
||||
? nhlib$getParticleIcon(null, 0, 0, 0, meta)
|
||||
: original.call(side, meta);
|
||||
}
|
||||
|
||||
@WrapMethod(method = "func_149735_b")
|
||||
private IIcon nhlib$wrapGetIconObf(int side, int meta, Operation<IIcon> original) {
|
||||
return RenderingRegistry.instance().blockRenderers.get(getRenderType()) instanceof ModelISBRH
|
||||
? nhlib$getParticleIcon(null, 0, 0, 0, meta)
|
||||
: original.call(side, meta);
|
||||
}
|
||||
|
||||
@WrapMethod(method = "getBlockTextureFromSide")
|
||||
private IIcon nhlib$wrapGetIcon(int side, Operation<IIcon> original) {
|
||||
// Set the blockIcon here so anything outside accessing it with ATs still gets the correct icon.
|
||||
// Can't do it in registericons because the icon isn't baked or somethimg, it crashes.
|
||||
return RenderingRegistry.instance().blockRenderers.get(getRenderType()) instanceof ModelISBRH
|
||||
? nhlib$getParticleIcon(null, 0, 0, 0, 0)
|
||||
: original.call(side);
|
||||
}
|
||||
|
||||
@WrapMethod(method = "registerBlockIcons")
|
||||
private void nhlib$setField(IIconRegister reg, Operation<Void> original) {
|
||||
original.call(reg);
|
||||
if (RenderingRegistry.instance().blockRenderers.get(getRenderType()) instanceof ModelISBRH
|
||||
&& blockIcon == null) {
|
||||
blockIcon = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite("missingno");
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private IIcon nhlib$getParticleIcon(@Nullable IBlockAccess world, int x, int y, int z, int meta) {
|
||||
IIcon particle = ModelISBRH.INSTANCE.getParticleIcon((Block) (Object) this, world, x, y, z, meta);
|
||||
return particle == null
|
||||
? FMLClientHandler.instance().getClient().getTextureMapBlocks().registerIcon("missingno")
|
||||
: particle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early.models;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.api.BlockModelInfo;
|
||||
|
||||
@Mixin(Block.class)
|
||||
public class MixinBlock_ModelInfo implements BlockModelInfo {
|
||||
|
||||
/// This is a shadowed field, not a unique one, because we injected it with ASM. See
|
||||
/// {@link com.gtnewhorizon.gtnhlib.core.fml.transformers.BlockIconTransformer}
|
||||
@SuppressWarnings("MixinAnnotationTarget")
|
||||
@Shadow(remap = false)
|
||||
private boolean nhlib$isModeled = false;
|
||||
|
||||
@Override
|
||||
public boolean nhlib$isModeled() {
|
||||
return nhlib$isModeled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nhlib$setModeled(boolean modeled) {
|
||||
nhlib$isModeled = modeled;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early.models;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.particle.EffectRenderer;
|
||||
import net.minecraft.client.particle.EntityDiggingFX;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.api.BlockModelInfo;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.ModelISBRH;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
|
||||
@Mixin(EffectRenderer.class)
|
||||
public class MixinEffectRenderer_FixParticleIcons {
|
||||
|
||||
@Shadow
|
||||
protected World worldObj;
|
||||
|
||||
@ModifyExpressionValue(
|
||||
method = "addBlockDestroyEffects",
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "(Lnet/minecraft/world/World;DDDDDDLnet/minecraft/block/Block;I)Lnet/minecraft/client/particle/EntityDiggingFX;"))
|
||||
private EntityDiggingFX nhlib$fixBreakIcon(EntityDiggingFX original, @Local(argsOnly = true) Block block,
|
||||
@Local(ordinal = 0, argsOnly = true) int x, @Local(ordinal = 1, argsOnly = true) int y,
|
||||
@Local(ordinal = 2, argsOnly = true) int z) {
|
||||
if (((BlockModelInfo) block).nhlib$isModeled())
|
||||
original.setParticleIcon(ModelISBRH.INSTANCE.getParticleIcon(worldObj, x, y, z));
|
||||
return original;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(
|
||||
method = "addBlockHitEffects",
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "(Lnet/minecraft/world/World;DDDDDDLnet/minecraft/block/Block;I)Lnet/minecraft/client/particle/EntityDiggingFX;"))
|
||||
private EntityDiggingFX nhlib$fixHitIcon(EntityDiggingFX original, @Local(ordinal = 0) Block block,
|
||||
@Local(ordinal = 0, argsOnly = true) int x, @Local(ordinal = 1, argsOnly = true) int y,
|
||||
@Local(ordinal = 2, argsOnly = true) int z) {
|
||||
if (((BlockModelInfo) block).nhlib$isModeled())
|
||||
original.setParticleIcon(ModelISBRH.INSTANCE.getParticleIcon(worldObj, x, y, z));
|
||||
return original;
|
||||
}
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early.models;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.particle.EntityDiggingFX;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.model.ModelISBRH;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
|
||||
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||
|
||||
@Mixin(EntityDiggingFX.class)
|
||||
public class MixinEntityDiggingFX_IconWrapper {
|
||||
|
||||
@WrapOperation(
|
||||
method = "<init>(Lnet/minecraft/world/World;DDDDDDLnet/minecraft/block/Block;II)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;getIcon(II)Lnet/minecraft/util/IIcon;"))
|
||||
private IIcon nhlib$useWorldIcon(Block block, int side, int meta, Operation<IIcon> original, World world, double x,
|
||||
double y, double z, double mx, double my, double mz, Block passedBlock, int passedMeta, int passedSide) {
|
||||
if (RenderingRegistry.instance().blockRenderers.get(block.getRenderType()) instanceof ModelISBRH) {
|
||||
int bx = (int) Math.floor(x);
|
||||
int by = (int) Math.floor(y);
|
||||
int bz = (int) Math.floor(z);
|
||||
|
||||
return block.getIcon(world, bx, by, bz, side);
|
||||
}
|
||||
|
||||
return original.call(block, side, meta);
|
||||
}
|
||||
}
|
||||
+17
-10
@@ -1,10 +1,11 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early.models;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry.MODEL_LOGGER;
|
||||
import static com.gtnewhorizon.gtnhlib.core.GTNHLibCore.MODEL_LOGGER;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.ZipFile;
|
||||
@@ -16,6 +17,7 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelResourcePack;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.loading.RPInfo;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.unbaked.JSONModel;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLists;
|
||||
@@ -32,21 +34,25 @@ public abstract class MixinFileResourcePack extends AbstractResourcePack impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> nhlib$getReferencedTextures(Function<Reader, JSONModel> jsonParser) {
|
||||
public RPInfo nhlib$gatherModelInfo(Function<Reader, JSONModel> jsonParser) {
|
||||
List<String> textures;
|
||||
final var models = new ArrayList<String>();
|
||||
|
||||
try {
|
||||
var zip = getResourcePackZipFile();
|
||||
final var jsons = zip.stream().filter(ze -> {
|
||||
var jsons = zip.stream().filter(ze -> {
|
||||
final var name = ze.getName();
|
||||
final var parts = name.split("/");
|
||||
|
||||
// Make sure it's long enough (assets/<domain>/<subdomain>/something.json), make sure the subdomain is
|
||||
// "models", make sure it's a file, and make sure it's a JSON
|
||||
// If it's a blockstate file, record the block and move on
|
||||
if (parts.length < 4) return false;
|
||||
if (!parts[2].equals("models")) return false;
|
||||
if (ze.isDirectory()) return false;
|
||||
return name.endsWith(".json");
|
||||
if (!name.endsWith(".json")) return false;
|
||||
if (parts[2].equals("blockstates")) { // file is assets/<domain>/blockstates/someblock.json
|
||||
models.add(parts[1] + ":" + parts[3].split("\\.")[0]);
|
||||
return false;
|
||||
} else return parts[2].equals("models"); // file is assets/<domain>/models/someblock.json
|
||||
}).map(ze -> {
|
||||
|
||||
try {
|
||||
return zip.getInputStream(ze);
|
||||
} catch (IOException e) {
|
||||
@@ -54,13 +60,14 @@ public abstract class MixinFileResourcePack extends AbstractResourcePack impleme
|
||||
}
|
||||
});
|
||||
|
||||
return nhlib$getReferencedTextures(jsons, jsonParser);
|
||||
textures = nhlib$getReferencedTextures(jsons, jsonParser);
|
||||
} catch (Exception e) {
|
||||
|
||||
MODEL_LOGGER.warn("Failed to walk resource pack {}", this);
|
||||
MODEL_LOGGER.warn(e);
|
||||
textures = ObjectLists.emptyList();
|
||||
}
|
||||
|
||||
return ObjectLists.emptyList();
|
||||
return new RPInfo(textures, models);
|
||||
}
|
||||
}
|
||||
+19
-7
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early.models;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry.MODEL_LOGGER;
|
||||
import static com.gtnewhorizon.gtnhlib.core.GTNHLibCore.MODEL_LOGGER;
|
||||
import static java.nio.file.Files.walk;
|
||||
|
||||
import java.io.File;
|
||||
@@ -8,6 +8,7 @@ import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -17,6 +18,7 @@ import net.minecraft.client.resources.FolderResourcePack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelResourcePack;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.loading.RPInfo;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.unbaked.JSONModel;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLists;
|
||||
@@ -30,7 +32,10 @@ public abstract class MixinFolderResourcePack extends AbstractResourcePack imple
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> nhlib$getReferencedTextures(Function<Reader, JSONModel> jsonParser) {
|
||||
public RPInfo nhlib$gatherModelInfo(Function<Reader, JSONModel> jsonParser) {
|
||||
List<String> textures;
|
||||
final var models = new ArrayList<String>();
|
||||
|
||||
try (var files = walk(resourcePackFile.toPath(), FileVisitOption.FOLLOW_LINKS)) {
|
||||
|
||||
final var jsons = files.filter(p -> {
|
||||
@@ -39,10 +44,16 @@ public abstract class MixinFolderResourcePack extends AbstractResourcePack imple
|
||||
|
||||
// Make sure it's long enough (<domain>/<subdomain>/something.json), make sure the subdomain is
|
||||
// "models", make sure it's a file, and make sure it's a JSON
|
||||
if (path.getNameCount() < 3) return false;
|
||||
if (!path.getName(1).toString().equals("models")) return false;
|
||||
if (path.getNameCount() != 3) return false;
|
||||
if (!path.toFile().isFile()) return false;
|
||||
return path.endsWith(".json");
|
||||
|
||||
final var filename = path.getFileName().toString();
|
||||
if (!filename.endsWith(".json")) return false;
|
||||
final var subdomain = path.getName(1).toString();
|
||||
if (subdomain.equals("blockstates")) {
|
||||
models.add(path.getName(0) + ":" + filename.split("\\.")[0]);
|
||||
return false;
|
||||
} else return subdomain.equals("models");
|
||||
}).map(p -> {
|
||||
try {
|
||||
return new FileInputStream(p.toFile());
|
||||
@@ -51,12 +62,13 @@ public abstract class MixinFolderResourcePack extends AbstractResourcePack imple
|
||||
}
|
||||
});
|
||||
|
||||
return nhlib$getReferencedTextures(jsons, jsonParser);
|
||||
textures = nhlib$getReferencedTextures(jsons, jsonParser);
|
||||
} catch (Exception e) {
|
||||
MODEL_LOGGER.warn("Failed to walk resource pack {}", this);
|
||||
MODEL_LOGGER.warn(e);
|
||||
textures = ObjectLists.emptyList();
|
||||
}
|
||||
|
||||
return ObjectLists.emptyList();
|
||||
return new RPInfo(textures, models);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early.models;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.api.BlockModelInfo;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.ModelISBRH;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
|
||||
@Mixin(RenderBlocks.class)
|
||||
public abstract class MixinRenderBlocks {
|
||||
|
||||
@Shadow
|
||||
public IBlockAccess blockAccess;
|
||||
|
||||
@Inject(
|
||||
method = "renderBlockByRenderType",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/RenderBlocks;setRenderBoundsFromBlock(Lnet/minecraft/block/Block;)V",
|
||||
shift = At.Shift.AFTER),
|
||||
cancellable = true)
|
||||
private void nhlib$injectModelISBRH(Block block, int x, int y, int z, CallbackInfoReturnable<Boolean> cir,
|
||||
@Local(name = "l") int renderType) {
|
||||
if (((BlockModelInfo) block).nhlib$isModeled()) {
|
||||
cir.setReturnValue(
|
||||
ModelISBRH.INSTANCE
|
||||
.renderWorldBlock(blockAccess, x, y, z, block, renderType, (RenderBlocks) (Object) this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,41 @@
|
||||
package com.gtnewhorizon.gtnhlib.test.block;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.properties.DirectionBlockProperty;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.properties.DirectionBlockProperty.AbstractDirectionBlockProperty;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.registry.BlockPropertyRegistry;
|
||||
import com.gtnewhorizon.gtnhlib.util.DirectionUtil;
|
||||
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
|
||||
public class BlockTest extends Block {
|
||||
|
||||
/// This default blockstate
|
||||
private final AbstractDirectionBlockProperty FACING_PROP = (AbstractDirectionBlockProperty) DirectionBlockProperty
|
||||
.facing();
|
||||
|
||||
public BlockTest() {
|
||||
super(Material.wood);
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
final var testBlock = new BlockTest();
|
||||
testBlock.setBlockName("model_test");
|
||||
GameRegistry.registerBlock(testBlock, "model_test");
|
||||
|
||||
/// This method allows you to adapt any property to itemstacks, with some default value. This is mostly relevant
|
||||
/// for modeled blocks, as they may require some state to match *any* of their models. If you're defining a
|
||||
/// custom blockstate, it's better to add an ItemStack default yourself - see [BlockTestTintMul] for an example
|
||||
/// of that.
|
||||
BlockPropertyRegistry.registerBlockItemProperty(testBlock, testBlock.FACING_PROP, ForgeDirection.EAST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaqueCube() {
|
||||
return false;
|
||||
@@ -22,12 +43,10 @@ public class BlockTest extends Block {
|
||||
|
||||
@Override
|
||||
public void onBlockPlacedBy(World worldIn, int x, int y, int z, EntityLivingBase placer, ItemStack itemIn) {
|
||||
int meta = MathHelper.floor_double((double) (placer.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
|
||||
worldIn.setBlockMetadataWithNotify(x, y, z, meta, 2);
|
||||
}
|
||||
var dir = DirectionUtil.yawToDirection(placer.rotationYaw);
|
||||
|
||||
@Override
|
||||
public int getRenderType() {
|
||||
return JSON_ISBRH_ID;
|
||||
/// This is a shortcut that works because this block only has a single meta property. See [BlockTestTintMul] for
|
||||
/// an example with non-meta blockstate.
|
||||
worldIn.setBlockMetadataWithNotify(x, y, z, FACING_PROP.getMeta(dir, 0), 2);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
package com.gtnewhorizon.gtnhlib.test.block;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.model.color.IBlockColor;
|
||||
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
|
||||
public class BlockTestTint extends Block implements IBlockColor {
|
||||
|
||||
public BlockTestTint() {
|
||||
@@ -20,46 +16,28 @@ public class BlockTestTint extends Block implements IBlockColor {
|
||||
setHardness(2f);
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
final var block = new BlockTestTint();
|
||||
block.setBlockName("model_test_tint");
|
||||
GameRegistry.registerBlock(block, "model_test_tint");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaqueCube() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onBlockPlaced(@NotNull World worldIn, int x, int y, int z, int side, float subX, float subY, float subZ,
|
||||
int meta) {
|
||||
|
||||
// Face NORTH if placed up or down
|
||||
final var s = ForgeDirection.getOrientation(side);
|
||||
if (s == ForgeDirection.UP || s == ForgeDirection.DOWN) return 2;
|
||||
|
||||
// Face the placed side
|
||||
return side - 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderType() {
|
||||
return JSON_ISBRH_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int colorMultiplier(IBlockAccess world, int x, int y, int z, int tintIndex) {
|
||||
switch (tintIndex) {
|
||||
case 0:
|
||||
return 0xFF0000; // red
|
||||
case 1:
|
||||
return 0x00FF00; // green
|
||||
case 2:
|
||||
return 0x0000FF; // blue
|
||||
case 3:
|
||||
return 0xFFFF00; // yellow
|
||||
case 4:
|
||||
return 0xFF00FF; // purple
|
||||
case 5:
|
||||
return 0x00FFFF; // cyan
|
||||
default:
|
||||
return 0xFFFFFF;// white
|
||||
}
|
||||
return switch (tintIndex) {
|
||||
case 0 -> 0xFF0000; // red
|
||||
case 1 -> 0x00FF00; // green
|
||||
case 2 -> 0x0000FF; // blue
|
||||
case 3 -> 0xFFFF00; // yellow
|
||||
case 4 -> 0xFF00FF; // purple
|
||||
case 5 -> 0x00FFFF; // cyan
|
||||
default -> 0xFFFFFF;// white
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
package com.gtnewhorizon.gtnhlib.test.block;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockPropertyTrait;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.core.BlockState;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.properties.DirectionBlockProperty;
|
||||
import com.gtnewhorizon.gtnhlib.blockstate.registry.BlockPropertyRegistry;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.color.IBlockColor;
|
||||
import com.gtnewhorizon.gtnhlib.util.DirectionUtil;
|
||||
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
|
||||
public class BlockTestTintMul extends Block implements IBlockColor {
|
||||
|
||||
@@ -23,6 +25,52 @@ public class BlockTestTintMul extends Block implements IBlockColor {
|
||||
setHardness(2f);
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
final var tintMulTestBlock = new BlockTestTintMul();
|
||||
tintMulTestBlock.setBlockName("model_test_tint_mul");
|
||||
GameRegistry.registerBlock(tintMulTestBlock, "model_test_tint_mul");
|
||||
GameRegistry.registerTileEntity(TileTestTintMul.class, "tile_test_tint_mul");
|
||||
|
||||
// Here's an example of a custom, TE-based property
|
||||
final var property = new DirectionBlockProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "facing";
|
||||
}
|
||||
|
||||
/// This DOES support stacks, otherwise the item wouldn't have a facing, and thus wouldn't have a model.
|
||||
@Override
|
||||
public boolean hasTrait(BlockPropertyTrait trait) {
|
||||
return switch (trait) {
|
||||
case SupportsWorld, WorldMutable, StackMutable, SupportsStacks -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeDirection getValue(IBlockAccess world, int x, int y, int z) {
|
||||
TileEntity te = world.getTileEntity(x, y, z);
|
||||
if (te instanceof TileTestTintMul tile) return tile.getFacing();
|
||||
return ForgeDirection.NORTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(World world, int x, int y, int z, ForgeDirection value) {
|
||||
TileEntity te = world.getTileEntity(x, y, z);
|
||||
if (te instanceof TileTestTintMul tile) tile.setFacing(value);
|
||||
}
|
||||
|
||||
/// This needs to be implemented for the stack support to work.
|
||||
@Override
|
||||
public ForgeDirection getValue(ItemStack stack) {
|
||||
return ForgeDirection.NORTH;
|
||||
}
|
||||
};
|
||||
|
||||
BlockPropertyRegistry.registerBlockItemProperty(tintMulTestBlock, property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaqueCube() {
|
||||
return false;
|
||||
@@ -30,25 +78,12 @@ public class BlockTestTintMul extends Block implements IBlockColor {
|
||||
|
||||
@Override
|
||||
public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase player, ItemStack itemIn) {
|
||||
super.onBlockPlacedBy(world, x, y, z, player, itemIn);
|
||||
int heading = MathHelper.floor_double(player.rotationYaw * 4.0F / 360.0F + 0.5D) & 3;
|
||||
ForgeDirection facing = getDirectionForHeading(heading);
|
||||
BlockState state = BlockPropertyRegistry.getBlockState(world, x, y, z);
|
||||
state.setPropertyValue("facing", facing);
|
||||
state.setPropertyValue("facing", DirectionUtil.yawToDirection(player.rotationYaw));
|
||||
state.place(world, x, y, z);
|
||||
state.close();
|
||||
}
|
||||
|
||||
private ForgeDirection getDirectionForHeading(int heading) {
|
||||
return switch (heading) {
|
||||
case 0 -> ForgeDirection.NORTH;
|
||||
case 1 -> ForgeDirection.EAST;
|
||||
case 2 -> ForgeDirection.SOUTH;
|
||||
case 3 -> ForgeDirection.WEST;
|
||||
default -> ForgeDirection.NORTH;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(int meta) {
|
||||
return true;
|
||||
@@ -59,29 +94,17 @@ public class BlockTestTintMul extends Block implements IBlockColor {
|
||||
return new TileTestTintMul();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderType() {
|
||||
return JSON_ISBRH_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int colorMultiplier(IBlockAccess world, int x, int y, int z, int tintIndex) {
|
||||
switch (tintIndex) {
|
||||
case 0:
|
||||
return 0xFF0000; // red
|
||||
case 1:
|
||||
return 0x00FF00; // green
|
||||
case 2:
|
||||
return 0x0000FF; // blue
|
||||
case 3:
|
||||
return 0xFFFF00; // yellow
|
||||
case 4:
|
||||
return 0xFF00FF; // purple
|
||||
case 5:
|
||||
return 0x00FFFF; // cyan
|
||||
default:
|
||||
return 0xFFFFFF;// white
|
||||
}
|
||||
return switch (tintIndex) {
|
||||
case 0 -> 0xFF0000; // red
|
||||
case 1 -> 0x00FF00; // green
|
||||
case 2 -> 0x0000FF; // blue
|
||||
case 3 -> 0xFFFF00; // yellow
|
||||
case 4 -> 0xFF00FF; // purple
|
||||
case 5 -> 0x00FFFF; // cyan
|
||||
default -> 0xFFFFFF;// white
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.gtnewhorizon.gtnhlib.util;
|
||||
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
public class DirectionUtil {
|
||||
|
||||
public static ForgeDirection yawToDirection(float yaw) {
|
||||
return switch (Math.round(MathUtil.mod(yaw, 360) / 360 * 4)) {
|
||||
case 1 -> ForgeDirection.EAST;
|
||||
case 2 -> ForgeDirection.SOUTH;
|
||||
case 3 -> ForgeDirection.WEST;
|
||||
default -> ForgeDirection.NORTH;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.gtnewhorizon.gtnhlib.util;
|
||||
|
||||
public class MathUtil {
|
||||
|
||||
/// I think this does the [Math#floorMod(int, int)] thing to floats. I'm sure it's implemented *somewhere*, but I
|
||||
/// don't know where.
|
||||
public static float mod(float a, float b) {
|
||||
if (a < 0) {
|
||||
final var ret = b - (-a % b);
|
||||
return ret == b ? 0 : ret; // when -a == b, should return 0, not b
|
||||
}
|
||||
return a % b;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
public cpw.mods.fml.client.registry.RenderingRegistry blockRenderers
|
||||
public net.minecraft.client.renderer.Tessellator field_78405_h # rawBuffer
|
||||
public net.minecraft.client.renderer.Tessellator field_78406_i # vertexCount
|
||||
public net.minecraft.client.renderer.Tessellator field_78403_j # textureU
|
||||
@@ -22,4 +23,4 @@ public net.minecraft.client.renderer.Tessellator field_78410_t # isColorDisabled
|
||||
public net.minecraft.client.renderer.Tessellator func_78379_d()V # void reset()
|
||||
public net.minecraft.client.renderer.Tessellator field_78394_d # byteBuffer
|
||||
public net.minecraft.command.CommandHandler field_71562_a # commandMap
|
||||
public cpw.mods.fml.client.registry.RenderingRegistry blockRenderers
|
||||
public net.minecraft.util.RegistrySimple field_82596_a # registryObjects
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"variants": {
|
||||
"meta=0": { "model": "gtnhlib:blocks/old_stairs", "y": 270, "uvlock": true },
|
||||
"meta=1": { "model": "gtnhlib:blocks/old_stairs", "y": 180, "uvlock": true },
|
||||
"meta=2": { "model": "gtnhlib:blocks/old_stairs", "y": 90, "uvlock": true },
|
||||
"meta=3": { "model": "gtnhlib:blocks/old_stairs", "y": 0, "uvlock": true }
|
||||
"facing=north": { "model": "gtnhlib:blocks/old_stairs", "y": 270, "uvlock": true },
|
||||
"facing=south": { "model": "gtnhlib:blocks/old_stairs", "y": 90, "uvlock": true },
|
||||
"facing=west": { "model": "gtnhlib:blocks/old_stairs", "y": 0, "uvlock": true },
|
||||
"facing=east": { "model": "gtnhlib:blocks/old_stairs", "y": 180, "uvlock": true }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
{
|
||||
"variants": {
|
||||
"meta=0": { "model": "gtnhlib:blocks/model_test_tint", "y": 270 },
|
||||
"meta=1": { "model": "gtnhlib:blocks/model_test_tint", "y": 90 },
|
||||
"meta=2": { "model": "gtnhlib:blocks/model_test_tint", "y": 0 },
|
||||
"meta=3": { "model": "gtnhlib:blocks/model_test_tint", "y": 180 }
|
||||
"": { "model": "gtnhlib:blocks/model_test_tint" }
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,8 @@
|
||||
# Test block names
|
||||
tile.model_test.name=Test Block
|
||||
tile.model_test_tint.name=Test Block (Tint)
|
||||
tile.model_test_tint_mul.name=Test Block (Tint Mul)
|
||||
|
||||
# Configuration
|
||||
fml.configgui.tooltip.default=[default: %s]
|
||||
fml.configgui.tooltip.defaultNumeric=[range: %2$s ~ %3$s, default: %1$s]
|
||||
@@ -116,4 +121,4 @@ gtnhlib.chat.rpupdater.info.header=§2Pack info for §3%s§2:
|
||||
gtnhlib.chat.rpupdater.info.no_updater=No updater metadata found for this pack.
|
||||
gtnhlib.chat.rpupdater.info.updater_name=Updater name: §7%s
|
||||
gtnhlib.chat.rpupdater.info.version=Version: §7%s (GTNH %s)
|
||||
gtnhlib.chat.rpupdater.info.source=Source: §7%s/%s
|
||||
gtnhlib.chat.rpupdater.info.source=Source: §7%s/%s
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 233 B After Width: | Height: | Size: 244 B |
Reference in new issue
Block a user