Compare commits

..

7 Commits

57 changed files with 2213 additions and 238 deletions

View File

@ -21,11 +21,8 @@ or based on preferences add the person(s) to the project
### What Shapes are planed to be added
Currently, the list of shapes to be added is pretty simple as the mod is still under development:
- Fence
- Button
- Pressure Plate
- Carpet (maybe redundant with Layer)
- Post
- Half Slab (maybe redundant with Layer)
- Slabs Stair (a stair with one end being of a second theme, might be done in multiple blocks)

View File

@ -84,6 +84,9 @@ repositories {
includeGroup "maven.modrinth"
}
}
maven {
url = "https://jitpack.io"
}
mavenCentral()
// Add repositories to retrieve artifacts from in here.
@ -120,6 +123,7 @@ dependencies {
// Axiom for blueprint support
modCompileOnly "maven.modrinth:N6n5dqoA:nvx3oDkz"
modCompileOnly "com.github.moulberry:AxiomClientAPI:1.0.5.3"
// Fabric API.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

View File

@ -37,7 +37,18 @@ public class ReFramed implements ModInitializer {
public static final String MODID = "reframed";
public static final ArrayList<Block> BLOCKS = new ArrayList<>();
public static Block CUBE, SMALL_CUBE, SMALL_CUBES_STEP, STAIR, HALF_STAIR, STAIRS_CUBE, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, SLAB, SLABS_CUBE, STEP, STEPS_SLAB, LAYER, PILLAR, PILLARS_WALL, WALL, PANE, TRAPDOOR, DOOR;
public static Block
CUBE,
SMALL_CUBE, SMALL_CUBES_STEP,
STAIR, STAIRS_CUBE,
HALF_STAIR, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR,
SLAB, SLABS_CUBE,
STEP, STEPS_SLAB,
LAYER,
PILLAR, PILLARS_WALL, WALL,
PANE, TRAPDOOR, DOOR,
BUTTON,
POST, POST_FENCE, FENCE;
public static final ArrayList<Item> ITEMS = new ArrayList<>();
public static Item HAMMER, SCREWDRIVER, BLUEPRINT, BLUEPRINT_WRITTEN;
@ -70,6 +81,10 @@ public class ReFramed implements ModInitializer {
PANE = registerBlock("pane" , new ReFramedPaneBlock(cp(Blocks.OAK_FENCE)));
TRAPDOOR = registerBlock("trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.OAK_TRAPDOOR)));
DOOR = registerBlock("door" , new ReFramedDoorBlock(cp(Blocks.OAK_DOOR)));
BUTTON = registerBlock("button" , new ReFramedButtonBlock(cp(Blocks.OAK_BUTTON)));
POST = registerBlock("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE)));
FENCE = registerBlock("fence" , new ReFramedFenceBlock(cp(Blocks.OAK_FENCE)));
POST_FENCE = registerBlock("post_fence" , new ReFramedPostFenceBlock(cp(Blocks.OAK_FENCE)));
HAMMER = registerItem("hammer" , new ReFramedHammerItem(new Item.Settings().maxCount(1)));
SCREWDRIVER = registerItem("screwdriver" , new ReFramedScrewdriverItem(new Item.Settings().maxCount(1)));

View File

@ -0,0 +1,104 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import org.apache.commons.lang3.function.TriFunction;
import org.jetbrains.annotations.Nullable;
import static net.minecraft.state.property.Properties.*;
import static net.minecraft.state.property.Properties.SOUTH;
public abstract class ConnectingReFramedBlock extends WaterloggableReFramedBlock {
public ConnectingReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(EAST, false)
.with(NORTH, false)
.with(WEST, false)
.with(SOUTH, false)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST));
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) {
BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved);
if (dir == Direction.DOWN) return new_state;
return placementState(new_state, world, pos, this::connectsTo);
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
return placementState(state, world, pos, this::connectsTo);
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getConnectionProperty(rotation.rotate(dir)), state.get(getConnectionProperty(dir)))
, (prev, next) -> next);
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getConnectionProperty(mirror.apply(dir)), state.get(getConnectionProperty(dir)))
, (prev, next) -> next);
}
protected abstract boolean connectsTo(BlockState state, boolean fs, Direction dir);
@Override
public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context);
public static BooleanProperty getConnectionProperty(Direction dir) {
return switch (dir) {
case NORTH -> NORTH;
case EAST -> EAST;
case SOUTH -> SOUTH;
case WEST -> WEST;
default -> null;
};
}
public static BlockState placementState(BlockState state, BlockView world, BlockPos pos, TriFunction<BlockState, Boolean, Direction, Boolean> connectsTo) {
for (Direction dir: Direction.Type.HORIZONTAL) {
BlockState neighbor = world.getBlockState(pos.offset(dir));
state = state.with(getConnectionProperty(dir), connectsTo.apply(
neighbor,
neighbor.isSideSolidFullSquare(world, pos.offset(dir), dir.getOpposite()),
dir
));
}
return state;
}
}

View File

@ -0,0 +1,46 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
import static net.minecraft.state.property.Properties.AXIS;
public abstract class PillarReFramedBlock extends WaterloggableReFramedBlock {
public PillarReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
}
@Override
protected void appendProperties(StateManager.Builder< Block, BlockState > builder) {
super.appendProperties(builder.add(AXIS));
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis());
}
@Override
public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context);
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(AXIS, mirror.apply(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
}
}

View File

@ -9,7 +9,6 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.registry.Registries;
@ -73,8 +72,8 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
if(!(newState.getBlock() instanceof ReFramedBlock) &&
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
if(!(new_state.getBlock() instanceof ReFramedBlock) &&
world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity &&
world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
) {
@ -85,19 +84,9 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock()));
});
if(frame_entity.emitsRedstone()
&& themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) != 0))
drops.add(new ItemStack(Items.REDSTONE_TORCH));
if(frame_entity.emitsLight()
&& themes.stream().noneMatch(theme -> theme.getLuminance() != 0))
drops.add(new ItemStack(Items.GLOWSTONE_DUST));
if(!frame_entity.isSolid()
&& themes.stream().anyMatch(AbstractBlockState::isSolid))
drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
ItemScatterer.spawn(world, pos, drops);
}
super.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, new_state, moved);
}
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack, BlockState old_state, BlockEntity old_entity) {

View File

@ -0,0 +1,238 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.*;
import net.minecraft.block.enums.BlockFace;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.WorldView;
import net.minecraft.world.event.GameEvent;
import net.minecraft.world.explosion.Explosion;
import org.jetbrains.annotations.Nullable;
import java.util.function.BiConsumer;
import static net.minecraft.state.property.Properties.*;
public class ReFramedButtonBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] BUTTON_VOXELS;
public ReFramedButtonBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(HORIZONTAL_FACING, Direction.NORTH)
.with(BLOCK_FACE, BlockFace.WALL)
.with(POWERED, false)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(HORIZONTAL_FACING, BLOCK_FACE, POWERED));
}
@Override
public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
return canPlaceAt(world, pos, getDirection(state).getOpposite());
}
public static boolean canPlaceAt(WorldView world, BlockPos pos, Direction direction) {
BlockPos other_pos = pos.offset(direction);
return world.getBlockState(other_pos).isSideSolidFullSquare(world, other_pos, direction.getOpposite());
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
Direction side = ctx.getSide();
return state
.with(HORIZONTAL_FACING, side.getAxis() == Direction.Axis.Y
? ctx.getHorizontalPlayerFacing()
: side
)
.with(BLOCK_FACE, side.getAxis() != Direction.Axis.Y
? BlockFace.WALL
: side == Direction.UP ? BlockFace.FLOOR : BlockFace.CEILING
);
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos other_pos) {
return getDirection(state).getOpposite() == direction && !state.canPlaceAt(world, pos)
? Blocks.AIR.getDefaultState()
: super.getStateForNeighborUpdate(state, direction, other_state, world, pos, other_pos);
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult result = super.onUse(state, world, pos, player, hand, hit);
if (result.isAccepted()) return result;
if (state.get(POWERED)) return ActionResult.CONSUME;
powerOn(state, world, pos);
playClickSound(player, world, pos, true);
world.emitGameEvent(player, GameEvent.BLOCK_ACTIVATE, pos);
return ActionResult.success(world.isClient);
}
@Override
public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stackMerger) {
if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK && !world.isClient() && !(Boolean)state.get(POWERED)) {
powerOn(state, world, pos);
}
super.onExploded(state, world, pos, explosion, stackMerger);
}
public void powerOn(BlockState state, World world, BlockPos pos) {
world.setBlockState(pos, state.with(POWERED, true), 3);
updateNeighbors(state, world, pos);
world.scheduleBlockTick(pos, this, 30);
}
protected void playClickSound(@Nullable PlayerEntity player, WorldAccess world, BlockPos pos, boolean powered) {
world.playSound(
powered ? player : null,
pos,
powered ? BlockSetType.OAK.buttonClickOn() : BlockSetType.OAK.buttonClickOff(),
SoundCategory.BLOCKS
);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return BUTTON_VOXELS[
(state.get(POWERED) ? 12 : 0) +
(4 * state.get(BLOCK_FACE).ordinal()) +
state.get(HORIZONTAL_FACING).ordinal() - 2
];
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(HORIZONTAL_FACING, rotation.rotate(state.get(HORIZONTAL_FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(HORIZONTAL_FACING, mirror.apply(state.get(HORIZONTAL_FACING)));
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, false);
if(!state.isOf(new_state.getBlock())) {
if (!moved && state.get(POWERED)) updateNeighbors(state, world, pos);
world.removeBlockEntity(pos);
}
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return state.get(POWERED) ? 15 : super.getWeakRedstonePower(state, view, pos, dir);
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return dir == getDirection(state) ? state.getWeakRedstonePower(view, pos, dir) : 0;
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return true;
}
@Override
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
if (state.get(POWERED)) tryPowerWithProjectiles(state, world, pos);
}
@Override
public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) {
if (!world.isClient && !state.get(POWERED)) tryPowerWithProjectiles(state, world, pos);
}
protected void tryPowerWithProjectiles(BlockState state, World world, BlockPos pos) {
PersistentProjectileEntity projectile = world.getNonSpectatingEntities(
PersistentProjectileEntity.class,
state.getOutlineShape(world, pos).getBoundingBox().offset(pos)
).stream().findFirst().orElse(null);
boolean has_projectile = projectile != null;
if (has_projectile != state.get(POWERED)) {
world.setBlockState(pos, state.with(POWERED, has_projectile), 3);
this.updateNeighbors(state, world, pos);
this.playClickSound(null, world, pos, has_projectile);
world.emitGameEvent(projectile, has_projectile ? GameEvent.BLOCK_ACTIVATE : GameEvent.BLOCK_DEACTIVATE, pos);
}
if (has_projectile) {
world.scheduleBlockTick(pos, this, 30);
}
}
private void updateNeighbors(BlockState state, World world, BlockPos pos) {
world.updateNeighborsAlways(pos, this);
world.updateNeighborsAlways(pos.offset(getDirection(state).getOpposite()), this);
}
protected static Direction getDirection(BlockState state) {
return switch (state.get(BLOCK_FACE)) {
case CEILING -> Direction.DOWN;
case FLOOR -> Direction.UP;
default -> state.get(HORIZONTAL_FACING);
};
}
static {
VoxelShape SHAPE = createCuboidShape(5, 0, 6, 11, 2, 10);
VoxelShape POWERED_SHAPE = createCuboidShape(5, 0, 6, 11, 1, 10);
BUTTON_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 24)
.add()
.add(0, VoxelHelper::rotateY)
.add()
.add(VoxelHelper::rotateZ, VoxelHelper::rotateY)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.add(0, VoxelHelper::mirrorY)
.add()
.add(2, VoxelHelper::mirrorY)
.add()
.add(POWERED_SHAPE)
.add()
.add(12, VoxelHelper::rotateY)
.add()
.add(VoxelHelper::rotateZ, VoxelHelper::rotateY)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.add(12, VoxelHelper::mirrorY)
.add()
.add(13, VoxelHelper::mirrorY)
.add()
.build();
}
}

