diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index bb04be4..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,55 +0,0 @@ -Versions before 2.1.2 have been backfilled; I gotta be more on top of changelogs. - -# next version (unreleased) - -* Start sketching out a proper API, accessible through `TemplatesClientApi.getInstance()`. - * Not everything has been moved into the API package yet for ABI reasons. -* Code cleanups that hopefully didn't break ABI compat -* Remove some unused stuff from the jar -* Vertical slab placement no longer completely sucks -* Fix a bug where templates that look like blocks with randomized models, such as stone, could reroll their blockstate on every resource load. - * Forgot to specify a random seed. - * All templated blocks still use the *same* model, so templated stone will still not be randomly rotated/flipped, but at least it's now the *same* same model. - -road map: - -* want to fix texture orientation on vertical slabs/doors -* really want to fix the way vertical slabs place lmao (it's so buggy) -* clean up `StairShapeMaker` - -# 2.1.1 (Aug 2, 2023) - -Enable ambient-occlusion ("smooth lighting") on all Templates except for the slopes, which are still bugged - -# 2.1.0 (Jul 31, 2023) - -* Add a vertical slab template -* Add a "tiny slope" template -* Change the block entity NBT format to be much smaller -* Reduce memory footprint of the block entity -* Respect `doTileDrops` -* Improve creative ctrl-pick behavior on glowing Templates -* Adding a Barrier block to a Template makes it remove its model (not unbreakable) - -# 2.0.4 (Jul 25, 2023) - -* Apply more block tags -* Apply item tags - -# 2.0.3 (Jul 23, 2023) - -* add Door and Iron Door templates -* cool rivulet - -# 2.0.2 (Jul 20, 2023) - -* Add an Iron Trapdoor template -* Add some more mod metadata (change name to "Templates 2", add authors, fix sources link) - -# 2.0.1 (Jul 11, 2023) - -Fix a duplication glitch with the Stair Template, which was retaining its block entity after being broken. - -# 2.0.0 (Jul 11, 2023) - -Initial release \ No newline at end of file diff --git a/build.gradle b/build.gradle index bb5f154..dfe9f23 100755 --- a/build.gradle +++ b/build.gradle @@ -94,7 +94,7 @@ dependencies { // Chipped to test athena implementation modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7" -// modRuntimeOnly "earth.terrarium.chipped:chipped-fabric-${project.minecraft_version}:3.1.2" TODO not working for some reasons + modRuntimeOnly "earth.terrarium.chipped:Chipped-fabric-${project.minecraft_version}:3.1.2" // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" diff --git a/gradle.properties b/gradle.properties index 42582c6..458489a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ yarn_mappings=1.20.4+build.3 loader_version=0.15.6 # Mod Properties -mod_version = 1.2 +mod_version = 1.3 maven_group = fr.adrien1106 archives_base_name = ReFramed mod_id = reframed diff --git a/src/main/java/fr/adrien1106/reframed/ReFramed.java b/src/main/java/fr/adrien1106/reframed/ReFramed.java index 56c8622..29bd323 100644 --- a/src/main/java/fr/adrien1106/reframed/ReFramed.java +++ b/src/main/java/fr/adrien1106/reframed/ReFramed.java @@ -25,15 +25,16 @@ import java.util.function.BiConsumer; import java.util.stream.Collectors; /** - * TODO multiple camos + * TODO self culling, fix other models, better connected textures */ 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, DOUBLE_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) -> {}; @@ -46,7 +47,9 @@ public class ReFramed implements ModInitializer { //and button, because they're redstoney... hopefully this ordering makes sense lol CUBE = registerReFramed("cube" , new ReFramedBlock(ReFramedInteractionUtil.makeSettings())); STAIRS = registerReFramed("stairs" , new ReFramedStairsBlock(cp(Blocks.OAK_STAIRS))); + DOUBLE_STAIRS = registerReFramed("double_stairs" , new ReFramedDoubleStairsBlock(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 +66,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..f015934 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 (other_state.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 new file mode 100644 index 0000000..5090b0a --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleEntity.java @@ -0,0 +1,62 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.ReFramed; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.nbt.NbtCompound; +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 { + + protected BlockState second_state = Blocks.AIR.getDefaultState(); + + public ReFramedDoubleEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Override + public BlockState getTheme(int i) { + return i == 2 ? second_state : super.getTheme(i); + } + + @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 + public void readNbt(NbtCompound nbt) { + super.readNbt(nbt); + + BlockState rendered_state = second_state;// keep previous state to check if rerender is needed + 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)) { + ReFramed.chunkRerenderProxy.accept(world, pos); + } + } + + @Override + public void writeNbt(NbtCompound nbt) { + super.writeNbt(nbt); + + if(second_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 2, NbtHelper.fromBlockState(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/ReFramedDoubleStairsBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleStairsBlock.java new file mode 100644 index 0000000..a38d1a3 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedDoubleStairsBlock.java @@ -0,0 +1,187 @@ +package fr.adrien1106.reframed.block; + +import fr.adrien1106.reframed.generator.MultipartBlockStateProvider; +import fr.adrien1106.reframed.util.property.StairDirection; +import fr.adrien1106.reframed.util.property.StairShape; +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.util.function.BooleanBiFunction; +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.World; +import net.minecraft.world.WorldAccess; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import static fr.adrien1106.reframed.block.ReFramedStairsBlock.*; +import static fr.adrien1106.reframed.util.property.StairShape.STRAIGHT; + +public class ReFramedDoubleStairsBlock extends ReFramedDoubleBlock implements MultipartBlockStateProvider { + + private static final List COMPLEMENT_LIST = new ArrayList<>(52); + + public ReFramedDoubleStairsBlock(Settings settings) { + super(settings); + setDefaultState(getDefaultState().with(FACING, StairDirection.NORTH_DOWN).with(SHAPE, STRAIGHT)); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder.add(FACING).add(SHAPE)); + } + + @Override + public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) { + return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved) + .with(SHAPE, getPlacementShape(this, state.get(FACING), world, pos)); + } + + + @Nullable + @Override + public BlockState getPlacementState(ItemPlacementContext ctx) { + return getPlacement(super.getPlacementState(ctx), ctx); + } + + @Override + public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { + super.onStateReplaced(state, world, pos, newState, moved); + + if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); + } + + @Override + public VoxelShape getShape(BlockState state, int i) { + return i == 2 ? getComplementOutline(state) : getOutline(state); + } + + private VoxelShape getComplementOutline(BlockState state) { + StairShape shape = state.get(SHAPE); + StairDirection direction = state.get(FACING); + return switch (shape) { + case STRAIGHT -> + switch (direction) { + case DOWN_SOUTH -> COMPLEMENT_LIST.get(0); + case NORTH_DOWN -> COMPLEMENT_LIST.get(1); + case UP_NORTH -> COMPLEMENT_LIST.get(2); + case SOUTH_UP -> COMPLEMENT_LIST.get(3); + case DOWN_EAST -> COMPLEMENT_LIST.get(4); + case WEST_DOWN -> COMPLEMENT_LIST.get(5); + case UP_WEST -> COMPLEMENT_LIST.get(6); + case EAST_UP -> COMPLEMENT_LIST.get(7); + case NORTH_EAST -> COMPLEMENT_LIST.get(8); + case EAST_SOUTH -> COMPLEMENT_LIST.get(9); + case SOUTH_WEST -> COMPLEMENT_LIST.get(10); + case WEST_NORTH -> COMPLEMENT_LIST.get(11); + }; + case INNER_LEFT -> + switch (direction) { + case WEST_DOWN, NORTH_DOWN -> COMPLEMENT_LIST.get(44); + case DOWN_EAST -> COMPLEMENT_LIST.get(45); + case DOWN_SOUTH -> COMPLEMENT_LIST.get(47); + case UP_WEST, UP_NORTH, WEST_NORTH -> COMPLEMENT_LIST.get(48); + case EAST_UP, NORTH_EAST -> COMPLEMENT_LIST.get(49); + case EAST_SOUTH -> COMPLEMENT_LIST.get(50); + case SOUTH_UP, SOUTH_WEST -> COMPLEMENT_LIST.get(51); + }; + case INNER_RIGHT -> + switch (direction) { + case WEST_NORTH -> COMPLEMENT_LIST.get(44); + case NORTH_DOWN, NORTH_EAST -> COMPLEMENT_LIST.get(45); + case DOWN_EAST, DOWN_SOUTH, EAST_SOUTH -> COMPLEMENT_LIST.get(46); + case WEST_DOWN, SOUTH_WEST -> COMPLEMENT_LIST.get(47); + case UP_NORTH -> COMPLEMENT_LIST.get(49); + case EAST_UP, SOUTH_UP -> COMPLEMENT_LIST.get(50); + case UP_WEST -> COMPLEMENT_LIST.get(51); + }; + case OUTER_LEFT -> + switch (direction) { + case DOWN_EAST -> COMPLEMENT_LIST.get(43); + case WEST_DOWN, NORTH_DOWN -> COMPLEMENT_LIST.get(42); + case DOWN_SOUTH -> COMPLEMENT_LIST.get(41); + case EAST_UP, NORTH_EAST -> COMPLEMENT_LIST.get(39); + case UP_WEST, UP_NORTH, WEST_NORTH -> COMPLEMENT_LIST.get(38); + case SOUTH_UP, SOUTH_WEST -> COMPLEMENT_LIST.get(37); + case EAST_SOUTH -> COMPLEMENT_LIST.get(36); + }; + case OUTER_RIGHT -> + switch (direction) { + case NORTH_DOWN, NORTH_EAST -> COMPLEMENT_LIST.get(43); + case WEST_NORTH -> COMPLEMENT_LIST.get(42); + case WEST_DOWN, SOUTH_WEST -> COMPLEMENT_LIST.get(41); + case DOWN_EAST, DOWN_SOUTH, EAST_SOUTH -> COMPLEMENT_LIST.get(40); + case UP_NORTH -> COMPLEMENT_LIST.get(39); + case UP_WEST -> COMPLEMENT_LIST.get(37); + case EAST_UP, SOUTH_UP -> COMPLEMENT_LIST.get(36); + }; + case FIRST_OUTER_LEFT -> + switch (direction) { + case WEST_DOWN, NORTH_DOWN -> COMPLEMENT_LIST.get(14); + case SOUTH_UP -> COMPLEMENT_LIST.get(17); + case EAST_UP -> COMPLEMENT_LIST.get(19); + case EAST_SOUTH -> COMPLEMENT_LIST.get(20); + case DOWN_SOUTH -> COMPLEMENT_LIST.get(22); + case UP_NORTH, WEST_NORTH -> COMPLEMENT_LIST.get(25); + case SOUTH_WEST -> COMPLEMENT_LIST.get(28); + case UP_WEST -> COMPLEMENT_LIST.get(31); + case DOWN_EAST -> COMPLEMENT_LIST.get(34); + case NORTH_EAST -> COMPLEMENT_LIST.get(35); + }; + case FIRST_OUTER_RIGHT -> + switch (direction) { + case NORTH_DOWN -> COMPLEMENT_LIST.get(15); + case SOUTH_UP, EAST_UP -> COMPLEMENT_LIST.get(16); + case WEST_DOWN -> COMPLEMENT_LIST.get(13); + case DOWN_SOUTH, EAST_SOUTH -> COMPLEMENT_LIST.get(23); + case UP_NORTH -> COMPLEMENT_LIST.get(24); + case WEST_NORTH -> COMPLEMENT_LIST.get(26); + case UP_WEST -> COMPLEMENT_LIST.get(28); + case SOUTH_WEST -> COMPLEMENT_LIST.get(29); + case DOWN_EAST -> COMPLEMENT_LIST.get(33); + case NORTH_EAST -> COMPLEMENT_LIST.get(34); + }; + case SECOND_OUTER_LEFT -> + switch (direction) { + case DOWN_EAST -> COMPLEMENT_LIST.get(15); + case DOWN_SOUTH -> COMPLEMENT_LIST.get(13); + case UP_WEST, UP_NORTH -> COMPLEMENT_LIST.get(18); + case SOUTH_UP, SOUTH_WEST -> COMPLEMENT_LIST.get(21); + case NORTH_EAST -> COMPLEMENT_LIST.get(24); + case NORTH_DOWN -> COMPLEMENT_LIST.get(26); + case WEST_DOWN -> COMPLEMENT_LIST.get(30); + case WEST_NORTH -> COMPLEMENT_LIST.get(31); + case EAST_SOUTH -> COMPLEMENT_LIST.get(32); + case EAST_UP -> COMPLEMENT_LIST.get(35); + }; + case SECOND_OUTER_RIGHT -> + switch (direction) { + case DOWN_SOUTH, DOWN_EAST -> COMPLEMENT_LIST.get(12); + case UP_WEST -> COMPLEMENT_LIST.get(17); + case UP_NORTH -> COMPLEMENT_LIST.get(19); + case SOUTH_UP -> COMPLEMENT_LIST.get(20); + case SOUTH_WEST -> COMPLEMENT_LIST.get(22); + case NORTH_EAST, NORTH_DOWN -> COMPLEMENT_LIST.get(27); + case WEST_DOWN -> COMPLEMENT_LIST.get(29); + case WEST_NORTH -> COMPLEMENT_LIST.get(30); + case EAST_UP -> COMPLEMENT_LIST.get(32); + case EAST_SOUTH -> COMPLEMENT_LIST.get(33); + }; + }; + } + + @Override + public MultipartBlockStateSupplier getMultipart() { + return getStairMultipart(this, true); + } + + static { + VOXEL_LIST.forEach(shape -> COMPLEMENT_LIST.add(VoxelShapes.combineAndSimplify(VoxelShapes.fullCube(), shape, BooleanBiFunction.ONLY_FIRST))); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/block/ReFramedEntity.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedEntity.java index 5f179a5..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,25 +20,21 @@ 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, //is pretty important since players might place a lot of them. There were tons and tons of these at Blanketcon. //To that end, most of the state has been crammed into a bitfield. public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity { - protected BlockState renderedState = Blocks.AIR.getDefaultState(); - protected byte bitfield = DEFAULT_BITFIELD; + protected BlockState first_state = Blocks.AIR.getDefaultState(); + protected byte bit_field = SOLIDITY_MASK; - protected static final int SPENT_GLOWSTONE_DUST_MASK = 0b00000001; - protected static final int SPENT_REDSTONE_TORCH_MASK = 0b00000010; - protected static final int SPENT_POPPED_CHORUS_MASK = 0b00000100; - protected static final int EMITS_REDSTONE_MASK = 0b00001000; - protected static final int IS_SOLID_MASK = 0b00010000; - protected static final byte DEFAULT_BITFIELD = IS_SOLID_MASK; //brand-new frames shall be solid - - //Using one-character names is a little brash, like, what if there's a mod that adds crap to the NBT of every - //block entity, and uses short names for the same reason I am (because there are lots and lots of block entities)? - //Kinda doubt it? + protected static final byte LIGHT_MASK = 0b001; + protected static final byte REDSTONE_MASK = 0b010; + protected static final byte SOLIDITY_MASK = 0b100; + protected static final String BLOCKSTATE_KEY = "s"; protected static final String BITFIELD_KEY = "b"; @@ -48,145 +43,116 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity } @Override - public void readNbt(NbtCompound tag) { - super.readNbt(tag); + public void readNbt(NbtCompound nbt) { + super.readNbt(nbt); - BlockState lastRenderedState = renderedState; + 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)) bit_field = nbt.getByte(BITFIELD_KEY); - if(tag.contains("BlockState")) { //2.0.4 and earlier - renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState")); - - if(tag.getBoolean("spentglow")) spentGlowstoneDust(); - if(tag.getBoolean("spentredst")) spentRedstoneTorch(); - if(tag.getBoolean("spentchor")) spentPoppedChorus(); - setEmitsRedstone(tag.getBoolean("emitsredst")); - setSolidity(!tag.contains("solid") || tag.getBoolean("solid")); //default to "true" if it's nonexistent - } else { - renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound(BLOCKSTATE_KEY)); - bitfield = tag.contains(BITFIELD_KEY) ? tag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD; - } - - //Force a chunk remesh on the client if the displayed blockstate has changed - if(world != null && world.isClient && !Objects.equals(lastRenderedState, renderedState)) { + // Force a chunk remesh on the client if the displayed blockstate has changed + if(world != null && world.isClient && !Objects.equals(rendered_state, first_state)) { ReFramed.chunkRerenderProxy.accept(world, pos); } } @Override - public void writeNbt(NbtCompound tag) { - super.writeNbt(tag); + public void writeNbt(NbtCompound nbt) { + super.writeNbt(nbt); - if(renderedState != Blocks.AIR.getDefaultState()) tag.put(BLOCKSTATE_KEY, NbtHelper.fromBlockState(renderedState)); - if(bitfield != DEFAULT_BITFIELD) tag.putByte(BITFIELD_KEY, bitfield); + if(first_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(first_state)); + if(bit_field != SOLIDITY_MASK) nbt.putByte(BITFIELD_KEY, bit_field); } - - 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); //2.0.5 - else if(blockEntityTag.contains("BlockState")) subElement = blockEntityTag.get("BlockState"); //old 2.0.4 items + 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 blockEntityTag = BlockItem.getBlockEntityNbt(stack); - if(blockEntityTag == null) return state; + NbtCompound nbt = BlockItem.getBlockEntityNbt(stack); + if(nbt == null) return state; - if(state.contains(ReFramedInteractionUtil.LIGHT)) { - state = state.with(ReFramedInteractionUtil.LIGHT, - blockEntityTag.getBoolean("spentglow") || //2.0.4 - ((blockEntityTag.contains(BITFIELD_KEY) ? blockEntityTag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD) & SPENT_GLOWSTONE_DUST_MASK) != 0 || //2.0.5 - readStateFromItem(stack).getLuminance() != 0 //glowstone dust wasn't manually added, the block just emits light + if(state.contains(ReFramedBlock.LIGHT)) { + state = state.with(ReFramedBlock.LIGHT, + ((nbt.contains(BITFIELD_KEY) + ? nbt.getByte(BITFIELD_KEY) + : SOLIDITY_MASK) + & LIGHT_MASK) != 0 ); } return state; } - - //RenderAttachmentBlockEntity impl. Note that ThemeableBlockEntity depends on this returning a BlockState object. + @Override - public BlockState getRenderAttachmentData() { - return renderedState; + public BlockState getTheme(int i) { + return first_state; } - - public void setRenderedState(BlockState newState) { - if(!Objects.equals(renderedState, newState)) { - renderedState = 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(); } } - - public boolean hasSpentGlowstoneDust() { - return (bitfield & SPENT_GLOWSTONE_DUST_MASK) != 0; + + /* --------------------------------------------------- ADDONS --------------------------------------------------- */ + public boolean emitsLight() { + return (bit_field & LIGHT_MASK) != 0; } - public void spentGlowstoneDust() { - bitfield |= SPENT_GLOWSTONE_DUST_MASK; + public void toggleLight() { + if (emitsLight()) bit_field &= ~LIGHT_MASK; + else bit_field |= LIGHT_MASK; markDirtyAndDispatch(); } - public boolean hasSpentRedstoneTorch() { - return (bitfield & SPENT_REDSTONE_TORCH_MASK) != 0; - } - - public void spentRedstoneTorch() { - bitfield |= SPENT_REDSTONE_TORCH_MASK; + public void toggleRedstone() { + if (emitsRedstone()) bit_field &= ~REDSTONE_MASK; + else bit_field |= REDSTONE_MASK; + + if(world != null) world.updateNeighbors(pos, getCachedState().getBlock()); markDirtyAndDispatch(); } - - public boolean hasSpentPoppedChorus() { - return (bitfield & SPENT_POPPED_CHORUS_MASK) != 0; - } - - public void spentPoppedChorus() { - bitfield |= SPENT_POPPED_CHORUS_MASK; - markDirtyAndDispatch(); - } - + public boolean emitsRedstone() { - return (bitfield & EMITS_REDSTONE_MASK) != 0; + return (bit_field & REDSTONE_MASK) != 0; } - public void setEmitsRedstone(boolean nextEmitsRedstone) { - boolean currentlyEmitsRedstone = emitsRedstone(); - - if(currentlyEmitsRedstone != nextEmitsRedstone) { - if(currentlyEmitsRedstone) bitfield &= ~EMITS_REDSTONE_MASK; - else bitfield |= EMITS_REDSTONE_MASK; - markDirtyAndDispatch(); - if(world != null) world.updateNeighbors(pos, getCachedState().getBlock()); - } + public void toggleSolidity() { + if (isSolid()) bit_field &= ~SOLIDITY_MASK; + else bit_field |= SOLIDITY_MASK; + + if(world != null) world.setBlockState(pos, getCachedState()); + markDirtyAndDispatch(); } public boolean isSolid() { - return (bitfield & IS_SOLID_MASK) != 0; + return (bit_field & SOLIDITY_MASK) != 0; } - - public void setSolidity(boolean nextSolid) { - boolean currentlySolid = isSolid(); - - if(currentlySolid != nextSolid) { - if(currentlySolid) bitfield &= ~IS_SOLID_MASK; - else bitfield |= IS_SOLID_MASK; - markDirtyAndDispatch(); - if(world != null) world.setBlockState(pos, getCachedState()); //do i need to invalidate any shape caches or something - } - } - - // + @Nullable @Override public Packet toUpdatePacket() { @@ -195,8 +161,6 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity @Override public NbtCompound toInitialChunkDataNbt() { - //TERRIBLE yarn name, this is "getUpdateTag", it's the nbt that will be sent to clients - //and it just calls "writeNbt" return createNbt(); } @@ -208,5 +172,4 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity markDirty(); dispatch(); } - // } 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/ReFramedStairsBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReFramedStairsBlock.java index 7454341..b8ce2ad 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReFramedStairsBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReFramedStairsBlock.java @@ -38,7 +38,7 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M public static final EnumProperty FACING = EnumProperty.of("facing", StairDirection.class); public static final EnumProperty SHAPE = EnumProperty.of("shape", StairShape.class); - private static final List VOXEL_LIST = new ArrayList<>(52); + public static final List VOXEL_LIST = new ArrayList<>(52); public ReFramedStairsBlock(Settings settings) { super(settings); @@ -53,13 +53,16 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M @Override public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) { return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved) - .with(SHAPE, getPlacementShape(state.get(FACING), world, pos)); + .with(SHAPE, getPlacementShape(state.getBlock(), state.get(FACING), world, pos)); } @Nullable @Override // Pretty happy of how clean it is (also got it on first try :) ) public BlockState getPlacementState(ItemPlacementContext ctx) { - BlockState state = super.getPlacementState(ctx); + return getPlacement(super.getPlacementState(ctx), ctx); + } + + public static BlockState getPlacement(BlockState state, ItemPlacementContext ctx) { Direction side = ctx.getSide().getOpposite(); BlockPos block_pos = ctx.getBlockPos(); Vec3d hit_pos = ctx.getHitPos(); @@ -84,20 +87,23 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M ); StairDirection face = StairDirection.getByDirections(side, part_direction); - return state.with(FACING, face).with(SHAPE, getPlacementShape(face, ctx.getWorld(), block_pos)); + return state.with(FACING, face).with(SHAPE, getPlacementShape(state.getBlock(), face, ctx.getWorld(), block_pos)); } @Override public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { super.onStateReplaced(state, world, pos, newState, moved); - //StairsBlock onStateReplaced is Weird! it doesn't delegate to regular block onStateReplaced! if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos); } /* ---------------------------------- DON'T GO FURTHER IF YOU LIKE HAVING EYES ---------------------------------- */ @Override public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return getOutline(state); + } + + public static VoxelShape getOutline(BlockState state) { StairShape shape = state.get(SHAPE); StairDirection direction = state.get(FACING); return switch (shape) { @@ -211,40 +217,40 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M }; } - private static String getNeighborPos(StairDirection face, Direction direction, Boolean reverse, Direction reference, BlockView world, BlockPos pos) { + public static String getNeighborPos(StairDirection face, Direction direction, Boolean reverse, Direction reference, BlockView world, BlockPos pos, Block block) { BlockState block_state = world.getBlockState( pos.offset(reverse ? direction.getOpposite() : direction) ); - if (block_state.getBlock() instanceof ReFramedStairsBlock && block_state.get(FACING).hasDirection(reference)) { + if (block_state.isOf(block) && block_state.get(FACING).hasDirection(reference)) { if (block_state.get(FACING).hasDirection(face.getLeftDirection())) return "left"; else if (block_state.get(FACING).hasDirection(face.getRightDirection())) return "right"; } return ""; } - private static StairShape getPlacementShape(StairDirection face, BlockView world, BlockPos pos) { + public static StairShape getPlacementShape(Block block, StairDirection face, BlockView world, BlockPos pos) { StairShape shape = STRAIGHT; - String sol = getNeighborPos(face, face.getFirstDirection(), true, face.getSecondDirection(), world, pos); + String sol = getNeighborPos(face, face.getFirstDirection(), true, face.getSecondDirection(), world, pos, block); switch (sol) { case "right": return INNER_RIGHT; case "left": return INNER_LEFT; } - sol = getNeighborPos(face, face.getSecondDirection(), true, face.getFirstDirection(), world, pos); + sol = getNeighborPos(face, face.getSecondDirection(), true, face.getFirstDirection(), world, pos, block); switch (sol) { case "right": return INNER_RIGHT; case "left": return INNER_LEFT; } - sol = getNeighborPos(face, face.getFirstDirection(), false, face.getSecondDirection(), world, pos); + sol = getNeighborPos(face, face.getFirstDirection(), false, face.getSecondDirection(), world, pos, block); switch (sol) { case "right" -> shape = FIRST_OUTER_RIGHT; case "left" -> shape = FIRST_OUTER_LEFT; } - sol = getNeighborPos(face, face.getSecondDirection(), false, face.getFirstDirection(), world, pos); + sol = getNeighborPos(face, face.getSecondDirection(), false, face.getFirstDirection(), world, pos, block); switch (sol) { case "right" -> { if (shape.equals(STRAIGHT)) shape = SECOND_OUTER_RIGHT; @@ -261,12 +267,17 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M @Override public MultipartBlockStateSupplier getMultipart() { - Identifier straight_id = ReFramed.id("stairs_special"); - Identifier double_outer_id = ReFramed.id("double_outer_stairs_special"); - Identifier inner_id = ReFramed.id("inner_stairs_special"); - Identifier outer_id = ReFramed.id("outer_stairs_special"); - Identifier outer_side_id = ReFramed.id("outer_side_stairs_special"); - return MultipartBlockStateSupplier.create(this) + return getStairMultipart(this, false); + } + + public static MultipartBlockStateSupplier getStairMultipart(Block block, boolean is_double) { + String prefix = is_double ? "double_" : ""; + Identifier straight_id = ReFramed.id(prefix + "stairs_special"); + Identifier double_outer_id = ReFramed.id(prefix + "outers_stairs_special"); + Identifier inner_id = ReFramed.id(prefix + "inner_stairs_special"); + Identifier outer_id = ReFramed.id(prefix + "outer_stairs_special"); + Identifier outer_side_id = ReFramed.id(prefix + "outer_side_stairs_special"); + return MultipartBlockStateSupplier.create(block) /* STRAIGHT X AXIS */ .with(GBlockstate.when(FACING, DOWN_EAST, SHAPE, STRAIGHT), GBlockstate.variant(straight_id, true, R0, R0)) 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..0c1b0d4 100644 --- a/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java +++ b/src/main/java/fr/adrien1106/reframed/client/ReFramedClient.java @@ -26,72 +26,80 @@ public class ReFramedClient implements ClientModInitializer { //all frames mustn't be on the SOLID layer because they are not opaque! BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0])); - HELPER.addReFramedModel(ReFramed.id("button_special") , HELPER.auto(new Identifier("block/button"))); - HELPER.addReFramedModel(ReFramed.id("button_pressed_special") , HELPER.auto(new Identifier("block/button_pressed"))); - HELPER.addReFramedModel(ReFramed.id("one_candle_special") , HELPER.auto(new Identifier("block/template_candle"))); - HELPER.addReFramedModel(ReFramed.id("two_candles_special") , HELPER.auto(new Identifier("block/template_two_candles"))); - HELPER.addReFramedModel(ReFramed.id("three_candles_special") , HELPER.auto(new Identifier("block/template_three_candles"))); - HELPER.addReFramedModel(ReFramed.id("four_candles_special") , HELPER.auto(new Identifier("block/template_four_candles"))); - HELPER.addReFramedModel(ReFramed.id("carpet_special") , HELPER.auto(new Identifier("block/carpet"))); - HELPER.addReFramedModel(ReFramed.id("cube_special") , HELPER.auto(new Identifier("block/cube"))); - HELPER.addReFramedModel(ReFramed.id("door_bottom_left_special") , HELPER.auto(new Identifier("block/door_bottom_left"))); - HELPER.addReFramedModel(ReFramed.id("door_bottom_right_special") , HELPER.auto(new Identifier("block/door_bottom_right"))); - HELPER.addReFramedModel(ReFramed.id("door_top_left_special") , HELPER.auto(new Identifier("block/door_top_left"))); - HELPER.addReFramedModel(ReFramed.id("door_top_right_special") , HELPER.auto(new Identifier("block/door_top_right"))); - HELPER.addReFramedModel(ReFramed.id("door_bottom_left_open_special"), HELPER.auto(new Identifier("block/door_bottom_left_open"))); - HELPER.addReFramedModel(ReFramed.id("door_bottom_right_open_special"), HELPER.auto(new Identifier("block/door_bottom_right_open"))); //This is why we dont format code as tables kids - HELPER.addReFramedModel(ReFramed.id("door_top_left_open_special") , HELPER.auto(new Identifier("block/door_top_left_open"))); - HELPER.addReFramedModel(ReFramed.id("door_top_right_open_special") , HELPER.auto(new Identifier("block/door_top_right_open"))); - HELPER.addReFramedModel(ReFramed.id("fence_post_special") , HELPER.auto(new Identifier("block/fence_post"))); - HELPER.addReFramedModel(ReFramed.id("fence_gate_special") , HELPER.auto(new Identifier("block/template_fence_gate"))); - HELPER.addReFramedModel(ReFramed.id("fence_gate_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_open"))); - HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall"))); - HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall_open"))); - HELPER.addReFramedModel(ReFramed.id("glass_pane_post_special") , HELPER.auto(new Identifier("block/glass_pane_post"))); - HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_special") , HELPER.auto(new Identifier("block/glass_pane_noside"))); - HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_alt_special"), HELPER.auto(new Identifier("block/glass_pane_noside_alt"))); - 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("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"))); - HELPER.addReFramedModel(ReFramed.id("outer_stairs_special") , HELPER.auto(ReFramed.id("block/outer_stairs"))); - HELPER.addReFramedModel(ReFramed.id("outer_side_stairs_special") , HELPER.auto(ReFramed.id("block/outer_side_stairs"))); - HELPER.addReFramedModel(ReFramed.id("trapdoor_bottom_special") , HELPER.auto(new Identifier("block/template_trapdoor_bottom"))); - HELPER.addReFramedModel(ReFramed.id("trapdoor_top_special") , HELPER.auto(new Identifier("block/template_trapdoor_top"))); - HELPER.addReFramedModel(ReFramed.id("wall_post_special") , HELPER.auto(new Identifier("block/template_wall_post"))); + HELPER.addReFramedModel("button_special" , HELPER.auto(new Identifier("block/button"))); + HELPER.addReFramedModel("button_pressed_special" , HELPER.auto(new Identifier("block/button_pressed"))); + HELPER.addReFramedModel("one_candle_special" , HELPER.auto(new Identifier("block/template_candle"))); + HELPER.addReFramedModel("two_candles_special" , HELPER.auto(new Identifier("block/template_two_candles"))); + HELPER.addReFramedModel("three_candles_special" , HELPER.auto(new Identifier("block/template_three_candles"))); + HELPER.addReFramedModel("four_candles_special" , HELPER.auto(new Identifier("block/template_four_candles"))); + HELPER.addReFramedModel("carpet_special" , HELPER.auto(new Identifier("block/carpet"))); + HELPER.addReFramedModel("cube_special" , HELPER.auto(new Identifier("block/cube"))); + HELPER.addReFramedModel("door_bottom_left_special" , HELPER.auto(new Identifier("block/door_bottom_left"))); + HELPER.addReFramedModel("door_bottom_right_special" , HELPER.auto(new Identifier("block/door_bottom_right"))); + HELPER.addReFramedModel("door_top_left_special" , HELPER.auto(new Identifier("block/door_top_left"))); + HELPER.addReFramedModel("door_top_right_special" , HELPER.auto(new Identifier("block/door_top_right"))); + HELPER.addReFramedModel("door_bottom_left_open_special" , HELPER.auto(new Identifier("block/door_bottom_left_open"))); + HELPER.addReFramedModel("door_bottom_right_open_special" , HELPER.auto(new Identifier("block/door_bottom_right_open"))); //This is why we dont format code as tables kids + HELPER.addReFramedModel("door_top_left_open_special" , HELPER.auto(new Identifier("block/door_top_left_open"))); + HELPER.addReFramedModel("door_top_right_open_special" , HELPER.auto(new Identifier("block/door_top_right_open"))); + HELPER.addReFramedModel("fence_post_special" , HELPER.auto(new Identifier("block/fence_post"))); + HELPER.addReFramedModel("fence_gate_special" , HELPER.auto(new Identifier("block/template_fence_gate"))); + HELPER.addReFramedModel("fence_gate_open_special" , HELPER.auto(new Identifier("block/template_fence_gate_open"))); + HELPER.addReFramedModel("fence_gate_wall_special" , HELPER.auto(new Identifier("block/template_fence_gate_wall"))); + HELPER.addReFramedModel("fence_gate_wall_open_special" , HELPER.auto(new Identifier("block/template_fence_gate_wall_open"))); + HELPER.addReFramedModel("glass_pane_post_special" , HELPER.auto(new Identifier("block/glass_pane_post"))); + HELPER.addReFramedModel("glass_pane_noside_special" , HELPER.auto(new Identifier("block/glass_pane_noside"))); + HELPER.addReFramedModel("glass_pane_noside_alt_special" , HELPER.auto(new Identifier("block/glass_pane_noside_alt"))); + HELPER.addReFramedModel("pressure_plate_up_special" , HELPER.auto(new Identifier("block/pressure_plate_up"))); + HELPER.addReFramedModel("pressure_plate_down_special" , HELPER.auto(new Identifier("block/pressure_plate_down"))); + HELPER.addReFramedModel("slab_special" , HELPER.auto(new Identifier("block/slab"))); + HELPER.addReFramedModel("double_slab_special" , HELPER.autoDouble(new Identifier("block/slab"), new Identifier("block/slab_top"))); + HELPER.addReFramedModel("stairs_special" , HELPER.auto(ReFramed.id("block/stairs"))); + HELPER.addReFramedModel("outers_stairs_special" , HELPER.auto(ReFramed.id("block/double_outer_stairs"))); + HELPER.addReFramedModel("inner_stairs_special" , HELPER.auto(ReFramed.id("block/inner_stairs"))); + HELPER.addReFramedModel("outer_stairs_special" , HELPER.auto(ReFramed.id("block/outer_stairs"))); + HELPER.addReFramedModel("outer_side_stairs_special" , HELPER.auto(ReFramed.id("block/outer_side_stairs"))); + HELPER.addReFramedModel("double_stairs_special" , HELPER.autoDouble(ReFramed.id("block/stairs"), ReFramed.id("block/stairs_complement"))); + HELPER.addReFramedModel("double_outers_stairs_special" , HELPER.autoDouble(ReFramed.id("block/double_outer_stairs"), ReFramed.id("block/double_outer_stairs_complement"))); + HELPER.addReFramedModel("double_inner_stairs_special" , HELPER.autoDouble(ReFramed.id("block/inner_stairs"), ReFramed.id("block/inner_stairs_complement"))); + HELPER.addReFramedModel("double_outer_stairs_special" , HELPER.autoDouble(ReFramed.id("block/outer_stairs"), ReFramed.id("block/outer_stairs_complement"))); + HELPER.addReFramedModel("double_outer_side_stairs_special", HELPER.autoDouble(ReFramed.id("block/outer_side_stairs"), ReFramed.id("block/outer_side_stairs_complement"))); + HELPER.addReFramedModel("trapdoor_bottom_special" , HELPER.auto(new Identifier("block/template_trapdoor_bottom"))); + HELPER.addReFramedModel("trapdoor_top_special" , HELPER.auto(new Identifier("block/template_trapdoor_top"))); + HELPER.addReFramedModel("wall_post_special" , HELPER.auto(new Identifier("block/template_wall_post"))); //vanilla style models (using "special-sprite replacement" method) - HELPER.addReFramedModel(ReFramed.id("lever_special") , HELPER.json(ReFramed.id("block/lever"))); - HELPER.addReFramedModel(ReFramed.id("trapdoor_open_special") , HELPER.json(ReFramed.id("block/trapdoor_open"))); - HELPER.addReFramedModel(ReFramed.id("lever_on_special") , HELPER.json(ReFramed.id("block/lever_on"))); + HELPER.addReFramedModel("lever_special" , HELPER.json(ReFramed.id("block/lever"))); + HELPER.addReFramedModel("trapdoor_open_special" , HELPER.json(ReFramed.id("block/trapdoor_open"))); + HELPER.addReFramedModel("lever_on_special" , HELPER.json(ReFramed.id("block/lever_on"))); //these next five only exist because AutoRetexturedModels don't seem to rotate their textures the right way when rotated from a multipart blockstate - HELPER.addReFramedModel(ReFramed.id("fence_side_special") , HELPER.json(ReFramed.id("block/fence_side"))); - HELPER.addReFramedModel(ReFramed.id("glass_pane_side_special") , HELPER.json(ReFramed.id("block/glass_pane_side"))); - HELPER.addReFramedModel(ReFramed.id("glass_pane_side_alt_special") , HELPER.json(ReFramed.id("block/glass_pane_side_alt"))); - HELPER.addReFramedModel(ReFramed.id("wall_side_special") , HELPER.json(ReFramed.id("block/wall_side"))); - HELPER.addReFramedModel(ReFramed.id("wall_side_tall_special") , HELPER.json(ReFramed.id("block/wall_side_tall"))); + HELPER.addReFramedModel("fence_side_special" , HELPER.json(ReFramed.id("block/fence_side"))); + HELPER.addReFramedModel("glass_pane_side_special" , HELPER.json(ReFramed.id("block/glass_pane_side"))); + HELPER.addReFramedModel("glass_pane_side_alt_special" , HELPER.json(ReFramed.id("block/glass_pane_side_alt"))); + HELPER.addReFramedModel("wall_side_special" , HELPER.json(ReFramed.id("block/wall_side"))); + HELPER.addReFramedModel("wall_side_tall_special" , HELPER.json(ReFramed.id("block/wall_side_tall"))); //item only models - HELPER.addReFramedModel(ReFramed.id("button_inventory_special") , HELPER.auto(new Identifier("block/button_inventory"))); - HELPER.addReFramedModel(ReFramed.id("fence_inventory_special") , HELPER.auto(new Identifier("block/fence_inventory"))); - HELPER.addReFramedModel(ReFramed.id("fence_post_inventory_special") , HELPER.auto(ReFramed.id("block/fence_post_inventory"))); - HELPER.addReFramedModel(ReFramed.id("wall_inventory_special") , HELPER.auto(new Identifier("block/wall_inventory"))); - + HELPER.addReFramedModel("button_inventory_special" , HELPER.auto(new Identifier("block/button_inventory"))); + HELPER.addReFramedModel("fence_inventory_special" , HELPER.auto(new Identifier("block/fence_inventory"))); + HELPER.addReFramedModel("fence_post_inventory_special" , HELPER.auto(ReFramed.id("block/fence_post_inventory"))); + HELPER.addReFramedModel("wall_inventory_special" , HELPER.auto(new Identifier("block/wall_inventory"))); + //item model assignments (in lieu of models/item/___.json) - HELPER.assignItemModel(ReFramed.id("button_inventory_special") , ReFramed.BUTTON); - HELPER.assignItemModel(ReFramed.id("carpet_special") , ReFramed.CARPET); - HELPER.assignItemModel(ReFramed.id("cube_special") , ReFramed.CUBE); - HELPER.assignItemModel(ReFramed.id("fence_inventory_special") , ReFramed.FENCE); - HELPER.assignItemModel(ReFramed.id("fence_gate_special") , ReFramed.FENCE_GATE); - HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.IRON_TRAPDOOR); - 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("stairs_special") , ReFramed.STAIRS); - HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.TRAPDOOR); - HELPER.assignItemModel(ReFramed.id("wall_inventory_special") , ReFramed.WALL); + HELPER.assignItemModel("button_inventory_special" , ReFramed.BUTTON); + HELPER.assignItemModel("carpet_special" , ReFramed.CARPET); + HELPER.assignItemModel("cube_special" , ReFramed.CUBE); + HELPER.assignItemModel("fence_inventory_special" , ReFramed.FENCE); + HELPER.assignItemModel("fence_gate_special" , ReFramed.FENCE_GATE); + HELPER.assignItemModel("trapdoor_bottom_special" , ReFramed.IRON_TRAPDOOR); + HELPER.assignItemModel("fence_post_inventory_special" , ReFramed.POST); + HELPER.assignItemModel("pressure_plate_up_special" , ReFramed.PRESSURE_PLATE); + HELPER.assignItemModel("slab_special" , ReFramed.SLAB); + HELPER.assignItemModel("double_slab_special" , ReFramed.DOUBLE_SLAB); + HELPER.assignItemModel("stairs_special" , ReFramed.STAIRS); + HELPER.assignItemModel("double_stairs_special" , ReFramed.DOUBLE_STAIRS); + HELPER.assignItemModel("trapdoor_bottom_special" , ReFramed.TRAPDOOR); + HELPER.assignItemModel("wall_inventory_special" , ReFramed.WALL); } private void privateInit() { diff --git a/src/main/java/fr/adrien1106/reframed/client/ReFramedClientHelper.java b/src/main/java/fr/adrien1106/reframed/client/ReFramedClientHelper.java index 87c7591..caa2c40 100644 --- a/src/main/java/fr/adrien1106/reframed/client/ReFramedClientHelper.java +++ b/src/main/java/fr/adrien1106/reframed/client/ReFramedClientHelper.java @@ -1,8 +1,11 @@ package fr.adrien1106.reframed.client; -import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager; +import fr.adrien1106.reframed.ReFramed; 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.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; @@ -23,20 +26,25 @@ public class ReFramedClientHelper { private final ReFramedModelProvider prov; - public UnbakedModel auto(Identifier parent) { + + public UnbakedRetexturedModel auto(Identifier parent) { return new UnbakedAutoRetexturedModel(parent); } - public UnbakedModel json(Identifier parent) { + public UnbakedRetexturedModel json(Identifier parent) { return new UnbakedJsonRetexturedModel(parent); } - public void addReFramedModel(Identifier id, UnbakedModel unbaked) { - prov.addReFramedModel(id, unbaked); + public UnbakedModel autoDouble(Identifier first, Identifier second) { + return new UnbakedDoubleRetexturedModel(auto(first), auto(second)); } - public void assignItemModel(Identifier model_id, ItemConvertible... item_convertibles) { - prov.assignItemModel(model_id, item_convertibles); + public void addReFramedModel(String id, UnbakedModel unbaked) { + prov.addReFramedModel(ReFramed.id(id), unbaked); + } + + public void assignItemModel(String id, ItemConvertible... item_convertibles) { + prov.assignItemModel(ReFramed.id(id), item_convertibles); } public CamoAppearanceManager getCamoApperanceManager(Function spriteLookup) { 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 new file mode 100644 index 0000000..a8bf841 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/client/model/DoubleRetexturingBakedModel.java @@ -0,0 +1,51 @@ +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; + +@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; + } + + @Override + public boolean isVanillaAdapter() { + return false; + } + + @Override + public Sprite getParticleSprite() { + return model_1.getParticleSprite(); + } + + @Override + public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) {} + + @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/DynamicBakedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/DynamicBakedModel.java index a6f0cf5..2f4d109 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/DynamicBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/DynamicBakedModel.java @@ -6,5 +6,5 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.BlockRenderView; public interface DynamicBakedModel { - BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos); + BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos, int theme_index); } 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 be809a6..548bdd9 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java @@ -1,12 +1,15 @@ 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; 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; @@ -29,30 +32,44 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; public abstract class RetexturingBakedModel extends ForwardingBakedModel { - public RetexturingBakedModel(BakedModel baseModel, CamoAppearanceManager tam, 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.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 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 abstract Mesh getBaseMesh(BlockState state); + 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); + } + + protected abstract Mesh convertModel(BlockState state); @Override public boolean isVanillaAdapter() { @@ -61,20 +78,20 @@ 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.getBlockEntityRenderData(pos) instanceof BlockState s) ? 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; - CamoAppearance camo = tam.getCamoAppearance(world, theme, pos); + CamoAppearance camo = tam.getCamoAppearance(world, theme, pos, theme_index); long seed = theme.getRenderingSeed(pos); int model_id = 0; if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed); @@ -105,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); + BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index); if(!theme.isAir()) { - nbtAppearance = tam.getCamoAppearance(null, theme, null); + nbtAppearance = tam.getCamoAppearance(null, theme, null, theme_index); 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) { @@ -132,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 { @@ -161,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(); @@ -175,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 216da7c..dba3f74 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedAutoRetexturedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedAutoRetexturedModel.java @@ -12,7 +12,6 @@ import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.Baker; import net.minecraft.client.render.model.ModelBakeSettings; -import net.minecraft.client.render.model.UnbakedModel; import net.minecraft.client.texture.Sprite; import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.util.Identifier; @@ -20,50 +19,27 @@ import net.minecraft.util.math.Direction; import net.minecraft.util.math.random.Random; import org.jetbrains.annotations.Nullable; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.function.Function; -public class UnbakedAutoRetexturedModel implements UnbakedModel { +public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel { + public UnbakedAutoRetexturedModel(Identifier parent) { - this.parent = parent; - } - - protected final Identifier parent; - protected BlockState itemModelState = Blocks.AIR.getDefaultState(); - protected boolean ao = true; - - @Override - public Collection getModelDependencies() { - return Collections.singletonList(parent); - } - - @Override - public void setParents(Function function) { - function.apply(parent).setParents(function); + super(parent); + item_state = Blocks.AIR.getDefaultState(); } @Nullable @Override - public BakedModel bake(Baker baker, Function spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) { + public BakedModel bake(Baker baker, Function texture_getter, ModelBakeSettings bake_settings, Identifier identifier) { return new RetexturingBakedModel( - baker.bake(parent, modelBakeSettings), - ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup), - modelBakeSettings, - itemModelState, + baker.bake(parent, bake_settings), + ReFramedClient.HELPER.getCamoApperanceManager(texture_getter), + theme_index, + bake_settings, + item_state, ao ) { - final ConcurrentMap jsonToMesh = new ConcurrentHashMap<>(); - - @Override - protected Mesh getBaseMesh(BlockState state) { - //Convert models to retexturable Meshes lazily, the first time we encounter each blockstate - return jsonToMesh.computeIfAbsent(state, this::convertModel); - } - - private Mesh convertModel(BlockState state) { + protected Mesh convertModel(BlockState state) { Renderer r = ReFramedClient.HELPER.getFabricRenderer(); MeshBuilder builder = r.meshBuilder(); QuadEmitter emitter = builder.getEmitter(); diff --git a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedDoubleRetexturedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedDoubleRetexturedModel.java new file mode 100644 index 0000000..70d53d6 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedDoubleRetexturedModel.java @@ -0,0 +1,48 @@ +package fr.adrien1106.reframed.client.model; + +import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.Baker; +import net.minecraft.client.render.model.ModelBakeSettings; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; + +public class UnbakedDoubleRetexturedModel implements UnbakedModel { + + protected final UnbakedModel model_1; + protected final 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(((List) model_1.getModelDependencies()).get(0), ((List) model_2.getModelDependencies()).get(0)); + } + + @Override + public void setParents(Function function) { + model_1.setParents(function); + model_2.setParents(function); + } + + @Nullable + @Override + public BakedModel bake(Baker baker, Function texture_getter, ModelBakeSettings model_bake_settings, Identifier identifier) { + return new DoubleRetexturingBakedModel( + (ForwardingBakedModel) model_1.bake(baker, texture_getter, model_bake_settings, identifier), + (ForwardingBakedModel) model_2.bake(baker, texture_getter, model_bake_settings, identifier) + ); + } +} 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 b984c3d..914ce00 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/UnbakedJsonRetexturedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedJsonRetexturedModel.java @@ -8,7 +8,10 @@ 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.minecraft.block.BlockState; -import net.minecraft.client.render.model.*; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedQuad; +import net.minecraft.client.render.model.Baker; +import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.texture.Sprite; import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.screen.PlayerScreenHandler; @@ -17,61 +20,34 @@ import net.minecraft.util.math.Direction; import net.minecraft.util.math.random.Random; import org.jetbrains.annotations.Nullable; -import java.util.Collection; -import java.util.Collections; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.function.Function; -public class UnbakedJsonRetexturedModel implements UnbakedModel { +public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel { public UnbakedJsonRetexturedModel(Identifier parent) { - this.parent = parent; - } - - protected final Identifier parent; - protected BlockState itemModelState; - protected boolean ao = true; - - @Override - public Collection getModelDependencies() { - return Collections.singletonList(parent); - } - - @Override - public void setParents(Function function) { - function.apply(parent).setParents(function); + super(parent); } @Nullable @Override - public BakedModel bake(Baker baker, Function spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) { - Direction[] DIRECTIONS = RetexturingBakedModel.DIRECTIONS; + public BakedModel bake(Baker baker, Function spriteLookup, ModelBakeSettings bake_settings, Identifier identifier) { + 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 + " !"); } - BakedModel model = baker.bake(parent, modelBakeSettings); - return new RetexturingBakedModel( - model, + baker.bake(parent, bake_settings), ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup), - modelBakeSettings, - itemModelState, + theme_index, + bake_settings, + item_state, ao ) { - final ConcurrentMap jsonToMesh = new ConcurrentHashMap<>(); - - @Override - 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); - } - - private Mesh convertModel(BlockState state) { + protected Mesh convertModel(BlockState state) { Renderer r = ReFramedClient.HELPER.getFabricRenderer(); MeshBuilder builder = r.meshBuilder(); QuadEmitter emitter = builder.getEmitter(); @@ -84,9 +60,9 @@ public class UnbakedJsonRetexturedModel implements UnbakedModel { 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 new file mode 100644 index 0000000..8345833 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/client/model/UnbakedRetexturedModel.java @@ -0,0 +1,36 @@ +package fr.adrien1106.reframed.client.model; + +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.util.Identifier; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; + +public abstract class UnbakedRetexturedModel implements UnbakedModel { + + protected final Identifier parent; + + protected int theme_index = 1; + protected BlockState item_state; + protected boolean ao = true; + + public UnbakedRetexturedModel(Identifier parent) { + this.parent = parent; + } + + public void setThemeIndex(int theme_index) { + this.theme_index = theme_index; + } + + @Override + public Collection getModelDependencies() { + return List.of(parent); + } + + @Override + public void setParents(Function function) { + function.apply(parent).setParents(function); + } +} 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..949545b 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,37 +44,42 @@ 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) { + public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index) { BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state); // add support for connected textures and more generally any compatible models injected so that they return baked quads if (model instanceof DynamicBakedModel dynamic_model) { - return computeAppearance(dynamic_model.computeQuads(world, state, pos), state); + return computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state); } return appearanceCache.computeIfAbsent(state, block_state -> computeAppearance(model, block_state)); } @@ -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..136e338 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, DOUBLE_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/BlockMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/BlockMixin.java deleted file mode 100644 index ee3fe25..0000000 --- a/src/main/java/fr/adrien1106/reframed/mixin/BlockMixin.java +++ /dev/null @@ -1,46 +0,0 @@ -package fr.adrien1106.reframed.mixin; - -import com.llamalad7.mixinextras.sugar.Local; -import fr.adrien1106.reframed.block.ReFramedEntity; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.util.function.BooleanBiFunction; -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 org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.*; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(Block.class) -public class BlockMixin { - - @Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isOpaque()Z")) - private static boolean isNeighborCamoOpaque(BlockState state, @Local(argsOnly = true) BlockView world, @Local(ordinal = 1, argsOnly = true) BlockPos pos) { - BlockEntity block_entity = world.getBlockEntity(pos); - if (!(block_entity instanceof ReFramedEntity frame_entity)) return state.isOpaque(); - return frame_entity.getThemeState().isOpaque(); - } - - @Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isSideInvisible(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/Direction;)Z")) - private static boolean isCamoInvisible(BlockState state, BlockState other_state, Direction direction, @Local(argsOnly = true) BlockView world, @Local(ordinal = 0, argsOnly = true) BlockPos pos, @Local(ordinal = 1, argsOnly = true) BlockPos other_pos) { - if (world.getBlockEntity(other_pos) instanceof ReFramedEntity entity) other_state = entity.getThemeState(); - if (world.getBlockEntity(pos) instanceof ReFramedEntity entity) state = entity.getThemeState(); - return state.isSideInvisible(other_state, direction); - } - - @Inject(method = "shouldDrawSide", at = @At(value = "RETURN", ordinal = 0), cancellable = true) - private static void shouldDrawGlassCamoSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, CallbackInfoReturnable cir, @Local(ordinal = 1) BlockState neighbor) { - if (!(world.getBlockEntity(pos) instanceof ReFramedEntity) && !(world.getBlockEntity(other_pos) instanceof ReFramedEntity)) return; - VoxelShape voxel_block = state.getCullingFace(world, pos, side); - if (voxel_block.isEmpty()) { - cir.setReturnValue(true); - return; - } - VoxelShape voxel_neighbor = neighbor.getCullingFace(world, pos, side.getOpposite()); - cir.setReturnValue(VoxelShapes.matchesAnywhere(voxel_block, voxel_neighbor, BooleanBiFunction.ONLY_FIRST)); - } -} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java index b622908..89c699f 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java @@ -6,6 +6,7 @@ import earth.terrarium.athena.api.client.models.AthenaBlockModel; import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.model.DynamicBakedModel; import fr.adrien1106.reframed.compat.RebakedAthenaModel; +import fr.adrien1106.reframed.util.ThemeableBlockEntity; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; @@ -39,35 +40,41 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM * @return - the rebakedmodel containing the computed quads */ @Override - public BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos) { - if (level == null || pos == null) return this; - + public BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos, int theme_index) { Map> face_quads = new HashMap<>(); - Renderer r = ReFramedClient.HELPER.getFabricRenderer(); QuadEmitter emitter = r.meshBuilder().getEmitter(); WrappedGetter getter = new WrappedGetter(level); Arrays.stream(Direction.values()).forEach(direction -> { face_quads.put(direction, new ArrayList<>()); - model.getQuads(getter, state, pos, direction).forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> { - Sprite texture = textures.get(sprite.sprite()); - if (texture == null) return quads; - emitter.square(direction, sprite.left(), sprite.bottom(), sprite.right(), sprite.top(), sprite.depth()); - int flag = MutableQuadView.BAKE_LOCK_UV; + (level == null || pos == null + ? model.getDefaultQuads(direction).get(direction) + : model.getQuads( + getter, + level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity + ? framed_entity.getTheme(theme_index) + : state, pos, direction) + ) + .forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> { + Sprite texture = textures.get(sprite.sprite()); + if (texture == null) return quads; + emitter.square(direction, sprite.left(), sprite.bottom(), sprite.right(), sprite.top(), sprite.depth()); - switch (sprite.rotation()) { - case CLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_90; - case CLOCKWISE_180 -> flag |= MutableQuadView.BAKE_ROTATE_180; - case COUNTERCLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_270; - } + int flag = MutableQuadView.BAKE_LOCK_UV; - emitter.spriteBake(texture, flag); - emitter.color(-1, -1, -1, -1); - quads.add(emitter.toBakedQuad(texture)); - return quads; - })); + switch (sprite.rotation()) { + case CLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_90; + case CLOCKWISE_180 -> flag |= MutableQuadView.BAKE_ROTATE_180; + case COUNTERCLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_270; + } + + emitter.spriteBake(texture, flag); + emitter.color(-1, -1, -1, -1); + quads.add(emitter.toBakedQuad(texture)); + return quads; + })); }); return new RebakedAthenaModel(face_quads); diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaConnectedBlockModelMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaConnectedBlockModelMixin.java index 54742db..efc633b 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaConnectedBlockModelMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaConnectedBlockModelMixin.java @@ -3,7 +3,7 @@ package fr.adrien1106.reframed.mixin.compat; import earth.terrarium.athena.api.client.utils.AppearanceAndTintGetter; import earth.terrarium.athena.api.client.utils.CtmUtils; import earth.terrarium.athena.impl.client.models.ConnectedBlockModel; -import fr.adrien1106.reframed.block.ReFramedEntity; +import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.minecraft.block.BlockState; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; @@ -22,8 +22,8 @@ public class AthenaConnectedBlockModelMixin { "Lnet/minecraft/util/math/Direction;)Z")) private boolean checkForCull(AppearanceAndTintGetter level, BlockState state, BlockPos pos, Direction direction) { // Always get all the textures unless its another block then use default behaviour - if (level.getBlockEntity(pos) instanceof ReFramedEntity - || level.getBlockEntity(pos.offset(direction)) instanceof ReFramedEntity) + if (level.getBlockEntity(pos) instanceof ThemeableBlockEntity + || level.getBlockEntity(pos.offset(direction)) instanceof ThemeableBlockEntity) return false; return CtmUtils.checkRelative(level, state, pos, direction); } 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 24632f3..73e1ea7 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java @@ -1,45 +1,30 @@ package fr.adrien1106.reframed.mixin.compat; +import com.llamalad7.mixinextras.sugar.Local; import earth.terrarium.athena.api.client.fabric.WrappedGetter; -import fr.adrien1106.reframed.block.ReFramedEntity; +import fr.adrien1106.reframed.util.ThemeableBlockEntity; import net.minecraft.block.BlockState; import net.minecraft.util.math.BlockPos; import net.minecraft.world.BlockRenderView; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(WrappedGetter.class) public class AthenaWrappedGetterMixin { - @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 ReFramedEntity framed_entity)) return; - cir.setReturnValue(framed_entity.getThemeState()); - } - - @Redirect(method = "getAppearance(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)" + - "Lnet/minecraft/block/BlockState;", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/BlockRenderView;" + - "getBlockState(Lnet/minecraft/util/math/BlockPos;)" + - "Lnet/minecraft/block/BlockState;")) - private BlockState appearanceCamoState(BlockRenderView world, BlockPos pos) { - if (world.getBlockEntity(pos) instanceof ReFramedEntity framed_entity) return framed_entity.getThemeState(); - return world.getBlockState(pos); - } - @Redirect(method = "query", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/BlockRenderView;" + "getBlockState(Lnet/minecraft/util/math/BlockPos;)" + "Lnet/minecraft/block/BlockState;")) - private BlockState queryCamoState(BlockRenderView world, BlockPos pos) { - if (world.getBlockEntity(pos) instanceof ReFramedEntity framed_entity) return framed_entity.getThemeState(); + private BlockState queryCamoState(BlockRenderView world, BlockPos pos, @Local(argsOnly = true) BlockState reference_state) { + // get Any that will connect or return any other (/!\ isOf is an uncertain check) + if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) + return framed_entity.getThemes() + .stream() + .filter(state -> reference_state.isOf(state.getBlock())) + .findAny() + .orElse(framed_entity.getTheme(1)); 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 d541d1e..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.getThemeState(); + 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 8e7e6ac..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.getThemeState(); - 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 ec86a44..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.getThemeState(); - 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 50% rename from src/main/java/fr/adrien1106/reframed/mixin/BlockRenderInfoMixin.java rename to src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderInfoMixin.java index 0e73ed7..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.ReFramedEntity; +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 ReFramedEntity frame_entity)) return state; - return frame_entity.getThemeState(); + if (!(block_entity instanceof ThemeableBlockEntity frame_entity)) return state; + 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 ReFramedEntity)) return; - cir.setReturnValue(Block.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face))); + if (!(block_entity instanceof ThemeableBlockEntity)) return; + 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 14d3f9a..7710155 100644 --- a/src/main/java/fr/adrien1106/reframed/util/ReFramedInteractionUtil.java +++ b/src/main/java/fr/adrien1106/reframed/util/ReFramedInteractionUtil.java @@ -1,19 +1,10 @@ package fr.adrien1106.reframed.util; import fr.adrien1106.reframed.block.ReFramedEntity; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.Block; -import net.minecraft.block.BlockEntityProvider; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.block.ShapeContext; +import net.minecraft.block.*; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemPlacementContext; -import net.minecraft.item.ItemStack; -import net.minecraft.item.ItemUsageContext; -import net.minecraft.item.Items; +import net.minecraft.item.*; import net.minecraft.nbt.NbtCompound; import net.minecraft.sound.BlockSoundGroup; import net.minecraft.sound.SoundCategory; @@ -37,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); } @@ -46,79 +39,92 @@ 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 be)) return ActionResult.PASS; + if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) return ActionResult.PASS; if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS; ItemStack held = player.getStackInHand(hand); ReframedInteractible ext = state.getBlock() instanceof ReframedInteractible e ? e : ReframedInteractible.Default.INSTANCE; - //Glowstone - if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST && !state.get(LIGHT) && !be.hasSpentGlowstoneDust()) { - world.setBlockState(pos, state.with(LIGHT, true)); - be.spentGlowstoneDust(); + // 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()) held.decrement(1); + 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; } - //Redstone - if(held.getItem() == Blocks.REDSTONE_TORCH.asItem() && - !be.emitsRedstone() && - !be.hasSpentRedstoneTorch() && - ext.canAddRedstoneEmission(state, world, pos) - ) { - be.setEmitsRedstone(true); - be.spentRedstoneTorch(); + // 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()) held.decrement(1); + 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; } - //Popped chorus fruit - if(held.getItem() == Items.POPPED_CHORUS_FRUIT && - be.isSolid() && - !be.hasSpentPoppedChorus() && - ext.canRemoveCollision(state, world, pos) - ) { - be.setSolidity(false); - be.spentPoppedChorus(); + // 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()) held.decrement(1); + 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; } - //Changing the theme - if(held.getItem() instanceof BlockItem bi && be.getThemeState().getBlock() == Blocks.AIR) { - Block block = bi.getBlock(); + // 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)) { - if(!world.isClient) be.setRenderedState(placementState); - - world.setBlockState(pos, state.with(LIGHT, be.hasSpentGlowstoneDust() || (placementState.getLuminance() != 0))); - be.setEmitsRedstone(be.hasSpentRedstoneTorch() || placementState.getWeakRedstonePower(world, pos, Direction.NORTH) != 0); + // TODO FOR SECOND + if(!world.isClient) block_entity.setTheme(placementState, 1); + + // check for default light emission + if (placementState.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 redstone emission + if (placementState.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, placementState.getSoundGroup().getPlaceSound(), SoundCategory.BLOCKS, 1f, 1.1f); @@ -128,27 +134,30 @@ public class ReFramedInteractionUtil { return ActionResult.PASS; } - - //Maybe an odd spot to put this logic but it's consistent w/ vanilla chests, barrels, etc + + @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 && + world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity && world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS) ) { DefaultedList drops = DefaultedList.of(); + + BlockState theme = frame_entity.getTheme(1); + if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock())); - //TODO: remember the specific ItemStack - Block theme = frame.getThemeState().getBlock(); - if(theme != Blocks.AIR) drops.add(new ItemStack(theme)); - - if(frame.hasSpentRedstoneTorch()) drops.add(new ItemStack(Items.REDSTONE_TORCH)); - if(frame.hasSpentGlowstoneDust()) drops.add(new ItemStack(Items.GLOWSTONE_DUST)); - if(frame.hasSpentPoppedChorus()) drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT)); + if(frame_entity.emitsRedstone() && theme.getWeakRedstonePower(world, pos, Direction.NORTH) == 0) + drops.add(new ItemStack(Items.REDSTONE_TORCH)); + if(frame_entity.emitsLight() && theme.getLuminance() == 0) + drops.add(new ItemStack(Items.GLOWSTONE_DUST)); + if(!frame_entity.isSolid() && theme.isSolid()) + drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT)); 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. @@ -157,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 92e76e1..2213826 100644 --- a/src/main/java/fr/adrien1106/reframed/util/ThemeableBlockEntity.java +++ b/src/main/java/fr/adrien1106/reframed/util/ThemeableBlockEntity.java @@ -1,10 +1,13 @@ package fr.adrien1106.reframed.util; -import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity; import net.minecraft.block.BlockState; -public interface ThemeableBlockEntity extends RenderAttachmentBlockEntity { - default BlockState getThemeState() { - return (BlockState) getRenderAttachmentData(); - } +import java.util.List; + +public interface ThemeableBlockEntity { + BlockState getTheme(int i); + + void setTheme(BlockState state, int i); + + 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/assets/reframed/models/block/double_outer_stairs_complement.json b/src/main/resources/assets/reframed/models/block/double_outer_stairs_complement.json new file mode 100644 index 0000000..bb824e7 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/double_outer_stairs_complement.json @@ -0,0 +1,51 @@ +{ + "credit": "Made with Blockbench", + "parent": "minecraft:block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [8, 8, 0], + "to": [16, 16, 8], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "north"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "east"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top", "cullface": "up"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#bottom"} + } + }, + { + "from": [0, 0, 0], + "to": [8, 8, 8], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#side", "cullface": "north"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#side", "cullface": "west"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#bottom", "cullface": "down"} + } + }, + { + "from": [0, 8, 0], + "to": [8, 16, 8], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top", "cullface": "up"} + } + }, + { + "from": [0, 8, 8], + "to": [8, 16, 16], + "faces": { + "east": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "south"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top", "cullface": "up"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#bottom"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/inner_stairs_complement.json b/src/main/resources/assets/reframed/models/block/inner_stairs_complement.json new file mode 100644 index 0000000..c77e977 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/inner_stairs_complement.json @@ -0,0 +1,20 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [0, 8, 0], + "to": [8, 16, 8], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top", "cullface": "up"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#bottom"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/outer_side_stairs_complement.json b/src/main/resources/assets/reframed/models/block/outer_side_stairs_complement.json new file mode 100644 index 0000000..b0afde7 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/outer_side_stairs_complement.json @@ -0,0 +1,48 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [0, 8, 0], + "to": [8, 16, 8], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top", "cullface": "up"} + } + }, + { + "from": [0, 8, 8], + "to": [8, 16, 16], + "faces": { + "east": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "south"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top", "cullface": "up"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#bottom"} + } + }, + { + "from": [0, 0, 0], + "to": [8, 8, 8], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#side", "cullface": "north"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#side", "cullface": "west"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#bottom", "cullface": "down"} + } + } + ], + "groups": [ + { + "name": "outer_stairs", + "origin": [8, 8, 8], + "color": 0, + "children": [0, 1, 2] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/outer_stairs_complement.json b/src/main/resources/assets/reframed/models/block/outer_stairs_complement.json new file mode 100644 index 0000000..7dd023e --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/outer_stairs_complement.json @@ -0,0 +1,40 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [0, 8, 8], + "to": [8, 16, 16], + "faces": { + "east": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "south"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top", "cullface": "up"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#bottom"} + } + }, + { + "from": [8, 8, 0], + "to": [16, 16, 8], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "north"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "east"}, + "south": {"uv": [9, 0, 16, 8], "texture": "#side"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top", "cullface": "up"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#bottom"} + } + }, + { + "from": [0, 8, 0], + "to": [8, 16, 8], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top", "cullface": "up"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#bottom"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/reframed/models/block/stairs_complement.json b/src/main/resources/assets/reframed/models/block/stairs_complement.json new file mode 100644 index 0000000..d3bfb33 --- /dev/null +++ b/src/main/resources/assets/reframed/models/block/stairs_complement.json @@ -0,0 +1,35 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "particle": "#side" + }, + "elements": [ + { + "from": [0, 8, 0], + "to": [8, 16, 16], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"}, + "east": {"uv": [0, 0, 16, 8], "texture": "#side"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "south"}, + "west": {"uv": [0, 8, 16, 16], "texture": "#side", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 16], "texture": "#top", "cullface": "up"}, + "down": {"uv": [0, 0, 8, 16], "texture": "#bottom"} + } + } + ], + "display": { + "thirdperson_lefthand": { + "rotation": [75, -135, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "gui": { + "rotation": [30, 135, 0], + "scale": [0.625, 0.625, 0.625] + }, + "head": { + "rotation": [0, -90, 0] + } + } +} \ 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