diff --git a/src/main/java/com/simibubi/create/AllBlocks.java b/src/main/java/com/simibubi/create/AllBlocks.java index 35de22404..6ef95116a 100644 --- a/src/main/java/com/simibubi/create/AllBlocks.java +++ b/src/main/java/com/simibubi/create/AllBlocks.java @@ -61,6 +61,7 @@ import com.simibubi.create.modules.logistics.management.base.LogisticalControlle import com.simibubi.create.modules.logistics.management.index.LogisticalIndexBlock; import com.simibubi.create.modules.logistics.transport.villager.LogisticiansTableBlock; import com.simibubi.create.modules.logistics.transport.villager.PackageFunnelBlock; +import com.simibubi.create.modules.palettes.CTGlassBlock; import com.simibubi.create.modules.palettes.GlassPaneBlock; import com.simibubi.create.modules.schematics.block.CreativeCrateBlock; import com.simibubi.create.modules.schematics.block.SchematicTableBlock; @@ -173,6 +174,7 @@ public enum AllBlocks { __PALETTES__(), TILED_GLASS(new GlassBlock(Properties.from(Blocks.GLASS))), TILED_GLASS_PANE(new GlassPaneBlock(Properties.from(Blocks.GLASS))), + FRAMED_GLASS(new CTGlassBlock(true)), ANDESITE_BRICKS(new Block(Properties.from(Blocks.ANDESITE))), DIORITE_BRICKS(new Block(Properties.from(Blocks.DIORITE))), @@ -264,7 +266,7 @@ public enum AllBlocks { blockItem = new MechanicalMixerBlockItem(standardItemProperties); else blockItem = new BlockItem(blockIn, standardItemProperties); - + registry.register(blockItem.setRegistryName(blockIn.getRegistryName())); } diff --git a/src/main/java/com/simibubi/create/CreateClient.java b/src/main/java/com/simibubi/create/CreateClient.java index 9ab7b1a15..a1205bb29 100644 --- a/src/main/java/com/simibubi/create/CreateClient.java +++ b/src/main/java/com/simibubi/create/CreateClient.java @@ -3,6 +3,9 @@ package com.simibubi.create; import java.util.Map; import java.util.function.Function; +import com.simibubi.create.foundation.block.CTModel; +import com.simibubi.create.foundation.block.IHaveConnectedTextures; +import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.modules.contraptions.CachedBufferReloader; import com.simibubi.create.modules.contraptions.WrenchModel; import com.simibubi.create.modules.contraptions.receivers.EncasedFanParticleHandler; @@ -27,6 +30,7 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.DistExecutor; @@ -41,7 +45,7 @@ public class CreateClient { public static SchematicAndQuillHandler schematicAndQuillHandler; public static EncasedFanParticleHandler fanParticles; public static int renderTicks; - + public static ModConfig config; public static void addListeners(IEventBus modEventBus) { @@ -50,6 +54,7 @@ public class CreateClient { modEventBus.addListener(CreateClient::createConfigs); modEventBus.addListener(CreateClient::onModelBake); modEventBus.addListener(CreateClient::onModelRegistry); + modEventBus.addListener(CreateClient::onTextureStitch); }); } @@ -86,16 +91,33 @@ public class CreateClient { schematicHologram.tick(); } + @OnlyIn(Dist.CLIENT) + public static void onTextureStitch(TextureStitchEvent.Pre event) { + if (!event.getMap().getBasePath().equals("textures")) + return; + for (AllBlocks allBlocks : AllBlocks.values()) { + if (!(allBlocks.get() instanceof IHaveConnectedTextures)) + continue; + event.addSprite(new ResourceLocation(Create.ID, "block/connected/" + Lang.asId(allBlocks.name()))); + } + } + @OnlyIn(Dist.CLIENT) public static void onModelBake(ModelBakeEvent event) { Map modelRegistry = event.getModelRegistry(); + for (AllBlocks allBlocks : AllBlocks.values()) { + if (!(allBlocks.get() instanceof IHaveConnectedTextures)) + continue; + swapModels(modelRegistry, getBlockModelLocation(allBlocks, ""), + t -> new CTModel(t, Lang.asId(allBlocks.name()))); + } + swapModels(modelRegistry, getItemModelLocation(AllItems.SYMMETRY_WAND), t -> new SymmetryWandModel(t).loadPartials(event)); swapModels(modelRegistry, getItemModelLocation(AllItems.PLACEMENT_HANDGUN), t -> new BuilderGunModel(t).loadPartials(event)); - swapModels(modelRegistry, getItemModelLocation(AllItems.WRENCH), - t -> new WrenchModel(t).loadPartials(event)); + swapModels(modelRegistry, getItemModelLocation(AllItems.WRENCH), t -> new WrenchModel(t).loadPartials(event)); swapModels(modelRegistry, getItemModelLocation(AllItems.DEFORESTER), t -> new DeforesterModel(t).loadPartials(event)); swapModels(modelRegistry, diff --git a/src/main/java/com/simibubi/create/foundation/block/CTModel.java b/src/main/java/com/simibubi/create/foundation/block/CTModel.java new file mode 100644 index 000000000..599148ff0 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/block/CTModel.java @@ -0,0 +1,109 @@ +package com.simibubi.create.foundation.block; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import com.simibubi.create.foundation.block.CTModelTextureHandler.TextureEntry; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IEnviromentBlockReader; +import net.minecraftforge.client.model.BakedModelWrapper; +import net.minecraftforge.client.model.data.EmptyModelData; +import net.minecraftforge.client.model.data.IModelData; +import net.minecraftforge.client.model.data.ModelDataMap; +import net.minecraftforge.client.model.data.ModelProperty; + +public class CTModel extends BakedModelWrapper { + + private static ModelProperty CT_PROPERTY = new ModelProperty<>(); + private TextureEntry texture; + + private class CTData { + int[] textures; + + public CTData() { + textures = new int[6]; + Arrays.fill(textures, -1); + } + + void put(Direction face, int texture) { + textures[face.getIndex()] = texture; + } + + int get(Direction face) { + return textures[face.getIndex()]; + } + } + + public CTModel(IBakedModel originalModel, String blockId) { + super(originalModel); + texture = CTModelTextureHandler.get(blockId); + } + + @Override + public IModelData getModelData(IEnviromentBlockReader world, BlockPos pos, BlockState state, IModelData tileData) { + if (!(state.getBlock() instanceof IHaveConnectedTextures)) + return EmptyModelData.INSTANCE; + CTData data = new CTData(); + IHaveConnectedTextures texDef = (IHaveConnectedTextures) state.getBlock(); + for (Direction face : Direction.values()) { + if (!Block.shouldSideBeRendered(state, world, pos, face)) + continue; + data.put(face, texDef.getTextureIndex(world, pos, state, face)); + } + return new ModelDataMap.Builder().withInitial(CT_PROPERTY, data).build(); + } + + @Override + public List getQuads(BlockState state, Direction side, Random rand, IModelData extraData) { + List quads = new ArrayList<>(super.getQuads(state, side, rand, extraData)); + if (!extraData.hasProperty(CT_PROPERTY)) + return quads; + IHaveConnectedTextures texDef = (IHaveConnectedTextures) state.getBlock(); + CTData data = extraData.getData(CT_PROPERTY); + + for (int i = 0; i < quads.size(); i++) { + BakedQuad quad = quads.get(i); + if (!texDef.appliesTo(quad)) + continue; + int index = data.get(quad.getFace()); + if (index == -1) + return quads; + + float textureSize = 16f / 128f / 8f; + float uShift = (index % 8) * textureSize; + float vShift = (index / 8) * textureSize * 2; + + uShift = texture.connectedTextures.getInterpolatedU((index % 8) * 2) - texture.originalTexture.getMinU(); + vShift = texture.connectedTextures.getInterpolatedV((index / 8) * 2) - texture.originalTexture.getMinV(); + + BakedQuad newQuad = new BakedQuad(Arrays.copyOf(quad.getVertexData(), quad.getVertexData().length), + quad.getTintIndex(), quad.getFace(), quad.getSprite(), quad.shouldApplyDiffuseLighting(), + quad.getFormat()); + VertexFormat format = quad.getFormat(); + int[] vertexData = newQuad.getVertexData(); + for (int vertex = 0; vertex < vertexData.length; vertex += format.getIntegerSize()) { + int uvOffset = format.getUvOffsetById(0) / 4; + int uIndex = vertex + uvOffset; + int vIndex = vertex + uvOffset + 1; + float u = Float.intBitsToFloat(vertexData[uIndex]); + float v = Float.intBitsToFloat(vertexData[vIndex]); + u += uShift; + v += vShift; + vertexData[uIndex] = Float.floatToIntBits(u); + vertexData[vIndex] = Float.floatToIntBits(v); + } + quads.set(i, newQuad); + } + return quads; + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/block/CTModelTextureHandler.java b/src/main/java/com/simibubi/create/foundation/block/CTModelTextureHandler.java new file mode 100644 index 000000000..838f8a913 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/block/CTModelTextureHandler.java @@ -0,0 +1,46 @@ +package com.simibubi.create.foundation.block; + +import java.util.HashMap; +import java.util.Map; + +import com.simibubi.create.Create; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.AtlasTexture; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.util.ResourceLocation; + +public class CTModelTextureHandler { + + static class TextureEntry { + ResourceLocation originalTextureLocation; + ResourceLocation connectedTextureLocation; + TextureAtlasSprite originalTexture; + TextureAtlasSprite connectedTextures; + + void loadTextures() { + AtlasTexture textureMap = Minecraft.getInstance().getTextureMap(); + originalTexture = textureMap.getSprite(originalTextureLocation); + connectedTextures = textureMap.getSprite(connectedTextureLocation); + } + } + + static Map textures = new HashMap<>(); + + public static TextureEntry get(String blockId) { + if (textures.containsKey(blockId)) + return textures.get(blockId); + + TextureEntry entry = new TextureEntry(); + entry.originalTextureLocation = new ResourceLocation(Create.ID, "block/" + blockId); + entry.connectedTextureLocation = new ResourceLocation(Create.ID, "block/connected/" + blockId); + entry.loadTextures(); + textures.put(blockId, entry); + return entry; + } + + public static void reloadUVs() { + textures.values().forEach(TextureEntry::loadTextures); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/block/IHaveConnectedTextures.java b/src/main/java/com/simibubi/create/foundation/block/IHaveConnectedTextures.java new file mode 100644 index 000000000..534accb8b --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/block/IHaveConnectedTextures.java @@ -0,0 +1,133 @@ +package com.simibubi.create.foundation.block; + +import java.util.function.BiPredicate; + +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.util.Direction; +import net.minecraft.util.Direction.Axis; +import net.minecraft.util.Direction.AxisDirection; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IEnviromentBlockReader; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +public interface IHaveConnectedTextures { + + class CTContext { + boolean up, down, left, right; + boolean topLeft, topRight, bottomLeft, bottomRight; + } + + @OnlyIn(Dist.CLIENT) + public boolean appliesTo(BakedQuad quad); + + default boolean connectsTo(BlockState state, BlockState other, IEnviromentBlockReader reader, BlockPos pos, + BlockPos otherPos, Direction face) { + + BlockPos blockingPos = otherPos.offset(face); + if ((face.getAxis().getCoordinate(pos.getX(), pos.getY(), pos.getZ()) == face.getAxis() + .getCoordinate(otherPos.getX(), otherPos.getY(), otherPos.getZ())) + && connectsTo(state, reader.getBlockState(blockingPos), reader, pos, blockingPos, face)) + return false; + + return state.getBlock() == other.getBlock(); + } + + default int getTextureIndex(IEnviromentBlockReader reader, BlockPos pos, BlockState state, Direction face) { + return getTextureIndexForContext(reader, pos, state, face, buildContext(reader, pos, state, face)); + } + + default CTContext buildContext(IEnviromentBlockReader reader, BlockPos pos, BlockState state, Direction face) { + Axis axis = face.getAxis(); + boolean positive = face.getAxisDirection() == AxisDirection.POSITIVE; + Direction h = axis == Axis.X ? Direction.SOUTH : Direction.WEST; + Direction v = axis.isHorizontal() ? Direction.UP : Direction.NORTH; + h = positive ? h.getOpposite() : h; + if (face == Direction.DOWN) { + v = v.getOpposite(); + h = h.getOpposite(); + } + + final Direction horizontal = h; + final Direction vertical = v; + + BiPredicate connection = (x, y) -> { + BlockPos p = pos.offset(horizontal, x).offset(vertical, y); + return connectsTo(state, reader.getBlockState(p), reader, pos, p, face); + }; + + CTContext context = new CTContext(); + context.up = connection.test(0, 1); + context.down = connection.test(0, -1); + context.left = connection.test(-1, 0); + context.right = connection.test(1, 0); + context.topLeft = connection.test(-1, 1); + context.topRight = connection.test(1, 1); + context.bottomLeft = connection.test(-1, -1); + context.bottomRight = connection.test(1, -1); + return context; + } + + default int getTextureIndexForContext(IEnviromentBlockReader reader, BlockPos pos, BlockState state, Direction face, + CTContext c) { + int tileX = 0, tileY = 0; + int borders = (!c.up ? 1 : 0) + (!c.down ? 1 : 0) + (!c.left ? 1 : 0) + (!c.right ? 1 : 0); + + if (c.up) + tileX++; + if (c.down) + tileX += 2; + if (c.left) + tileY++; + if (c.right) + tileY += 2; + + if (borders == 0) { + if (c.topRight) + tileX++; + if (c.topLeft) + tileX += 2; + if (c.bottomRight) + tileY += 2; + if (c.bottomLeft) + tileY++; + } + + if (borders == 1) { + if (!c.right) { + if (c.topLeft || c.bottomLeft) { + tileY = 4; + tileX = -1 + (c.bottomLeft ? 1 : 0) + (c.topLeft ? 1 : 0) * 2; + } + } + if (!c.left) { + if (c.topRight || c.bottomRight) { + tileY = 5; + tileX = -1 + (c.bottomRight ? 1 : 0) + (c.topRight ? 1 : 0) * 2; + } + } + if (!c.down) { + if (c.topLeft || c.topRight) { + tileY = 6; + tileX = -1 + (c.topLeft ? 1 : 0) + (c.topRight ? 1 : 0) * 2; + } + } + if (!c.up) { + if (c.bottomLeft || c.bottomRight) { + tileY = 7; + tileX = -1 + (c.bottomLeft ? 1 : 0) + (c.bottomRight ? 1 : 0) * 2; + } + } + } + + if (borders == 2) { + if ((c.up && c.left && c.topLeft) || (c.down && c.left && c.bottomLeft) || (c.up && c.right && c.topRight) + || (c.down && c.right && c.bottomRight)) + tileX += 3; + } + + return tileX + 8 * tileY; + } + +} diff --git a/src/main/java/com/simibubi/create/modules/contraptions/CachedBufferReloader.java b/src/main/java/com/simibubi/create/modules/contraptions/CachedBufferReloader.java index 862496802..6a900e39e 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/CachedBufferReloader.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/CachedBufferReloader.java @@ -1,5 +1,6 @@ package com.simibubi.create.modules.contraptions; +import com.simibubi.create.foundation.block.CTModelTextureHandler; import com.simibubi.create.foundation.utility.ColoredIndicatorRenderer; import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.modules.contraptions.receivers.constructs.ContraptionRenderer; @@ -22,6 +23,7 @@ public class CachedBufferReloader extends ReloadListener { ContraptionRenderer.invalidateCache(); MechanicalBearingTileEntityRenderer.invalidateCache(); ColoredIndicatorRenderer.invalidateCache(); + CTModelTextureHandler.reloadUVs(); } diff --git a/src/main/java/com/simibubi/create/modules/palettes/CTGlassBlock.java b/src/main/java/com/simibubi/create/modules/palettes/CTGlassBlock.java new file mode 100644 index 000000000..536ea4751 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/palettes/CTGlassBlock.java @@ -0,0 +1,37 @@ +package com.simibubi.create.modules.palettes; + +import java.util.function.Supplier; + +import com.simibubi.create.Create; +import com.simibubi.create.foundation.block.IHaveConnectedTextures; + +import net.minecraft.block.Blocks; +import net.minecraft.block.GlassBlock; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.ResourceLocation; + +public class CTGlassBlock extends GlassBlock implements IHaveConnectedTextures { + + private Supplier textureToReplace; + private boolean hasAlpha; + + public CTGlassBlock(boolean hasAlpha) { + super(Properties.from(Blocks.GLASS)); + textureToReplace = () -> { + return new ResourceLocation(Create.ID, "block/" + getRegistryName().getPath()); + }; + this.hasAlpha = hasAlpha; + } + + @Override + public boolean appliesTo(BakedQuad quad) { + return quad.getSprite().getName().equals(textureToReplace.get()); + } + + @Override + public BlockRenderLayer getRenderLayer() { + return hasAlpha ? BlockRenderLayer.TRANSLUCENT : super.getRenderLayer(); + } + +} diff --git a/src/main/resources/assets/create/blockstates/framed_glass.json b/src/main/resources/assets/create/blockstates/framed_glass.json new file mode 100644 index 000000000..cf1f52360 --- /dev/null +++ b/src/main/resources/assets/create/blockstates/framed_glass.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "create:block/palettes/framed_glass" } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/block/ctblock.json b/src/main/resources/assets/create/models/block/ctblock.json new file mode 100644 index 000000000..7607a59ae --- /dev/null +++ b/src/main/resources/assets/create/models/block/ctblock.json @@ -0,0 +1,17 @@ +{ + "parent": "block/block", + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 0, 2, 2], "texture": "#sheet", "cullface": "north"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#sheet", "cullface": "east"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#sheet", "cullface": "south"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#sheet", "cullface": "west"}, + "up": {"uv": [0, 0, 2, 2], "texture": "#sheet", "cullface": "up"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#sheet", "cullface": "down"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/block/palettes/framed_glass.json b/src/main/resources/assets/create/models/block/palettes/framed_glass.json new file mode 100644 index 000000000..47c73aae8 --- /dev/null +++ b/src/main/resources/assets/create/models/block/palettes/framed_glass.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "create:block/framed_glass" + } +} diff --git a/src/main/resources/assets/create/models/item/framed_glass.json b/src/main/resources/assets/create/models/item/framed_glass.json new file mode 100644 index 000000000..a8e4e28b8 --- /dev/null +++ b/src/main/resources/assets/create/models/item/framed_glass.json @@ -0,0 +1,3 @@ +{ + "parent": "create:block/palettes/framed_glass" +} \ No newline at end of file diff --git a/src/main/resources/assets/create/textures/block/connected/framed_glass.png b/src/main/resources/assets/create/textures/block/connected/framed_glass.png new file mode 100644 index 000000000..240f72174 Binary files /dev/null and b/src/main/resources/assets/create/textures/block/connected/framed_glass.png differ diff --git a/src/main/resources/assets/create/textures/block/framed_glass.png b/src/main/resources/assets/create/textures/block/framed_glass.png new file mode 100644 index 000000000..9c22d68c8 Binary files /dev/null and b/src/main/resources/assets/create/textures/block/framed_glass.png differ