View File

@ -113,7 +113,7 @@ public class ReFramedDoorBlock extends WaterloggableReFramedBlock {
@Override
public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) {
if (!world.isClient() && (player.isCreative() || !player.canHarvest(state))) {
if (!world.isClient() && (player.isCreative() || player.canHarvest(state))) {
DoubleBlockHalf half = state.get(DOUBLE_BLOCK_HALF);
BlockPos other_pos = half == DoubleBlockHalf.LOWER ? pos.up() : pos.down();
BlockState other_state = world.getBlockState(other_pos);
@ -127,10 +127,10 @@ public class ReFramedDoorBlock extends WaterloggableReFramedBlock {
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override

View File

@ -0,0 +1,105 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.FenceGateBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.LeadItem;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
public class ReFramedFenceBlock extends ConnectingReFramedBlock {
public static final VoxelShape[] FENCE_VOXELS;
public ReFramedFenceBlock(Settings settings) {
super(settings);
}
@Override
protected boolean connectsTo(BlockState state, boolean fs, Direction dir) {
return fs || state.isIn(BlockTags.FENCES)
|| (state.getBlock() instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, dir));
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
VoxelShape shape = FENCE_VOXELS[0];
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]);
}
return shape;
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
VoxelShape shape = FENCE_VOXELS[5];
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() + 4]);
}
return shape;
}
@Override
public VoxelShape getCameraCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getOutlineShape(state, world, pos, context);
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return getOutlineShape(state, view, pos, ShapeContext.absent());
}
@Override
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return false;
}
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult result = super.onUse(state, world, pos, player, hand, hit);
if (result.isAccepted()) return result;
if (world.isClient) {
ItemStack itemStack = player.getStackInHand(hand);
return itemStack.isOf(Items.LEAD) ? ActionResult.SUCCESS : ActionResult.PASS;
} else {
return LeadItem.attachHeldMobsToBlock(player, world, pos);
}
}
static {
VoxelShape POST = createCuboidShape(6, 0, 6, 10, 16, 10);
VoxelShape POST_COLLISION = createCuboidShape(6, 0, 6, 10, 24, 10);
VoxelShape SIDE = VoxelShapes.combineAndSimplify(
createCuboidShape(7, 12, 0, 9, 15, 6),
createCuboidShape(7, 6, 0, 9, 9, 6),
BooleanBiFunction.OR
);
VoxelShape SIDE_COLLISION = createCuboidShape(7, 0, 0, 9, 24, 6);
FENCE_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 5)
.add(SIDE)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.add(POST_COLLISION)
.add(SIDE_COLLISION)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.build();
}
}

View File

@ -6,6 +6,8 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.context.LootContextParameterSet;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
@ -14,6 +16,8 @@ import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.state.property.Properties.LAYERS;
@ -27,6 +31,16 @@ public class ReFramedLayerBlock extends ReFramedSlabBlock {
setDefaultState(getDefaultState().with(LAYERS, 1));
}
@Override
public List<ItemStack> getDroppedStacks(BlockState state, LootContextParameterSet.Builder builder) {
List<ItemStack> drops = super.getDroppedStacks(state, builder);
drops.forEach((stack) -> {
if (stack.getItem() instanceof BlockItem bi && bi.getBlock() instanceof ReFramedLayerBlock)
stack.setCount(state.get(LAYERS));
});
return drops;
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(LAYERS));

View File

