From ce216d1f9fbb4d43bb644b7df07cc28603068617 Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Fri, 1 Mar 2024 21:56:22 +0100 Subject: [PATCH] some rework / cleanup with full rework of culling to work by parts. no self culling yet? still missing work on connected textures --- .../java/fr/adrien1106/reframed/ReFramed.java | 21 +- .../reframed/block/ReFramedBlock.java | 262 ++++++++++++++++-- .../reframed/block/ReFramedDoubleBlock.java | 96 +++++++ .../reframed/block/ReFramedDoubleEntity.java | 23 +- .../block/ReFramedDoubleSlabBlock.java | 64 +++++ .../reframed/block/ReFramedEntity.java | 62 +++-- .../reframed/block/ReFramedSlabBlock.java | 46 ++- .../block/WaterloggableReFramedBlock.java | 1 - .../reframed/client/ReFramedClient.java | 2 + .../reframed/client/ReFramedClientHelper.java | 23 +- .../client/ReFramedModelProvider.java | 6 +- .../model/DoubleRetexturingBakedModel.java | 22 +- .../client/model/MeshTransformUtil.java | 83 ------ .../client/model/MultiRetexturableModel.java | 10 + .../client/model/RetexturingBakedModel.java | 71 +++-- .../model/UnbakedAutoRetexturedModel.java | 7 +- .../model/UnbakedDoubleRetexturedModel.java | 5 +- .../model/UnbakedJsonRetexturedModel.java | 23 +- .../client/model/UnbakedRetexturedModel.java | 10 +- .../apperance/CamoAppearanceManager.java | 35 ++- .../reframed/generator/Generator.java | 2 +- .../compat/AthenaWrappedGetterMixin.java | 7 +- .../particles/MixinBlockDustParticle.java | 8 +- .../reframed/mixin/particles/MixinEntity.java | 16 +- .../mixin/particles/MixinLivingEntity.java | 22 +- .../{ => render}/BlockRenderInfoMixin.java | 31 ++- .../render/MultipartBakedModelMixin.java | 25 ++ .../render/TerrainRenderContextMixin.java | 41 +++ .../mixin/render/WorldRendererMixin.java | 35 +++ .../reframed/util/IBlockRenderInfoMixin.java | 9 + .../util/IMultipartBakedModelMixin.java | 9 + .../util/ReFramedInteractionUtil.java | 45 ++- .../reframed/util/ThemeableBlockEntity.java | 14 +- .../assets/reframed/blockstates/slab.json | 65 ----- src/main/resources/reframed.mixins.json | 10 +- 35 files changed, 846 insertions(+), 365 deletions(-) create mode 100644 src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleBlock.java create mode 100644 src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleSlabBlock.java delete mode 100644 src/main/java/fr/adrien1106/reframed/client/model/MeshTransformUtil.java create mode 100644 src/main/java/fr/adrien1106/reframed/client/model/MultiRetexturableModel.java rename src/main/java/fr/adrien1106/reframed/mixin/{ => render}/BlockRenderInfoMixin.java (57%) create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/render/MultipartBakedModelMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/render/TerrainRenderContextMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/render/WorldRendererMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/util/IBlockRenderInfoMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/util/IMultipartBakedModelMixin.java delete mode 100644 src/main/resources/assets/reframed/blockstates/slab.json diff --git a/src/main/java/fr/adrien1106/reframed/ReFramed.java b/src/main/java/fr/adrien1106/reframed/ReFramed.java index 56c8622..f7e75c8 100644 --- a/src/main/java/fr/adrien1106/reframed/ReFramed.java +++ b/src/main/java/fr/adrien1106/reframed/ReFramed.java @@ -24,16 +24,14 @@ import java.util.ArrayList; import java.util.function.BiConsumer; import java.util.stream.Collectors; -/** - * TODO multiple camos - */ public class ReFramed implements ModInitializer { public static final String MODID = "reframed"; public static final ArrayList BLOCKS = new ArrayList<>(); - public static Block CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, DOOR, TRAPDOOR, IRON_DOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE, CANDLE; + public static Block CUBE, STAIRS, SLAB, DOUBLE_SLAB, POST, FENCE, FENCE_GATE, DOOR, TRAPDOOR, IRON_DOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE, CANDLE; public static BlockEntityType REFRAMED_BLOCK_ENTITY; + public static BlockEntityType REFRAMED_DOUBLE_BLOCK_ENTITY; public static BiConsumer chunkRerenderProxy = (world, pos) -> {}; @@ -47,6 +45,7 @@ public class ReFramed implements ModInitializer { CUBE = registerReFramed("cube" , new ReFramedBlock(ReFramedInteractionUtil.makeSettings())); STAIRS = registerReFramed("stairs" , new ReFramedStairsBlock(cp(Blocks.OAK_STAIRS))); SLAB = registerReFramed("slab" , new ReFramedSlabBlock(cp(Blocks.OAK_SLAB))); + DOUBLE_SLAB = registerReFramed("double_slab" , new ReFramedDoubleSlabBlock(cp(Blocks.OAK_SLAB))); POST = registerReFramed("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE))); FENCE = registerReFramed("fence" , new ReFramedFenceBlock(cp(Blocks.OAK_FENCE))); FENCE_GATE = registerReFramed("fence_gate" , new ReFramedFenceGateBlock(cp(Blocks.OAK_FENCE_GATE))); @@ -63,7 +62,19 @@ public class ReFramed implements ModInitializer { CANDLE = registerReFramed("candle" , new ReFramedCandleBlock(ReFramedCandleBlock.configureSettings(cp(Blocks.CANDLE)))); REFRAMED_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("camo"), - FabricBlockEntityTypeBuilder.create((pos, state) -> new ReFramedEntity(REFRAMED_BLOCK_ENTITY, pos, state), BLOCKS.toArray(new Block[0])).build(null) + FabricBlockEntityTypeBuilder.create( + (pos, state) -> new ReFramedEntity(REFRAMED_BLOCK_ENTITY, pos, state), + BLOCKS.stream() + .filter(block -> !(block instanceof ReFramedDoubleBlock)) + .toArray(Block[]::new)).build(null) + ); + + REFRAMED_DOUBLE_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("double_camo"), + FabricBlockEntityTypeBuilder.create( + (pos, state) -> new ReFramedDoubleEntity(REFRAMED_DOUBLE_BLOCK_ENTITY, pos, state), + BLOCKS.stream() + .filter(block -> block instanceof ReFramedDoubleBlock) + .toArray(Block[]::new)).build(null) ); Registry.register(Registries.ITEM_GROUP, id("tab"), FabricItemGroup.builder() diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java index 85f447b..9ed3ccf 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedBlock.java @@ -1,29 +1,44 @@ package fr.adrien1106.reframed.block; -import com.google.common.base.MoreObjects; import fr.adrien1106.reframed.ReFramed; -import fr.adrien1106.reframed.util.ReFramedInteractionUtil; +import fr.adrien1106.reframed.util.ReframedInteractible; +import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.minecraft.block.*; import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemPlacementContext; -import net.minecraft.item.ItemStack; +import net.minecraft.item.*; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvents; import net.minecraft.state.StateManager; +import net.minecraft.state.property.BooleanProperty; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; +import net.minecraft.util.ItemScatterer; +import net.minecraft.util.collection.DefaultedList; +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.GameRules; import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; +import java.util.List; + +import static net.minecraft.util.shape.VoxelShapes.*; + public class ReFramedBlock extends Block implements BlockEntityProvider { + + public static final BooleanProperty LIGHT = BooleanProperty.of("frame_light"); + public ReFramedBlock(Settings settings) { super(settings); - setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState())); + setDefaultState(getDefaultState().with(LIGHT, false)); } //For addon devs: override this so your blocks don't end up trying to place my block entity, my BlockEntityType only handles blocks internal to the mod @@ -35,51 +50,262 @@ public class ReFramedBlock extends Block implements BlockEntityProvider { @Override protected void appendProperties(StateManager.Builder builder) { - super.appendProperties(ReFramedInteractionUtil.appendProperties(builder)); + super.appendProperties(builder.add(LIGHT)); } @Nullable @Override public BlockState getPlacementState(ItemPlacementContext ctx) { - return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx); + return ReFramedEntity.getNbtLightLevel(super.getPlacementState(ctx), ctx.getStack()); } @Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { - ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit); - if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit); - return r; + if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit); + ActionResult result = useUpgrade(state, world, pos, player, hand); + if (result.isAccepted()) return result; + return useCamo(state, world, pos, player, hand, hit, 1); + + } + + // don't like this but might be useful + protected ActionResult superUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + return super.onUse(state, world, pos, player, hand, hit); + } + + protected boolean canUse(World world, BlockPos pos, PlayerEntity player) { + return player.canModifyBlocks() && world.canPlayerModifyAt(player, pos); + } + + protected static ActionResult useUpgrade(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand) { + 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) { + block_entity.toggleLight(); + world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight())); + + if(!player.isCreative()) + if (block_entity.emitsLight()) held.decrement(1); + else held.increment(1); + world.playSound(player, pos, SoundEvents.BLOCK_GLASS_HIT, SoundCategory.BLOCKS, 1f, 1f); + return ActionResult.SUCCESS; + } + + // 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)) { + block_entity.toggleRedstone(); + + if(!player.isCreative()) + if (block_entity.emitsRedstone()) held.decrement(1); + else held.increment(1); + 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)) { + block_entity.toggleSolidity(); + + if(!player.isCreative()) + if (!block_entity.isSolid()) held.decrement(1); + else held.increment(1); + world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f); + return ActionResult.SUCCESS; + } + + return ActionResult.PASS; + } + + protected static ActionResult useCamo(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, int theme_index) { + if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) return ActionResult.PASS; + + // Changing the theme + ItemStack held = player.getStackInHand(hand); + if(held.getItem() instanceof BlockItem block_item && block_entity.getTheme(theme_index).getBlock() == Blocks.AIR) { + Block block = block_item.getBlock(); + ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit)); + BlockState placement_state = block.getPlacementState(ctx); + if(placement_state != null && isShapeFullCube(placement_state.getCollisionShape(world, pos)) && !(block instanceof BlockEntityProvider)) { + List themes = block_entity.getThemes(); + if(!world.isClient) block_entity.setTheme(placement_state, theme_index); + + // check for default light emission + if (placement_state.getLuminance() > 0 + && themes.stream().noneMatch(theme -> theme.getLuminance() > 0)) + if (block_entity.emitsLight()) Block.dropStack(world, pos, new ItemStack(Items.GLOWSTONE_DUST)); + else block_entity.toggleLight(); + + world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight())); + + // check for default redstone emission + if (placement_state.getWeakRedstonePower(world, pos, Direction.NORTH) > 0 + && themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) > 0)) + if (block_entity.emitsRedstone()) Block.dropStack(world, pos, new ItemStack(Items.GLOWSTONE_DUST)); + else block_entity.toggleRedstone(); + + if(!player.isCreative()) held.decrement(1); + world.playSound(player, pos, placement_state.getSoundGroup().getPlaceSound(), SoundCategory.BLOCKS, 1f, 1.1f); + return ActionResult.SUCCESS; + } + } + + return ActionResult.PASS; } @Override public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved); + if(!state.isOf(newState.getBlock()) && + world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity && + world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS) + ) { + DefaultedList drops = DefaultedList.of(); + + List themes = frame_entity.getThemes(); + themes.forEach(theme -> { + 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(theme -> theme.isSolid())) + drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT)); + + ItemScatterer.spawn(world, pos, drops); + } super.onStateReplaced(state, world, pos, newState, moved); } @Override public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { - ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack); + if(world.isClient && world.getBlockEntity(pos) instanceof ReFramedEntity be) { + NbtCompound tag = BlockItem.getBlockEntityNbt(stack); + if(tag != null) be.readNbt(tag); + } super.onPlaced(world, pos, state, placer, stack); } @Override public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { - return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx)); + return isGhost(view, pos) + ? VoxelShapes.empty() + : super.getCollisionShape(state, view, pos, ctx); } - + @Override - public boolean emitsRedstonePower(BlockState state) { - return ReFramedInteractionUtil.emitsRedstonePower(state); + public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { + return isGhost(view, pos) + ? VoxelShapes.empty() + : super.getCullingShape(state, view, pos); + } + + public VoxelShape getShape(BlockState state, int i) { + // assuming the shape don't need the world and position + return getOutlineShape(state, null, null, null); + } + + public boolean isGhost(BlockView view, BlockPos pos) { + return view.getBlockEntity(pos) instanceof ReFramedEntity be && !be.isSolid(); } @Override public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { - return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir); + return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0; } @Override public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { - return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir); + return getWeakRedstonePower(state, view, pos, dir); + } + + public int getTopThemeIndex(BlockState state) { + return 1; + } + + // 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); + + // normal behaviour + if (self == null && other == null) return shouldDrawSide(self_state, world, pos, side, other_pos); + + // self is a normal Block + if (self == null && other_state.getBlock() instanceof ReFramedBlock other_block) { + VoxelShape self_shape = self_state.getCullingShape(world, pos); + if (self_shape.isEmpty()) return true; + + int i = 0; + VoxelShape other_shape = VoxelShapes.empty(); + for (BlockState s: other.getThemes()) { + i++; + if (self_state.isSideInvisible(s, side) || s.isOpaque()) + other_shape = combine( + other_shape, + other_block + .getShape(other_state, i) + .getFace(side.getOpposite()), + BooleanBiFunction.OR + ); + } + + // determine if side needs to be rendered + return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); + } + + BlockState self_theme = self.getTheme(theme_index); + // other is normal Block + if (other == null && self_state.getBlock() instanceof ReFramedBlock self_block) { + // Transparent is simple if self and the neighbor are invisible don't render side (like default) + if (self_theme.isSideInvisible(other_state, side)) return false; + + // Opaque is also simple as each model are rendered one by one + if (self_theme.isOpaque()) { + // no cache section :( because it differs between each instance of the frame + VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side); + if (self_shape.isEmpty()) return true; + VoxelShape other_shape = other_state.getCullingFace(world, other_pos, side.getOpposite()); + + // determine if side needs to be rendered + return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); + } + + return true; + } + + // Both are frames + // here both are computed in the same zone as there will necessarily a shape comparison + if (self_state.getBlock() instanceof ReFramedBlock self_block && other_state.getBlock() instanceof ReFramedBlock other_block) { + VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side); + if (self_shape.isEmpty()) return true; + + int i = 0; + VoxelShape other_shape = VoxelShapes.empty(); + for (BlockState s: other.getThemes()) { + i++; + if (self_theme.isSideInvisible(s, side) || s.isOpaque()) + other_shape = combine( + other_shape, + other_block + .getShape(other_state, i) + .getFace(side.getOpposite()), + BooleanBiFunction.OR + ); + } + + // determine if side needs to be rendered + return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); + } + + return true; } } diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleBlock.java new file mode 100644 index 0000000..348979d --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleBlock.java @@ -0,0 +1,96 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.util.ThemeableBlockEntity; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.ActionResult; +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.Vec3d; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +import static net.minecraft.util.shape.VoxelShapes.empty; +import static net.minecraft.util.shape.VoxelShapes.fullCube; + +public abstract class ReFramedDoubleBlock extends ReFramedBlock { + public ReFramedDoubleBlock(Settings settings) { + super(settings); + } + + @Override + public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return ReFramed.REFRAMED_DOUBLE_BLOCK_ENTITY.instantiate(pos, state); + } + + protected int getHitPart(BlockState state, BlockHitResult hit) { + Direction side = hit.getSide(); + VoxelShape first_shape = getShape(state, 1); + VoxelShape second_shape = getShape(state, 2); + + // Determine if any of the two shape is covering the side entirely + if (isFaceFullSquare(first_shape, side)) return 1; + if (isFaceFullSquare(second_shape, side)) return 2; + + Vec3d pos = hit.getPos(); + BlockPos origin = hit.getBlockPos(); + Map axes = Arrays.stream(Direction.Axis.values()) + .filter(axis -> axis != side.getAxis()) + .collect(Collectors.toMap( + axis -> axis, + axis -> axis.choose(pos.getX() - origin.getX(), pos.getY() - origin.getY(), pos.getZ() - origin.getZ())) + ); + + if (matchesFace(first_shape.getFace(side), axes)) return 1; + if (matchesFace(second_shape.getFace(side), axes)) return 2; + return 0; + } + + private static boolean matchesFace(VoxelShape shape, Map axes) { + return shape.getBoundingBoxes().stream() + .anyMatch(box -> + axes.keySet().stream() + .map(axis -> box.getMin(axis) <= axes.get(axis) && box.getMax(axis) >= axes.get(axis)) + .reduce((prev, current) -> prev && current).get() + ); + } + + @Override + public boolean isTransparent(BlockState state, BlockView world, BlockPos pos) { + return world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity + && framed_entity.getThemes().stream().allMatch(theme -> theme.isTransparent(world, pos)); + } + + public VoxelShape getRenderOutline(BlockState state, BlockHitResult hit) { + return getShape(state, getHitPart(state, hit)); + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { + return isGhost(view, pos) ? empty() : fullCube(); + } + + @Override + public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { + return isGhost(view, pos) ? empty() : fullCube(); + } + + @Override + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit); + ActionResult result = useUpgrade(state, world, pos, player, hand); + if (result.isAccepted()) return result; + return useCamo(state, world, pos, player, hand, hit, getHitPart(state, hit)); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleEntity.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleEntity.java index 76eadbf..5090b0a 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleEntity.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleEntity.java @@ -9,6 +9,7 @@ import net.minecraft.nbt.NbtHelper; import net.minecraft.registry.Registries; import net.minecraft.util.math.BlockPos; +import java.util.List; import java.util.Objects; public class ReFramedDoubleEntity extends ReFramedEntity { @@ -20,15 +21,23 @@ public class ReFramedDoubleEntity extends ReFramedEntity { } @Override - public BlockState getSecondTheme() { - return second_state; + public BlockState getTheme(int i) { + return i == 2 ? second_state : super.getTheme(i); } - public void setSecondTheme(BlockState newState) { - if(!Objects.equals(second_state, newState)) { - second_state = newState; + @Override + public List getThemes() { + List themes = super.getThemes(); + themes.add(second_state); + return themes; + } + + public void setTheme(BlockState new_state, int i) { + if(i == 2) { + if (Objects.equals(second_state, new_state)) return; + second_state = new_state; markDirtyAndDispatch(); - } + } else super.setTheme(new_state, i); } @Override @@ -36,7 +45,7 @@ public class ReFramedDoubleEntity extends ReFramedEntity { super.readNbt(nbt); BlockState rendered_state = second_state;// keep previous state to check if rerender is needed - first_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 2)); + second_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 2)); // Force a chunk remesh on the client if the displayed blockstate has changed if(world != null && world.isClient && !Objects.equals(rendered_state, second_state)) { diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleSlabBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleSlabBlock.java new file mode 100644 index 0000000..7efd17f --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleSlabBlock.java @@ -0,0 +1,64 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.GBlockstate; +import fr.adrien1106.reframed.generator.MultipartBlockStateProvider; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.data.client.MultipartBlockStateSupplier; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.Properties; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import org.jetbrains.annotations.Nullable; + +import static fr.adrien1106.reframed.block.ReFramedSlabBlock.*; +import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.state.property.Properties.AXIS; + +public class ReFramedDoubleSlabBlock extends ReFramedDoubleBlock implements MultipartBlockStateProvider { + public ReFramedDoubleSlabBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState().with(Properties.AXIS, Direction.Axis.Y)); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder.add(Properties.AXIS)); + } + + @Nullable + @Override + public BlockState getPlacementState(ItemPlacementContext ctx) { + return super.getPlacementState(ctx).with(Properties.AXIS, ctx.getSide().getAxis()); + } + + @Override + public VoxelShape getShape(BlockState state, int i) { + return switch (state.get(Properties.AXIS)) { + case Y -> i == 2 ? UP : DOWN; + case Z -> i == 2 ? NORTH : SOUTH; + case X -> i == 2 ? EAST : WEST; + }; + } + + @Override + public int getTopThemeIndex(BlockState state) { + // when the side is shared just return one + return state.get(AXIS) == Direction.Axis.Y ? 2: super.getTopThemeIndex(state); + } + + @Override + public MultipartBlockStateSupplier getMultipart() { + Identifier model_id = ReFramed.id("double_slab_special"); + return MultipartBlockStateSupplier.create(this) + .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)) + .with(GBlockstate.when(AXIS, Direction.Axis.X), + GBlockstate.variant(model_id, true, R90, R90)); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedEntity.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedEntity.java index c2ad09d..5d537db 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedEntity.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedEntity.java @@ -1,7 +1,6 @@ package fr.adrien1106.reframed.block; import fr.adrien1106.reframed.ReFramed; -import fr.adrien1106.reframed.util.ReFramedInteractionUtil; import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; @@ -21,6 +20,8 @@ import net.minecraft.util.math.BlockPos; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; //Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic, @@ -28,7 +29,7 @@ import java.util.Objects; //To that end, most of the state has been crammed into a bitfield. public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity { protected BlockState first_state = Blocks.AIR.getDefaultState(); - protected byte bitfield = SOLIDITY_MASK; + protected byte bit_field = SOLIDITY_MASK; protected static final byte LIGHT_MASK = 0b001; protected static final byte REDSTONE_MASK = 0b010; @@ -47,7 +48,7 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity BlockState rendered_state = first_state; // keep previous state to check if rerender is needed first_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 1)); - if (nbt.contains(BITFIELD_KEY)) bitfield = nbt.getByte(BITFIELD_KEY); + if (nbt.contains(BITFIELD_KEY)) bit_field = nbt.getByte(BITFIELD_KEY); // Force a chunk remesh on the client if the displayed blockstate has changed if(world != null && world.isClient && !Objects.equals(rendered_state, first_state)) { @@ -60,35 +61,34 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity super.writeNbt(nbt); if(first_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(first_state)); - if(bitfield != SOLIDITY_MASK) nbt.putByte(BITFIELD_KEY, bitfield); + if(bit_field != SOLIDITY_MASK) nbt.putByte(BITFIELD_KEY, bit_field); } - // TODO revisit - public static @NotNull BlockState readStateFromItem(ItemStack stack) { - NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack); - if(blockEntityTag == null) return Blocks.AIR.getDefaultState(); + public static @NotNull BlockState readStateFromItem(ItemStack stack, int state) { + NbtCompound nbt = BlockItem.getBlockEntityNbt(stack); + if(nbt == null) return Blocks.AIR.getDefaultState(); //slightly paranoid NBT handling cause you never know what mysteries are afoot with items - NbtElement subElement; - if(blockEntityTag.contains(BLOCKSTATE_KEY)) subElement = blockEntityTag.get(BLOCKSTATE_KEY + 1); + NbtElement element; + if(nbt.contains(BLOCKSTATE_KEY + state)) element = nbt.get(BLOCKSTATE_KEY + state); else return Blocks.AIR.getDefaultState(); - if(!(subElement instanceof NbtCompound subCompound)) return Blocks.AIR.getDefaultState(); - else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), subCompound); + if(!(element instanceof NbtCompound compound)) return Blocks.AIR.getDefaultState(); + else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), compound); } //Awkward: usually the BlockState is the source of truth for things like the "emits light" blockstate, but if you //ctrl-pick a glowing block and place it, it should still be glowing. This is some hacky shit that guesses the value of //the LIGHT blockstate based off information in the NBT tag, and also prevents bugginess like "the blockstate is not //glowing but the copied NBT thinks glowstone dust was already added, so it refuses to accept more dust" - public static @Nullable BlockState weirdNbtLightLevelStuff(@Nullable BlockState state, ItemStack stack) { + public static @Nullable BlockState getNbtLightLevel(@Nullable BlockState state, ItemStack stack) { if(state == null || stack == null) return state; NbtCompound nbt = BlockItem.getBlockEntityNbt(stack); if(nbt == null) return state; - if(state.contains(ReFramedInteractionUtil.LIGHT)) { - state = state.with(ReFramedInteractionUtil.LIGHT, + if(state.contains(ReFramedBlock.LIGHT)) { + state = state.with(ReFramedBlock.LIGHT, ((nbt.contains(BITFIELD_KEY) ? nbt.getByte(BITFIELD_KEY) : SOLIDITY_MASK) @@ -100,47 +100,57 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity } @Override - public BlockState getFirstTheme() { + public BlockState getTheme(int i) { return first_state; } - - public void setFirstTheme(BlockState newState) { - if(!Objects.equals(first_state, newState)) { - first_state = newState; + + @Override + public List getThemes() { + List themes = new ArrayList<>(); + themes.add(first_state); + return themes; + } + + public void setTheme(BlockState new_state, int i) { + if(!Objects.equals(first_state, new_state)) { + first_state = new_state; markDirtyAndDispatch(); } } /* --------------------------------------------------- ADDONS --------------------------------------------------- */ public boolean emitsLight() { - return (bitfield & LIGHT_MASK) != 0; + return (bit_field & LIGHT_MASK) != 0; } public void toggleLight() { - bitfield |= emitsLight() ? ~LIGHT_MASK: LIGHT_MASK; + if (emitsLight()) bit_field &= ~LIGHT_MASK; + else bit_field |= LIGHT_MASK; markDirtyAndDispatch(); } public void toggleRedstone() { - bitfield |= emitsRedstone() ? ~REDSTONE_MASK: REDSTONE_MASK; + if (emitsRedstone()) bit_field &= ~REDSTONE_MASK; + else bit_field |= REDSTONE_MASK; if(world != null) world.updateNeighbors(pos, getCachedState().getBlock()); markDirtyAndDispatch(); } public boolean emitsRedstone() { - return (bitfield & REDSTONE_MASK) != 0; + return (bit_field & REDSTONE_MASK) != 0; } public void toggleSolidity() { - bitfield |= isSolid() ? ~SOLIDITY_MASK: SOLIDITY_MASK; + if (isSolid()) bit_field &= ~SOLIDITY_MASK; + else bit_field |= SOLIDITY_MASK; if(world != null) world.setBlockState(pos, getCachedState()); markDirtyAndDispatch(); } public boolean isSolid() { - return (bitfield & SOLIDITY_MASK) != 0; + return (bit_field & SOLIDITY_MASK) != 0; } @Nullable diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedSlabBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedSlabBlock.java index 4782cf1..1a75a87 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedSlabBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedSlabBlock.java @@ -1,11 +1,15 @@ package fr.adrien1106.reframed.block; +import fr.adrien1106.reframed.ReFramed; +import fr.adrien1106.reframed.generator.GBlockstate; import fr.adrien1106.reframed.generator.MultipartBlockStateProvider; -import net.minecraft.block.*; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; import net.minecraft.data.client.MultipartBlockStateSupplier; import net.minecraft.item.ItemPlacementContext; import net.minecraft.state.StateManager; -import net.minecraft.state.property.Properties; +import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.shape.VoxelShape; @@ -13,34 +17,37 @@ import net.minecraft.util.shape.VoxelShapes; import net.minecraft.world.BlockView; import org.jetbrains.annotations.Nullable; +import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.state.property.Properties.FACING; + public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements MultipartBlockStateProvider { - private static final VoxelShape DOWN = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.5f, 1f); - private static final VoxelShape UP = VoxelShapes.cuboid(0f, 0.5f, 0f, 1f, 1f, 1f); - private static final VoxelShape NORTH = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 1f, 0.5f); - private static final VoxelShape SOUTH = VoxelShapes.cuboid(0f, 0f, 0.5f, 1f, 1f, 1f); - private static final VoxelShape EAST = VoxelShapes.cuboid(0.5f, 0f, 0f, 1f, 1f, 1f); - private static final VoxelShape WEST = VoxelShapes.cuboid(0f, 0f, 0f, 0.5f, 1f, 1f); + protected static final VoxelShape DOWN = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.5f, 1f); + protected static final VoxelShape UP = VoxelShapes.cuboid(0f, 0.5f, 0f, 1f, 1f, 1f); + protected static final VoxelShape NORTH = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 1f, 0.5f); + protected static final VoxelShape SOUTH = VoxelShapes.cuboid(0f, 0f, 0.5f, 1f, 1f, 1f); + protected static final VoxelShape EAST = VoxelShapes.cuboid(0.5f, 0f, 0f, 1f, 1f, 1f); + protected static final VoxelShape WEST = VoxelShapes.cuboid(0f, 0f, 0f, 0.5f, 1f, 1f); public ReFramedSlabBlock(Settings settings) { super(settings); - setDefaultState(getDefaultState().with(Properties.FACING, Direction.DOWN)); + setDefaultState(getDefaultState().with(FACING, Direction.DOWN)); } @Override protected void appendProperties(StateManager.Builder builder) { - super.appendProperties(builder.add(Properties.FACING)); + super.appendProperties(builder.add(FACING)); } @Nullable @Override public BlockState getPlacementState(ItemPlacementContext ctx) { - return super.getPlacementState(ctx).with(Properties.FACING, ctx.getSide().getOpposite()); + return super.getPlacementState(ctx).with(FACING, ctx.getSide().getOpposite()); } @Override public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { - return switch (state.get(Properties.FACING)) { + return switch (state.get(FACING)) { case DOWN -> DOWN; case UP -> UP; case NORTH -> NORTH; @@ -52,6 +59,19 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements Mul @Override public MultipartBlockStateSupplier getMultipart() { - return null; + Identifier model_id = ReFramed.id("slab_special"); + return MultipartBlockStateSupplier.create(this) + .with(GBlockstate.when(FACING, Direction.DOWN), + GBlockstate.variant(model_id, true, R0, R0)) + .with(GBlockstate.when(FACING, Direction.SOUTH), + GBlockstate.variant(model_id, true, R90, R0)) + .with(GBlockstate.when(FACING, Direction.UP), + GBlockstate.variant(model_id, true, R180, R0)) + .with(GBlockstate.when(FACING, Direction.NORTH), + GBlockstate.variant(model_id, true, R270, R0)) + .with(GBlockstate.when(FACING, Direction.WEST), + GBlockstate.variant(model_id, true, R90, R90)) + .with(GBlockstate.when(FACING, Direction.EAST), + GBlockstate.variant(model_id, true, R90, R270)); } } diff --git a/src/main/java/fr/adrien1106/reframed/block/WaterloggableReFramedBlock.java b/src/main/java/fr/adrien1106/reframed/block/WaterloggableReFramedBlock.java index 319d59a..daea024 100644 --- a/src/main/java/fr/adrien1106/reframed/block/WaterloggableReFramedBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/WaterloggableReFramedBlock.java @@ -16,7 +16,6 @@ import org.jetbrains.annotations.Nullable; public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterloggable { public WaterloggableReFramedBlock(Settings settings) { super(settings); - setDefaultState(getDefaultState().with(Properties.WATERLOGGED, false)); } diff --git a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java index da80ca2..6935fe9 100644 --- a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java +++ b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java @@ -53,6 +53,7 @@ public class ReFramedClient implements ClientModInitializer { HELPER.addReFramedModel(ReFramed.id("pressure_plate_up_special") , HELPER.auto(new Identifier("block/pressure_plate_up"))); HELPER.addReFramedModel(ReFramed.id("pressure_plate_down_special") , HELPER.auto(new Identifier("block/pressure_plate_down"))); HELPER.addReFramedModel(ReFramed.id("slab_special") , HELPER.auto(new Identifier("block/slab"))); + HELPER.addReFramedModel(ReFramed.id("double_slab_special") , HELPER.autoDouble(new Identifier("block/slab"), new Identifier("block/slab_top"))); HELPER.addReFramedModel(ReFramed.id("stairs_special") , HELPER.auto(ReFramed.id("block/stairs"))); HELPER.addReFramedModel(ReFramed.id("double_outer_stairs_special") , HELPER.auto(ReFramed.id("block/double_outer_stairs"))); HELPER.addReFramedModel(ReFramed.id("inner_stairs_special") , HELPER.auto(ReFramed.id("block/inner_stairs"))); @@ -89,6 +90,7 @@ public class ReFramedClient implements ClientModInitializer { HELPER.assignItemModel(ReFramed.id("fence_post_inventory_special") , ReFramed.POST); HELPER.assignItemModel(ReFramed.id("pressure_plate_up_special") , ReFramed.PRESSURE_PLATE); HELPER.assignItemModel(ReFramed.id("slab_special") , ReFramed.SLAB); + HELPER.assignItemModel(ReFramed.id("double_slab_special") , ReFramed.DOUBLE_SLAB); HELPER.assignItemModel(ReFramed.id("stairs_special") , ReFramed.STAIRS); HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.TRAPDOOR); HELPER.assignItemModel(ReFramed.id("wall_inventory_special") , ReFramed.WALL); diff --git a/src/main/java/fr/adrien1106/reframed/client/ReFramedClientHelper.java b/src/main/java/fr/adrien1106/reframed/client/ReFramedClientHelper.java index 9ac76d7..43ad7a9 100644 --- a/src/main/java/fr/adrien1106/reframed/client/ReFramedClientHelper.java +++ b/src/main/java/fr/adrien1106/reframed/client/ReFramedClientHelper.java @@ -1,13 +1,13 @@ package fr.adrien1106.reframed.client; -import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager; import fr.adrien1106.reframed.client.model.UnbakedAutoRetexturedModel; +import fr.adrien1106.reframed.client.model.UnbakedDoubleRetexturedModel; import fr.adrien1106.reframed.client.model.UnbakedJsonRetexturedModel; -import fr.adrien1106.reframed.util.ThemeableBlockEntity; +import fr.adrien1106.reframed.client.model.UnbakedRetexturedModel; +import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager; import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.RendererAccess; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.block.BlockState; import net.minecraft.client.render.model.UnbakedModel; import net.minecraft.client.texture.Sprite; import net.minecraft.client.util.SpriteIdentifier; @@ -25,20 +25,17 @@ public class ReFramedClientHelper { private final ReFramedModelProvider prov; - public UnbakedModel auto(Identifier parent) { - return auto(parent, ThemeableBlockEntity::getFirstTheme); + + public UnbakedRetexturedModel auto(Identifier parent) { + return new UnbakedAutoRetexturedModel(parent); } - public UnbakedModel auto(Identifier parent, Function state_getter) { - return new UnbakedAutoRetexturedModel(parent, state_getter); + public UnbakedRetexturedModel json(Identifier parent) { + return new UnbakedJsonRetexturedModel(parent); } - public UnbakedModel json(Identifier parent) { - return json(parent, ThemeableBlockEntity::getFirstTheme); - } - - public UnbakedModel json(Identifier parent, Function state_getter) { - return new UnbakedJsonRetexturedModel(parent, state_getter); + public UnbakedModel autoDouble(Identifier first, Identifier second) { + return new UnbakedDoubleRetexturedModel(auto(first), auto(second)); } public void addReFramedModel(Identifier id, UnbakedModel unbaked) { diff --git a/src/main/java/fr/adrien1106/reframed/client/ReFramedModelProvider.java b/src/main/java/fr/adrien1106/reframed/client/ReFramedModelProvider.java index a70a005..86b673c 100644 --- a/src/main/java/fr/adrien1106/reframed/client/ReFramedModelProvider.java +++ b/src/main/java/fr/adrien1106/reframed/client/ReFramedModelProvider.java @@ -37,9 +37,9 @@ public class ReFramedModelProvider implements ModelResourceProvider, ModelVarian //but json models are never allowed to have non-json models as a parent, and frame unbaked models are not json models. Ah well. //So, instead, we use a ModelVariantProvider to redirect attempts to load the item:id#inventory model. @Override - public @Nullable UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) { - Identifier customModelId = itemAssignments.get(modelId); - return customModelId == null ? null : loadModelResource(customModelId, context); + public @Nullable UnbakedModel loadModelVariant(ModelIdentifier model, ModelProviderContext context) { + Identifier custom_model = itemAssignments.get(model); + return custom_model == null ? null : loadModelResource(custom_model, context); } /// camo appearance manager cache 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 c255fb9..a8bf841 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java @@ -1,20 +1,26 @@ package fr.adrien1106.reframed.client.model; +import net.fabricmc.api.EnvType; +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.BakedModel; import net.minecraft.client.texture.Sprite; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.random.Random; import net.minecraft.world.BlockRenderView; +import java.util.List; import java.util.function.Supplier; -public class DoubleRetexturingBakedModel extends ForwardingBakedModel { +@Environment(EnvType.CLIENT) +public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements MultiRetexturableModel { private final ForwardingBakedModel model_1, model_2; public DoubleRetexturingBakedModel(ForwardingBakedModel model_1, ForwardingBakedModel model_2) { + this.wrapped = model_1.getWrappedModel(); this.model_1 = model_1; this.model_2 = model_2; } @@ -26,18 +32,20 @@ public class DoubleRetexturingBakedModel extends ForwardingBakedModel { @Override public Sprite getParticleSprite() { - return model_1.getParticleSprite(); // TODO determine which face is on top + return model_1.getParticleSprite(); } @Override - 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); - } + public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) {} - @Override + @Override // models are emitted here because no checks are done on items public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { model_1.emitItemQuads(stack, randomSupplier, context); model_2.emitItemQuads(stack, randomSupplier, context); } + + @Override + public List models() { + return List.of(model_1, model_2); + } } diff --git a/src/main/java/fr/adrien1106/reframed/client/model/MeshTransformUtil.java b/src/main/java/fr/adrien1106/reframed/client/model/MeshTransformUtil.java deleted file mode 100644 index 768cb27..0000000 --- a/src/main/java/fr/adrien1106/reframed/client/model/MeshTransformUtil.java +++ /dev/null @@ -1,83 +0,0 @@ -package fr.adrien1106.reframed.client.model; - -import fr.adrien1106.reframed.client.ReFramedClient; -import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; -import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; -import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; -import net.minecraft.client.render.model.ModelBakeSettings; -import net.minecraft.util.math.Direction; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector4f; - -import java.util.EnumMap; -import java.util.Map; - -public class MeshTransformUtil { - public static Mesh pretransformMesh(Mesh mesh, RetexturingBakedModel.RetexturingTransformer transform) { - MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder(); - QuadEmitter emitter = builder.getEmitter(); - - mesh.forEach(quad -> { - int i = -1; - do { - emitter.copyFrom(quad); - i = transform.transform(emitter, i); - } while (i > 0); - }); - - return builder.build(); - } - - public static Map facePermutation(Matrix4f mat) { - Map facePermutation = new EnumMap<>(Direction.class); - for(Direction input : Direction.values()) { - Direction output = Direction.transform(mat, input); - facePermutation.put(input, output); - } - return facePermutation; - } - - public static RenderContext.QuadTransform applyAffine(ModelBakeSettings settings) { - return applyMatrix(settings.getRotation().getMatrix()); - } - - public static RenderContext.QuadTransform applyMatrix(Matrix4f mat) { - Map facePermutation = facePermutation(mat); - Vector3f pos3 = new Vector3f(); - Vector4f pos4 = new Vector4f(); - - return quad -> { - //For each vertex: - for(int i = 0; i < 4; i++) { - //Copy pos into a vec3, then a vec4. the w component is set to 0 since this is a point, not a normal - quad.copyPos(i, pos3); - pos3.add(-0.5f, -0.5f, -0.5f); - pos4.set(pos3, 0); - - //Compute the matrix-vector product. This function mutates the vec4 in-place. - //Note that `transformAffine` has the same purpose as `transform`; the difference is it - //assumes (without checking) that the last row of the matrix is 0,0,0,1, as an optimization - mat.transform(pos4); - - //Manually copy the data back onto the vertex - quad.pos(i, pos4.x + 0.5f, pos4.y + 0.5f, pos4.z + 0.5f); - } - - //permute tags - int tag = quad.tag(); - if(tag != 0) quad.tag(facePermutation.get(RetexturingBakedModel.DIRECTIONS[tag - 1]).ordinal() + 1); - - //permute lighting face (?) - quad.nominalFace(facePermutation.get(quad.lightFace())); - - //permute cullface - Direction cull = quad.cullFace(); - if(cull != null) quad.cullFace(facePermutation.get(cull)); - - //Output the quad - return true; - }; - } -} diff --git a/src/main/java/fr/adrien1106/reframed/client/model/MultiRetexturableModel.java b/src/main/java/fr/adrien1106/reframed/client/model/MultiRetexturableModel.java new file mode 100644 index 0000000..b1d7cea --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/client/model/MultiRetexturableModel.java @@ -0,0 +1,10 @@ +package fr.adrien1106.reframed.client.model; + +import net.minecraft.client.render.model.BakedModel; + +import java.util.List; + +public interface MultiRetexturableModel { + + List models(); +} 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 32782b0..c16d1e7 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java @@ -1,6 +1,7 @@ package fr.adrien1106.reframed.client.model; import fr.adrien1106.reframed.block.ReFramedEntity; +import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.model.apperance.SpriteProperties; import fr.adrien1106.reframed.mixin.MinecraftAccessor; import fr.adrien1106.reframed.client.model.apperance.CamoAppearance; @@ -8,6 +9,7 @@ import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager; import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance; import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; +import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; @@ -27,39 +29,41 @@ import net.minecraft.world.BlockRenderView; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; import java.util.function.Supplier; public abstract class RetexturingBakedModel extends ForwardingBakedModel { - public RetexturingBakedModel(BakedModel baseModel, CamoAppearanceManager tam, Function state_getter, ModelBakeSettings settings, BlockState itemModelState, boolean ao) { - this.wrapped = baseModel; //field from the superclass; vanilla getQuads etc. will delegate through to this - + public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state, boolean ao) { + this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this + this.tam = tam; - this.state_getter = state_getter; - this.uvlock = settings.isUvLocked(); - this.itemModelState = itemModelState; + this.theme_index = theme_index; + this.uv_lock = settings.isUvLocked(); + this.item_state = item_state; this.ao = ao; } protected final CamoAppearanceManager tam; - protected final Function state_getter; - protected final boolean uvlock; - protected final BlockState itemModelState; + protected final int theme_index; + protected final boolean uv_lock; + protected final BlockState item_state; protected final boolean ao; - + + /* ----------------------------------------------- CACHE ELEMENT ------------------------------------------------ */ + // TODO make static ? for connected textures ? protected record MeshCacheKey(BlockState state, TransformCacheKey transform) {} - protected final ConcurrentMap retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache protected record TransformCacheKey(CamoAppearance appearance, int model_id) {} protected final ConcurrentMap retextured_transforms = new ConcurrentHashMap<>(); - - protected static final Direction[] DIRECTIONS = Direction.values(); - protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1]; - static { System.arraycopy(DIRECTIONS, 0, DIRECTIONS_AND_NULL, 0, DIRECTIONS.length); } + protected final ConcurrentMap retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache + protected static final Direction[] DIRECTIONS_AND_NULL; + static { + Direction[] values = Direction.values(); + DIRECTIONS_AND_NULL = new Direction[values.length + 1]; + System.arraycopy(values, 0, DIRECTIONS_AND_NULL, 0, values.length); + } protected final ConcurrentMap jsonToMesh = new ConcurrentHashMap<>(); - protected Mesh getBaseMesh(BlockState state) { //Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate return jsonToMesh.computeIfAbsent(state, this::convertModel); @@ -74,15 +78,15 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { @Override public Sprite getParticleSprite() { - return tam.getDefaultAppearance().getSprites(Direction.UP, 0).get(0).sprite(); + return tam.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite(); } @Override public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { - BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? state_getter.apply(s) : null; + BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null; QuadEmitter quad_emitter = context.getEmitter(); if(theme == null || theme.isAir()) { - getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(), 0)), 0).outputTo(quad_emitter); + getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(theme_index), 0)), 0).outputTo(quad_emitter); return; } if(theme.getBlock() == Blocks.BARRIER) return; @@ -118,16 +122,16 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { //none of this is accessible unless you're in creative mode doing ctrl-pick btw CamoAppearance nbtAppearance; int tint; - BlockState theme = ReFramedEntity.readStateFromItem(stack); // TODO Different states for both models + BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index); if(!theme.isAir()) { nbtAppearance = tam.getCamoAppearance(null, theme, null); tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0); } else { - nbtAppearance = tam.getDefaultAppearance(); + nbtAppearance = tam.getDefaultAppearance(theme_index); tint = 0xFFFFFFFF; } - Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(itemModelState, new TransformCacheKey(nbtAppearance, 0)), 0); + Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(item_state, new TransformCacheKey(nbtAppearance, 0)), 0); QuadEmitter quad_emitter = context.getEmitter(); if(tint == 0xFFFFFFFF) { @@ -145,7 +149,22 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { protected Mesh createUntintedRetexturedMesh(MeshCacheKey key, long seed) { RetexturingTransformer transformer = retextured_transforms.computeIfAbsent(key.transform, (k) -> new RetexturingTransformer(k.appearance, seed)); - return MeshTransformUtil.pretransformMesh(getBaseMesh(key.state), transformer); + return pretransformMesh(getBaseMesh(key.state), transformer); + } + + private static Mesh pretransformMesh(Mesh mesh, RetexturingTransformer transform) { + MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder(); + QuadEmitter emitter = builder.getEmitter(); + + mesh.forEach(quad -> { + int i = -1; + do { + emitter.copyFrom(quad); + i = transform.transform(emitter, i); + } while (i > 0); + }); + + return builder.build(); } public class RetexturingTransformer { @@ -174,7 +193,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { properties.sprite(), MutableQuadView.BAKE_NORMALIZED | properties.flags() - | (uvlock ? MutableQuadView.BAKE_LOCK_UV : 0) + | (uv_lock ? MutableQuadView.BAKE_LOCK_UV : 0) ); quad.tag(i+1); quad.emit(); @@ -188,7 +207,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { // apply new quad shape quad.material(ta.getRenderMaterial(ao)); bounds.intersection(origin_bounds, direction.getAxis()).apply(quad, origin_bounds); - quad.spriteBake( // TODO check if the flags are usefull because it seems to be braking the functioning of it + quad.spriteBake( // seems to work without the flags and break with it properties.sprite(), MutableQuadView.BAKE_NORMALIZED | MutableQuadView.BAKE_LOCK_UV diff --git a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedAutoRetexturedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedAutoRetexturedModel.java index c6b4266..dba3f74 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedAutoRetexturedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedAutoRetexturedModel.java @@ -1,7 +1,6 @@ package fr.adrien1106.reframed.client.model; import fr.adrien1106.reframed.client.ReFramedClient; -import fr.adrien1106.reframed.util.ThemeableBlockEntity; 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.Mesh; @@ -24,8 +23,8 @@ import java.util.function.Function; public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel { - public UnbakedAutoRetexturedModel(Identifier parent, Function state_getter) { - super(parent, state_getter); + public UnbakedAutoRetexturedModel(Identifier parent) { + super(parent); item_state = Blocks.AIR.getDefaultState(); } @@ -35,7 +34,7 @@ public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel { return new RetexturingBakedModel( baker.bake(parent, bake_settings), ReFramedClient.HELPER.getCamoApperanceManager(texture_getter), - state_getter, + theme_index, bake_settings, item_state, ao diff --git a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedDoubleRetexturedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedDoubleRetexturedModel.java index d765e75..70d53d6 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedDoubleRetexturedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedDoubleRetexturedModel.java @@ -19,15 +19,16 @@ public class UnbakedDoubleRetexturedModel implements UnbakedModel { protected final UnbakedModel model_1; protected final UnbakedModel model_2; - public UnbakedDoubleRetexturedModel(UnbakedModel model_1, UnbakedModel model_2) { + public UnbakedDoubleRetexturedModel(UnbakedRetexturedModel model_1, UnbakedRetexturedModel model_2) { this.model_1 = model_1; this.model_2 = model_2; + model_2.setThemeIndex(2); } @Override public Collection getModelDependencies() { - return List.of(model_1.getModelDependencies().iterator().next(), model_2.getModelDependencies().iterator().next()); + return List.of(((List) model_1.getModelDependencies()).get(0), ((List) model_2.getModelDependencies()).get(0)); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedJsonRetexturedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedJsonRetexturedModel.java index 40cba62..914ce00 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedJsonRetexturedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedJsonRetexturedModel.java @@ -2,7 +2,6 @@ package fr.adrien1106.reframed.client.model; import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.client.ReFramedClient; -import fr.adrien1106.reframed.util.ThemeableBlockEntity; 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.Mesh; @@ -25,25 +24,25 @@ import java.util.Objects; import java.util.function.Function; public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel { - public UnbakedJsonRetexturedModel(Identifier parent, Function state_getter) { - super(parent, state_getter); + public UnbakedJsonRetexturedModel(Identifier parent) { + super(parent); } @Nullable @Override public BakedModel bake(Baker baker, Function spriteLookup, ModelBakeSettings bake_settings, Identifier identifier) { - Direction[] DIRECTIONS = RetexturingBakedModel.DIRECTIONS; + Direction[] directions = Direction.values(); - Sprite[] specialSprites = new Sprite[DIRECTIONS.length]; - for(int i = 0; i < DIRECTIONS.length; i++) { - SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + DIRECTIONS[i].getName())); - specialSprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !"); + Sprite[] sprites = new Sprite[directions.length]; + for(int i = 0; i < directions.length; i++) { + SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + directions[i].getName())); + sprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !"); } return new RetexturingBakedModel( baker.bake(parent, bake_settings), ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup), - state_getter, + theme_index, bake_settings, item_state, ao @@ -61,9 +60,9 @@ public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel { emitter.fromVanilla(quad, mat, cullFace); QuadUvBounds bounds = QuadUvBounds.read(emitter); - for(int i = 0; i < specialSprites.length; i++) { - if(bounds.displaysSprite(specialSprites[i])) { - bounds.normalizeUv(emitter, specialSprites[i]); + for(int i = 0; i < sprites.length; i++) { + if(bounds.displaysSprite(sprites[i])) { + bounds.normalizeUv(emitter, sprites[i]); emitter.tag(i + 1); break; } diff --git a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedRetexturedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedRetexturedModel.java index 7d88d8e..8345833 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedRetexturedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedRetexturedModel.java @@ -1,6 +1,5 @@ package fr.adrien1106.reframed.client.model; -import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.minecraft.block.BlockState; import net.minecraft.client.render.model.UnbakedModel; import net.minecraft.util.Identifier; @@ -12,14 +11,17 @@ import java.util.function.Function; public abstract class UnbakedRetexturedModel implements UnbakedModel { protected final Identifier parent; - protected final Function state_getter; + protected int theme_index = 1; protected BlockState item_state; protected boolean ao = true; - public UnbakedRetexturedModel(Identifier parent, Function state_getter) { + public UnbakedRetexturedModel(Identifier parent) { this.parent = parent; - this.state_getter = state_getter; + } + + public void setThemeIndex(int theme_index) { + this.theme_index = theme_index; } @Override diff --git a/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java b/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java index 72d0195..0abffee 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java @@ -44,29 +44,34 @@ public class CamoAppearanceManager { materialsWithAo.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO } - Sprite defaultSprite = spriteLookup.apply(DEFAULT_SPRITE_ID); - if(defaultSprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_ID + " !"); - this.defaultAppearance = new SingleSpriteAppearance(defaultSprite, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement()); - - Sprite barrier = spriteLookup.apply(BARRIER_SPRITE_ID); - if(barrier == null) barrier = defaultSprite; //eh - this.barrierItemAppearance = new SingleSpriteAppearance(barrier, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement()); + Sprite sprite = spriteLookup.apply(DEFAULT_SPRITE_MAIN); + if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !"); + this.default_appearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement()); + + sprite = spriteLookup.apply(DEFAULT_SPRITE_SECONDARY); + if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !"); + this.accent_appearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement()); + + sprite = spriteLookup.apply(BARRIER_SPRITE_ID); + this.barrierItemAppearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement()); } - protected static final SpriteIdentifier DEFAULT_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block")); + protected static final SpriteIdentifier DEFAULT_SPRITE_MAIN = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block")); + protected static final SpriteIdentifier DEFAULT_SPRITE_SECONDARY = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_accent_block")); private static final SpriteIdentifier BARRIER_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:item/barrier")); - private final CamoAppearance defaultAppearance; + private final CamoAppearance default_appearance; + private final CamoAppearance accent_appearance; private final CamoAppearance barrierItemAppearance; private final ConcurrentHashMap appearanceCache = new ConcurrentHashMap<>(); //Mutable, append-only cache - private final AtomicInteger serialNumber = new AtomicInteger(0); //Mutable + private final AtomicInteger serial_number = new AtomicInteger(0); //Mutable private final EnumMap materialsWithAo = new EnumMap<>(BlendMode.class); private final EnumMap materialsWithoutAo = new EnumMap<>(BlendMode.class); //Immutable contents - public CamoAppearance getDefaultAppearance() { - return defaultAppearance; + public CamoAppearance getDefaultAppearance(int appearance) { + return appearance == 2 ? accent_appearance: default_appearance; } public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos) { @@ -96,7 +101,7 @@ public class CamoAppearanceManager { getAppearance(model), getCachedMaterial(state, true), getCachedMaterial(state, false), - serialNumber.getAndIncrement() + serial_number.getAndIncrement() ); } List> appearances = weighted_model.getModels().stream() @@ -107,7 +112,7 @@ public class CamoAppearanceManager { appearances, getCachedMaterial(state, true), getCachedMaterial(state, false), - serialNumber.getAndIncrement() + serial_number.getAndIncrement() ); } @@ -124,7 +129,7 @@ public class CamoAppearanceManager { Arrays.stream(Direction.values()).forEach(direction -> { List quads = model.getQuads(null, direction, random); if(quads.isEmpty()) { // add default appearance if none present - sprites.put(direction, defaultAppearance.getSprites(direction, 0)); + sprites.put(direction, default_appearance.getSprites(direction, 0)); return; } diff --git a/src/main/java/fr/adrien1106/reframed/generator/Generator.java b/src/main/java/fr/adrien1106/reframed/generator/Generator.java index 902ee3a..3a86d36 100644 --- a/src/main/java/fr/adrien1106/reframed/generator/Generator.java +++ b/src/main/java/fr/adrien1106/reframed/generator/Generator.java @@ -13,7 +13,7 @@ public class Generator implements DataGeneratorEntrypoint { /** * missing DOOR, IRON_DOOR, CANDLE */ - public static List BLOCKS = List.of(CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, TRAPDOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE); + public static List BLOCKS = List.of(CUBE, STAIRS, SLAB, DOUBLE_SLAB, POST, FENCE, FENCE_GATE, TRAPDOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE); @Override public void onInitializeDataGenerator(FabricDataGenerator data_generator) { FabricDataGenerator.Pack myPack = data_generator.createPack(); diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java index 6cbee5d..73b5359 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java @@ -16,12 +16,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(WrappedGetter.class) public class AthenaWrappedGetterMixin { + // TODO return only the state that might be of interest @Shadow @Final private BlockRenderView getter; @Inject(method = "getBlockState", at = @At(value = "HEAD"), cancellable = true) private void getCamoState(BlockPos pos, CallbackInfoReturnable cir) { if (!(getter.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)) return; - cir.setReturnValue(framed_entity.getFirstTheme()); + cir.setReturnValue(framed_entity.getTheme(1)); // TODO theme } @Redirect(method = "getAppearance(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)" + @@ -30,7 +31,7 @@ public class AthenaWrappedGetterMixin { "getBlockState(Lnet/minecraft/util/math/BlockPos;)" + "Lnet/minecraft/block/BlockState;")) private BlockState appearanceCamoState(BlockRenderView world, BlockPos pos) { - if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) return framed_entity.getFirstTheme(); + if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) return framed_entity.getTheme(1); // TODO theme return world.getBlockState(pos); } @@ -39,7 +40,7 @@ public class AthenaWrappedGetterMixin { "getBlockState(Lnet/minecraft/util/math/BlockPos;)" + "Lnet/minecraft/block/BlockState;")) private BlockState queryCamoState(BlockRenderView world, BlockPos pos) { - if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) return framed_entity.getFirstTheme(); + if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) return framed_entity.getTheme(1); // TODO theme return world.getBlockState(pos); } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinBlockDustParticle.java b/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinBlockDustParticle.java index 16c9018..5348a91 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinBlockDustParticle.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinBlockDustParticle.java @@ -1,5 +1,6 @@ package fr.adrien1106.reframed.mixin.particles; +import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; @@ -20,8 +21,11 @@ public class MixinBlockDustParticle { ) void modifyParticleSprite(ClientWorld clientWorld, double d, double e, double f, double g, double h, double i, BlockState state, BlockPos pos, CallbackInfo ci) { AccessorParticle a = (AccessorParticle) this; - if(a.getRandom().nextBoolean() && clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable) { - BlockState theme = themeable.getFirstTheme(); + if(a.getRandom().nextBoolean() + && clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable + && state.getBlock() instanceof ReFramedBlock block + ) { + BlockState theme = themeable.getTheme(block.getTopThemeIndex(state)); if(theme == null || theme.isAir()) return; Sprite replacement = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(theme); diff --git a/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinEntity.java b/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinEntity.java index 8235431..bbba8fb 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinEntity.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinEntity.java @@ -1,31 +1,31 @@ package fr.adrien1106.reframed.mixin.particles; +import com.llamalad7.mixinextras.sugar.Local; +import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.minecraft.block.BlockState; import net.minecraft.entity.Entity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; @Mixin(Entity.class) public abstract class MixinEntity { - @Shadow @Deprecated public abstract BlockPos getLandingPos(); //TODO, somewhat expensive method - @ModifyArg( method = "spawnSprintingParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V") ) - private BlockState modifyParticleState(BlockState origState) { + private BlockState modifyParticleState(BlockState state, @Local(ordinal = 0) BlockPos landing_pos) { World world = ((Entity) (Object) this).getWorld(); - if(world.getBlockEntity(getLandingPos()) instanceof ThemeableBlockEntity themeable) { - BlockState theme = themeable.getFirstTheme(); - if(!theme.isAir()) return theme; + if(world.getBlockEntity(landing_pos) instanceof ThemeableBlockEntity themeable + && state.getBlock() instanceof ReFramedBlock block) { + BlockState theme = themeable.getTheme(block.getTopThemeIndex(state)); + if(!theme.isAir()) return theme; } - return origState; + return state; } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinLivingEntity.java b/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinLivingEntity.java index 884bf81..d19193f 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinLivingEntity.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/particles/MixinLivingEntity.java @@ -1,5 +1,7 @@ package fr.adrien1106.reframed.mixin.particles; +import com.llamalad7.mixinextras.sugar.Local; +import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.minecraft.block.BlockState; import net.minecraft.entity.Entity; @@ -7,33 +9,25 @@ import net.minecraft.entity.LivingEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; 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.Inject; import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(LivingEntity.class) public class MixinLivingEntity { - @Unique private BlockPos lastFallCheckPos; - - @Inject(method = "fall", at = @At("HEAD")) - private void onFall(double d, boolean bl, BlockState blockState, BlockPos blockPos, CallbackInfo ci) { - lastFallCheckPos = blockPos; - } @ModifyArg( method = "fall", at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V") ) - private BlockState modifyParticleState(BlockState origState) { + private BlockState modifyParticleState(BlockState state, @Local(ordinal = 0, argsOnly = true) BlockPos land_pos) { World world = ((Entity) (Object) this).getWorld(); - if(lastFallCheckPos != null && world.getBlockEntity(lastFallCheckPos) instanceof ThemeableBlockEntity themeable) { - BlockState theme = themeable.getFirstTheme(); - if(!theme.isAir()) return theme; + if(world.getBlockEntity(land_pos) instanceof ThemeableBlockEntity themeable + && state.getBlock() instanceof ReFramedBlock block) { + BlockState theme = themeable.getTheme(block.getTopThemeIndex(state)); + if(!theme.isAir()) return theme; } - return origState; + return state; } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/BlockRenderInfoMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderInfoMixin.java similarity index 57% rename from src/main/java/fr/adrien1106/reframed/mixin/BlockRenderInfoMixin.java rename to src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderInfoMixin.java index 254275a..23f9f3c 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/BlockRenderInfoMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderInfoMixin.java @@ -1,25 +1,29 @@ -package fr.adrien1106.reframed.mixin; +package fr.adrien1106.reframed.mixin.render; import com.llamalad7.mixinextras.sugar.Local; +import fr.adrien1106.reframed.block.ReFramedBlock; +import fr.adrien1106.reframed.util.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo; -import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.MinecraftClient; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.world.BlockRenderView; +import net.minecraft.world.BlockView; 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.ModifyArg; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(BlockRenderInfo.class) -public abstract class BlockRenderInfoMixin { +public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin { @Shadow public BlockPos blockPos; @@ -29,21 +33,38 @@ public abstract class BlockRenderInfoMixin { @Shadow @Final private BlockPos.Mutable searchPos; + @Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo); + + @Unique + private int theme_index = 1; + @ModifyArg(method = "prepareForBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" + "getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;")) public BlockState prepareCamoLayer(BlockState state, @Local(argsOnly = true) BlockPos pos) { BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(pos); if (!(block_entity instanceof ThemeableBlockEntity frame_entity)) return state; - return frame_entity.getFirstTheme(); + return frame_entity.getTheme(theme_index); } @Inject(method = "shouldDrawFace", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/util/math/Direction;getId()I"), cancellable = true) private void shouldDrawCamoFace(Direction face, CallbackInfoReturnable cir) { + // early injection for camos themselves BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(blockPos); if (!(block_entity instanceof ThemeableBlockEntity)) return; - cir.setReturnValue(Block.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face))); + cir.setReturnValue(ReFramedBlock.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face), theme_index)); + } + + @Redirect(method = "shouldDrawFace", 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 shouldDrawAdjacentCamoSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos) { + return ReFramedBlock.shouldDrawSide(state, world, pos, side, other_pos, theme_index); + } + + @Override + public void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index) { + this.theme_index = theme_index; + prepareForBlock(state, pos, ao); } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/render/MultipartBakedModelMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/MultipartBakedModelMixin.java new file mode 100644 index 0000000..86c1819 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/MultipartBakedModelMixin.java @@ -0,0 +1,25 @@ +package fr.adrien1106.reframed.mixin.render; + +import fr.adrien1106.reframed.util.IMultipartBakedModelMixin; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.MultipartBakedModel; +import org.apache.commons.lang3.tuple.Pair; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; + +@Mixin(MultipartBakedModel.class) +public class MultipartBakedModelMixin implements IMultipartBakedModelMixin { + + @Shadow @Final private List, BakedModel>> components; + + @Override + public BakedModel getModel(BlockState state) { + return components.stream().map(pair -> pair.getLeft().test(state) ? pair.getRight(): null).filter(Objects::nonNull).findAny().orElse(null); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/render/TerrainRenderContextMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/TerrainRenderContextMixin.java new file mode 100644 index 0000000..3fa58df --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/TerrainRenderContextMixin.java @@ -0,0 +1,41 @@ +package fr.adrien1106.reframed.mixin.render; + +import fr.adrien1106.reframed.client.model.MultiRetexturableModel; +import fr.adrien1106.reframed.util.IBlockRenderInfoMixin; +import fr.adrien1106.reframed.util.IMultipartBakedModelMixin; +import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext; +import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.BlockPos; +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.CallbackInfo; + +import java.util.List; + +@Mixin(TerrainRenderContext.class) +public abstract class TerrainRenderContextMixin extends AbstractBlockRenderContext { + + @Inject(method = "tessellateBlock", at = @At( + value = "INVOKE", + target = "Lnet/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoCalculator;clear()V", + shift = At.Shift.AFTER + ), cancellable = true) + private void renderMultipleModels(BlockState state, BlockPos pos, BakedModel wrapper, MatrixStack matrixStack, CallbackInfo ci) { + if (!(wrapper instanceof IMultipartBakedModelMixin wrapped) + || !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return; + + List models = retexturing_model.models(); + int i = 0; + for (BakedModel bakedModel : models) { + i++; + aoCalc.clear(); + ((IBlockRenderInfoMixin) blockInfo).prepareForBlock(state, pos, bakedModel.useAmbientOcclusion(), i); + bakedModel.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); + } + ci.cancel(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/render/WorldRendererMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/WorldRendererMixin.java new file mode 100644 index 0000000..c7f8c5a --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/WorldRendererMixin.java @@ -0,0 +1,35 @@ +package fr.adrien1106.reframed.mixin.render; + +import fr.adrien1106.reframed.block.ReFramedDoubleBlock; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; +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.Redirect; + +@Mixin(WorldRenderer.class) +public class WorldRendererMixin { + + @Shadow @Final private MinecraftClient client; + + @Redirect(method = "drawBlockOutline", + at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;" + + "getOutlineShape(" + + "Lnet/minecraft/world/BlockView;" + + "Lnet/minecraft/util/math/BlockPos;" + + "Lnet/minecraft/block/ShapeContext;" + + ")Lnet/minecraft/util/shape/VoxelShape;")) + private VoxelShape getRenderOutline(BlockState state, BlockView world, BlockPos pos, ShapeContext shape_context) { + if (state.getBlock() instanceof ReFramedDoubleBlock double_frame_block) // cast is already checked in render + return double_frame_block.getRenderOutline(state, (BlockHitResult) client.crosshairTarget); + return state.getOutlineShape(world, pos, shape_context); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/util/IBlockRenderInfoMixin.java b/src/main/java/fr/adrien1106/reframed/util/IBlockRenderInfoMixin.java new file mode 100644 index 0000000..ca7c51c --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/util/IBlockRenderInfoMixin.java @@ -0,0 +1,9 @@ +package fr.adrien1106.reframed.util; + +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; + +public interface IBlockRenderInfoMixin { + + void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index); +} diff --git a/src/main/java/fr/adrien1106/reframed/util/IMultipartBakedModelMixin.java b/src/main/java/fr/adrien1106/reframed/util/IMultipartBakedModelMixin.java new file mode 100644 index 0000000..275f01d --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/util/IMultipartBakedModelMixin.java @@ -0,0 +1,9 @@ +package fr.adrien1106.reframed.util; + +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedModel; + +public interface IMultipartBakedModelMixin { + + BakedModel getModel(BlockState state); +} diff --git a/src/main/java/fr/adrien1106/reframed/util/ReFramedInteractionUtil.java b/src/main/java/fr/adrien1106/reframed/util/ReFramedInteractionUtil.java index f334fd0..7710155 100644 --- a/src/main/java/fr/adrien1106/reframed/util/ReFramedInteractionUtil.java +++ b/src/main/java/fr/adrien1106/reframed/util/ReFramedInteractionUtil.java @@ -28,8 +28,10 @@ import org.jetbrains.annotations.Nullable; //For an example of how to use this class, have a look at TemplateBlock. //Basically there are several methods that would like to modify the return value of something. public class ReFramedInteractionUtil { + @Deprecated // TODO remove public static final BooleanProperty LIGHT = BooleanProperty.of("frame_light"); - + + @Deprecated // TODO remove public static StateManager.Builder appendProperties(StateManager.Builder builder) { return builder.add(LIGHT); } @@ -37,24 +39,31 @@ public class ReFramedInteractionUtil { //Use this to obtain a Block.Settings that'll make your Template act like the ones in the mod. //(To complete the look, don't forget to tag your blocks with mineable/axe.) private static final AbstractBlock.ContextPredicate NOPE = (blah, blahdey, blahh) -> false; + + // TODO Find better place public static AbstractBlock.Settings configureSettings(AbstractBlock.Settings s) { return s.luminance(ReFramedInteractionUtil::luminance).nonOpaque().sounds(BlockSoundGroup.WOOD).hardness(0.2f).suffocates(NOPE).blockVision(NOPE); } - + + + // TODO Find better place //And if you don't have a Block.Settings to copy off of. public static AbstractBlock.Settings makeSettings() { return configureSettings(AbstractBlock.Settings.create()); } - + + @Deprecated // TODO remove public static BlockState setDefaultStates(BlockState in) { if(in.contains(LIGHT)) in = in.with(LIGHT, false); return in; } - + + @Deprecated // TODO remove public static @Nullable BlockState modifyPlacementState(@Nullable BlockState in, ItemPlacementContext ctx) { - return ReFramedEntity.weirdNbtLightLevelStuff(in, ctx.getStack()); + return ReFramedEntity.getNbtLightLevel(in, ctx.getStack()); } - + + @Deprecated // TODO remove public static ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) return ActionResult.PASS; if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS; @@ -96,14 +105,14 @@ public class ReFramedInteractionUtil { return ActionResult.SUCCESS; } - // Changing the theme TODO Move outside - if(held.getItem() instanceof BlockItem block_item && block_entity.getFirstTheme().getBlock() == Blocks.AIR) { + // Changing the theme + if(held.getItem() instanceof BlockItem block_item && block_entity.getTheme(1).getBlock() == Blocks.AIR) { Block block = block_item.getBlock(); ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit)); BlockState placementState = block.getPlacementState(ctx); if(placementState != null && Block.isShapeFullCube(placementState.getCollisionShape(world, pos)) && !(block instanceof BlockEntityProvider)) { // TODO FOR SECOND - if(!world.isClient) block_entity.setFirstTheme(placementState); + if(!world.isClient) block_entity.setTheme(placementState, 1); // check for default light emission if (placementState.getLuminance() > 0) @@ -126,6 +135,7 @@ public class ReFramedInteractionUtil { return ActionResult.PASS; } + @Deprecated public static void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { if(!state.isOf(newState.getBlock()) && world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity && @@ -133,7 +143,7 @@ public class ReFramedInteractionUtil { ) { DefaultedList drops = DefaultedList.of(); - BlockState theme = frame_entity.getFirstTheme(); + BlockState theme = frame_entity.getTheme(1); if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock())); if(frame_entity.emitsRedstone() && theme.getWeakRedstonePower(world, pos, Direction.NORTH) == 0) @@ -146,7 +156,8 @@ public class ReFramedInteractionUtil { ItemScatterer.spawn(world, pos, drops); } } - + + @Deprecated public static void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { //Load the BlockEntityTag clientside, which fixes the template briefly showing its default state when placing it. //I'm surprised this doesn't happen by default; the BlockEntityTag stuff is only done serverside. @@ -155,21 +166,25 @@ public class ReFramedInteractionUtil { if(tag != null) be.readNbt(tag); } } - + + @Deprecated //Returns "null" to signal "no opinion". Imagine it like an InteractionResult.PASS. public static @Nullable VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { return view.getBlockEntity(pos) instanceof ReFramedEntity be && !be.isSolid() ? VoxelShapes.empty() : null; } - + + @Deprecated // TODO remove public static boolean emitsRedstonePower(BlockState state) { //return state.contains(REDSTONE) ? state.get(REDSTONE) : false; return false; //TODO, not available after punting this to BlockEntity. Yarn makes this method sound more important than it is, it's just for dust redirection. } - + + @Deprecated // TODO remove public static int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0; } - + + @Deprecated // TODO remove public static int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0; } diff --git a/src/main/java/fr/adrien1106/reframed/util/ThemeableBlockEntity.java b/src/main/java/fr/adrien1106/reframed/util/ThemeableBlockEntity.java index d84542b..2213826 100644 --- a/src/main/java/fr/adrien1106/reframed/util/ThemeableBlockEntity.java +++ b/src/main/java/fr/adrien1106/reframed/util/ThemeableBlockEntity.java @@ -2,16 +2,12 @@ package fr.adrien1106.reframed.util; import net.minecraft.block.BlockState; +import java.util.List; + public interface ThemeableBlockEntity { - BlockState getFirstTheme(); + BlockState getTheme(int i); - default BlockState getSecondTheme() { - return getFirstTheme(); - } + void setTheme(BlockState state, int i); - void setFirstTheme(BlockState state); - - default void setSecondTheme(BlockState state) { - setFirstTheme(state); - } + List getThemes(); } diff --git a/src/main/resources/assets/reframed/blockstates/slab.json b/src/main/resources/assets/reframed/blockstates/slab.json deleted file mode 100644 index acc6312..0000000 --- a/src/main/resources/assets/reframed/blockstates/slab.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "multipart": [ - { - "apply": { - "model": "reframed:slab_special", - "uvlock": true - }, - "when": { - "facing": "down" - } - }, - { - "apply": { - "model": "reframed:slab_special", - "uvlock": true, - "x": 180 - }, - "when": { - "facing": "up" - } - }, - { - "apply": { - "model": "reframed:slab_special", - "uvlock": true, - "x": 270 - }, - "when": { - "facing": "north" - } - }, - { - "apply": { - "model": "reframed:slab_special", - "uvlock": true, - "x": 90 - }, - "when": { - "facing": "south" - } - }, - { - "apply": { - "model": "reframed:slab_special", - "uvlock": true, - "x": 90, - "y": 90 - }, - "when": { - "facing": "west" - } - }, - { - "apply": { - "model": "reframed:slab_special", - "uvlock": true, - "x": 90, - "y": 270 - }, - "when": { - "facing": "east" - } - } - ] -} \ No newline at end of file diff --git a/src/main/resources/reframed.mixins.json b/src/main/resources/reframed.mixins.json index 6af6750..b3858b4 100644 --- a/src/main/resources/reframed.mixins.json +++ b/src/main/resources/reframed.mixins.json @@ -9,16 +9,18 @@ "particles.MixinLivingEntity" ], "client": [ - "BlockMixin", - "BlockRenderInfoMixin", "MinecraftAccessor", "compat.AthenaBakedModelMixin", - "compat.AthenaWrappedGetterMixin", "compat.AthenaConnectedBlockModelMixin", + "compat.AthenaWrappedGetterMixin", "model.WeightedBakedModelAccessor", "particles.AccessorParticle", "particles.AccessorSpriteBillboardParticle", - "particles.MixinBlockDustParticle" + "particles.MixinBlockDustParticle", + "render.BlockRenderInfoMixin", + "render.MultipartBakedModelMixin", + "render.TerrainRenderContextMixin", + "render.WorldRendererMixin" ], "injectors": { "defaultRequire": 1