From 52fb6840addc13e958c1cb0cfcffd9c821d3515b Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Wed, 12 Jun 2024 00:12:07 +0200 Subject: [PATCH 1/6] fix: added check in injections and fixed modifiers interactions and Layer drops --- .../reframed/block/ReFramedBlock.java | 11 ---------- .../reframed/block/ReFramedDoorBlock.java | 2 +- .../reframed/block/ReFramedLayerBlock.java | 14 +++++++++++++ .../reframed/mixin/BlockItemMixin.java | 6 ++++++ .../reframed/util/blocks/BlockHelper.java | 5 ++--- .../util/blocks/ReframedInteractible.java | 20 ------------------- 6 files changed, 23 insertions(+), 35 deletions(-) delete mode 100644 src/main/java/fr/adrien1106/reframed/util/blocks/ReframedInteractible.java diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java index 0db4a18..236d4eb 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java @@ -9,7 +9,6 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.BlockItem; import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; import net.minecraft.registry.Registries; @@ -85,16 +84,6 @@ public class ReFramedBlock extends Block implements BlockEntityProvider { if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock())); }); - if(frame_entity.emitsRedstone() - && themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) != 0)) - drops.add(new ItemStack(Items.REDSTONE_TORCH)); - if(frame_entity.emitsLight() - && themes.stream().noneMatch(theme -> theme.getLuminance() != 0)) - drops.add(new ItemStack(Items.GLOWSTONE_DUST)); - if(!frame_entity.isSolid() - && themes.stream().anyMatch(AbstractBlockState::isSolid)) - drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT)); - ItemScatterer.spawn(world, pos, drops); } super.onStateReplaced(state, world, pos, newState, moved); diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java index 3fd988e..3c587b3 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java @@ -113,7 +113,7 @@ public class ReFramedDoorBlock extends WaterloggableReFramedBlock { @Override public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) { - if (!world.isClient() && (player.isCreative() || !player.canHarvest(state))) { + if (!world.isClient() && (player.isCreative() || player.canHarvest(state))) { DoubleBlockHalf half = state.get(DOUBLE_BLOCK_HALF); BlockPos other_pos = half == DoubleBlockHalf.LOWER ? pos.up() : pos.down(); BlockState other_state = world.getBlockState(other_pos); diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedLayerBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedLayerBlock.java index 2b0efe7..46d8037 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedLayerBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedLayerBlock.java @@ -6,6 +6,8 @@ import net.minecraft.block.BlockState; import net.minecraft.block.ShapeContext; import net.minecraft.item.BlockItem; import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.loot.context.LootContextParameterSet; import net.minecraft.state.StateManager; import net.minecraft.util.BlockMirror; import net.minecraft.util.BlockRotation; @@ -14,6 +16,8 @@ import net.minecraft.util.shape.VoxelShape; import net.minecraft.world.BlockView; import org.jetbrains.annotations.Nullable; +import java.util.List; + import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder; import static net.minecraft.state.property.Properties.FACING; import static net.minecraft.state.property.Properties.LAYERS; @@ -27,6 +31,16 @@ public class ReFramedLayerBlock extends ReFramedSlabBlock { setDefaultState(getDefaultState().with(LAYERS, 1)); } + @Override + public List getDroppedStacks(BlockState state, LootContextParameterSet.Builder builder) { + List drops = super.getDroppedStacks(state, builder); + drops.forEach((stack) -> { + if (stack.getItem() instanceof BlockItem bi && bi.getBlock() instanceof ReFramedLayerBlock) + stack.setCount(state.get(LAYERS)); + }); + return drops; + } + @Override protected void appendProperties(StateManager.Builder builder) { super.appendProperties(builder.add(LAYERS)); diff --git a/src/main/java/fr/adrien1106/reframed/mixin/BlockItemMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/BlockItemMixin.java index 7e081da..c7331a4 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/BlockItemMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/BlockItemMixin.java @@ -2,6 +2,7 @@ package fr.adrien1106.reframed.mixin; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import fr.adrien1106.reframed.block.ReFramedBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockEntityProvider; import net.minecraft.entity.player.PlayerEntity; @@ -9,6 +10,7 @@ import net.minecraft.item.BlockItem; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; +import net.minecraft.state.property.Properties; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; @@ -32,8 +34,12 @@ public class BlockItemMixin { private static void placeBlockWithOffHandCamo(World world, PlayerEntity player, BlockPos pos, ItemStack stack, CallbackInfoReturnable cir, @Local LocalRef compound) { if (compound.get() != null || player.getOffHandStack().isEmpty() + || player.getMainHandStack().isEmpty() + || !(player.getMainHandStack().getItem() instanceof BlockItem frame) + || !(frame.getBlock() instanceof ReFramedBlock) || !(player.getOffHandStack().getItem() instanceof BlockItem block) || block.getBlock() instanceof BlockEntityProvider + || (world.getBlockState(pos).contains(Properties.LAYERS) && world.getBlockState(pos).get(Properties.LAYERS) > 1) || !Block.isShapeFullCube(block.getBlock().getDefaultState().getCollisionShape(world, pos)) ) return; NbtCompound new_comp = new NbtCompound(); diff --git a/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java b/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java index 0306a1e..7de128d 100644 --- a/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java +++ b/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java @@ -188,7 +188,6 @@ public class BlockHelper { if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) return ActionResult.PASS; ItemStack held = player.getStackInHand(hand); - ReframedInteractible ext = state.getBlock() instanceof ReframedInteractible e ? e : ReframedInteractible.Default.INSTANCE; // frame will emit light if applied with glowstone if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST) { @@ -199,14 +198,14 @@ public class BlockHelper { } // frame will emit redstone if applied with redstone torch can deactivate redstone block camo emission - if(held.getItem() == Items.REDSTONE_TORCH && ext.canAddRedstoneEmission(state, world, pos)) { + if(held.getItem() == Items.REDSTONE_TORCH) { block_entity.toggleRedstone(); world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f); return ActionResult.SUCCESS; } // Frame will lose its collision if applied with popped chorus fruit - if(held.getItem() == Items.POPPED_CHORUS_FRUIT && ext.canRemoveCollision(state, world, pos)) { + if(held.getItem() == Items.POPPED_CHORUS_FRUIT) { block_entity.toggleSolidity(); world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f); return ActionResult.SUCCESS; diff --git a/src/main/java/fr/adrien1106/reframed/util/blocks/ReframedInteractible.java b/src/main/java/fr/adrien1106/reframed/util/blocks/ReframedInteractible.java deleted file mode 100644 index 4209add..0000000 --- a/src/main/java/fr/adrien1106/reframed/util/blocks/ReframedInteractible.java +++ /dev/null @@ -1,20 +0,0 @@ -package fr.adrien1106.reframed.util.blocks; - -import net.minecraft.block.BlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.world.BlockView; - -public interface ReframedInteractible { - default boolean canAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) { - return state.getWeakRedstonePower(view, pos, Direction.UP) == 0; - } - - default boolean canRemoveCollision(BlockState state, BlockView view, BlockPos pos) { - return !state.getCollisionShape(view, pos).isEmpty(); - } - - class Default implements ReframedInteractible { - public static final Default INSTANCE = new Default(); - } -} -- 2.39.5 From 520430d5bc447bd975736935eae31ea7a4364df1 Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Wed, 12 Jun 2024 00:13:38 +0200 Subject: [PATCH 2/6] feat: Axiom now renders frames in selections properly --- build.gradle | 4 + .../model/DoubleRetexturingBakedModel.java | 10 ++ .../client/model/RetexturingBakedModel.java | 42 +++++- .../reframed/mixin/CompatMixinPlugin.java | 7 + .../compat/AxiomChunkedBlockRegionMixin.java | 141 ++++++++++++++++++ .../mixin/compat/AxiomClipboardMixin.java | 25 ++++ .../compat/AxiomCloneBuilderToolMixin.java | 38 +++++ .../compat/AxiomMoveBuilderToolMixin.java | 38 +++++ .../mixin/compat/AxiomPlacementMixin.java | 39 +++++ .../mixin/compat/AxiomRotSpriteMixin.java | 27 ++++ .../mixin/compat/AxiomScale3xMixin.java | 29 ++++ .../mixin/IAxiomChunkedBlockRegionMixin.java | 14 ++ src/main/resources/reframed.mixins.json | 7 + 13 files changed, 418 insertions(+), 3 deletions(-) create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomClipboardMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomCloneBuilderToolMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomMoveBuilderToolMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomPlacementMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomRotSpriteMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomScale3xMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/util/mixin/IAxiomChunkedBlockRegionMixin.java diff --git a/build.gradle b/build.gradle index f438f10..f38109b 100755 --- a/build.gradle +++ b/build.gradle @@ -84,6 +84,9 @@ repositories { includeGroup "maven.modrinth" } } + maven { + url = "https://jitpack.io" + } mavenCentral() // Add repositories to retrieve artifacts from in here. @@ -120,6 +123,7 @@ dependencies { // Axiom for blueprint support modCompileOnly "maven.modrinth:N6n5dqoA:nvx3oDkz" + modCompileOnly "com.github.moulberry:AxiomClientAPI:1.0.5.3" // Fabric API. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" diff --git a/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java index c700b87..2661a1f 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java @@ -5,12 +5,15 @@ import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.texture.Sprite; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.minecraft.util.math.random.Random; import net.minecraft.world.BlockRenderView; +import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; @@ -34,6 +37,13 @@ public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements return model_1.getParticleSprite(); } + @Override + public List getQuads(BlockState blockState, Direction face, Random rand) { + List quads = new ArrayList<>(model_1.getQuads(blockState, face, rand)); + quads.addAll(model_2.getQuads(blockState, face, rand)); + return quads; + } + @Override public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) {} diff --git a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java index fb1a218..9dcbc87 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java @@ -12,11 +12,13 @@ import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; import net.fabricmc.fabric.api.renderer.v1.mesh.*; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; +import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.texture.Sprite; import net.minecraft.item.ItemStack; @@ -25,6 +27,7 @@ import net.minecraft.util.math.Direction; import net.minecraft.util.math.random.Random; import net.minecraft.world.BlockRenderView; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; @@ -62,7 +65,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { System.arraycopy(values, 0, DIRECTIONS_AND_NULL, 0, values.length); } - protected Mesh getBaseMesh(Object key, BlockState state) { + protected Mesh getBaseMesh(Object key, BlockState state) { //Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate if (BASE_MESH_CACHE.containsKey(key)) return BASE_MESH_CACHE.getAndMoveToFirst(key); Mesh mesh = convertModel(state); @@ -70,7 +73,40 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { return mesh; } - protected abstract Mesh convertModel(BlockState state); + private List[] quads = null; + + @Override + public List getQuads(BlockState state, Direction face, Random rand) { + if (quads == null) { + quads = ModelHelper.toQuadLists( + getRetexturedMesh( + new MeshCacheKey( + hashCode(), + appearance_manager.getDefaultAppearance(theme_index), + 0 + ), + state + ) + ); + } + return quads[ModelHelper.toFaceIndex(face)]; + } + + public void setCamo(BlockRenderView world, BlockState state, BlockPos pos) { + if (state == null || state.isAir()) { + quads = null; + return; + } + CamoAppearance camo = appearance_manager.getCamoAppearance(world, state, pos, theme_index, false); + MeshCacheKey key = new MeshCacheKey( + hashCode(), + camo, + 0 + ); + quads = ModelHelper.toQuadLists(camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state)); + } + + protected abstract Mesh convertModel(BlockState state); @Override public boolean isVanillaAdapter() { @@ -176,7 +212,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { emitter.copyFrom(quad); i = key.appearance.transformQuad(emitter, i, quad_index.get(), key.model_id, uv_lock); } while (i > 0); - // kinda weird to do it like that but other directions don't use the quad_index so it doesn't matter + // kinda weird to do it like that but other directions don't use the quad_index, so it doesn't matter if (quad.cullFace() == null) quad_index.getAndIncrement(); }); diff --git a/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java b/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java index cb8014f..81f784f 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java @@ -33,7 +33,14 @@ public class CompatMixinPlugin implements IMixinConfigPlugin { CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMQuadTransformMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityModelWrappingHandlerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomChunkedBlockRegionMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClientBlockEntitySerializerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClipboardMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomCloneBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomPlacementMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomMoveBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomScale3xMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomRotSpriteMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java new file mode 100644 index 0000000..b221fb3 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java @@ -0,0 +1,141 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.moulberry.axiom.render.regions.ChunkedBlockRegion; +import com.moulberry.axiom.utils.IntMatrix; +import com.moulberry.axiom.world_modification.CompressedBlockEntity; +import fr.adrien1106.reframed.client.model.MultiRetexturableModel; +import fr.adrien1106.reframed.client.model.RetexturingBakedModel; +import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; +import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import net.minecraft.block.BlockRenderType; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.block.BlockRenderManager; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtHelper; +import net.minecraft.registry.Registries; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; +import org.joml.Matrix4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; +import java.util.stream.Stream; + +import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY; + +@Mixin(ChunkedBlockRegion.class) +public class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlockRegionMixin { + + @Shadow + private static void renderBlock(BufferBuilder blockBuilder, BlockRenderManager renderManager, BlockPos.Mutable blockPos, Random rand, MatrixStack matrices, BlockRenderView blockAndTintGetter, Matrix4f currentPoseMatrix, Matrix4f basePoseMatrix, int x, int y, int z, BlockState dataState, boolean useAmbientOcclusion) {} + + @Unique + private IntMatrix transform; + @Unique + private IntMatrix inverse_transform; + @Unique + private Long2ObjectMap block_entities; + + + @Redirect( + method = "uploadDirty", + at = @At( + value = "INVOKE", + target = "Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;renderBlock(Lnet/minecraft/client/render/BufferBuilder;Lnet/minecraft/client/render/block/BlockRenderManager;Lnet/minecraft/util/math/BlockPos$Mutable;Lnet/minecraft/util/math/random/Random;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/world/BlockRenderView;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;IIILnet/minecraft/block/BlockState;Z)V" + ) + ) + private void onRenderBlock(BufferBuilder buffer, BlockRenderManager renderer, BlockPos.Mutable pos, Random rand, MatrixStack matrices, BlockRenderView world, Matrix4f current_pos, Matrix4f base_pos, int x, int y, int z, BlockState state, boolean use_ao) { + BakedModel model; + List models; + if (block_entities != null + && state.getRenderType() == BlockRenderType.MODEL + && (model = renderer.getModel(state)) != null + && model instanceof IMultipartBakedModelMixin mpm + && !(models = mpm.getModels(state) + .stream() + .filter(m -> m instanceof RetexturingBakedModel || m instanceof MultiRetexturableModel) + .toList()).isEmpty() + ) { + long key = BlockPos.asLong( + inverse_transform.transformX(pos.getX(), pos.getY(), pos.getZ()), + inverse_transform.transformY(pos.getX(), pos.getY(), pos.getZ()), + inverse_transform.transformZ(pos.getX(), pos.getY(), pos.getZ()) + ); + if (block_entities.containsKey(key)) { + NbtCompound compound = block_entities.get(key).decompress(); + models.stream() + .flatMap(m -> m instanceof MultiRetexturableModel mm + ? mm.models().stream() + : Stream.of((RetexturingBakedModel)m) + ) + .forEach(m -> m.setCamo( + world, + compound.contains(BLOCKSTATE_KEY + m.getThemeIndex()) + ? NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), compound.getCompound(BLOCKSTATE_KEY + m.getThemeIndex())) + : null, + pos + )); + } + } + renderBlock(buffer, renderer, pos, rand, matrices, world, current_pos, base_pos, x, y, z, state, use_ao); + } + + @Inject( + method = "uploadDirty", + at = @At("HEAD") + ) + private void onUploadDirty(Camera camera, Vec3d translation, boolean canResort, boolean canUseAmbientOcclusion, CallbackInfo ci) { + if (transform == null) inverse_transform = new IntMatrix(); + else inverse_transform = transform.copy(); + inverse_transform.invert(); + } + + @Inject( + method = "flip", + at = @At("RETURN") + ) + private void onFlip(Direction.Axis axis, CallbackInfoReturnable cir) { + ((IAxiomChunkedBlockRegionMixin) cir.getReturnValue()).setTransform(transform, block_entities); + } + + @Inject( + method = "rotate", + at = @At("RETURN") + ) + private void onRotate(Direction.Axis axis, int count, CallbackInfoReturnable cir) { + ((IAxiomChunkedBlockRegionMixin) cir.getReturnValue()).setTransform(transform, block_entities); + } + + @Override + public void setTransform(IntMatrix transform, Long2ObjectMap block_entities) { + this.transform = transform; + this.block_entities = block_entities; + } + + @Override + public IntMatrix getTransform() { + return transform; + } + + @Override + public Long2ObjectMap getBlockEntities() { + return block_entities; + } + + +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomClipboardMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomClipboardMixin.java new file mode 100644 index 0000000..eef1e1a --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomClipboardMixin.java @@ -0,0 +1,25 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.moulberry.axiom.clipboard.Clipboard; +import com.moulberry.axiom.clipboard.ClipboardObject; +import com.moulberry.axiom.utils.IntMatrix; +import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Clipboard.class) +public class AxiomClipboardMixin { + + @Inject( + method = "setClipboard(Lcom/moulberry/axiom/clipboard/ClipboardObject;)I", + at = @At( + value = "TAIL" + ), + remap = false + ) + private void onInit(ClipboardObject object, CallbackInfoReturnable cir) { + ((IAxiomChunkedBlockRegionMixin) object.blockRegion()).setTransform(new IntMatrix(), object.blockEntities()); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomCloneBuilderToolMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomCloneBuilderToolMixin.java new file mode 100644 index 0000000..ff2d29e --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomCloneBuilderToolMixin.java @@ -0,0 +1,38 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.moulberry.axiom.buildertools.CloneBuilderTool; +import com.moulberry.axiom.clipboard.SelectionBuffer; +import com.moulberry.axiom.render.regions.ChunkedBlockRegion; +import com.moulberry.axiom.utils.IntMatrix; +import com.moulberry.axiom.world_modification.CompressedBlockEntity; +import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import org.spongepowered.asm.mixin.Final; +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.CallbackInfo; + +@Mixin(CloneBuilderTool.class) +public class AxiomCloneBuilderToolMixin { + + @Shadow(remap = false) private ChunkedBlockRegion blockRegion; + + @Shadow(remap = false) @Final private IntMatrix transformMatrix; + + @Shadow(remap = false) private Long2ObjectMap blockEntities; + + @Inject( + method = "lambda$initiateClone$0", + at = @At( + value = "INVOKE_ASSIGN", + target = "Lcom/moulberry/axiom/clipboard/SelectionBuffer$CopyResult;blockEntities()Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;", + shift = At.Shift.AFTER + ), + remap = false + ) + private void onInitiateClone(int copyId, int offsetX, int offsetY, int offsetZ, SelectionBuffer.CopyResult copyResult, CallbackInfo ci) { + ((IAxiomChunkedBlockRegionMixin) blockRegion).setTransform(transformMatrix, blockEntities); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomMoveBuilderToolMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomMoveBuilderToolMixin.java new file mode 100644 index 0000000..d13c54d --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomMoveBuilderToolMixin.java @@ -0,0 +1,38 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.moulberry.axiom.buildertools.MoveBuilderTool; +import com.moulberry.axiom.clipboard.SelectionBuffer; +import com.moulberry.axiom.render.regions.ChunkedBlockRegion; +import com.moulberry.axiom.utils.IntMatrix; +import com.moulberry.axiom.world_modification.CompressedBlockEntity; +import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import org.spongepowered.asm.mixin.Final; +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.CallbackInfo; + +@Mixin(MoveBuilderTool.class) +public class AxiomMoveBuilderToolMixin { + + @Shadow(remap = false) private ChunkedBlockRegion blockRegion; + + @Shadow(remap = false) @Final private IntMatrix transformMatrix; + + @Shadow(remap = false) private Long2ObjectMap blockEntities; + + @Inject( + method = "lambda$initiateMovement$1", + at = @At( + value = "INVOKE_ASSIGN", + target = "Lcom/moulberry/axiom/clipboard/SelectionBuffer$CopyResult;blockEntities()Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;", + shift = At.Shift.AFTER + ), + remap = false + ) + private void onInitiateClone(int copyId, int offsetX, int offsetY, int offsetZ, SelectionBuffer.CopyResult copyResult, CallbackInfo ci) { + ((IAxiomChunkedBlockRegionMixin) blockRegion).setTransform(transformMatrix, blockEntities); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomPlacementMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomPlacementMixin.java new file mode 100644 index 0000000..485d0c3 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomPlacementMixin.java @@ -0,0 +1,39 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.moulberry.axiom.clipboard.Placement; +import com.moulberry.axiom.render.regions.ChunkedBlockRegion; +import com.moulberry.axiom.utils.IntMatrix; +import com.moulberry.axiom.world_modification.CompressedBlockEntity; +import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import net.minecraft.util.math.BlockPos; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Placement.class) +public class AxiomPlacementMixin { + + @Shadow(remap = false) private Long2ObjectMap blockEntities; + + @Inject( + method = "replacePlacement(Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;Ljava/lang/String;)V", + at = @At("HEAD"), + remap = false + ) + private void onReplacePlacement(ChunkedBlockRegion region, String description, CallbackInfo ci) { + ((IAxiomChunkedBlockRegionMixin) region).setTransform(new IntMatrix(), blockEntities); + } + + @Inject( + method = "startPlacement(Lnet/minecraft/util/math/BlockPos;Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;Ljava/lang/String;)I", + at = @At("HEAD"), + remap = false + ) + private void onStartPlacement(BlockPos target, ChunkedBlockRegion region, Long2ObjectMap entities, String description, CallbackInfoReturnable cir) { + ((IAxiomChunkedBlockRegionMixin) region).setTransform(new IntMatrix(), entities); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomRotSpriteMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomRotSpriteMixin.java new file mode 100644 index 0000000..70d3d27 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomRotSpriteMixin.java @@ -0,0 +1,27 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.moulberry.axiom.render.regions.ChunkedBlockRegion; +import com.moulberry.axiom.scaling.RotSprite; +import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; +import org.joml.Matrix4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(RotSprite.class) +public class AxiomRotSpriteMixin { + + @Inject( + method = "rotateCachedWithOutput", + at = @At( + value = "HEAD" + ), + remap = false + ) + private static void onRotateCachedWithOutput(ChunkedBlockRegion in, Matrix4f matrix4f, ChunkedBlockRegion out, int x, int y, int z, CallbackInfoReturnable cir) { + IAxiomChunkedBlockRegionMixin iin = (IAxiomChunkedBlockRegionMixin) in; + ((IAxiomChunkedBlockRegionMixin) out).setTransform(iin.getTransform(), iin.getBlockEntities()); + } + +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomScale3xMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomScale3xMixin.java new file mode 100644 index 0000000..cd1d39f --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomScale3xMixin.java @@ -0,0 +1,29 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.llamalad7.mixinextras.sugar.Local; +import com.moulberry.axiom.render.regions.ChunkedBlockRegion; +import com.moulberry.axiom.scaling.Scale3x; +import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Scale3x.class) +public class AxiomScale3xMixin { + + @Inject( + method = "scale3x", + at = @At( + value = "INVOKE_ASSIGN", + target = "Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;()V", + shift = At.Shift.AFTER + ), + remap = false + ) + private static void onInit(ChunkedBlockRegion in, boolean postProcessing, CallbackInfoReturnable cir, @Local(ordinal = 1) ChunkedBlockRegion out) { + IAxiomChunkedBlockRegionMixin iin = (IAxiomChunkedBlockRegionMixin) in; + ((IAxiomChunkedBlockRegionMixin) out).setTransform(iin.getTransform(), iin.getBlockEntities()); + } + +} diff --git a/src/main/java/fr/adrien1106/reframed/util/mixin/IAxiomChunkedBlockRegionMixin.java b/src/main/java/fr/adrien1106/reframed/util/mixin/IAxiomChunkedBlockRegionMixin.java new file mode 100644 index 0000000..e360304 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/util/mixin/IAxiomChunkedBlockRegionMixin.java @@ -0,0 +1,14 @@ +package fr.adrien1106.reframed.util.mixin; + +import com.moulberry.axiom.utils.IntMatrix; +import com.moulberry.axiom.world_modification.CompressedBlockEntity; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; + +public interface IAxiomChunkedBlockRegionMixin { + + void setTransform(IntMatrix transform, Long2ObjectMap block_entities); + + IntMatrix getTransform(); + + Long2ObjectMap getBlockEntities(); +} diff --git a/src/main/resources/reframed.mixins.json b/src/main/resources/reframed.mixins.json index 035eb75..14488fb 100644 --- a/src/main/resources/reframed.mixins.json +++ b/src/main/resources/reframed.mixins.json @@ -15,7 +15,14 @@ "compat.AthenaBakedModelMixin", "compat.AthenaConnectedBlockModelMixin", "compat.AthenaWrappedGetterMixin", + "compat.AxiomChunkedBlockRegionMixin", "compat.AxiomClientBlockEntitySerializerMixin", + "compat.AxiomClipboardMixin", + "compat.AxiomCloneBuilderToolMixin", + "compat.AxiomMoveBuilderToolMixin", + "compat.AxiomPlacementMixin", + "compat.AxiomRotSpriteMixin", + "compat.AxiomScale3xMixin", "compat.ContinuityConnectionPredicateMixin", "compat.ContinuityCTMBakedModelMixin", "compat.ContinuityCTMQuadTransformMixin", -- 2.39.5 From 1061431af7e071452936a2cfc167b7abd68dbafd Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Wed, 12 Jun 2024 18:59:08 +0200 Subject: [PATCH 3/6] feat: added button frame --- README.md | 1 - .../java/fr/adrien1106/reframed/ReFramed.java | 3 +- .../reframed/block/ReFramedBlock.java | 6 +- .../reframed/block/ReFramedButtonBlock.java | 238 ++++++++++++++++++ .../reframed/block/ReFramedDoorBlock.java | 6 +- .../reframed/block/ReFramedPaneBlock.java | 6 +- .../block/ReFramedPillarsWallBlock.java | 6 +- .../reframed/block/ReFramedStairBlock.java | 6 +- .../block/ReFramedStairsCubeBlock.java | 6 +- .../reframed/block/ReFramedTrapdoorBlock.java | 6 +- .../reframed/client/ReFramedClient.java | 5 + .../reframed/generator/GBlockstate.java | 1 + .../reframed/generator/GRecipe.java | 1 + .../reframed/generator/block/Button.java | 95 +++++++ .../compat/AxiomChunkedBlockRegionMixin.java | 2 +- 15 files changed, 364 insertions(+), 24 deletions(-) create mode 100644 src/main/java/fr/adrien1106/reframed/block/ReFramedButtonBlock.java create mode 100644 src/main/java/fr/adrien1106/reframed/generator/block/Button.java diff --git a/README.md b/README.md index 7f0f146..3ddd190 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ or based on preferences add the person(s) to the project ### What Shapes are planed to be added Currently, the list of shapes to be added is pretty simple as the mod is still under development: - Fence -- Button - Pressure Plate - Carpet (maybe redundant with Layer) - Post diff --git a/src/main/java/fr/adrien1106/reframed/ReFramed.java b/src/main/java/fr/adrien1106/reframed/ReFramed.java index 3e9b5f9..f3dca2c 100644 --- a/src/main/java/fr/adrien1106/reframed/ReFramed.java +++ b/src/main/java/fr/adrien1106/reframed/ReFramed.java @@ -37,7 +37,7 @@ public class ReFramed implements ModInitializer { public static final String MODID = "reframed"; public static final ArrayList BLOCKS = new ArrayList<>(); - public static Block CUBE, SMALL_CUBE, SMALL_CUBES_STEP, STAIR, HALF_STAIR, STAIRS_CUBE, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, SLAB, SLABS_CUBE, STEP, STEPS_SLAB, LAYER, PILLAR, PILLARS_WALL, WALL, PANE, TRAPDOOR, DOOR; + public static Block CUBE, SMALL_CUBE, SMALL_CUBES_STEP, STAIR, HALF_STAIR, STAIRS_CUBE, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, SLAB, SLABS_CUBE, STEP, STEPS_SLAB, LAYER, PILLAR, PILLARS_WALL, WALL, PANE, TRAPDOOR, DOOR, BUTTON; public static final ArrayList ITEMS = new ArrayList<>(); public static Item HAMMER, SCREWDRIVER, BLUEPRINT, BLUEPRINT_WRITTEN; @@ -70,6 +70,7 @@ public class ReFramed implements ModInitializer { PANE = registerBlock("pane" , new ReFramedPaneBlock(cp(Blocks.OAK_FENCE))); TRAPDOOR = registerBlock("trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.OAK_TRAPDOOR))); DOOR = registerBlock("door" , new ReFramedDoorBlock(cp(Blocks.OAK_DOOR))); + BUTTON = registerBlock("button" , new ReFramedButtonBlock(cp(Blocks.OAK_BUTTON))); HAMMER = registerItem("hammer" , new ReFramedHammerItem(new Item.Settings().maxCount(1))); SCREWDRIVER = registerItem("screwdriver" , new ReFramedScrewdriverItem(new Item.Settings().maxCount(1))); diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java index 236d4eb..ceac63c 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java @@ -72,8 +72,8 @@ public class ReFramedBlock extends Block implements BlockEntityProvider { } @Override - public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - if(!(newState.getBlock() instanceof ReFramedBlock) && + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + if(!(new_state.getBlock() instanceof ReFramedBlock) && world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity && world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS) ) { @@ -86,7 +86,7 @@ public class ReFramedBlock extends Block implements BlockEntityProvider { ItemScatterer.spawn(world, pos, drops); } - super.onStateReplaced(state, world, pos, newState, moved); + super.onStateReplaced(state, world, pos, new_state, moved); } public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack, BlockState old_state, BlockEntity old_entity) { diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedButtonBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedButtonBlock.java new file mode 100644 index 0000000..15d8f9b --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedButtonBlock.java @@ -0,0 +1,238 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.util.VoxelHelper; +import net.minecraft.block.*; +import net.minecraft.block.enums.BlockFace; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.PersistentProjectileEntity; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.sound.SoundCategory; +import net.minecraft.state.StateManager; +import net.minecraft.util.ActionResult; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.random.Random; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; +import net.minecraft.world.WorldView; +import net.minecraft.world.event.GameEvent; +import net.minecraft.world.explosion.Explosion; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; + +import static net.minecraft.state.property.Properties.*; + +public class ReFramedButtonBlock extends WaterloggableReFramedBlock { + + public static final VoxelShape[] BUTTON_VOXELS; + + public ReFramedButtonBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState() + .with(HORIZONTAL_FACING, Direction.NORTH) + .with(BLOCK_FACE, BlockFace.WALL) + .with(POWERED, false) + ); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder.add(HORIZONTAL_FACING, BLOCK_FACE, POWERED)); + } + + @Override + public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) { + return canPlaceAt(world, pos, getDirection(state).getOpposite()); + } + + public static boolean canPlaceAt(WorldView world, BlockPos pos, Direction direction) { + BlockPos other_pos = pos.offset(direction); + return world.getBlockState(other_pos).isSideSolidFullSquare(world, other_pos, direction.getOpposite()); + } + + @Override + public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { + BlockState state = super.getPlacementState(ctx); + Direction side = ctx.getSide(); + return state + .with(HORIZONTAL_FACING, side.getAxis() == Direction.Axis.Y + ? ctx.getHorizontalPlayerFacing() + : side + ) + .with(BLOCK_FACE, side.getAxis() != Direction.Axis.Y + ? BlockFace.WALL + : side == Direction.UP ? BlockFace.FLOOR : BlockFace.CEILING + ); + } + + @Override + public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos other_pos) { + return getDirection(state).getOpposite() == direction && !state.canPlaceAt(world, pos) + ? Blocks.AIR.getDefaultState() + : super.getStateForNeighborUpdate(state, direction, other_state, world, pos, other_pos); + } + + @Override + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + ActionResult result = super.onUse(state, world, pos, player, hand, hit); + if (result.isAccepted()) return result; + + if (state.get(POWERED)) return ActionResult.CONSUME; + powerOn(state, world, pos); + playClickSound(player, world, pos, true); + world.emitGameEvent(player, GameEvent.BLOCK_ACTIVATE, pos); + + return ActionResult.success(world.isClient); + } + + @Override + public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer stackMerger) { + if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK && !world.isClient() && !(Boolean)state.get(POWERED)) { + powerOn(state, world, pos); + } + + super.onExploded(state, world, pos, explosion, stackMerger); + } + + public void powerOn(BlockState state, World world, BlockPos pos) { + world.setBlockState(pos, state.with(POWERED, true), 3); + updateNeighbors(state, world, pos); + world.scheduleBlockTick(pos, this, 30); + } + + protected void playClickSound(@Nullable PlayerEntity player, WorldAccess world, BlockPos pos, boolean powered) { + world.playSound( + powered ? player : null, + pos, + powered ? BlockSetType.OAK.buttonClickOn() : BlockSetType.OAK.buttonClickOff(), + SoundCategory.BLOCKS + ); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return BUTTON_VOXELS[ + (state.get(POWERED) ? 12 : 0) + + (4 * state.get(BLOCK_FACE).ordinal()) + + state.get(HORIZONTAL_FACING).ordinal() - 2 + ]; + } + + @Override + public BlockState rotate(BlockState state, BlockRotation rotation) { + return state.with(HORIZONTAL_FACING, rotation.rotate(state.get(HORIZONTAL_FACING))); + } + + @Override + public BlockState mirror(BlockState state, BlockMirror mirror) { + return state.with(HORIZONTAL_FACING, mirror.apply(state.get(HORIZONTAL_FACING))); + } + + @Override + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, false); + + if(!state.isOf(new_state.getBlock())) { + if (!moved && state.get(POWERED)) updateNeighbors(state, world, pos); + world.removeBlockEntity(pos); + } + } + + @Override + public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { + return state.get(POWERED) ? 15 : super.getWeakRedstonePower(state, view, pos, dir); + } + + @Override + public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { + return dir == getDirection(state) ? state.getWeakRedstonePower(view, pos, dir) : 0; + } + + @Override + public boolean emitsRedstonePower(BlockState state) { + return true; + } + + @Override + public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { + if (state.get(POWERED)) tryPowerWithProjectiles(state, world, pos); + } + + @Override + public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) { + if (!world.isClient && !state.get(POWERED)) tryPowerWithProjectiles(state, world, pos); + } + + protected void tryPowerWithProjectiles(BlockState state, World world, BlockPos pos) { + PersistentProjectileEntity projectile = world.getNonSpectatingEntities( + PersistentProjectileEntity.class, + state.getOutlineShape(world, pos).getBoundingBox().offset(pos) + ).stream().findFirst().orElse(null); + boolean has_projectile = projectile != null; + if (has_projectile != state.get(POWERED)) { + world.setBlockState(pos, state.with(POWERED, has_projectile), 3); + this.updateNeighbors(state, world, pos); + this.playClickSound(null, world, pos, has_projectile); + world.emitGameEvent(projectile, has_projectile ? GameEvent.BLOCK_ACTIVATE : GameEvent.BLOCK_DEACTIVATE, pos); + } + + if (has_projectile) { + world.scheduleBlockTick(pos, this, 30); + } + + } + + private void updateNeighbors(BlockState state, World world, BlockPos pos) { + world.updateNeighborsAlways(pos, this); + world.updateNeighborsAlways(pos.offset(getDirection(state).getOpposite()), this); + } + + protected static Direction getDirection(BlockState state) { + return switch (state.get(BLOCK_FACE)) { + case CEILING -> Direction.DOWN; + case FLOOR -> Direction.UP; + default -> state.get(HORIZONTAL_FACING); + }; + } + + + static { + VoxelShape SHAPE = createCuboidShape(5, 0, 6, 11, 2, 10); + VoxelShape POWERED_SHAPE = createCuboidShape(5, 0, 6, 11, 1, 10); + BUTTON_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 24) + .add() + .add(0, VoxelHelper::rotateY) + .add() + .add(VoxelHelper::rotateZ, VoxelHelper::rotateY) + .add(VoxelHelper::mirrorZ) + .add(VoxelHelper::rotateY) + .add(VoxelHelper::mirrorX) + .add(0, VoxelHelper::mirrorY) + .add() + .add(2, VoxelHelper::mirrorY) + .add() + .add(POWERED_SHAPE) + .add() + .add(12, VoxelHelper::rotateY) + .add() + .add(VoxelHelper::rotateZ, VoxelHelper::rotateY) + .add(VoxelHelper::mirrorZ) + .add(VoxelHelper::rotateY) + .add(VoxelHelper::mirrorX) + .add(12, VoxelHelper::mirrorY) + .add() + .add(13, VoxelHelper::mirrorY) + .add() + .build(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java index 3c587b3..4cc239f 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoorBlock.java @@ -127,10 +127,10 @@ public class ReFramedDoorBlock extends WaterloggableReFramedBlock { } @Override - public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - super.onStateReplaced(state, world, pos, newState, moved); + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, moved); - if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java index a800d1c..6ea5dd2 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java @@ -73,10 +73,10 @@ public class ReFramedPaneBlock extends WaterloggableReFramedBlock { } @Override - public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - super.onStateReplaced(state, world, pos, newState, moved); + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, moved); - if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedPillarsWallBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedPillarsWallBlock.java index 952fc7e..1896945 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedPillarsWallBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedPillarsWallBlock.java @@ -72,10 +72,10 @@ public class ReFramedPillarsWallBlock extends WaterloggableReFramedDoubleBlock { } @Override - public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - super.onStateReplaced(state, world, pos, newState, moved); + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, moved); - if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedStairBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedStairBlock.java index 522edfd..60f7601 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedStairBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedStairBlock.java @@ -87,10 +87,10 @@ public class ReFramedStairBlock extends WaterloggableReFramedBlock { } @Override - public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - super.onStateReplaced(state, world, pos, newState, moved); + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, moved); - if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedStairsCubeBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedStairsCubeBlock.java index b1dcb6f..ab16582 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedStairsCubeBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedStairsCubeBlock.java @@ -77,10 +77,10 @@ public class ReFramedStairsCubeBlock extends ReFramedDoubleBlock { } @Override - public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - super.onStateReplaced(state, world, pos, newState, moved); + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, moved); - if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedTrapdoorBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedTrapdoorBlock.java index d2a2c09..3ae4785 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedTrapdoorBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedTrapdoorBlock.java @@ -84,10 +84,10 @@ public class ReFramedTrapdoorBlock extends WaterloggableReFramedBlock { } @Override - public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - super.onStateReplaced(state, world, pos, newState, moved); + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, moved); - if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java index a4a8520..6aed4ed 100644 --- a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java +++ b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java @@ -130,6 +130,10 @@ public class ReFramedClient implements ClientModInitializer { HELPER.addReFramedModel("trapdoor_top" , HELPER.auto(new Identifier("block/oak_trapdoor_top"))); // DOOR HELPER.addReFramedModel("door_inventory" , HELPER.auto(ReFramed.id("block/door"))); + // BUTTON + HELPER.addReFramedModel("button_inventory" , HELPER.auto(new Identifier("block/button_inventory"))); + HELPER.addReFramedModel("button" , HELPER.auto(new Identifier("block/button"))); + HELPER.addReFramedModel("button_pressed" , HELPER.auto(new Identifier("block/button_pressed"))); //item model assignments (in lieu of models/item/___.json) @@ -152,6 +156,7 @@ public class ReFramedClient implements ClientModInitializer { HELPER.assignItemModel("pane_inventory" , ReFramed.PANE); HELPER.assignItemModel("trapdoor_bottom" , ReFramed.TRAPDOOR); HELPER.assignItemModel("door_inventory" , ReFramed.DOOR); + HELPER.assignItemModel("button_inventory" , ReFramed.BUTTON); } private void privateInit() { diff --git a/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java b/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java index d111af5..4b0c049 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java +++ b/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java @@ -37,6 +37,7 @@ public class GBlockstate extends FabricModelProvider { providers.put(ReFramedPaneBlock.class, new Pane()); providers.put(ReFramedTrapdoorBlock.class, new Trapdoor()); providers.put(ReFramedDoorBlock.class, new Door()); + providers.put(ReFramedButtonBlock.class, new Button()); } public GBlockstate(FabricDataOutput output) { diff --git a/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java b/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java index 7ac1beb..ed156b1 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java +++ b/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java @@ -39,6 +39,7 @@ public class GRecipe extends FabricRecipeProvider { providers.put(ReFramedPaneBlock.class, new Pane()); providers.put(ReFramedTrapdoorBlock.class, new Trapdoor()); providers.put(ReFramedDoorBlock.class, new Door()); + providers.put(ReFramedButtonBlock.class, new Button()); providers.put(ReFramedBlueprintItem.class, new Blueprint()); providers.put(ReFramedHammerItem.class, new Hammer()); providers.put(ReFramedScrewdriverItem.class, new Screwdriver()); diff --git a/src/main/java/fr/adrien1106/reframed/generator/block/Button.java b/src/main/java/fr/adrien1106/reframed/generator/block/Button.java new file mode 100644 index 0000000..2ad3e8d --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/generator/block/Button.java @@ -0,0 +1,95 @@ +package fr.adrien1106.reframed.generator.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.BlockStateProvider; +import fr.adrien1106.reframed.generator.GBlockstate; +import fr.adrien1106.reframed.generator.RecipeSetter; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; +import net.minecraft.block.Block; +import net.minecraft.block.enums.BlockFace; +import net.minecraft.data.client.BlockStateSupplier; +import net.minecraft.data.client.MultipartBlockStateSupplier; +import net.minecraft.data.server.recipe.RecipeExporter; +import net.minecraft.data.server.recipe.RecipeProvider; +import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.recipe.book.RecipeCategory; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Direction; + +import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.state.property.Properties.*; + +public class Button implements RecipeSetter, BlockStateProvider { + + @Override + public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) { + RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 8); + ShapelessRecipeJsonBuilder.create(RecipeCategory.BUILDING_BLOCKS, convertible, 1) + .input(ReFramed.CUBE, 1) + .criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE)) + .criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible)) + .offerTo(exporter); + } + + @Override + public BlockStateSupplier getMultipart(Block block) { + Identifier button = ReFramed.id("button_special"); + Identifier button_pressed = ReFramed.id("button_pressed_special"); + return MultipartBlockStateSupplier.create(block) + // FLOOR OFF + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.FLOOR), + GBlockstate.variant(button, true, R0, R0)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.FLOOR), + GBlockstate.variant(button, true, R0, R90)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.FLOOR), + GBlockstate.variant(button, true, R0, R180)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.FLOOR), + GBlockstate.variant(button, true, R0, R270)) + // CEILING OFF + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.CEILING), + GBlockstate.variant(button, true, R180, R0)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.CEILING), + GBlockstate.variant(button, true, R180, R90)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.CEILING), + GBlockstate.variant(button, true, R180, R180)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.CEILING), + GBlockstate.variant(button, true, R180, R270)) + // WALL OFF + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.WALL), + GBlockstate.variant(button, true, R90, R0)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.WALL), + GBlockstate.variant(button, true, R90, R90)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.WALL), + GBlockstate.variant(button, true, R90, R180)) + .with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.WALL), + GBlockstate.variant(button, true, R90, R270)) + // FLOOR ON + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.FLOOR), + GBlockstate.variant(button_pressed, true, R0, R0)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.FLOOR), + GBlockstate.variant(button_pressed, true, R0, R90)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.FLOOR), + GBlockstate.variant(button_pressed, true, R0, R180)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.FLOOR), + GBlockstate.variant(button_pressed, true, R0, R270)) + // CEILING ON + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.CEILING), + GBlockstate.variant(button_pressed, true, R180, R0)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.CEILING), + GBlockstate.variant(button_pressed, true, R180, R90)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.CEILING), + GBlockstate.variant(button_pressed, true, R180, R180)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.CEILING), + GBlockstate.variant(button_pressed, true, R180, R270)) + // WALL ON + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.WALL), + GBlockstate.variant(button_pressed, true, R90, R0)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.WALL), + GBlockstate.variant(button_pressed, true, R90, R90)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.WALL), + GBlockstate.variant(button_pressed, true, R90, R180)) + .with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.WALL), + GBlockstate.variant(button_pressed, true, R90, R270)); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java index b221fb3..c0b4689 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java @@ -38,7 +38,7 @@ import java.util.stream.Stream; import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY; -@Mixin(ChunkedBlockRegion.class) +@Mixin(ChunkedBlockRegion.class) // TODO: Look here for better rotation/flip support public class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlockRegionMixin { @Shadow -- 2.39.5 From 535fd6151e92c7dc8720229cde7459ed468acb2c Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Thu, 13 Jun 2024 01:28:28 +0200 Subject: [PATCH 4/6] feat: added post fence and postfence frames --- README.md | 2 - .../java/fr/adrien1106/reframed/ReFramed.java | 16 +- .../block/ConnectingReFramedBlock.java | 104 ++++++++++++ .../reframed/block/PillarReFramedBlock.java | 46 ++++++ .../reframed/block/ReFramedFenceBlock.java | 105 ++++++++++++ .../reframed/block/ReFramedPaneBlock.java | 89 +--------- .../reframed/block/ReFramedPillarBlock.java | 29 +--- .../reframed/block/ReFramedPostBlock.java | 37 +++++ .../block/ReFramedPostFenceBlock.java | 152 ++++++++++++++++++ .../reframed/client/ReFramedClient.java | 13 ++ .../reframed/generator/GBlockTag.java | 2 + .../reframed/generator/GBlockstate.java | 3 + .../reframed/generator/GRecipe.java | 3 + .../reframed/generator/block/Fence.java | 73 +++++++++ .../reframed/generator/block/Post.java | 50 ++++++ .../reframed/generator/block/PostFence.java | 69 ++++++++ .../reframed/models/block/fence/core.json | 17 ++ .../models/block/fence/full/inventory.json | 60 +++++++ .../models/block/fence/full/side_bars.json | 34 ++++ .../models/block/fence/full/side_core.json | 62 +++++++ .../models/block/fence/inventory.json | 68 ++++++++ .../reframed/models/block/fence/side_off.json | 20 +++ .../reframed/models/block/fence/side_on.json | 69 ++++++++ .../assets/reframed/models/block/post.json | 21 +++ 24 files changed, 1027 insertions(+), 117 deletions(-) create mode 100644 src/main/java/fr/adrien1106/reframed/block/ConnectingReFramedBlock.java create mode 100644 src/main/java/fr/adrien1106/reframed/block/PillarReFramedBlock.java create mode 100644 src/main/java/fr/adrien1106/reframed/block/ReFramedFenceBlock.java create mode 100644 src/main/java/fr/adrien1106/reframed/block/ReFramedPostBlock.java create mode 100644 src/main/java/fr/adrien1106/reframed/block/ReFramedPostFenceBlock.java create mode 100644 src/main/java/fr/adrien1106/reframed/generator/block/Fence.java create mode 100644 src/main/java/fr/adrien1106/reframed/generator/block/Post.java create mode 100644 src/main/java/fr/adrien1106/reframed/generator/block/PostFence.java create mode 100644 src/main/resources/assets/reframed/models/block/fence/core.json create mode 100644 src/main/resources/assets/reframed/models/block/fence/full/inventory.json create mode 100644 src/main/resources/assets/reframed/models/block/fence/full/side_bars.json create mode 100644 src/main/resources/assets/reframed/models/block/fence/full/side_core.json create mode 100644 src/main/resources/assets/reframed/models/block/fence/inventory.json create mode 100644 src/main/resources/assets/reframed/models/block/fence/side_off.json create mode 100644 src/main/resources/assets/reframed/models/block/fence/side_on.json create mode 100644 src/main/resources/assets/reframed/models/block/post.json diff --git a/README.md b/README.md index 3ddd190..2aa9500 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,8 @@ or based on preferences add the person(s) to the project ### What Shapes are planed to be added Currently, the list of shapes to be added is pretty simple as the mod is still under development: -- Fence - Pressure Plate - Carpet (maybe redundant with Layer) -- Post - Half Slab (maybe redundant with Layer) - Slabs Stair (a stair with one end being of a second theme, might be done in multiple blocks) diff --git a/src/main/java/fr/adrien1106/reframed/ReFramed.java b/src/main/java/fr/adrien1106/reframed/ReFramed.java index f3dca2c..211cf8a 100644 --- a/src/main/java/fr/adrien1106/reframed/ReFramed.java +++ b/src/main/java/fr/adrien1106/reframed/ReFramed.java @@ -37,7 +37,18 @@ public class ReFramed implements ModInitializer { public static final String MODID = "reframed"; public static final ArrayList BLOCKS = new ArrayList<>(); - public static Block CUBE, SMALL_CUBE, SMALL_CUBES_STEP, STAIR, HALF_STAIR, STAIRS_CUBE, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, SLAB, SLABS_CUBE, STEP, STEPS_SLAB, LAYER, PILLAR, PILLARS_WALL, WALL, PANE, TRAPDOOR, DOOR, BUTTON; + public static Block + CUBE, + SMALL_CUBE, SMALL_CUBES_STEP, + STAIR, STAIRS_CUBE, + HALF_STAIR, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, + SLAB, SLABS_CUBE, + STEP, STEPS_SLAB, + LAYER, + PILLAR, PILLARS_WALL, WALL, + PANE, TRAPDOOR, DOOR, + BUTTON, + POST, POST_FENCE, FENCE; public static final ArrayList ITEMS = new ArrayList<>(); public static Item HAMMER, SCREWDRIVER, BLUEPRINT, BLUEPRINT_WRITTEN; @@ -71,6 +82,9 @@ public class ReFramed implements ModInitializer { TRAPDOOR = registerBlock("trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.OAK_TRAPDOOR))); DOOR = registerBlock("door" , new ReFramedDoorBlock(cp(Blocks.OAK_DOOR))); BUTTON = registerBlock("button" , new ReFramedButtonBlock(cp(Blocks.OAK_BUTTON))); + POST = registerBlock("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE))); + FENCE = registerBlock("fence" , new ReFramedFenceBlock(cp(Blocks.OAK_FENCE))); + POST_FENCE = registerBlock("post_fence" , new ReFramedPostFenceBlock(cp(Blocks.OAK_FENCE))); HAMMER = registerItem("hammer" , new ReFramedHammerItem(new Item.Settings().maxCount(1))); SCREWDRIVER = registerItem("screwdriver" , new ReFramedScrewdriverItem(new Item.Settings().maxCount(1))); diff --git a/src/main/java/fr/adrien1106/reframed/block/ConnectingReFramedBlock.java b/src/main/java/fr/adrien1106/reframed/block/ConnectingReFramedBlock.java new file mode 100644 index 0000000..5b63d7a --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ConnectingReFramedBlock.java @@ -0,0 +1,104 @@ +package fr.adrien1106.reframed.block; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; +import org.apache.commons.lang3.function.TriFunction; +import org.jetbrains.annotations.Nullable; + +import static net.minecraft.state.property.Properties.*; +import static net.minecraft.state.property.Properties.SOUTH; + +public abstract class ConnectingReFramedBlock extends WaterloggableReFramedBlock { + + public ConnectingReFramedBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState() + .with(EAST, false) + .with(NORTH, false) + .with(WEST, false) + .with(SOUTH, false) + ); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST)); + } + + @Override + public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) { + BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved); + if (dir == Direction.DOWN) return new_state; + + return placementState(new_state, world, pos, this::connectsTo); + } + + @Override + public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { + BlockState state = super.getPlacementState(ctx); + World world = ctx.getWorld(); + BlockPos pos = ctx.getBlockPos(); + + return placementState(state, world, pos, this::connectsTo); + } + + @Override + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, moved); + + if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); + } + + @Override + public BlockState rotate(BlockState state, BlockRotation rotation) { + return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) -> + s.with(getConnectionProperty(rotation.rotate(dir)), state.get(getConnectionProperty(dir))) + , (prev, next) -> next); + } + + @Override + public BlockState mirror(BlockState state, BlockMirror mirror) { + return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) -> + s.with(getConnectionProperty(mirror.apply(dir)), state.get(getConnectionProperty(dir))) + , (prev, next) -> next); + } + + protected abstract boolean connectsTo(BlockState state, boolean fs, Direction dir); + + @Override + public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context); + + public static BooleanProperty getConnectionProperty(Direction dir) { + return switch (dir) { + case NORTH -> NORTH; + case EAST -> EAST; + case SOUTH -> SOUTH; + case WEST -> WEST; + default -> null; + }; + } + + public static BlockState placementState(BlockState state, BlockView world, BlockPos pos, TriFunction connectsTo) { + for (Direction dir: Direction.Type.HORIZONTAL) { + BlockState neighbor = world.getBlockState(pos.offset(dir)); + state = state.with(getConnectionProperty(dir), connectsTo.apply( + neighbor, + neighbor.isSideSolidFullSquare(world, pos.offset(dir), dir.getOpposite()), + dir + )); + } + return state; + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/PillarReFramedBlock.java b/src/main/java/fr/adrien1106/reframed/block/PillarReFramedBlock.java new file mode 100644 index 0000000..1360d9e --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/PillarReFramedBlock.java @@ -0,0 +1,46 @@ +package fr.adrien1106.reframed.block; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.state.StateManager; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; +import org.jetbrains.annotations.Nullable; + +import static net.minecraft.state.property.Properties.AXIS; + +public abstract class PillarReFramedBlock extends WaterloggableReFramedBlock { + public PillarReFramedBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y)); + } + + @Override + protected void appendProperties(StateManager.Builder< Block, BlockState > builder) { + super.appendProperties(builder.add(AXIS)); + } + + @Override + public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { + return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis()); + } + + @Override + public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context); + + @Override + public BlockState rotate(BlockState state, BlockRotation rotation) { + return state.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis()); + } + + @Override + public BlockState mirror(BlockState state, BlockMirror mirror) { + return state.with(AXIS, mirror.apply(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis()); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedFenceBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedFenceBlock.java new file mode 100644 index 0000000..d34b717 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedFenceBlock.java @@ -0,0 +1,105 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.util.VoxelHelper; +import net.minecraft.block.BlockState; +import net.minecraft.block.FenceGateBlock; +import net.minecraft.block.ShapeContext; +import net.minecraft.entity.ai.pathing.NavigationType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.LeadItem; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.function.BooleanBiFunction; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; + +public class ReFramedFenceBlock extends ConnectingReFramedBlock { + + public static final VoxelShape[] FENCE_VOXELS; + + public ReFramedFenceBlock(Settings settings) { + super(settings); + } + + @Override + protected boolean connectsTo(BlockState state, boolean fs, Direction dir) { + return fs || state.isIn(BlockTags.FENCES) + || (state.getBlock() instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, dir)); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + VoxelShape shape = FENCE_VOXELS[0]; + for (Direction dir: Direction.Type.HORIZONTAL) { + if (state.get(getConnectionProperty(dir))) + shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]); + } + return shape; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { + VoxelShape shape = FENCE_VOXELS[5]; + for (Direction dir: Direction.Type.HORIZONTAL) { + if (state.get(getConnectionProperty(dir))) + shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() + 4]); + } + return shape; + } + + @Override + public VoxelShape getCameraCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return getOutlineShape(state, world, pos, context); + } + + @Override + public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { + return getOutlineShape(state, view, pos, ShapeContext.absent()); + } + + @Override + public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) { + return false; + } + + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + ActionResult result = super.onUse(state, world, pos, player, hand, hit); + if (result.isAccepted()) return result; + if (world.isClient) { + ItemStack itemStack = player.getStackInHand(hand); + return itemStack.isOf(Items.LEAD) ? ActionResult.SUCCESS : ActionResult.PASS; + } else { + return LeadItem.attachHeldMobsToBlock(player, world, pos); + } + } + + static { + VoxelShape POST = createCuboidShape(6, 0, 6, 10, 16, 10); + VoxelShape POST_COLLISION = createCuboidShape(6, 0, 6, 10, 24, 10); + VoxelShape SIDE = VoxelShapes.combineAndSimplify( + createCuboidShape(7, 12, 0, 9, 15, 6), + createCuboidShape(7, 6, 0, 9, 9, 6), + BooleanBiFunction.OR + ); + VoxelShape SIDE_COLLISION = createCuboidShape(7, 0, 0, 9, 24, 6); + FENCE_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 5) + .add(SIDE) + .add(VoxelHelper::mirrorZ) + .add(VoxelHelper::rotateY) + .add(VoxelHelper::mirrorX) + .add(POST_COLLISION) + .add(SIDE_COLLISION) + .add(VoxelHelper::mirrorZ) + .add(VoxelHelper::rotateY) + .add(VoxelHelper::mirrorX) + .build(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java index 6ea5dd2..34d10c1 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedPaneBlock.java @@ -1,122 +1,39 @@ package fr.adrien1106.reframed.block; import fr.adrien1106.reframed.util.VoxelHelper; -import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.PaneBlock; import net.minecraft.block.ShapeContext; -import net.minecraft.item.ItemPlacementContext; import net.minecraft.registry.tag.BlockTags; -import net.minecraft.state.StateManager; -import net.minecraft.state.property.BooleanProperty; -import net.minecraft.util.BlockMirror; -import net.minecraft.util.BlockRotation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShapes; import net.minecraft.world.BlockView; -import net.minecraft.world.World; -import net.minecraft.world.WorldAccess; -import org.jetbrains.annotations.Nullable; -import static net.minecraft.state.property.Properties.*; - -public class ReFramedPaneBlock extends WaterloggableReFramedBlock { +public class ReFramedPaneBlock extends ConnectingReFramedBlock { public static final VoxelShape[] PANE_VOXELS; public ReFramedPaneBlock(Settings settings) { super(settings); - setDefaultState(getDefaultState() - .with(EAST, false) - .with(NORTH, false) - .with(WEST, false) - .with(SOUTH, false) - ); - } - - @Override - protected void appendProperties(StateManager.Builder builder) { - super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST)); - } - - @Override - public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) { - BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved); - if (dir == Direction.DOWN) return new_state; - - for (Direction side: Direction.Type.HORIZONTAL) { - BlockState neighbor = world.getBlockState(pos.offset(side)); - new_state = new_state.with(getPaneProperty(side), connectsTo( - neighbor, - neighbor.isSideSolidFullSquare(world, pos.offset(side), side.getOpposite()) - )); - } - return new_state; - } - - @Override - public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { - BlockState state = super.getPlacementState(ctx); - World world = ctx.getWorld(); - BlockPos pos = ctx.getBlockPos(); - - for (Direction dir: Direction.Type.HORIZONTAL) { - BlockState neighbor = world.getBlockState(pos.offset(dir)); - state = state.with(getPaneProperty(dir), connectsTo( - neighbor, - neighbor.isSideSolidFullSquare(world, pos.offset(dir), dir.getOpposite()) - )); - } - return state; - } - - @Override - public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { - super.onStateReplaced(state, world, pos, new_state, moved); - - if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); } @Override public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { VoxelShape shape = PANE_VOXELS[0]; for (Direction dir: Direction.Type.HORIZONTAL) { - if (state.get(getPaneProperty(dir))) + if (state.get(getConnectionProperty(dir))) shape = VoxelShapes.union(shape, PANE_VOXELS[dir.ordinal() - 1]); } return shape; } @Override - public BlockState rotate(BlockState state, BlockRotation rotation) { - return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) -> - s.with(getPaneProperty(rotation.rotate(dir)), state.get(getPaneProperty(dir))) - , (prev, next) -> next); - } - - @Override - public BlockState mirror(BlockState state, BlockMirror mirror) { - return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) -> - s.with(getPaneProperty(mirror.apply(dir)), state.get(getPaneProperty(dir))) - , (prev, next) -> next); - } - - public static boolean connectsTo(BlockState state, boolean fs) { + protected boolean connectsTo(BlockState state, boolean fs, Direction dir) { return !cannotConnect(state) && fs || state.getBlock() instanceof PaneBlock || state.isIn(BlockTags.WALLS); } - public static BooleanProperty getPaneProperty(Direction dir) { - return switch (dir) { - case NORTH -> NORTH; - case EAST -> EAST; - case SOUTH -> SOUTH; - case WEST -> WEST; - default -> null; - }; - } - static { VoxelShape POST = createCuboidShape(7, 0, 7, 9, 16, 9); VoxelShape SIDE = createCuboidShape(7, 0, 0, 9, 16, 7); diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedPillarBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedPillarBlock.java index 9b9a379..ed520c3 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedPillarBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedPillarBlock.java @@ -1,38 +1,21 @@ package fr.adrien1106.reframed.block; import fr.adrien1106.reframed.util.VoxelHelper; -import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.ShapeContext; -import net.minecraft.item.ItemPlacementContext; -import net.minecraft.state.StateManager; -import net.minecraft.util.BlockMirror; -import net.minecraft.util.BlockRotation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.shape.VoxelShape; import net.minecraft.world.BlockView; -import org.jetbrains.annotations.Nullable; import static net.minecraft.state.property.Properties.AXIS; -public class ReFramedPillarBlock extends WaterloggableReFramedBlock { +public class ReFramedPillarBlock extends PillarReFramedBlock { public static final VoxelShape[] PILLAR_VOXELS; public ReFramedPillarBlock(Settings settings) { super(settings); - setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y)); - } - - @Override - protected void appendProperties(StateManager.Builder builder) { - super.appendProperties(builder.add(AXIS)); - } - - @Override - public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { - return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis()); } @Override @@ -44,16 +27,6 @@ public class ReFramedPillarBlock extends WaterloggableReFramedBlock { return PILLAR_VOXELS[axis.ordinal()]; } - @Override - public BlockState rotate(BlockState state, BlockRotation rotation) { - return state.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis()); - } - - @Override - public BlockState mirror(BlockState state, BlockMirror mirror) { - return state.with(AXIS, mirror.apply(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis()); - } - static { final VoxelShape PILLAR = createCuboidShape(0, 4, 4, 16, 12, 12); PILLAR_VOXELS = VoxelHelper.VoxelListBuilder.create(PILLAR, 3) diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedPostBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedPostBlock.java new file mode 100644 index 0000000..14c053f --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedPostBlock.java @@ -0,0 +1,37 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.util.VoxelHelper; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; + +import static net.minecraft.state.property.Properties.AXIS; + +public class ReFramedPostBlock extends PillarReFramedBlock { + + public static final VoxelShape[] POST_VOXELS; + + public ReFramedPostBlock(Settings settings) { + super(settings); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return getPillarShape(state.get(AXIS)); + } + + public static VoxelShape getPillarShape(Direction.Axis axis) { + return POST_VOXELS[axis.ordinal()]; + } + + static { + final VoxelShape POST = createCuboidShape(0, 6, 6, 16, 10, 10); + POST_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 3) + .add(VoxelHelper::rotateZ) + .add(VoxelHelper::rotateX) + .build(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedPostFenceBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedPostFenceBlock.java new file mode 100644 index 0000000..a90423d --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedPostFenceBlock.java @@ -0,0 +1,152 @@ +package fr.adrien1106.reframed.block; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.FenceGateBlock; +import net.minecraft.block.ShapeContext; +import net.minecraft.entity.ai.pathing.NavigationType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.LeadItem; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.state.StateManager; +import net.minecraft.util.ActionResult; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; +import org.jetbrains.annotations.Nullable; + +import static fr.adrien1106.reframed.block.ConnectingReFramedBlock.getConnectionProperty; +import static fr.adrien1106.reframed.block.ConnectingReFramedBlock.placementState; +import static fr.adrien1106.reframed.block.ReFramedFenceBlock.FENCE_VOXELS; +import static net.minecraft.state.property.Properties.*; +import static net.minecraft.state.property.Properties.WEST; + +public class ReFramedPostFenceBlock extends WaterloggableReFramedDoubleBlock { + + public ReFramedPostFenceBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState() + .with(EAST, false) + .with(NORTH, false) + .with(WEST, false) + .with(SOUTH, false) + ); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST)); + } + + @Override + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) { + super.onStateReplaced(state, world, pos, new_state, moved); + + if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos); + } + + @Override + public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) { + BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved); + if (dir == Direction.DOWN) return new_state; + + return placementState(new_state, world, pos, this::connectsTo); + } + + @Override + public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { + BlockState state = super.getPlacementState(ctx); + World world = ctx.getWorld(); + BlockPos pos = ctx.getBlockPos(); + + return placementState(state, world, pos, this::connectsTo); + } + + @Override + public BlockState rotate(BlockState state, BlockRotation rotation) { + return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) -> + s.with(getConnectionProperty(rotation.rotate(dir)), state.get(getConnectionProperty(dir))) + , (prev, next) -> next); + } + + @Override + public BlockState mirror(BlockState state, BlockMirror mirror) { + return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) -> + s.with(getConnectionProperty(mirror.apply(dir)), state.get(getConnectionProperty(dir))) + , (prev, next) -> next); + } + + private boolean connectsTo(BlockState state, boolean fs, Direction dir) { + return fs || state.isIn(BlockTags.FENCES) + || (state.getBlock() instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, dir)); + } + + @Override + public VoxelShape getShape(BlockState state, int i) { + if (i == 1) return FENCE_VOXELS[0]; + VoxelShape shape = VoxelShapes.empty(); + for (Direction dir: Direction.Type.HORIZONTAL) { + if (state.get(getConnectionProperty(dir))) + shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]); + } + return shape; + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + VoxelShape shape = FENCE_VOXELS[0]; + for (Direction dir: Direction.Type.HORIZONTAL) { + if (state.get(getConnectionProperty(dir))) + shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]); + } + return shape; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { + VoxelShape shape = FENCE_VOXELS[5]; + for (Direction dir: Direction.Type.HORIZONTAL) { + if (state.get(getConnectionProperty(dir))) + shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() + 4]); + } + return shape; + } + + @Override + public VoxelShape getCameraCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return getOutlineShape(state, world, pos, context); + } + + @Override + public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { + return getOutlineShape(state, view, pos, ShapeContext.absent()); + } + + @Override + public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) { + return false; + } + + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + ActionResult result = super.onUse(state, world, pos, player, hand, hit); + if (result.isAccepted()) return result; + if (world.isClient) { + ItemStack itemStack = player.getStackInHand(hand); + return itemStack.isOf(Items.LEAD) ? ActionResult.SUCCESS : ActionResult.PASS; + } else { + return LeadItem.attachHeldMobsToBlock(player, world, pos); + } + } + +} diff --git a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java index 6aed4ed..4bb9123 100644 --- a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java +++ b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java @@ -134,6 +134,16 @@ public class ReFramedClient implements ClientModInitializer { HELPER.addReFramedModel("button_inventory" , HELPER.auto(new Identifier("block/button_inventory"))); HELPER.addReFramedModel("button" , HELPER.auto(new Identifier("block/button"))); HELPER.addReFramedModel("button_pressed" , HELPER.auto(new Identifier("block/button_pressed"))); + // POST + HELPER.addReFramedModel("post" , HELPER.auto(ReFramed.id("block/post"))); + // FENCE + HELPER.addReFramedModel("fence_inventory" , HELPER.auto(ReFramed.id("block/fence/inventory"))); + HELPER.addReFramedModel("fence_core" , HELPER.auto(ReFramed.id("block/fence/core"))); + HELPER.addReFramedModel("fence_side_off" , HELPER.auto(ReFramed.id("block/fence/side_off"))); + HELPER.addReFramedModel("fence_side_on" , HELPER.auto(ReFramed.id("block/fence/side_on"))); + // POST FENCE + HELPER.addReFramedModel("post_fence_inventory" , HELPER.autoDouble(ReFramed.id("block/post"), ReFramed.id("block/fence/full/inventory"))); + HELPER.addReFramedModel("post_fence_side" , HELPER.autoDouble(ReFramed.id("block/fence/full/side_core"), ReFramed.id("block/fence/full/side_bars"))); //item model assignments (in lieu of models/item/___.json) @@ -157,6 +167,9 @@ public class ReFramedClient implements ClientModInitializer { HELPER.assignItemModel("trapdoor_bottom" , ReFramed.TRAPDOOR); HELPER.assignItemModel("door_inventory" , ReFramed.DOOR); HELPER.assignItemModel("button_inventory" , ReFramed.BUTTON); + HELPER.assignItemModel("post" , ReFramed.POST); + HELPER.assignItemModel("fence_inventory" , ReFramed.FENCE); + HELPER.assignItemModel("post_fence_inventory" , ReFramed.POST_FENCE); } private void privateInit() { diff --git a/src/main/java/fr/adrien1106/reframed/generator/GBlockTag.java b/src/main/java/fr/adrien1106/reframed/generator/GBlockTag.java index cae2d8e..0d017e2 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/GBlockTag.java +++ b/src/main/java/fr/adrien1106/reframed/generator/GBlockTag.java @@ -19,6 +19,8 @@ public class GBlockTag extends BlockTagProvider { providers.put(ReFramedPillarsWallBlock.class, new PillarsWall()); providers.put(ReFramedWallBlock.class, new Wall()); providers.put(ReFramedPaneBlock.class, new Pane()); + providers.put(ReFramedFenceBlock.class, new Fence()); + providers.put(ReFramedPostFenceBlock.class, new PostFence()); } public GBlockTag(FabricDataOutput output, CompletableFuture registries) { diff --git a/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java b/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java index 4b0c049..84b5862 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java +++ b/src/main/java/fr/adrien1106/reframed/generator/GBlockstate.java @@ -38,6 +38,9 @@ public class GBlockstate extends FabricModelProvider { providers.put(ReFramedTrapdoorBlock.class, new Trapdoor()); providers.put(ReFramedDoorBlock.class, new Door()); providers.put(ReFramedButtonBlock.class, new Button()); + providers.put(ReFramedPostBlock.class, new Post()); + providers.put(ReFramedFenceBlock.class, new Fence()); + providers.put(ReFramedPostFenceBlock.class, new PostFence()); } public GBlockstate(FabricDataOutput output) { diff --git a/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java b/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java index ed156b1..650fe86 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java +++ b/src/main/java/fr/adrien1106/reframed/generator/GRecipe.java @@ -40,6 +40,9 @@ public class GRecipe extends FabricRecipeProvider { providers.put(ReFramedTrapdoorBlock.class, new Trapdoor()); providers.put(ReFramedDoorBlock.class, new Door()); providers.put(ReFramedButtonBlock.class, new Button()); + providers.put(ReFramedPostBlock.class, new Post()); + providers.put(ReFramedFenceBlock.class, new Fence()); + providers.put(ReFramedPostFenceBlock.class, new PostFence()); providers.put(ReFramedBlueprintItem.class, new Blueprint()); providers.put(ReFramedHammerItem.class, new Hammer()); providers.put(ReFramedScrewdriverItem.class, new Screwdriver()); diff --git a/src/main/java/fr/adrien1106/reframed/generator/block/Fence.java b/src/main/java/fr/adrien1106/reframed/generator/block/Fence.java new file mode 100644 index 0000000..fea8a79 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/generator/block/Fence.java @@ -0,0 +1,73 @@ +package fr.adrien1106.reframed.generator.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.BlockStateProvider; +import fr.adrien1106.reframed.generator.GBlockstate; +import fr.adrien1106.reframed.generator.RecipeSetter; +import fr.adrien1106.reframed.generator.TagGetter; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.data.client.BlockStateSupplier; +import net.minecraft.data.client.MultipartBlockStateSupplier; +import net.minecraft.data.server.recipe.RecipeExporter; +import net.minecraft.data.server.recipe.RecipeProvider; +import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.recipe.book.RecipeCategory; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.registry.tag.TagKey; +import net.minecraft.util.Identifier; + +import java.util.List; + +import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.state.property.Properties.*; + +public class Fence implements RecipeSetter, TagGetter, BlockStateProvider { + + @Override + public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) { + RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 4); + ShapedRecipeJsonBuilder + .create(RecipeCategory.BUILDING_BLOCKS, convertible, 4) + .pattern("I-I") + .pattern("I-I") + .input('I', ReFramed.CUBE) + .input('-', Blocks.BAMBOO) + .criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE)) + .criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible)) + .offerTo(exporter); + } + + @Override + public List> getTags() { + return List.of(BlockTags.FENCES, BlockTags.WOODEN_FENCES); + } + + @Override + public BlockStateSupplier getMultipart(Block block) { + Identifier side_on = ReFramed.id("fence_side_on_special"); + Identifier side_off = ReFramed.id("fence_side_off_special"); + return MultipartBlockStateSupplier.create(block) + .with(GBlockstate.variant(ReFramed.id("fence_core_special"), true, R0, R0)) + // SIDE ON + .with(GBlockstate.when(NORTH, true), + GBlockstate.variant(side_on, true, R0, R0)) + .with(GBlockstate.when(EAST, true), + GBlockstate.variant(side_on, true, R0, R90)) + .with(GBlockstate.when(SOUTH, true), + GBlockstate.variant(side_on, true, R0, R180)) + .with(GBlockstate.when(WEST, true), + GBlockstate.variant(side_on, true, R0, R270)) + // SIDE OFF + .with(GBlockstate.when(NORTH, false), + GBlockstate.variant(side_off, true, R0, R0)) + .with(GBlockstate.when(EAST, false), + GBlockstate.variant(side_off, true, R0, R90)) + .with(GBlockstate.when(SOUTH, false), + GBlockstate.variant(side_off, true, R0, R180)) + .with(GBlockstate.when(WEST, false), + GBlockstate.variant(side_off, true, R0, R270)); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/generator/block/Post.java b/src/main/java/fr/adrien1106/reframed/generator/block/Post.java new file mode 100644 index 0000000..e7cb465 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/generator/block/Post.java @@ -0,0 +1,50 @@ +package fr.adrien1106.reframed.generator.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.BlockStateProvider; +import fr.adrien1106.reframed.generator.GBlockstate; +import fr.adrien1106.reframed.generator.RecipeSetter; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; +import net.minecraft.block.Block; +import net.minecraft.data.client.BlockStateSupplier; +import net.minecraft.data.client.MultipartBlockStateSupplier; +import net.minecraft.data.server.recipe.RecipeExporter; +import net.minecraft.data.server.recipe.RecipeProvider; +import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.recipe.book.RecipeCategory; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Direction; + +import static net.minecraft.data.client.VariantSettings.Rotation.R0; +import static net.minecraft.data.client.VariantSettings.Rotation.R90; +import static net.minecraft.state.property.Properties.AXIS; + +public class Post implements RecipeSetter, BlockStateProvider { + + @Override + public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) { + RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 6); + ShapedRecipeJsonBuilder + .create(RecipeCategory.BUILDING_BLOCKS, convertible, 12) + .pattern("I") + .pattern("I") + .pattern("I") + .input('I', ReFramed.CUBE) + .criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE)) + .criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible)) + .offerTo(exporter); + } + + @Override + public BlockStateSupplier getMultipart(Block block) { + Identifier model_id = ReFramed.id("post_special"); + return MultipartBlockStateSupplier.create(block) + .with(GBlockstate.when(AXIS, Direction.Axis.X), + GBlockstate.variant(model_id, true, R90, R90)) + .with(GBlockstate.when(AXIS, Direction.Axis.Y), + GBlockstate.variant(model_id, true, R0, R0)) + .with(GBlockstate.when(AXIS, Direction.Axis.Z), + GBlockstate.variant(model_id, true, R90, R0)); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/generator/block/PostFence.java b/src/main/java/fr/adrien1106/reframed/generator/block/PostFence.java new file mode 100644 index 0000000..97747ea --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/generator/block/PostFence.java @@ -0,0 +1,69 @@ +package fr.adrien1106.reframed.generator.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.BlockStateProvider; +import fr.adrien1106.reframed.generator.GBlockstate; +import fr.adrien1106.reframed.generator.RecipeSetter; +import fr.adrien1106.reframed.generator.TagGetter; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; +import net.minecraft.block.Block; +import net.minecraft.data.client.BlockStateSupplier; +import net.minecraft.data.client.MultipartBlockStateSupplier; +import net.minecraft.data.server.recipe.RecipeExporter; +import net.minecraft.data.server.recipe.RecipeProvider; +import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.recipe.book.RecipeCategory; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.registry.tag.TagKey; +import net.minecraft.util.Identifier; + +import java.util.List; + +import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.state.property.Properties.*; + +public class PostFence implements RecipeSetter, TagGetter, BlockStateProvider { + + @Override + public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) { + RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.FENCE, 1); + ShapelessRecipeJsonBuilder + .create(RecipeCategory.BUILDING_BLOCKS, convertible, 2) + .input(ReFramed.FENCE, 2) + .criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE)) + .criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible)) + .offerTo(exporter); + } + + @Override + public List> getTags() { + return List.of(BlockTags.FENCES, BlockTags.WOODEN_FENCES); + } + + @Override + public BlockStateSupplier getMultipart(Block block) { + Identifier side_on = ReFramed.id("post_fence_side_special"); + Identifier side_off = ReFramed.id("fence_side_off_special"); + return MultipartBlockStateSupplier.create(block) + .with(GBlockstate.variant(ReFramed.id("fence_core_special"), true, R0, R0)) + // SIDE ON + .with(GBlockstate.when(NORTH, true), + GBlockstate.variant(side_on, true, R0, R0)) + .with(GBlockstate.when(EAST, true), + GBlockstate.variant(side_on, true, R0, R90)) + .with(GBlockstate.when(SOUTH, true), + GBlockstate.variant(side_on, true, R0, R180)) + .with(GBlockstate.when(WEST, true), + GBlockstate.variant(side_on, true, R0, R270)) + // SIDE OFF + .with(GBlockstate.when(NORTH, false), + GBlockstate.variant(side_off, true, R0, R0)) + .with(GBlockstate.when(EAST, false), + GBlockstate.variant(side_off, true, R0, R90)) + .with(GBlockstate.when(SOUTH, false), + GBlockstate.variant(side_off, true, R0, R180)) + .with(GBlockstate.when(WEST, false), + GBlockstate.variant(side_off, true, R0, R270)); + } +} diff --git a/src/main/resources/assets/reframed/models/block/fence/core.json b/src/main/resources/assets/reframed/models/block/fence/core.json new file mode 100644 index 0000000..c1fc907 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/fence/core.json @@ -0,0 +1,17 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [7, 0, 7], + "to": [9, 16, 9], + "faces": { + "up": {"uv": [7, 7, 9, 9], "texture": "#top", "cullface": "up"}, + "down": {"uv": [7, 7, 9, 9], "texture": "#bottom", "cullface": "down"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/fence/full/inventory.json b/src/main/resources/assets/reframed/models/block/fence/full/inventory.json new file mode 100644 index 0000000..9ca0508 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/fence/full/inventory.json @@ -0,0 +1,60 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [7, 12, 0], + "to": [9, 15, 6], + "faces": { + "north": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "north"}, + "east": {"uv": [10, 1, 16, 4], "texture": "#side"}, + "south": {"uv": [7, 1, 9, 4], "texture": "#side"}, + "west": {"uv": [0, 1, 6, 4], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + }, + { + "from": [7, 6, 10], + "to": [9, 9, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [0, -5, 0]}, + "faces": { + "north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "south"}, + "east": {"uv": [10, 7, 16, 10], "texture": "#side"}, + "south": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 7, 6, 10], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + }, + { + "from": [7, 12, 10], + "to": [9, 15, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 10]}, + "faces": { + "north": {"uv": [7, 1, 9, 4], "texture": "#side"}, + "east": {"uv": [10, 1, 16, 4], "texture": "#side"}, + "south": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 1, 6, 4], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + }, + { + "from": [7, 6, 0], + "to": [9, 9, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [0, -5, -10]}, + "faces": { + "north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "north"}, + "east": {"uv": [10, 7, 16, 10], "texture": "#side"}, + "south": {"uv": [7, 7, 9, 10], "texture": "#side"}, + "west": {"uv": [0, 7, 6, 10], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/fence/full/side_bars.json b/src/main/resources/assets/reframed/models/block/fence/full/side_bars.json new file mode 100644 index 0000000..020fa7c --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/fence/full/side_bars.json @@ -0,0 +1,34 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [7, 12, 0], + "to": [9, 15, 6], + "faces": { + "north": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "north"}, + "east": {"uv": [10, 1, 16, 4], "texture": "#side"}, + "south": {"uv": [7, 1, 9, 4], "texture": "#side"}, + "west": {"uv": [0, 1, 6, 4], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + }, + { + "from": [7, 6, 0], + "to": [9, 9, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [0, -5, -10]}, + "faces": { + "north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "north"}, + "east": {"uv": [10, 7, 16, 10], "texture": "#side"}, + "south": {"uv": [7, 7, 9, 10], "texture": "#side"}, + "west": {"uv": [0, 7, 6, 10], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/fence/full/side_core.json b/src/main/resources/assets/reframed/models/block/fence/full/side_core.json new file mode 100644 index 0000000..8dc1b0d --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/fence/full/side_core.json @@ -0,0 +1,62 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [6, 0, 6], + "to": [7, 16, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, -1]}, + "faces": { + "north": {"uv": [9, 0, 10, 16], "texture": "#side"}, + "west": {"uv": [6, 0, 7, 16], "texture": "#side"}, + "up": {"uv": [6, 6, 7, 7], "texture": "#top", "cullface": "up"}, + "down": {"uv": [6, 9, 7, 10], "texture": "#bottom", "cullface": "down"} + } + }, + { + "from": [7, 0, 6], + "to": [9, 6, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, -1]}, + "faces": { + "north": {"uv": [7, 10, 9, 16], "texture": "#side"}, + "down": {"uv": [7, 9, 9, 10], "texture": "#bottom", "cullface": "down"} + } + }, + { + "from": [7, 9, 6], + "to": [9, 12, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 6, -1]}, + "faces": { + "north": {"uv": [7, 4, 9, 7], "texture": "#side"} + } + }, + { + "from": [7, 12, 6], + "to": [9, 15, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 9, -1]}, + "faces": { + "north": {"uv": [7, 1, 9, 4], "texture": "#side"} + } + }, + { + "from": [7, 6, 6], + "to": [9, 9, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 3, -1]}, + "faces": { + "north": {"uv": [7, 7, 9, 10], "texture": "#side"} + } + }, + { + "from": [7, 15, 6], + "to": [9, 16, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 10, -1]}, + "faces": { + "north": {"uv": [7, 0, 9, 1], "texture": "#side"}, + "up": {"uv": [7, 6, 9, 7], "texture": "#top", "cullface": "up"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/fence/inventory.json b/src/main/resources/assets/reframed/models/block/fence/inventory.json new file mode 100644 index 0000000..3fd2053 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/fence/inventory.json @@ -0,0 +1,68 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [7, 12, 10], + "to": [9, 15, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 1, 0]}, + "faces": { + "east": {"uv": [0, 1, 6, 4], "texture": "#side"}, + "south": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "south"}, + "west": {"uv": [10, 1, 16, 4], "texture": "#side"}, + "up": {"uv": [7, 10, 9, 16], "texture": "#top"}, + "down": {"uv": [7, 0, 9, 6], "texture": "#bottom"} + } + }, + { + "from": [7, 6, 10], + "to": [9, 9, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [0, -5, 0]}, + "faces": { + "east": {"uv": [0, 7, 6, 10], "texture": "#side"}, + "south": {"uv": [7, 2, 9, 5], "texture": "#side", "cullface": "south"}, + "west": {"uv": [10, 7, 16, 10], "texture": "#side"}, + "up": {"uv": [7, 10, 9, 16], "texture": "#top"}, + "down": {"uv": [7, 0, 9, 6], "texture": "#bottom"} + } + }, + { + "from": [7, 12, 0], + "to": [9, 15, 6], + "faces": { + "north": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "north"}, + "east": {"uv": [10, 1, 16, 4], "texture": "#side"}, + "west": {"uv": [0, 1, 6, 4], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + }, + { + "from": [7, 6, 0], + "to": [9, 9, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [0, -5, -10]}, + "faces": { + "north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "north"}, + "east": {"uv": [10, 7, 16, 10], "texture": "#side"}, + "west": {"uv": [0, 7, 6, 10], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + }, + { + "from": [6, 0, 6], + "to": [10, 16, 10], + "faces": { + "north": {"uv": [6, 0, 10, 16], "texture": "#side"}, + "east": {"uv": [6, 0, 10, 16], "texture": "#side"}, + "south": {"uv": [6, 0, 10, 16], "texture": "#side", "cullface": "south"}, + "west": {"uv": [6, 0, 10, 16], "texture": "#side"}, + "up": {"uv": [6, 6, 10, 10], "texture": "#top"}, + "down": {"uv": [6, 6, 10, 10], "texture": "#bottom"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/fence/side_off.json b/src/main/resources/assets/reframed/models/block/fence/side_off.json new file mode 100644 index 0000000..1c71bbe --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/fence/side_off.json @@ -0,0 +1,20 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [6, 0, 6], + "to": [9, 16, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, -1]}, + "faces": { + "north": {"uv": [7, 0, 10, 16], "texture": "#side"}, + "west": {"uv": [6, 0, 7, 16], "texture": "#side"}, + "up": {"uv": [6, 6, 9, 7], "texture": "#top", "cullface": "up"}, + "down": {"uv": [6, 9, 9, 10], "texture": "#bottom", "cullface": "down"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/fence/side_on.json b/src/main/resources/assets/reframed/models/block/fence/side_on.json new file mode 100644 index 0000000..aac6165 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/fence/side_on.json @@ -0,0 +1,69 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [7, 12, 0], + "to": [9, 15, 6], + "faces": { + "north": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "north"}, + "east": {"uv": [10, 1, 16, 4], "texture": "#side"}, + "west": {"uv": [0, 1, 6, 4], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + }, + { + "from": [7, 6, 0], + "to": [9, 9, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [0, -5, -10]}, + "faces": { + "north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "north"}, + "east": {"uv": [10, 7, 16, 10], "texture": "#side"}, + "west": {"uv": [0, 7, 6, 10], "texture": "#side"}, + "up": {"uv": [7, 0, 9, 6], "texture": "#top"}, + "down": {"uv": [7, 10, 9, 16], "texture": "#bottom"} + } + }, + { + "from": [6, 0, 6], + "to": [7, 16, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, -1]}, + "faces": { + "north": {"uv": [9, 0, 10, 16], "texture": "#side"}, + "west": {"uv": [6, 0, 7, 16], "texture": "#side"}, + "up": {"uv": [6, 6, 7, 7], "texture": "#top", "cullface": "up"}, + "down": {"uv": [6, 9, 7, 10], "texture": "#bottom", "cullface": "down"} + } + }, + { + "from": [7, 0, 6], + "to": [9, 6, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, -1]}, + "faces": { + "north": {"uv": [7, 10, 9, 16], "texture": "#side"}, + "down": {"uv": [7, 9, 9, 10], "texture": "#bottom", "cullface": "down"} + } + }, + { + "from": [7, 9, 6], + "to": [9, 12, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 6, -1]}, + "faces": { + "north": {"uv": [7, 4, 9, 7], "texture": "#side"} + } + }, + { + "from": [7, 15, 6], + "to": [9, 16, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 10, -1]}, + "faces": { + "north": {"uv": [7, 0, 9, 1], "texture": "#side"}, + "up": {"uv": [7, 6, 9, 7], "texture": "#top", "cullface": "up"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/post.json b/src/main/resources/assets/reframed/models/block/post.json new file mode 100644 index 0000000..82ad85e --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/post.json @@ -0,0 +1,21 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [6, 0, 6], + "to": [10, 16, 10], + "faces": { + "north": {"uv": [6, 0, 10, 16], "texture": "#side"}, + "east": {"uv": [4, 0, 10, 16], "texture": "#side"}, + "south": {"uv": [6, 0, 10, 16], "texture": "#side"}, + "west": {"uv": [6, 0, 10, 16], "texture": "#side"}, + "up": {"uv": [6, 6, 10, 10], "texture": "#top", "cullface": "up"}, + "down": {"uv": [6, 6, 10, 10], "texture": "#bottom", "cullface": "down"} + } + } + ] +} \ No newline at end of file -- 2.39.5 From ce650abc762b4e3750130b0f7bd19ad8de6a30f2 Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Thu, 13 Jun 2024 02:50:16 +0200 Subject: [PATCH 5/6] feat: added connected texture support on Axiom selection (includes blueprint previews etc) --- .../compat/AxiomChunkedBlockRegionMixin.java | 26 ++++++++++- .../util/mixin/ThemedBlockEntity.java | 44 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/main/java/fr/adrien1106/reframed/util/mixin/ThemedBlockEntity.java diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java index c0b4689..d5b2ad9 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java @@ -7,9 +7,11 @@ import fr.adrien1106.reframed.client.model.MultiRetexturableModel; import fr.adrien1106.reframed.client.model.RetexturingBakedModel; import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; +import fr.adrien1106.reframed.util.mixin.ThemedBlockEntity; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Camera; import net.minecraft.client.render.block.BlockRenderManager; @@ -39,11 +41,13 @@ import java.util.stream.Stream; import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY; @Mixin(ChunkedBlockRegion.class) // TODO: Look here for better rotation/flip support -public class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlockRegionMixin { +public abstract class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlockRegionMixin { @Shadow private static void renderBlock(BufferBuilder blockBuilder, BlockRenderManager renderManager, BlockPos.Mutable blockPos, Random rand, MatrixStack matrices, BlockRenderView blockAndTintGetter, Matrix4f currentPoseMatrix, Matrix4f basePoseMatrix, int x, int y, int z, BlockState dataState, boolean useAmbientOcclusion) {} + @Shadow public abstract BlockState getBlockState(BlockPos pos); + @Unique private IntMatrix transform; @Unique @@ -95,6 +99,26 @@ public class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlockRegionMix renderBlock(buffer, renderer, pos, rand, matrices, world, current_pos, base_pos, x, y, z, state, use_ao); } + @Inject( + method = "getBlockEntity", + at = @At("HEAD"), + remap = false, + cancellable = true + ) + private void onGetBlockEntity(BlockPos pos, CallbackInfoReturnable cir) { + if (inverse_transform == null) return; + long key = BlockPos.asLong( + inverse_transform.transformX(pos.getX(), pos.getY(), pos.getZ()), + inverse_transform.transformY(pos.getX(), pos.getY(), pos.getZ()), + inverse_transform.transformZ(pos.getX(), pos.getY(), pos.getZ()) + ); + NbtCompound compound; + if (!block_entities.containsKey(key) + || !(compound = block_entities.get(key).decompress()).contains(BLOCKSTATE_KEY + 1) + ) return; + cir.setReturnValue(new ThemedBlockEntity(compound, pos, getBlockState(pos))); + } + @Inject( method = "uploadDirty", at = @At("HEAD") diff --git a/src/main/java/fr/adrien1106/reframed/util/mixin/ThemedBlockEntity.java b/src/main/java/fr/adrien1106/reframed/util/mixin/ThemedBlockEntity.java new file mode 100644 index 0000000..257921a --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/util/mixin/ThemedBlockEntity.java @@ -0,0 +1,44 @@ +package fr.adrien1106.reframed.util.mixin; + +import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtHelper; +import net.minecraft.registry.Registries; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.List; + +import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY; + +public class ThemedBlockEntity extends BlockEntity implements ThemeableBlockEntity { + private final List themes; + + public ThemedBlockEntity(NbtCompound compound, BlockPos pos, BlockState state) { + super(null, pos, state); + themes = new ArrayList<>(); + for (int i = 1; compound.contains(BLOCKSTATE_KEY + i ); i++) { + themes.add(NbtHelper.toBlockState( + Registries.BLOCK.getReadOnlyWrapper(), + compound.getCompound(BLOCKSTATE_KEY + i) + )); + } + } + + @Override + public BlockState getTheme(int i) { + return themes.get(Math.max(0, i-1)); + } + + @Override + public void setTheme(BlockState state, int i) { + themes.set(Math.max(0, i-1), state); + } + + @Override + public List getThemes() { + return themes; + } +} -- 2.39.5 From b88a4abfe5e8421793bbc1d6701a90eed374121e Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Thu, 13 Jun 2024 22:51:05 +0200 Subject: [PATCH 6/6] feat: more axiom support (connected textures and culling in selections) + newer version --- .../model/DoubleRetexturingBakedModel.java | 5 +- .../client/model/RetexturingBakedModel.java | 1 + .../reframed/client/util/RenderHelper.java | 14 +-- .../reframed/mixin/CompatMixinPlugin.java | 5 +- .../compat/AxiomChunkedBlockRegionMixin.java | 88 +++++++++---------- .../AxiomMappedBlockAndTintGetterMixin.java | 32 +++++++ .../compat/IndiumBlockRenderInfoMixin.java | 66 ++++++++++++++ ...diumNonTerrainBlockRenderContextMixin.java | 84 ++++++++++++++++++ .../IndiumTerrainBlockRenderInfoMixin.java | 55 ------------ .../mixin/render/BlockModelRendererMixin.java | 40 +++++++++ .../mixin/render/BlockRenderContextMixin.java | 78 ++++++++++++++++ src/main/resources/reframed.mixins.json | 6 +- 12 files changed, 364 insertions(+), 110 deletions(-) create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomMappedBlockAndTintGetterMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumBlockRenderInfoMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumNonTerrainBlockRenderContextMixin.java delete mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainBlockRenderInfoMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/render/BlockModelRendererMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderContextMixin.java diff --git a/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java index 2661a1f..6757e42 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java @@ -45,7 +45,10 @@ public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements } @Override - public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) {} + public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + model_1.emitBlockQuads(world, state, pos, randomSupplier, context); + model_2.emitBlockQuads(world, state, pos, randomSupplier, context); + } @Override // models are emitted here because no checks are done on items public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { diff --git a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java index 9dcbc87..4c1ad02 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java @@ -125,6 +125,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { @Override public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null; + QuadEmitter quad_emitter = context.getEmitter(); if(theme == null || theme.isAir()) { getRetexturedMesh( diff --git a/src/main/java/fr/adrien1106/reframed/client/util/RenderHelper.java b/src/main/java/fr/adrien1106/reframed/client/util/RenderHelper.java index a881b7d..095e714 100644 --- a/src/main/java/fr/adrien1106/reframed/client/util/RenderHelper.java +++ b/src/main/java/fr/adrien1106/reframed/client/util/RenderHelper.java @@ -12,7 +12,6 @@ import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; -import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.util.function.BooleanBiFunction; @@ -52,8 +51,6 @@ public class RenderHelper { Random random = Random.create(); List> model_bounds = models.stream() - .map(ForwardingBakedModel::getWrappedModel) - .filter(Objects::nonNull) .map(wrapped -> wrapped.getQuads(state, null, random)) .map(quads -> quads.stream().map(quad -> { quad_emitter.fromVanilla(quad, material, null); @@ -98,12 +95,17 @@ public class RenderHelper { // Doing this method from scratch as it is simpler to do than injecting everywhere public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) { - ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null; - ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e ? e : null; BlockState other_state = world.getBlockState(other_pos); + ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e + && self_state.getBlock() instanceof ReFramedBlock + ? e : null; + ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e + && other_state.getBlock() instanceof ReFramedBlock + ? e : null; // normal behaviour - if (self == null && other == null) return Block.shouldDrawSide(self_state, world, pos, side, other_pos); + if (theme_index == 0 || (self == null && other == null)) + return Block.shouldDrawSide(self_state, world, pos, side, other_pos); // self is a normal Block if (self == null && other_state.getBlock() instanceof ReFramedBlock other_block) { diff --git a/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java b/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java index 81f784f..054d8ad 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java @@ -26,8 +26,10 @@ public class CompatMixinPlugin implements IMixinConfigPlugin { CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1))); CONDITIONS.put("fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1))); - CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumNonTerrainBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityConnectionPredicateMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); @@ -38,6 +40,7 @@ public class CompatMixinPlugin implements IMixinConfigPlugin { CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClipboardMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomCloneBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomPlacementMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomMappedBlockAndTintGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomMoveBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomScale3xMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomRotSpriteMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5))); diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java index d5b2ad9..1ded159 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomChunkedBlockRegionMixin.java @@ -1,5 +1,6 @@ package fr.adrien1106.reframed.mixin.compat; +import com.llamalad7.mixinextras.sugar.Local; import com.moulberry.axiom.render.regions.ChunkedBlockRegion; import com.moulberry.axiom.utils.IntMatrix; import com.moulberry.axiom.world_modification.CompressedBlockEntity; @@ -9,29 +10,27 @@ import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin; import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; import fr.adrien1106.reframed.util.mixin.ThemedBlockEntity; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Camera; +import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.block.BlockRenderManager; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtHelper; -import net.minecraft.registry.Registries; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.random.Random; import net.minecraft.world.BlockRenderView; +import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -43,11 +42,10 @@ import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY; @Mixin(ChunkedBlockRegion.class) // TODO: Look here for better rotation/flip support public abstract class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlockRegionMixin { - @Shadow - private static void renderBlock(BufferBuilder blockBuilder, BlockRenderManager renderManager, BlockPos.Mutable blockPos, Random rand, MatrixStack matrices, BlockRenderView blockAndTintGetter, Matrix4f currentPoseMatrix, Matrix4f basePoseMatrix, int x, int y, int z, BlockState dataState, boolean useAmbientOcclusion) {} - @Shadow public abstract BlockState getBlockState(BlockPos pos); + @Shadow public abstract @Nullable BlockEntity getBlockEntity(BlockPos pos); + @Unique private IntMatrix transform; @Unique @@ -55,48 +53,46 @@ public abstract class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlock @Unique private Long2ObjectMap block_entities; + @Unique + private static boolean isFrameModel(BakedModel model) { + return model instanceof RetexturingBakedModel || model instanceof MultiRetexturableModel; + } - @Redirect( - method = "uploadDirty", + @Unique + private static List getModels(BakedModel model, BlockState state) { + if (isFrameModel(model)) + return List.of(model); + else if (model instanceof IMultipartBakedModelMixin mpm) + return mpm.getModels(state).stream().filter(AxiomChunkedBlockRegionMixin::isFrameModel).toList(); + else + return List.of(); + } + + @Inject( + method = "renderBlock", at = @At( - value = "INVOKE", - target = "Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;renderBlock(Lnet/minecraft/client/render/BufferBuilder;Lnet/minecraft/client/render/block/BlockRenderManager;Lnet/minecraft/util/math/BlockPos$Mutable;Lnet/minecraft/util/math/random/Random;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/world/BlockRenderView;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;IIILnet/minecraft/block/BlockState;Z)V" - ) - ) - private void onRenderBlock(BufferBuilder buffer, BlockRenderManager renderer, BlockPos.Mutable pos, Random rand, MatrixStack matrices, BlockRenderView world, Matrix4f current_pos, Matrix4f base_pos, int x, int y, int z, BlockState state, boolean use_ao) { - BakedModel model; + value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/client/render/block/BlockRenderManager;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;", + shift = At.Shift.AFTER + ), + cancellable = true) + private static void onRenderBlock(BufferBuilder blockBuilder, BlockRenderManager renderer, BlockPos.Mutable pos, Random rand, MatrixStack matrices, BlockRenderView world, Matrix4f currentPoseMatrix, Matrix4f basePoseMatrix, int x, int y, int z, BlockState state, boolean useAmbientOcclusion, CallbackInfo ci, @Local BakedModel model) { List models; - if (block_entities != null - && state.getRenderType() == BlockRenderType.MODEL - && (model = renderer.getModel(state)) != null - && model instanceof IMultipartBakedModelMixin mpm - && !(models = mpm.getModels(state) - .stream() - .filter(m -> m instanceof RetexturingBakedModel || m instanceof MultiRetexturableModel) - .toList()).isEmpty() - ) { - long key = BlockPos.asLong( - inverse_transform.transformX(pos.getX(), pos.getY(), pos.getZ()), - inverse_transform.transformY(pos.getX(), pos.getY(), pos.getZ()), - inverse_transform.transformZ(pos.getX(), pos.getY(), pos.getZ()) - ); - if (block_entities.containsKey(key)) { - NbtCompound compound = block_entities.get(key).decompress(); - models.stream() - .flatMap(m -> m instanceof MultiRetexturableModel mm - ? mm.models().stream() - : Stream.of((RetexturingBakedModel)m) - ) - .forEach(m -> m.setCamo( - world, - compound.contains(BLOCKSTATE_KEY + m.getThemeIndex()) - ? NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), compound.getCompound(BLOCKSTATE_KEY + m.getThemeIndex())) - : null, - pos - )); - } - } - renderBlock(buffer, renderer, pos, rand, matrices, world, current_pos, base_pos, x, y, z, state, use_ao); + if ((models = getModels(model, state)).isEmpty() // not a retexturable model + || !(world.getBlockEntity(pos) instanceof ThemedBlockEntity block_entity) // not a themed block entity + ) return; + + models.stream().flatMap(m -> m instanceof MultiRetexturableModel mm + ? mm.models().stream() + : Stream.of((RetexturingBakedModel)m) + ).forEach(m -> { + m.setCamo(world, block_entity.getTheme(m.getThemeIndex()), pos); + if (useAmbientOcclusion && state.getLuminance() == 0 && m.useAmbientOcclusion()) renderer.getModelRenderer() + .renderSmooth(world, m, state, pos, matrices, blockBuilder, true, rand, state.getRenderingSeed(pos), OverlayTexture.DEFAULT_UV); + else renderer.getModelRenderer() + .renderFlat(world, m, state, pos, matrices, blockBuilder, true, rand, state.getRenderingSeed(pos), OverlayTexture.DEFAULT_UV); + }); + ci.cancel(); } @Inject( diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomMappedBlockAndTintGetterMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomMappedBlockAndTintGetterMixin.java new file mode 100644 index 0000000..45fb241 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AxiomMappedBlockAndTintGetterMixin.java @@ -0,0 +1,32 @@ +package fr.adrien1106.reframed.mixin.compat; + +import fr.adrien1106.reframed.block.ReFramedEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Final; +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; + +@Mixin(targets = "com.moulberry.axiom.render.ChunkRenderOverrider$MappedBlockAndTintGetter") +public class AxiomMappedBlockAndTintGetterMixin { + + @Shadow @Final private World level; + + @Inject( + method = "getBlockEntity", + at = @At( + value = "RETURN" + ), + remap = false, + cancellable = true + ) + private void onGetBlockEntity(BlockPos pos, CallbackInfoReturnable cir) { + if (!(level.getBlockEntity(pos) instanceof ReFramedEntity frame_entity)) return; + cir.setReturnValue(frame_entity); + } + +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumBlockRenderInfoMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumBlockRenderInfoMixin.java new file mode 100644 index 0000000..cfe0aaf --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumBlockRenderInfoMixin.java @@ -0,0 +1,66 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.llamalad7.mixinextras.sugar.Local; +import fr.adrien1106.reframed.client.util.RenderHelper; +import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; +import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; +import link.infra.indium.renderer.render.BlockRenderInfo; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockRenderView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(BlockRenderInfo.class) +public abstract class IndiumBlockRenderInfoMixin implements IBlockRenderInfoMixin { + + @Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo); + + @Shadow public BlockPos blockPos; + @Shadow public BlockRenderView blockView; + @Shadow public BlockState blockState; + @Shadow(remap = false) private int cullResultFlags; + + @Unique private int theme_index = 0; + @Unique private int model_hash = 0; + + @Inject( + method = "shouldDrawFace", + at = @At( + value = "INVOKE", + target = "Llink/infra/indium/renderer/render/BlockRenderInfo;shouldDrawFaceInner(Lnet/minecraft/util/math/Direction;)Z" + ), + cancellable = true + ) + private void shouldDrawInnerFace(Direction face, CallbackInfoReturnable cir, @Local int mask) { + BlockPos other_pos = blockPos.offset(face); + if (!(blockView.getBlockEntity(blockPos) instanceof ThemeableBlockEntity + || blockView.getBlockEntity(other_pos) instanceof ThemeableBlockEntity) + ) return; + boolean result = RenderHelper.shouldDrawSide(blockState, blockView, blockPos, face, other_pos, theme_index); + if (result) cullResultFlags |= mask; + cir.setReturnValue(result); + } + + @Override + public void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo, int theme_index, int model_hash) { + this.theme_index = theme_index; + this.model_hash = model_hash; + prepareForBlock(blockState, blockPos, seed, modelAo); + } + + @Override + public int getThemeIndex() { + return theme_index; + } + + @Override + public int getModelHash() { + return model_hash; + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumNonTerrainBlockRenderContextMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumNonTerrainBlockRenderContextMixin.java new file mode 100644 index 0000000..f435b3d --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumNonTerrainBlockRenderContextMixin.java @@ -0,0 +1,84 @@ +package fr.adrien1106.reframed.mixin.compat; + +import fr.adrien1106.reframed.client.model.MultiRetexturableModel; +import fr.adrien1106.reframed.client.model.RetexturingBakedModel; +import fr.adrien1106.reframed.client.util.RenderHelper; +import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; +import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; +import link.infra.indium.renderer.aocalc.AoCalculator; +import link.infra.indium.renderer.render.AbstractBlockRenderContext; +import link.infra.indium.renderer.render.BlockRenderInfo; +import link.infra.indium.renderer.render.NonTerrainBlockRenderContext; +import link.infra.indium.renderer.render.SingleBlockLightDataCache; +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(NonTerrainBlockRenderContext.class) +public abstract class IndiumNonTerrainBlockRenderContextMixin extends AbstractBlockRenderContext { + + @Shadow(remap = false) @Final private SingleBlockLightDataCache lightCache; + + @Shadow private VertexConsumer vertexConsumer; + + @Inject( + method = "render", + at = @At( + value = "INVOKE", + target = "Llink/infra/indium/renderer/render/BlockRenderInfo;prepareForWorld(Lnet/minecraft/world/BlockRenderView;Z)V", + shift = At.Shift.AFTER + ), + remap = false, + cancellable = true + ) + private void renderMultipleModels(BlockRenderView blockView, BakedModel wrapper, BlockState state, BlockPos pos, MatrixStack matrixStack, VertexConsumer buffer, boolean cull, Random random, long seed, int overlay, CallbackInfo ci) { + if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)) return; + List models = wrapped.getModels(state); + if (models.stream().noneMatch(bakedModel -> + bakedModel instanceof MultiRetexturableModel + || bakedModel instanceof RetexturingBakedModel + )) return; + + models.forEach(model -> { + if (model instanceof MultiRetexturableModel multi_model) { + RenderHelper.computeInnerCull(state, multi_model.models(), model.hashCode()); + multi_model.models().forEach(rexteruable_model -> + renderModel(state, pos, seed, rexteruable_model, aoCalc, blockInfo, this, model.hashCode()) + ); + } else if (model instanceof RetexturingBakedModel rexteruable_model) + renderModel(state, pos, seed, rexteruable_model, aoCalc, blockInfo, this, model.hashCode()); + else model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); + }); + + blockInfo.release(); + lightCache.release(); + vertexConsumer = null; + ci.cancel(); + } + + @Unique + private static void renderModel(BlockState state, BlockPos pos, long seed, RetexturingBakedModel model, AoCalculator aoCalc, BlockRenderInfo block_info, RenderContext context, int model_hash) { + aoCalc.clear(); + ((IBlockRenderInfoMixin) block_info).prepareForBlock( + state, pos, seed, + model.useAmbientOcclusion(block_info.blockView, pos), + model.getThemeIndex(), model_hash + ); + model.emitBlockQuads(block_info.blockView, block_info.blockState, block_info.blockPos, block_info.randomSupplier, context); + } + +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainBlockRenderInfoMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainBlockRenderInfoMixin.java deleted file mode 100644 index 2e925c5..0000000 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainBlockRenderInfoMixin.java +++ /dev/null @@ -1,55 +0,0 @@ -package fr.adrien1106.reframed.mixin.compat; - -import fr.adrien1106.reframed.client.util.RenderHelper; -import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; -import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; -import link.infra.indium.renderer.render.BlockRenderInfo; -import link.infra.indium.renderer.render.TerrainBlockRenderInfo; -import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache; -import net.minecraft.block.BlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.world.BlockView; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(TerrainBlockRenderInfo.class) -public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin { - - @Unique private int theme_index = 0; - @Unique private int model_hash = 0; - - @Redirect( - method = "shouldDrawFaceInner", - at = @At( - value = "INVOKE", - target = "Lme/jellysquid/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Z" - ) - ) - private boolean shouldDrawCamoSide(BlockOcclusionCache instance, BlockState state, BlockView view, BlockPos pos, Direction face) { - BlockPos other_pos = pos.offset(face); - if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity - || view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity)) - return instance.shouldDrawSide(state, view, pos, face); - return RenderHelper.shouldDrawSide(state, view, pos, face, other_pos, theme_index); - } - - @Override - public void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo, int theme_index, int model_hash) { - this.theme_index = theme_index; - this.model_hash = model_hash; - prepareForBlock(blockState, blockPos, seed, modelAo); - } - - @Override - public int getThemeIndex() { - return theme_index; - } - - @Override - public int getModelHash() { - return model_hash; - } -} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/render/BlockModelRendererMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/BlockModelRendererMixin.java new file mode 100644 index 0000000..136be5c --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/BlockModelRendererMixin.java @@ -0,0 +1,40 @@ +package fr.adrien1106.reframed.mixin.render; + +import com.llamalad7.mixinextras.sugar.Local; +import fr.adrien1106.reframed.client.model.RetexturingBakedModel; +import fr.adrien1106.reframed.client.util.RenderHelper; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.block.BlockModelRenderer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(BlockModelRenderer.class) +public class BlockModelRendererMixin { + + @Redirect( + method = "renderSmooth", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/BlockPos;)Z" + ) + ) + private boolean shouldDrawFrameSideS(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, @Local(argsOnly = true) BakedModel model) { + return RenderHelper.shouldDrawSide(state, world, pos, side, other_pos, model instanceof RetexturingBakedModel rm ? rm.getThemeIndex() : 0); + } + + @Redirect( + method = "renderFlat", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/BlockPos;)Z" + ) + ) + private boolean shouldDrawFrameSideF(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, @Local(argsOnly = true) BakedModel model) { + return RenderHelper.shouldDrawSide(state, world, pos, side, other_pos, model instanceof RetexturingBakedModel rm ? rm.getThemeIndex() : 0); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderContextMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderContextMixin.java new file mode 100644 index 0000000..eba2635 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderContextMixin.java @@ -0,0 +1,78 @@ +package fr.adrien1106.reframed.mixin.render; + +import fr.adrien1106.reframed.client.model.MultiRetexturableModel; +import fr.adrien1106.reframed.client.model.RetexturingBakedModel; +import fr.adrien1106.reframed.client.util.RenderHelper; +import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; +import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; +import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator; +import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext; +import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderContext; +import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(BlockRenderContext.class) +public abstract class BlockRenderContextMixin extends AbstractBlockRenderContext { + + @Shadow private VertexConsumer vertexConsumer; + + @Inject( + method = "render", + at = @At( + value = "INVOKE", + target = "Lnet/fabricmc/fabric/impl/client/indigo/renderer/render/BlockRenderInfo;prepareForWorld(Lnet/minecraft/world/BlockRenderView;Z)V", + shift = At.Shift.AFTER + ), + cancellable = true + ) + private void renderMultipleModels(BlockRenderView blockView, BakedModel wrapper, BlockState state, BlockPos pos, MatrixStack matrixStack, VertexConsumer buffer, boolean cull, Random random, long seed, int overlay, CallbackInfo ci) { + if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)) return; + List models = wrapped.getModels(state); + if (models.stream().noneMatch(bakedModel -> + bakedModel instanceof MultiRetexturableModel + || bakedModel instanceof RetexturingBakedModel + )) return; + + models.forEach(model -> { + if (model instanceof MultiRetexturableModel multi_model) { + RenderHelper.computeInnerCull(state, multi_model.models(), model.hashCode()); + multi_model.models().forEach(rexteruable_model -> + renderModel(state, pos, rexteruable_model, aoCalc, blockInfo, this, model.hashCode()) + ); + } else if (model instanceof RetexturingBakedModel rexteruable_model) + renderModel(state, pos, rexteruable_model, aoCalc, blockInfo, this, model.hashCode()); + else model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); + }); + + blockInfo.release(); + vertexConsumer = null; + ci.cancel(); + } + + @Unique + private static void renderModel(BlockState state, BlockPos pos, RetexturingBakedModel model, AoCalculator aoCalc, BlockRenderInfo block_info, RenderContext context, int model_hash) { + aoCalc.clear(); + ((IBlockRenderInfoMixin) block_info).prepareForBlock( + state, pos, + model.useAmbientOcclusion(block_info.blockView, pos), + model.getThemeIndex(), model_hash + ); + model.emitBlockQuads(block_info.blockView, block_info.blockState, block_info.blockPos, block_info.randomSupplier, context); + } + +} diff --git a/src/main/resources/reframed.mixins.json b/src/main/resources/reframed.mixins.json index 14488fb..aa9083f 100644 --- a/src/main/resources/reframed.mixins.json +++ b/src/main/resources/reframed.mixins.json @@ -19,6 +19,7 @@ "compat.AxiomClientBlockEntitySerializerMixin", "compat.AxiomClipboardMixin", "compat.AxiomCloneBuilderToolMixin", + "compat.AxiomMappedBlockAndTintGetterMixin", "compat.AxiomMoveBuilderToolMixin", "compat.AxiomPlacementMixin", "compat.AxiomRotSpriteMixin", @@ -28,7 +29,8 @@ "compat.ContinuityCTMQuadTransformMixin", "compat.ContinuityModelWrappingHandlerMixin", "compat.IndiumAbstractBlockRenderContextMixin", - "compat.IndiumTerrainBlockRenderInfoMixin", + "compat.IndiumBlockRenderInfoMixin", + "compat.IndiumNonTerrainBlockRenderContextMixin", "compat.IndiumTerrainRenderContextMixin", "compat.SodiumBlockOcclusionCacheMixin", "model.WeightedBakedModelAccessor", @@ -36,6 +38,8 @@ "particles.AccessorSpriteBillboardParticle", "particles.MixinBlockDustParticle", "render.AbstractBlockRenderContextMixin", + "render.BlockModelRendererMixin", + "render.BlockRenderContextMixin", "render.BlockRenderInfoMixin", "render.MultipartBakedModelMixin", "render.TerrainRenderContextMixin", -- 2.39.5