@ -1,122 +1,39 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.PaneBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
import static net.minecraft.state.property.Properties.*;
public class ReFramedPaneBlock extends WaterloggableReFramedBlock {
public class ReFramedPaneBlock extends ConnectingReFramedBlock {
public static final VoxelShape[] PANE_VOXELS;
public ReFramedPaneBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(EAST, false)
.with(NORTH, false)
.with(WEST, false)
.with(SOUTH, false)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST));
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) {
BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved);
if (dir == Direction.DOWN) return new_state;
for (Direction side: Direction.Type.HORIZONTAL) {
BlockState neighbor = world.getBlockState(pos.offset(side));
new_state = new_state.with(getPaneProperty(side), connectsTo(
neighbor,
neighbor.isSideSolidFullSquare(world, pos.offset(side), side.getOpposite())
));
}
return new_state;
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
for (Direction dir: Direction.Type.HORIZONTAL) {
BlockState neighbor = world.getBlockState(pos.offset(dir));
state = state.with(getPaneProperty(dir), connectsTo(
neighbor,
neighbor.isSideSolidFullSquare(world, pos.offset(dir), dir.getOpposite())
));
}
return state;
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
VoxelShape shape = PANE_VOXELS[0];
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getPaneProperty(dir)))
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, PANE_VOXELS[dir.ordinal() - 1]);
}
return shape;
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getPaneProperty(rotation.rotate(dir)), state.get(getPaneProperty(dir)))
, (prev, next) -> next);
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getPaneProperty(mirror.apply(dir)), state.get(getPaneProperty(dir)))
, (prev, next) -> next);
}
public static boolean connectsTo(BlockState state, boolean fs) {
protected boolean connectsTo(BlockState state, boolean fs, Direction dir) {
return !cannotConnect(state) && fs || state.getBlock() instanceof PaneBlock || state.isIn(BlockTags.WALLS);
}
public static BooleanProperty getPaneProperty(Direction dir) {
return switch (dir) {
case NORTH -> NORTH;
case EAST -> EAST;
case SOUTH -> SOUTH;
case WEST -> WEST;
default -> null;
};
}
static {
VoxelShape POST = createCuboidShape(7, 0, 7, 9, 16, 9);
VoxelShape SIDE = createCuboidShape(7, 0, 0, 9, 16, 7);

View File

@ -1,38 +1,21 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedPillarBlock extends WaterloggableReFramedBlock {
public class ReFramedPillarBlock extends PillarReFramedBlock {
public static final VoxelShape[] PILLAR_VOXELS;
public ReFramedPillarBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(AXIS));
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis());
}
@Override
@ -44,16 +27,6 @@ public class ReFramedPillarBlock extends WaterloggableReFramedBlock {
return PILLAR_VOXELS[axis.ordinal()];
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(AXIS, mirror.apply(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
}
static {
final VoxelShape PILLAR = createCuboidShape(0, 4, 4, 16, 12, 12);
PILLAR_VOXELS = VoxelHelper.VoxelListBuilder.create(PILLAR, 3)

View File

@ -72,10 +72,10 @@ public class ReFramedPillarsWallBlock extends WaterloggableReFramedDoubleBlock {
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override

View File

@ -0,0 +1,37 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedPostBlock extends PillarReFramedBlock {
public static final VoxelShape[] POST_VOXELS;
public ReFramedPostBlock(Settings settings) {
super(settings);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getPillarShape(state.get(AXIS));
}
public static VoxelShape getPillarShape(Direction.Axis axis) {
return POST_VOXELS[axis.ordinal()];
}
static {
final VoxelShape POST = createCuboidShape(0, 6, 6, 16, 10, 10);
POST_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 3)
.add(VoxelHelper::rotateZ)
.add(VoxelHelper::rotateX)
.build();
}
}

View File

@ -0,0 +1,152 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.FenceGateBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.LeadItem;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
import static fr.adrien1106.reframed.block.ConnectingReFramedBlock.getConnectionProperty;
import static fr.adrien1106.reframed.block.ConnectingReFramedBlock.placementState;
import static fr.adrien1106.reframed.block.ReFramedFenceBlock.FENCE_VOXELS;
import static net.minecraft.state.property.Properties.*;
import static net.minecraft.state.property.Properties.WEST;
public class ReFramedPostFenceBlock extends WaterloggableReFramedDoubleBlock {
public ReFramedPostFenceBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(EAST, false)
.with(NORTH, false)
.with(WEST, false)
.with(SOUTH, false)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST));
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) {
BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved);
if (dir == Direction.DOWN) return new_state;
return placementState(new_state, world, pos, this::connectsTo);
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
return placementState(state, world, pos, this::connectsTo);
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getConnectionProperty(rotation.rotate(dir)), state.get(getConnectionProperty(dir)))
, (prev, next) -> next);
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getConnectionProperty(mirror.apply(dir)), state.get(getConnectionProperty(dir)))
, (prev, next) -> next);
}
private boolean connectsTo(BlockState state, boolean fs, Direction dir) {
return fs || state.isIn(BlockTags.FENCES)
|| (state.getBlock() instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, dir));
}
@Override
public VoxelShape getShape(BlockState state, int i) {
if (i == 1) return FENCE_VOXELS[0];
VoxelShape shape = VoxelShapes.empty();
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]);
}
return shape;
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
VoxelShape shape = FENCE_VOXELS[0];
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]);
}
return shape;
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
VoxelShape shape = FENCE_VOXELS[5];
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() + 4]);
}
return shape;
}
@Override
public VoxelShape getCameraCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getOutlineShape(state, world, pos, context);
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return getOutlineShape(state, view, pos, ShapeContext.absent());
}
@Override
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return false;
}
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult result = super.onUse(state, world, pos, player, hand, hit);
if (result.isAccepted()) return result;
if (world.isClient) {
ItemStack itemStack = player.getStackInHand(hand);
return itemStack.isOf(Items.LEAD) ? ActionResult.SUCCESS : ActionResult.PASS;
} else {
return LeadItem.attachHeldMobsToBlock(player, world, pos);
}
}
}

View File

@ -87,10 +87,10 @@ public class ReFramedStairBlock extends WaterloggableReFramedBlock {
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override

View File

@ -77,10 +77,10 @@ public class ReFramedStairsCubeBlock extends ReFramedDoubleBlock {
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override

View File

@ -84,10 +84,10 @@ public class ReFramedTrapdoorBlock extends WaterloggableReFramedBlock {
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override

View File

@ -130,6 +130,20 @@ public class ReFramedClient implements ClientModInitializer {
HELPER.addReFramedModel("trapdoor_top" , HELPER.auto(new Identifier("block/oak_trapdoor_top")));
// DOOR
HELPER.addReFramedModel("door_inventory" , HELPER.auto(ReFramed.id("block/door")));
// BUTTON
HELPER.addReFramedModel("button_inventory" , HELPER.auto(new Identifier("block/button_inventory")));
HELPER.addReFramedModel("button" , HELPER.auto(new Identifier("block/button")));
HELPER.addReFramedModel("button_pressed" , HELPER.auto(new Identifier("block/button_pressed")));
// POST
HELPER.addReFramedModel("post" , HELPER.auto(ReFramed.id("block/post")));
// FENCE
HELPER.addReFramedModel("fence_inventory" , HELPER.auto(ReFramed.id("block/fence/inventory")));
HELPER.addReFramedModel("fence_core" , HELPER.auto(ReFramed.id("block/fence/core")));
HELPER.addReFramedModel("fence_side_off" , HELPER.auto(ReFramed.id("block/fence/side_off")));
HELPER.addReFramedModel("fence_side_on" , HELPER.auto(ReFramed.id("block/fence/side_on")));
// POST FENCE
HELPER.addReFramedModel("post_fence_inventory" , HELPER.autoDouble(ReFramed.id("block/post"), ReFramed.id("block/fence/full/inventory")));
HELPER.addReFramedModel("post_fence_side" , HELPER.autoDouble(ReFramed.id("block/fence/full/side_core"), ReFramed.id("block/fence/full/side_bars")));
//item model assignments (in lieu of models/item/___.json)
@ -152,6 +166,10 @@ public class ReFramedClient implements ClientModInitializer {
HELPER.assignItemModel("pane_inventory" , ReFramed.PANE);
HELPER.assignItemModel("trapdoor_bottom" , ReFramed.TRAPDOOR);
HELPER.assignItemModel("door_inventory" , ReFramed.DOOR);
HELPER.assignItemModel("button_inventory" , ReFramed.BUTTON);
HELPER.assignItemModel("post" , ReFramed.POST);
HELPER.assignItemModel("fence_inventory" , ReFramed.FENCE);
HELPER.assignItemModel("post_fence_inventory" , ReFramed.POST_FENCE);
}
private void privateInit() {

View File

@ -5,12 +5,15 @@ import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
@ -35,7 +38,17 @@ public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements
}
@Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {}
public List<BakedQuad> getQuads(BlockState blockState, Direction face, Random rand) {
List<BakedQuad> quads = new ArrayList<>(model_1.getQuads(blockState, face, rand));
quads.addAll(model_2.getQuads(blockState, face, rand));
return quads;
}
@Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
model_1.emitBlockQuads(world, state, pos, randomSupplier, context);
model_2.emitBlockQuads(world, state, pos, randomSupplier, context);
}
@Override // models are emitted here because no checks are done on items
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {

View File

@ -12,11 +12,13 @@ import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import net.fabricmc.fabric.api.renderer.v1.mesh.*;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
@ -25,6 +27,7 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@ -70,6 +73,39 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
return mesh;
}
private List<BakedQuad>[] quads = null;
@Override
public List<BakedQuad> getQuads(BlockState state, Direction face, Random rand) {
if (quads == null) {
quads = ModelHelper.toQuadLists(
getRetexturedMesh(
new MeshCacheKey(
hashCode(),
appearance_manager.getDefaultAppearance(theme_index),
0
),
state
)
);
}
return quads[ModelHelper.toFaceIndex(face)];
}
public void setCamo(BlockRenderView world, BlockState state, BlockPos pos) {
if (state == null || state.isAir()) {
quads = null;
return;
}
CamoAppearance camo = appearance_manager.getCamoAppearance(world, state, pos, theme_index, false);
MeshCacheKey key = new MeshCacheKey(
hashCode(),
camo,
0
);
quads = ModelHelper.toQuadLists(camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state));
}
protected abstract Mesh convertModel(BlockState state);
@Override
@ -89,6 +125,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
@Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null;
QuadEmitter quad_emitter = context.getEmitter();
if(theme == null || theme.isAir()) {
getRetexturedMesh(
@ -176,7 +213,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
emitter.copyFrom(quad);
i = key.appearance.transformQuad(emitter, i, quad_index.get(), key.model_id, uv_lock);
} while (i > 0);
// kinda weird to do it like that but other directions don't use the quad_index so it doesn't matter
// kinda weird to do it like that but other directions don't use the quad_index, so it doesn't matter
if (quad.cullFace() == null) quad_index.getAndIncrement();
});

View File

@ -12,7 +12,6 @@ import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.function.BooleanBiFunction;
@ -52,8 +51,6 @@ public class RenderHelper {
Random random = Random.create();
List<List<QuadPosBounds>> model_bounds = models.stream()
.map(ForwardingBakedModel::getWrappedModel)
.filter(Objects::nonNull)
.map(wrapped -> wrapped.getQuads(state, null, random))
.map(quads -> quads.stream().map(quad -> {
quad_emitter.fromVanilla(quad, material, null);
@ -98,12 +95,17 @@ public class RenderHelper {
// Doing this method from scratch as it is simpler to do than injecting everywhere
public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) {
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null;
ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e ? e : null;
BlockState other_state = world.getBlockState(other_pos);
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e
&& self_state.getBlock() instanceof ReFramedBlock
? e : null;
ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e
&& other_state.getBlock() instanceof ReFramedBlock
? e : null;
// normal behaviour
if (self == null && other == null) return Block.shouldDrawSide(self_state, world, pos, side, other_pos);
if (theme_index == 0 || (self == null && other == null))
return Block.shouldDrawSide(self_state, world, pos, side, other_pos);
// self is a normal Block
if (self == null && other_state.getBlock() instanceof ReFramedBlock other_block) {

View File

@ -19,6 +19,8 @@ public class GBlockTag extends BlockTagProvider {
providers.put(ReFramedPillarsWallBlock.class, new PillarsWall());
providers.put(ReFramedWallBlock.class, new Wall());
providers.put(ReFramedPaneBlock.class, new Pane());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
}
public GBlockTag(FabricDataOutput output, CompletableFuture<WrapperLookup> registries) {

View File

@ -37,6 +37,10 @@ public class GBlockstate extends FabricModelProvider {
providers.put(ReFramedPaneBlock.class, new Pane());
providers.put(ReFramedTrapdoorBlock.class, new Trapdoor());
providers.put(ReFramedDoorBlock.class, new Door());
providers.put(ReFramedButtonBlock.class, new Button());
providers.put(ReFramedPostBlock.class, new Post());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
}
public GBlockstate(FabricDataOutput output) {

View File

@ -39,6 +39,10 @@ public class GRecipe extends FabricRecipeProvider {
providers.put(ReFramedPaneBlock.class, new Pane());
providers.put(ReFramedTrapdoorBlock.class, new Trapdoor());
providers.put(ReFramedDoorBlock.class, new Door());
providers.put(ReFramedButtonBlock.class, new Button());
providers.put(ReFramedPostBlock.class, new Post());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
providers.put(ReFramedBlueprintItem.class, new Blueprint());
providers.put(ReFramedHammerItem.class, new Hammer());
providers.put(ReFramedScrewdriverItem.class, new Screwdriver());

View File

@ -0,0 +1,95 @@
package fr.adrien1106.reframed.generator.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.RecipeSetter;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.enums.BlockFace;
import net.minecraft.data.client.BlockStateSupplier;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
import net.minecraft.item.ItemConvertible;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.*;
public class Button implements RecipeSetter, BlockStateProvider {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 8);
ShapelessRecipeJsonBuilder.create(RecipeCategory.BUILDING_BLOCKS, convertible, 1)
.input(ReFramed.CUBE, 1)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible))
.offerTo(exporter);
}
@Override
public BlockStateSupplier getMultipart(Block block) {
Identifier button = ReFramed.id("button_special");
Identifier button_pressed = ReFramed.id("button_pressed_special");
return MultipartBlockStateSupplier.create(block)
// FLOOR OFF
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button, true, R0, R0))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button, true, R0, R90))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button, true, R0, R180))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button, true, R0, R270))
// CEILING OFF
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button, true, R180, R0))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button, true, R180, R90))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button, true, R180, R180))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button, true, R180, R270))
// WALL OFF
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button, true, R90, R0))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button, true, R90, R90))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button, true, R90, R180))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button, true, R90, R270))
// FLOOR ON
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button_pressed, true, R0, R0))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button_pressed, true, R0, R90))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button_pressed, true, R0, R180))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button_pressed, true, R0, R270))
// CEILING ON
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button_pressed, true, R180, R0))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button_pressed, true, R180, R90))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button_pressed, true, R180, R180))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button_pressed, true, R180, R270))
// WALL ON
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button_pressed, true, R90, R0))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button_pressed, true, R90, R90))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button_pressed, true, R90, R180))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button_pressed, true, R90, R270));
}
}

View File

@ -0,0 +1,73 @@
package fr.adrien1106.reframed.generator.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.RecipeSetter;
import fr.adrien1106.reframed.generator.TagGetter;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.data.client.BlockStateSupplier;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
import net.minecraft.item.ItemConvertible;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import java.util.List;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.*;
public class Fence implements RecipeSetter, TagGetter, BlockStateProvider {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 4);
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, convertible, 4)
.pattern("I-I")
.pattern("I-I")
.input('I', ReFramed.CUBE)
.input('-', Blocks.BAMBOO)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible))
.offerTo(exporter);
}
@Override
public List<TagKey<Block>> getTags() {
return List.of(BlockTags.FENCES, BlockTags.WOODEN_FENCES);
}
@Override
public BlockStateSupplier getMultipart(Block block) {
Identifier side_on = ReFramed.id("fence_side_on_special");
Identifier side_off = ReFramed.id("fence_side_off_special");
return MultipartBlockStateSupplier.create(block)
.with(GBlockstate.variant(ReFramed.id("fence_core_special"), true, R0, R0))
// SIDE ON
.with(GBlockstate.when(NORTH, true),
GBlockstate.variant(side_on, true, R0, R0))
.with(GBlockstate.when(EAST, true),
GBlockstate.variant(side_on, true, R0, R90))
.with(GBlockstate.when(SOUTH, true),
GBlockstate.variant(side_on, true, R0, R180))
.with(GBlockstate.when(WEST, true),
GBlockstate.variant(side_on, true, R0, R270))
// SIDE OFF
.with(GBlockstate.when(NORTH, false),
GBlockstate.variant(side_off, true, R0, R0))
.with(GBlockstate.when(EAST, false),
GBlockstate.variant(side_off, true, R0, R90))
.with(GBlockstate.when(SOUTH, false),
GBlockstate.variant(side_off, true, R0, R180))
.with(GBlockstate.when(WEST, false),
GBlockstate.variant(side_off, true, R0, R270));
}
}

View File

@ -0,0 +1,50 @@
package fr.adrien1106.reframed.generator.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.RecipeSetter;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.data.client.BlockStateSupplier;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
import net.minecraft.item.ItemConvertible;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import static net.minecraft.data.client.VariantSettings.Rotation.R0;
import static net.minecraft.data.client.VariantSettings.Rotation.R90;
import static net.minecraft.state.property.Properties.AXIS;
public class Post implements RecipeSetter, BlockStateProvider {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 6);
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, convertible, 12)
.pattern("I")
.pattern("I")
.pattern("I")
.input('I', ReFramed.CUBE)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible))
.offerTo(exporter);
}
@Override
public BlockStateSupplier getMultipart(Block block) {
Identifier model_id = ReFramed.id("post_special");
return MultipartBlockStateSupplier.create(block)
.with(GBlockstate.when(AXIS, Direction.Axis.X),
GBlockstate.variant(model_id, true, R90, R90))
.with(GBlockstate.when(AXIS, Direction.Axis.Y),
GBlockstate.variant(model_id, true, R0, R0))
.with(GBlockstate.when(AXIS, Direction.Axis.Z),
GBlockstate.variant(model_id, true, R90, R0));
}
}

View File

@ -0,0 +1,69 @@
package fr.adrien1106.reframed.generator.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.RecipeSetter;
import fr.adrien1106.reframed.generator.TagGetter;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.data.client.BlockStateSupplier;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
import net.minecraft.item.ItemConvertible;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import java.util.List;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.*;
public class PostFence implements RecipeSetter, TagGetter, BlockStateProvider {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.FENCE, 1);
ShapelessRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, convertible, 2)
.input(ReFramed.FENCE, 2)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible))
.offerTo(exporter);
}
@Override
public List<TagKey<Block>> getTags() {
return List.of(BlockTags.FENCES, BlockTags.WOODEN_FENCES);
}
@Override
public BlockStateSupplier getMultipart(Block block) {
Identifier side_on = ReFramed.id("post_fence_side_special");
Identifier side_off = ReFramed.id("fence_side_off_special");
return MultipartBlockStateSupplier.create(block)
.with(GBlockstate.variant(ReFramed.id("fence_core_special"), true, R0, R0))
// SIDE ON
.with(GBlockstate.when(NORTH, true),
GBlockstate.variant(side_on, true, R0, R0))
.with(GBlockstate.when(EAST, true),
GBlockstate.variant(side_on, true, R0, R90))
.with(GBlockstate.when(SOUTH, true),
GBlockstate.variant(side_on, true, R0, R180))
.with(GBlockstate.when(WEST, true),
GBlockstate.variant(side_on, true, R0, R270))
// SIDE OFF
.with(GBlockstate.when(NORTH, false),
GBlockstate.variant(side_off, true, R0, R0))
.with(GBlockstate.when(EAST, false),
GBlockstate.variant(side_off, true, R0, R90))
.with(GBlockstate.when(SOUTH, false),
GBlockstate.variant(side_off, true, R0, R180))
.with(GBlockstate.when(WEST, false),
GBlockstate.variant(side_off, true, R0, R270));
}
}

View File

@ -2,6 +2,7 @@ package fr.adrien1106.reframed.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import fr.adrien1106.reframed.block.ReFramedBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.entity.player.PlayerEntity;
@ -9,6 +10,7 @@ import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
@ -32,8 +34,12 @@ public class BlockItemMixin {
private static void placeBlockWithOffHandCamo(World world, PlayerEntity player, BlockPos pos, ItemStack stack, CallbackInfoReturnable<Boolean> cir, @Local LocalRef<NbtCompound> compound) {
if (compound.get() != null
|| player.getOffHandStack().isEmpty()
|| player.getMainHandStack().isEmpty()
|| !(player.getMainHandStack().getItem() instanceof BlockItem frame)
|| !(frame.getBlock() instanceof ReFramedBlock)
|| !(player.getOffHandStack().getItem() instanceof BlockItem block)
|| block.getBlock() instanceof BlockEntityProvider
|| (world.getBlockState(pos).contains(Properties.LAYERS) && world.getBlockState(pos).get(Properties.LAYERS) > 1)
|| !Block.isShapeFullCube(block.getBlock().getDefaultState().getCollisionShape(world, pos))
) return;
NbtCompound new_comp = new NbtCompound();

View File

@ -26,14 +26,24 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumNonTerrainBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityConnectionPredicateMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMQuadTransformMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityModelWrappingHandlerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomChunkedBlockRegionMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClientBlockEntitySerializerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClipboardMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomCloneBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomPlacementMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomMappedBlockAndTintGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomMoveBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomScale3xMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomRotSpriteMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
}

View File

@ -0,0 +1,161 @@
package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.client.model.RetexturingBakedModel;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
import fr.adrien1106.reframed.util.mixin.ThemedBlockEntity;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
import java.util.stream.Stream;
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
@Mixin(ChunkedBlockRegion.class) // TODO: Look here for better rotation/flip support
public abstract class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlockRegionMixin {
@Shadow public abstract BlockState getBlockState(BlockPos pos);
@Shadow public abstract @Nullable BlockEntity getBlockEntity(BlockPos pos);
@Unique
private IntMatrix transform;
@Unique
private IntMatrix inverse_transform;
@Unique
private Long2ObjectMap<CompressedBlockEntity> block_entities;
@Unique
private static boolean isFrameModel(BakedModel model) {
return model instanceof RetexturingBakedModel || model instanceof MultiRetexturableModel;
}
@Unique
private static List<BakedModel> getModels(BakedModel model, BlockState state) {
if (isFrameModel(model))
return List.of(model);
else if (model instanceof IMultipartBakedModelMixin mpm)
return mpm.getModels(state).stream().filter(AxiomChunkedBlockRegionMixin::isFrameModel).toList();
else
return List.of();
}
@Inject(
method = "renderBlock",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lnet/minecraft/client/render/block/BlockRenderManager;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;",
shift = At.Shift.AFTER
),
cancellable = true)
private static void onRenderBlock(BufferBuilder blockBuilder, BlockRenderManager renderer, BlockPos.Mutable pos, Random rand, MatrixStack matrices, BlockRenderView world, Matrix4f currentPoseMatrix, Matrix4f basePoseMatrix, int x, int y, int z, BlockState state, boolean useAmbientOcclusion, CallbackInfo ci, @Local BakedModel model) {
List<BakedModel> models;
if ((models = getModels(model, state)).isEmpty() // not a retexturable model
|| !(world.getBlockEntity(pos) instanceof ThemedBlockEntity block_entity) // not a themed block entity
) return;
models.stream().flatMap(m -> m instanceof MultiRetexturableModel mm
? mm.models().stream()
: Stream.of((RetexturingBakedModel)m)
).forEach(m -> {
m.setCamo(world, block_entity.getTheme(m.getThemeIndex()), pos);
if (useAmbientOcclusion && state.getLuminance() == 0 && m.useAmbientOcclusion()) renderer.getModelRenderer()
.renderSmooth(world, m, state, pos, matrices, blockBuilder, true, rand, state.getRenderingSeed(pos), OverlayTexture.DEFAULT_UV);
else renderer.getModelRenderer()
.renderFlat(world, m, state, pos, matrices, blockBuilder, true, rand, state.getRenderingSeed(pos), OverlayTexture.DEFAULT_UV);
});
ci.cancel();
}
@Inject(
method = "getBlockEntity",
at = @At("HEAD"),
remap = false,
cancellable = true
)
private void onGetBlockEntity(BlockPos pos, CallbackInfoReturnable<BlockEntity> cir) {
if (inverse_transform == null) return;
long key = BlockPos.asLong(
inverse_transform.transformX(pos.getX(), pos.getY(), pos.getZ()),
inverse_transform.transformY(pos.getX(), pos.getY(), pos.getZ()),
inverse_transform.transformZ(pos.getX(), pos.getY(), pos.getZ())
);
NbtCompound compound;
if (!block_entities.containsKey(key)
|| !(compound = block_entities.get(key).decompress()).contains(BLOCKSTATE_KEY + 1)
) return;
cir.setReturnValue(new ThemedBlockEntity(compound, pos, getBlockState(pos)));
}
@Inject(
method = "uploadDirty",
at = @At("HEAD")
)
private void onUploadDirty(Camera camera, Vec3d translation, boolean canResort, boolean canUseAmbientOcclusion, CallbackInfo ci) {
if (transform == null) inverse_transform = new IntMatrix();
else inverse_transform = transform.copy();
inverse_transform.invert();
}
@Inject(
method = "flip",
at = @At("RETURN")
)
private void onFlip(Direction.Axis axis, CallbackInfoReturnable<ChunkedBlockRegion> cir) {
((IAxiomChunkedBlockRegionMixin) cir.getReturnValue()).setTransform(transform, block_entities);
}
@Inject(
method = "rotate",
at = @At("RETURN")
)
private void onRotate(Direction.Axis axis, int count, CallbackInfoReturnable<ChunkedBlockRegion> cir) {
((IAxiomChunkedBlockRegionMixin) cir.getReturnValue()).setTransform(transform, block_entities);
}
@Override
public void setTransform(IntMatrix transform, Long2ObjectMap<CompressedBlockEntity> block_entities) {
this.transform = transform;
this.block_entities = block_entities;
}
@Override
public IntMatrix getTransform() {
return transform;
}
@Override
public Long2ObjectMap<CompressedBlockEntity> getBlockEntities() {
return block_entities;
}
}

View File

@ -0,0 +1,25 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.clipboard.Clipboard;
import com.moulberry.axiom.clipboard.ClipboardObject;
import com.moulberry.axiom.utils.IntMatrix;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Clipboard.class)
public class AxiomClipboardMixin {
@Inject(
method = "setClipboard(Lcom/moulberry/axiom/clipboard/ClipboardObject;)I",
at = @At(
value = "TAIL"
),
remap = false
)
private void onInit(ClipboardObject object, CallbackInfoReturnable<Integer> cir) {
((IAxiomChunkedBlockRegionMixin) object.blockRegion()).setTransform(new IntMatrix(), object.blockEntities());
}
}

View File

@ -0,0 +1,38 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.buildertools.CloneBuilderTool;
import com.moulberry.axiom.clipboard.SelectionBuffer;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(CloneBuilderTool.class)
public class AxiomCloneBuilderToolMixin {
@Shadow(remap = false) private ChunkedBlockRegion blockRegion;
@Shadow(remap = false) @Final private IntMatrix transformMatrix;
@Shadow(remap = false) private Long2ObjectMap<CompressedBlockEntity> blockEntities;
@Inject(
method = "lambda$initiateClone$0",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lcom/moulberry/axiom/clipboard/SelectionBuffer$CopyResult;blockEntities()Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;",
shift = At.Shift.AFTER
),
remap = false
)
private void onInitiateClone(int copyId, int offsetX, int offsetY, int offsetZ, SelectionBuffer.CopyResult copyResult, CallbackInfo ci) {
((IAxiomChunkedBlockRegionMixin) blockRegion).setTransform(transformMatrix, blockEntities);
}
}

View File

@ -0,0 +1,32 @@
package fr.adrien1106.reframed.mixin.compat;
import fr.adrien1106.reframed.block.ReFramedEntity;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(targets = "com.moulberry.axiom.render.ChunkRenderOverrider$MappedBlockAndTintGetter")
public class AxiomMappedBlockAndTintGetterMixin {
@Shadow @Final private World level;
@Inject(
method = "getBlockEntity",
at = @At(
value = "RETURN"
),
remap = false,
cancellable = true
)
private void onGetBlockEntity(BlockPos pos, CallbackInfoReturnable<BlockEntity> cir) {
if (!(level.getBlockEntity(pos) instanceof ReFramedEntity frame_entity)) return;
cir.setReturnValue(frame_entity);
}
}

View File

@ -0,0 +1,38 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.buildertools.MoveBuilderTool;
import com.moulberry.axiom.clipboard.SelectionBuffer;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MoveBuilderTool.class)
public class AxiomMoveBuilderToolMixin {
@Shadow(remap = false) private ChunkedBlockRegion blockRegion;
@Shadow(remap = false) @Final private IntMatrix transformMatrix;
@Shadow(remap = false) private Long2ObjectMap<CompressedBlockEntity> blockEntities;
@Inject(
method = "lambda$initiateMovement$1",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lcom/moulberry/axiom/clipboard/SelectionBuffer$CopyResult;blockEntities()Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;",
shift = At.Shift.AFTER
),
remap = false
)
private void onInitiateClone(int copyId, int offsetX, int offsetY, int offsetZ, SelectionBuffer.CopyResult copyResult, CallbackInfo ci) {
((IAxiomChunkedBlockRegionMixin) blockRegion).setTransform(transformMatrix, blockEntities);
}
}

View File

@ -0,0 +1,39 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.clipboard.Placement;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Placement.class)
public class AxiomPlacementMixin {
@Shadow(remap = false) private Long2ObjectMap<CompressedBlockEntity> blockEntities;
@Inject(
method = "replacePlacement(Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;Ljava/lang/String;)V",
at = @At("HEAD"),
remap = false
)
private void onReplacePlacement(ChunkedBlockRegion region, String description, CallbackInfo ci) {
((IAxiomChunkedBlockRegionMixin) region).setTransform(new IntMatrix(), blockEntities);
}
@Inject(
method = "startPlacement(Lnet/minecraft/util/math/BlockPos;Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;Ljava/lang/String;)I",
at = @At("HEAD"),
remap = false
)
private void onStartPlacement(BlockPos target, ChunkedBlockRegion region, Long2ObjectMap<CompressedBlockEntity> entities, String description, CallbackInfoReturnable<Integer> cir) {
((IAxiomChunkedBlockRegionMixin) region).setTransform(new IntMatrix(), entities);
}
}

View File

@ -0,0 +1,27 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.scaling.RotSprite;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(RotSprite.class)
public class AxiomRotSpriteMixin {
@Inject(
method = "rotateCachedWithOutput",
at = @At(
value = "HEAD"
),
remap = false
)
private static void onRotateCachedWithOutput(ChunkedBlockRegion in, Matrix4f matrix4f, ChunkedBlockRegion out, int x, int y, int z, CallbackInfoReturnable<ChunkedBlockRegion> cir) {
IAxiomChunkedBlockRegionMixin iin = (IAxiomChunkedBlockRegionMixin) in;
((IAxiomChunkedBlockRegionMixin) out).setTransform(iin.getTransform(), iin.getBlockEntities());
}
}

View File

@ -0,0 +1,29 @@
package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.scaling.Scale3x;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Scale3x.class)
public class AxiomScale3xMixin {
@Inject(
method = "scale3x",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;<init>()V",
shift = At.Shift.AFTER
),
remap = false
)
private static void onInit(ChunkedBlockRegion in, boolean postProcessing, CallbackInfoReturnable<ChunkedBlockRegion> cir, @Local(ordinal = 1) ChunkedBlockRegion out) {
IAxiomChunkedBlockRegionMixin iin = (IAxiomChunkedBlockRegionMixin) in;
((IAxiomChunkedBlockRegionMixin) out).setTransform(iin.getTransform(), iin.getBlockEntities());
}
}

View File

@ -0,0 +1,66 @@
package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.client.util.RenderHelper;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import link.infra.indium.renderer.render.BlockRenderInfo;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockRenderView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(BlockRenderInfo.class)
public abstract class IndiumBlockRenderInfoMixin implements IBlockRenderInfoMixin {
@Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo);
@Shadow public BlockPos blockPos;
@Shadow public BlockRenderView blockView;
@Shadow public BlockState blockState;
@Shadow(remap = false) private int cullResultFlags;
@Unique private int theme_index = 0;
@Unique private int model_hash = 0;
@Inject(
method = "shouldDrawFace",
at = @At(
value = "INVOKE",
target = "Llink/infra/indium/renderer/render/BlockRenderInfo;shouldDrawFaceInner(Lnet/minecraft/util/math/Direction;)Z"
),
cancellable = true
)
private void shouldDrawInnerFace(Direction face, CallbackInfoReturnable<Boolean> cir, @Local int mask) {
BlockPos other_pos = blockPos.offset(face);
if (!(blockView.getBlockEntity(blockPos) instanceof ThemeableBlockEntity
|| blockView.getBlockEntity(other_pos) instanceof ThemeableBlockEntity)
) return;
boolean result = RenderHelper.shouldDrawSide(blockState, blockView, blockPos, face, other_pos, theme_index);
if (result) cullResultFlags |= mask;
cir.setReturnValue(result);
}
@Override
public void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo, int theme_index, int model_hash) {
this.theme_index = theme_index;
this.model_hash = model_hash;
prepareForBlock(blockState, blockPos, seed, modelAo);
}
@Override
public int getThemeIndex() {
return theme_index;
}
@Override
public int getModelHash() {
return model_hash;
}
}

View File

@ -0,0 +1,84 @@
package fr.adrien1106.reframed.mixin.compat;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.client.model.RetexturingBakedModel;
import fr.adrien1106.reframed.client.util.RenderHelper;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
import link.infra.indium.renderer.aocalc.AoCalculator;
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
import link.infra.indium.renderer.render.BlockRenderInfo;
import link.infra.indium.renderer.render.NonTerrainBlockRenderContext;
import link.infra.indium.renderer.render.SingleBlockLightDataCache;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(NonTerrainBlockRenderContext.class)
public abstract class IndiumNonTerrainBlockRenderContextMixin extends AbstractBlockRenderContext {
@Shadow(remap = false) @Final private SingleBlockLightDataCache lightCache;
@Shadow private VertexConsumer vertexConsumer;
@Inject(
method = "render",
at = @At(
value = "INVOKE",
target = "Llink/infra/indium/renderer/render/BlockRenderInfo;prepareForWorld(Lnet/minecraft/world/BlockRenderView;Z)V",
shift = At.Shift.AFTER
),
remap = false,
cancellable = true
)
private void renderMultipleModels(BlockRenderView blockView, BakedModel wrapper, BlockState state, BlockPos pos, MatrixStack matrixStack, VertexConsumer buffer, boolean cull, Random random, long seed, int overlay, CallbackInfo ci) {
if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)) return;
List<BakedModel> models = wrapped.getModels(state);
if (models.stream().noneMatch(bakedModel ->
bakedModel instanceof MultiRetexturableModel
|| bakedModel instanceof RetexturingBakedModel
)) return;
models.forEach(model -> {
if (model instanceof MultiRetexturableModel multi_model) {
RenderHelper.computeInnerCull(state, multi_model.models(), model.hashCode());
multi_model.models().forEach(rexteruable_model ->
renderModel(state, pos, seed, rexteruable_model, aoCalc, blockInfo, this, model.hashCode())
);
} else if (model instanceof RetexturingBakedModel rexteruable_model)
renderModel(state, pos, seed, rexteruable_model, aoCalc, blockInfo, this, model.hashCode());
else model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
});
blockInfo.release();
lightCache.release();
vertexConsumer = null;
ci.cancel();
}
@Unique
private static void renderModel(BlockState state, BlockPos pos, long seed, RetexturingBakedModel model, AoCalculator aoCalc, BlockRenderInfo block_info, RenderContext context, int model_hash) {
aoCalc.clear();
((IBlockRenderInfoMixin) block_info).prepareForBlock(
state, pos, seed,
model.useAmbientOcclusion(block_info.blockView, pos),
model.getThemeIndex(), model_hash
);
model.emitBlockQuads(block_info.blockView, block_info.blockState, block_info.blockPos, block_info.randomSupplier, context);
}
}

View File

@ -1,55 +0,0 @@
package fr.adrien1106.reframed.mixin.compat;
import fr.adrien1106.reframed.client.util.RenderHelper;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import link.infra.indium.renderer.render.BlockRenderInfo;
import link.infra.indium.renderer.render.TerrainBlockRenderInfo;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TerrainBlockRenderInfo.class)
public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin {
@Unique private int theme_index = 0;
@Unique private int model_hash = 0;
@Redirect(
method = "shouldDrawFaceInner",
at = @At(
value = "INVOKE",
target = "Lme/jellysquid/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Z"
)
)
private boolean shouldDrawCamoSide(BlockOcclusionCache instance, BlockState state, BlockView view, BlockPos pos, Direction face) {
BlockPos other_pos = pos.offset(face);
if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity
|| view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity))
return instance.shouldDrawSide(state, view, pos, face);
return RenderHelper.shouldDrawSide(state, view, pos, face, other_pos, theme_index);
}
@Override
public void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo, int theme_index, int model_hash) {
this.theme_index = theme_index;
this.model_hash = model_hash;
prepareForBlock(blockState, blockPos, seed, modelAo);
}
@Override
public int getThemeIndex() {
return theme_index;
}
@Override
public int getModelHash() {
return model_hash;
}
}

View File

@ -0,0 +1,40 @@
package fr.adrien1106.reframed.mixin.render;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.client.model.RetexturingBakedModel;
import fr.adrien1106.reframed.client.util.RenderHelper;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.block.BlockModelRenderer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(BlockModelRenderer.class)
public class BlockModelRendererMixin {
@Redirect(
method = "renderSmooth",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/BlockPos;)Z"
)
)
private boolean shouldDrawFrameSideS(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, @Local(argsOnly = true) BakedModel model) {
return RenderHelper.shouldDrawSide(state, world, pos, side, other_pos, model instanceof RetexturingBakedModel rm ? rm.getThemeIndex() : 0);
}
@Redirect(
method = "renderFlat",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/BlockPos;)Z"
)
)
private boolean shouldDrawFrameSideF(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, @Local(argsOnly = true) BakedModel model) {
return RenderHelper.shouldDrawSide(state, world, pos, side, other_pos, model instanceof RetexturingBakedModel rm ? rm.getThemeIndex() : 0);
}
}

View File

@ -0,0 +1,78 @@
package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.client.model.RetexturingBakedModel;
import fr.adrien1106.reframed.client.util.RenderHelper;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(BlockRenderContext.class)
public abstract class BlockRenderContextMixin extends AbstractBlockRenderContext {
@Shadow private VertexConsumer vertexConsumer;
@Inject(
method = "render",
at = @At(
value = "INVOKE",
target = "Lnet/fabricmc/fabric/impl/client/indigo/renderer/render/BlockRenderInfo;prepareForWorld(Lnet/minecraft/world/BlockRenderView;Z)V",
shift = At.Shift.AFTER
),
cancellable = true
)
private void renderMultipleModels(BlockRenderView blockView, BakedModel wrapper, BlockState state, BlockPos pos, MatrixStack matrixStack, VertexConsumer buffer, boolean cull, Random random, long seed, int overlay, CallbackInfo ci) {
if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)) return;
List<BakedModel> models = wrapped.getModels(state);
if (models.stream().noneMatch(bakedModel ->
bakedModel instanceof MultiRetexturableModel
|| bakedModel instanceof RetexturingBakedModel
)) return;
models.forEach(model -> {
if (model instanceof MultiRetexturableModel multi_model) {
RenderHelper.computeInnerCull(state, multi_model.models(), model.hashCode());
multi_model.models().forEach(rexteruable_model ->
renderModel(state, pos, rexteruable_model, aoCalc, blockInfo, this, model.hashCode())
);
} else if (model instanceof RetexturingBakedModel rexteruable_model)
renderModel(state, pos, rexteruable_model, aoCalc, blockInfo, this, model.hashCode());
else model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
});
blockInfo.release();
vertexConsumer = null;
ci.cancel();
}
@Unique
private static void renderModel(BlockState state, BlockPos pos, RetexturingBakedModel model, AoCalculator aoCalc, BlockRenderInfo block_info, RenderContext context, int model_hash) {
aoCalc.clear();
((IBlockRenderInfoMixin) block_info).prepareForBlock(
state, pos,
model.useAmbientOcclusion(block_info.blockView, pos),
model.getThemeIndex(), model_hash
);
model.emitBlockQuads(block_info.blockView, block_info.blockState, block_info.blockPos, block_info.randomSupplier, context);
}
}

View File

@ -188,7 +188,6 @@ public class BlockHelper {
if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) return ActionResult.PASS;
ItemStack held = player.getStackInHand(hand);
ReframedInteractible ext = state.getBlock() instanceof ReframedInteractible e ? e : ReframedInteractible.Default.INSTANCE;
// frame will emit light if applied with glowstone
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST) {
@ -199,14 +198,14 @@ public class BlockHelper {
}
// frame will emit redstone if applied with redstone torch can deactivate redstone block camo emission
if(held.getItem() == Items.REDSTONE_TORCH && ext.canAddRedstoneEmission(state, world, pos)) {
if(held.getItem() == Items.REDSTONE_TORCH) {
block_entity.toggleRedstone();
world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS;
}
// Frame will lose its collision if applied with popped chorus fruit
if(held.getItem() == Items.POPPED_CHORUS_FRUIT && ext.canRemoveCollision(state, world, pos)) {
if(held.getItem() == Items.POPPED_CHORUS_FRUIT) {
block_entity.toggleSolidity();
world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS;

View File

@ -1,20 +0,0 @@
package fr.adrien1106.reframed.util.blocks;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
public interface ReframedInteractible {
default boolean canAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) {
return state.getWeakRedstonePower(view, pos, Direction.UP) == 0;
}
default boolean canRemoveCollision(BlockState state, BlockView view, BlockPos pos) {
return !state.getCollisionShape(view, pos).isEmpty();
}
class Default implements ReframedInteractible {
public static final Default INSTANCE = new Default();
}
}

View File

@ -0,0 +1,14 @@
package fr.adrien1106.reframed.util.mixin;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
public interface IAxiomChunkedBlockRegionMixin {
void setTransform(IntMatrix transform, Long2ObjectMap<CompressedBlockEntity> block_entities);
IntMatrix getTransform();
Long2ObjectMap<CompressedBlockEntity> getBlockEntities();
}

View File

@ -0,0 +1,44 @@
package fr.adrien1106.reframed.util.mixin;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.registry.Registries;
import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
import java.util.List;
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
public class ThemedBlockEntity extends BlockEntity implements ThemeableBlockEntity {
private final List<BlockState> themes;
public ThemedBlockEntity(NbtCompound compound, BlockPos pos, BlockState state) {
super(null, pos, state);
themes = new ArrayList<>();
for (int i = 1; compound.contains(BLOCKSTATE_KEY + i ); i++) {
themes.add(NbtHelper.toBlockState(
Registries.BLOCK.getReadOnlyWrapper(),
compound.getCompound(BLOCKSTATE_KEY + i)
));
}
}
@Override
public BlockState getTheme(int i) {
return themes.get(Math.max(0, i-1));
}
@Override
public void setTheme(BlockState state, int i) {
themes.set(Math.max(0, i-1), state);
}
@Override
public List<BlockState> getThemes() {
return themes;
}
}

View File

@ -0,0 +1,17 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [7, 0, 7],
"to": [9, 16, 9],
"faces": {
"up": {"uv": [7, 7, 9, 9], "texture": "#top", "cullface": "up"},
"down": {"uv": [7, 7, 9, 9], "texture": "#bottom", "cullface": "down"}
}
}
]
}

View File

@ -0,0 +1,60 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [7, 12, 0],
"to": [9, 15, 6],
"faces": {
"north": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "north"},
"east": {"uv": [10, 1, 16, 4], "texture": "#side"},
"south": {"uv": [7, 1, 9, 4], "texture": "#side"},
"west": {"uv": [0, 1, 6, 4], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
},
{
"from": [7, 6, 10],
"to": [9, 9, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [0, -5, 0]},
"faces": {
"north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "south"},
"east": {"uv": [10, 7, 16, 10], "texture": "#side"},
"south": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "south"},
"west": {"uv": [0, 7, 6, 10], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
},
{
"from": [7, 12, 10],
"to": [9, 15, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 10]},
"faces": {
"north": {"uv": [7, 1, 9, 4], "texture": "#side"},
"east": {"uv": [10, 1, 16, 4], "texture": "#side"},
"south": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "south"},
"west": {"uv": [0, 1, 6, 4], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
},
{
"from": [7, 6, 0],
"to": [9, 9, 6],
"rotation": {"angle": 0, "axis": "y", "origin": [0, -5, -10]},
"faces": {
"north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "north"},
"east": {"uv": [10, 7, 16, 10], "texture": "#side"},
"south": {"uv": [7, 7, 9, 10], "texture": "#side"},
"west": {"uv": [0, 7, 6, 10], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
}
]
}

View File

@ -0,0 +1,34 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [7, 12, 0],
"to": [9, 15, 6],
"faces": {
"north": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "north"},
"east": {"uv": [10, 1, 16, 4], "texture": "#side"},
"south": {"uv": [7, 1, 9, 4], "texture": "#side"},
"west": {"uv": [0, 1, 6, 4], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
},
{
"from": [7, 6, 0],
"to": [9, 9, 6],
"rotation": {"angle": 0, "axis": "y", "origin": [0, -5, -10]},
"faces": {
"north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "north"},
"east": {"uv": [10, 7, 16, 10], "texture": "#side"},
"south": {"uv": [7, 7, 9, 10], "texture": "#side"},
"west": {"uv": [0, 7, 6, 10], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
}
]
}

View File

@ -0,0 +1,62 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [6, 0, 6],
"to": [7, 16, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, -1]},
"faces": {
"north": {"uv": [9, 0, 10, 16], "texture": "#side"},
"west": {"uv": [6, 0, 7, 16], "texture": "#side"},
"up": {"uv": [6, 6, 7, 7], "texture": "#top", "cullface": "up"},
"down": {"uv": [6, 9, 7, 10], "texture": "#bottom", "cullface": "down"}
}
},
{
"from": [7, 0, 6],
"to": [9, 6, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, -1]},
"faces": {
"north": {"uv": [7, 10, 9, 16], "texture": "#side"},
"down": {"uv": [7, 9, 9, 10], "texture": "#bottom", "cullface": "down"}
}
},
{
"from": [7, 9, 6],
"to": [9, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 6, -1]},
"faces": {
"north": {"uv": [7, 4, 9, 7], "texture": "#side"}
}
},
{
"from": [7, 12, 6],
"to": [9, 15, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 9, -1]},
"faces": {
"north": {"uv": [7, 1, 9, 4], "texture": "#side"}
}
},
{
"from": [7, 6, 6],
"to": [9, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 3, -1]},
"faces": {
"north": {"uv": [7, 7, 9, 10], "texture": "#side"}
}
},
{
"from": [7, 15, 6],
"to": [9, 16, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 10, -1]},
"faces": {
"north": {"uv": [7, 0, 9, 1], "texture": "#side"},
"up": {"uv": [7, 6, 9, 7], "texture": "#top", "cullface": "up"}
}
}
]
}

View File

@ -0,0 +1,68 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [7, 12, 10],
"to": [9, 15, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 1, 0]},
"faces": {
"east": {"uv": [0, 1, 6, 4], "texture": "#side"},
"south": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "south"},
"west": {"uv": [10, 1, 16, 4], "texture": "#side"},
"up": {"uv": [7, 10, 9, 16], "texture": "#top"},
"down": {"uv": [7, 0, 9, 6], "texture": "#bottom"}
}
},
{
"from": [7, 6, 10],
"to": [9, 9, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [0, -5, 0]},
"faces": {
"east": {"uv": [0, 7, 6, 10], "texture": "#side"},
"south": {"uv": [7, 2, 9, 5], "texture": "#side", "cullface": "south"},
"west": {"uv": [10, 7, 16, 10], "texture": "#side"},
"up": {"uv": [7, 10, 9, 16], "texture": "#top"},
"down": {"uv": [7, 0, 9, 6], "texture": "#bottom"}
}
},
{
"from": [7, 12, 0],
"to": [9, 15, 6],
"faces": {
"north": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "north"},
"east": {"uv": [10, 1, 16, 4], "texture": "#side"},
"west": {"uv": [0, 1, 6, 4], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
},
{
"from": [7, 6, 0],
"to": [9, 9, 6],
"rotation": {"angle": 0, "axis": "y", "origin": [0, -5, -10]},
"faces": {
"north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "north"},
"east": {"uv": [10, 7, 16, 10], "texture": "#side"},
"west": {"uv": [0, 7, 6, 10], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
},
{
"from": [6, 0, 6],
"to": [10, 16, 10],
"faces": {
"north": {"uv": [6, 0, 10, 16], "texture": "#side"},
"east": {"uv": [6, 0, 10, 16], "texture": "#side"},
"south": {"uv": [6, 0, 10, 16], "texture": "#side", "cullface": "south"},
"west": {"uv": [6, 0, 10, 16], "texture": "#side"},
"up": {"uv": [6, 6, 10, 10], "texture": "#top"},
"down": {"uv": [6, 6, 10, 10], "texture": "#bottom"}
}
}
]
}

View File

@ -0,0 +1,20 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [6, 0, 6],
"to": [9, 16, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, -1]},
"faces": {
"north": {"uv": [7, 0, 10, 16], "texture": "#side"},
"west": {"uv": [6, 0, 7, 16], "texture": "#side"},
"up": {"uv": [6, 6, 9, 7], "texture": "#top", "cullface": "up"},
"down": {"uv": [6, 9, 9, 10], "texture": "#bottom", "cullface": "down"}
}
}
]
}

View File

@ -0,0 +1,69 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [7, 12, 0],
"to": [9, 15, 6],
"faces": {
"north": {"uv": [7, 1, 9, 4], "texture": "#side", "cullface": "north"},
"east": {"uv": [10, 1, 16, 4], "texture": "#side"},
"west": {"uv": [0, 1, 6, 4], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
},
{
"from": [7, 6, 0],
"to": [9, 9, 6],
"rotation": {"angle": 0, "axis": "y", "origin": [0, -5, -10]},
"faces": {
"north": {"uv": [7, 7, 9, 10], "texture": "#side", "cullface": "north"},
"east": {"uv": [10, 7, 16, 10], "texture": "#side"},
"west": {"uv": [0, 7, 6, 10], "texture": "#side"},
"up": {"uv": [7, 0, 9, 6], "texture": "#top"},
"down": {"uv": [7, 10, 9, 16], "texture": "#bottom"}
}
},
{
"from": [6, 0, 6],
"to": [7, 16, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, -1]},
"faces": {
"north": {"uv": [9, 0, 10, 16], "texture": "#side"},
"west": {"uv": [6, 0, 7, 16], "texture": "#side"},
"up": {"uv": [6, 6, 7, 7], "texture": "#top", "cullface": "up"},
"down": {"uv": [6, 9, 7, 10], "texture": "#bottom", "cullface": "down"}
}
},
{
"from": [7, 0, 6],
"to": [9, 6, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, -1]},
"faces": {
"north": {"uv": [7, 10, 9, 16], "texture": "#side"},
"down": {"uv": [7, 9, 9, 10], "texture": "#bottom", "cullface": "down"}
}
},
{
"from": [7, 9, 6],
"to": [9, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 6, -1]},
"faces": {
"north": {"uv": [7, 4, 9, 7], "texture": "#side"}
}
},
{
"from": [7, 15, 6],
"to": [9, 16, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 10, -1]},
"faces": {
"north": {"uv": [7, 0, 9, 1], "texture": "#side"},
"up": {"uv": [7, 6, 9, 7], "texture": "#top", "cullface": "up"}
}
}
]
}

View File

@ -0,0 +1,21 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [6, 0, 6],
"to": [10, 16, 10],
"faces": {
"north": {"uv": [6, 0, 10, 16], "texture": "#side"},
"east": {"uv": [4, 0, 10, 16], "texture": "#side"},
"south": {"uv": [6, 0, 10, 16], "texture": "#side"},
"west": {"uv": [6, 0, 10, 16], "texture": "#side"},
"up": {"uv": [6, 6, 10, 10], "texture": "#top", "cullface": "up"},
"down": {"uv": [6, 6, 10, 10], "texture": "#bottom", "cullface": "down"}
}
}
]
}

View File

@ -15,13 +15,22 @@
"compat.AthenaBakedModelMixin",
"compat.AthenaConnectedBlockModelMixin",
"compat.AthenaWrappedGetterMixin",
"compat.AxiomChunkedBlockRegionMixin",
"compat.AxiomClientBlockEntitySerializerMixin",
"compat.AxiomClipboardMixin",
"compat.AxiomCloneBuilderToolMixin",
"compat.AxiomMappedBlockAndTintGetterMixin",
"compat.AxiomMoveBuilderToolMixin",
"compat.AxiomPlacementMixin",
"compat.AxiomRotSpriteMixin",
"compat.AxiomScale3xMixin",
"compat.ContinuityConnectionPredicateMixin",
"compat.ContinuityCTMBakedModelMixin",
"compat.ContinuityCTMQuadTransformMixin",
"compat.ContinuityModelWrappingHandlerMixin",
"compat.IndiumAbstractBlockRenderContextMixin",
"compat.IndiumTerrainBlockRenderInfoMixin",
"compat.IndiumBlockRenderInfoMixin",
"compat.IndiumNonTerrainBlockRenderContextMixin",
"compat.IndiumTerrainRenderContextMixin",
"compat.SodiumBlockOcclusionCacheMixin",
"model.WeightedBakedModelAccessor",
@ -29,6 +38,8 @@
"particles.AccessorSpriteBillboardParticle",
"particles.MixinBlockDustParticle",
"render.AbstractBlockRenderContextMixin",
"render.BlockModelRendererMixin",
"render.BlockRenderContextMixin",
"render.BlockRenderInfoMixin",
"render.MultipartBakedModelMixin",
"render.TerrainRenderContextMixin",