Merge pull request 'Improvement for cache fixing issues + 4 new blocks' (#14) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m39s

Reviewed-on: #14
This commit is contained in:
Adrien1106 2024-05-14 15:53:28 +02:00
commit cfdbfd3a6a
59 changed files with 1679 additions and 376 deletions

View File

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

View File

@ -4,7 +4,7 @@ import fr.altarik.CreateTag
plugins { plugins {
id "com.modrinth.minotaur" version "2.+" id "com.modrinth.minotaur" version "2.+"
id 'fabric-loom' version '1.5-SNAPSHOT' id 'fabric-loom' version '1.6-SNAPSHOT'
id 'maven-publish' id 'maven-publish'
} }
@ -106,11 +106,11 @@ dependencies {
// modRuntimeOnly "maven.modrinth:indium:${project.indium_version}+mc${project.minecraft_version}" // modRuntimeOnly "maven.modrinth:indium:${project.indium_version}+mc${project.minecraft_version}"
// modRuntimeOnly "maven.modrinth:sodium:mc${project.minecraft_version}-${project.sodium_version}" // modRuntimeOnly "maven.modrinth:sodium:mc${project.minecraft_version}-${project.sodium_version}"
// Athena for connected texture // Athena for connected textures
modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}" modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
modRuntimeOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}" modRuntimeOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
// Continuity for connectedTextures // Continuity for connected textures
modCompileOnly "maven.modrinth:continuity:${project.continuity_version}" modCompileOnly "maven.modrinth:continuity:${project.continuity_version}"
modRuntimeOnly "maven.modrinth:continuity:${project.continuity_version}" modRuntimeOnly "maven.modrinth:continuity:${project.continuity_version}"
@ -200,8 +200,8 @@ publishing {
modrinth { modrinth {
token = getEnv("MODRINTH_TOKEN", local.getProperty("modrinth_token")) token = getEnv("MODRINTH_TOKEN", local.getProperty("modrinth_token"))
projectId = project.modrinth_id projectId = project.modrinth_id
versionNumber = project.mod_version versionNumber = "${project.mod_version}-${project.minecraft_version}"
versionName = "${project.archives_base_name} ${project.mod_version}" versionName = "${project.archives_base_name} ${project.mod_version}-${project.minecraft_version}"
versionType = project.mod_version.endsWith('SNAPSHOT') ? 'beta' : 'release' versionType = project.mod_version.endsWith('SNAPSHOT') ? 'beta' : 'release'
uploadFile = remapJar uploadFile = remapJar
gameVersions = [project.minecraft_version] gameVersions = [project.minecraft_version]

View File

@ -5,18 +5,18 @@ org.gradle.jvmargs=-Xmx1G
# check these on https://modmuss50.me/fabric.html # check these on https://modmuss50.me/fabric.html
minecraft_version=1.20.4 minecraft_version=1.20.4
yarn_mappings=1.20.4+build.3 yarn_mappings=1.20.4+build.3
loader_version=0.15.6 loader_version=0.15.11
# Mod Properties # Mod Properties
modrinth_id = jCpoCBpn modrinth_id = jCpoCBpn
mod_version = 1.5.8 mod_version = 1.5.9
maven_group = fr.adrien1106 maven_group = fr.adrien1106
archives_base_name = ReFramed archives_base_name = ReFramed
mod_id = reframed mod_id = reframed
# Dependencies # Dependencies
# check this on https://modmuss50.me/fabric.html # check this on https://modmuss50.me/fabric.html
fabric_version=0.95.4+1.20.4 fabric_version=0.97.0+1.20.4
git_owner=Altarik git_owner=Altarik
git_repo=ReFramed git_repo=ReFramed

View File

@ -37,7 +37,7 @@ public class ReFramed implements ModInitializer {
public static final String MODID = "reframed"; public static final String MODID = "reframed";
public static final ArrayList<Block> BLOCKS = new ArrayList<>(); 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, WALL; 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 final ArrayList<Item> ITEMS = new ArrayList<>(); public static final ArrayList<Item> ITEMS = new ArrayList<>();
public static Item HAMMER, SCREWDRIVER, BLUEPRINT, BLUEPRINT_WRITTEN; public static Item HAMMER, SCREWDRIVER, BLUEPRINT, BLUEPRINT_WRITTEN;
@ -65,7 +65,11 @@ public class ReFramed implements ModInitializer {
STEP = registerBlock("step" , new ReFramedStepBlock(cp(Blocks.OAK_SLAB))); STEP = registerBlock("step" , new ReFramedStepBlock(cp(Blocks.OAK_SLAB)));
STEPS_SLAB = registerBlock("steps_slab" , new ReFramedStepsSlabBlock(cp(Blocks.OAK_SLAB))); STEPS_SLAB = registerBlock("steps_slab" , new ReFramedStepsSlabBlock(cp(Blocks.OAK_SLAB)));
PILLAR = registerBlock("pillar" , new ReFramedPillarBlock(cp(Blocks.OAK_FENCE))); PILLAR = registerBlock("pillar" , new ReFramedPillarBlock(cp(Blocks.OAK_FENCE)));
WALL = registerBlock("wall" , new ReframedWallBlock(cp(Blocks.OAK_FENCE))); PILLARS_WALL = registerBlock("pillars_wall" , new ReFramedPillarsWallBlock(cp(Blocks.OAK_FENCE)));
WALL = registerBlock("wall" , new ReFramedWallBlock(cp(Blocks.OAK_FENCE)));
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)));
HAMMER = registerItem("hammer" , new ReFramedHammerItem(new Item.Settings().maxCount(1))); HAMMER = registerItem("hammer" , new ReFramedHammerItem(new Item.Settings().maxCount(1)));
SCREWDRIVER = registerItem("screwdriver" , new ReFramedScrewdriverItem(new Item.Settings().maxCount(1))); SCREWDRIVER = registerItem("screwdriver" , new ReFramedScrewdriverItem(new Item.Settings().maxCount(1)));

View File

@ -43,18 +43,6 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
setDefaultState(getDefaultState().with(LIGHT, false)); setDefaultState(getDefaultState().with(LIGHT, false));
} }
/**
* Generates a record for the key so that it replaces the blockstate
* which may have states that returns same models
* @param state - the state_key to generate the key from
* @return a cache key with only relevant properties
*/
public Object getModelCacheKey(BlockState state) {
return "";
}
//For addon devs: override this so your blocks don't end up trying to place my block entity, my BlockEntityType only handles blocks internal to the mod
//Just make your own BlockEntityType, it's fine, you can even use the same ReFramedEntity class
@Override @Override
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
@ -73,18 +61,13 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
@Override @Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit); if (!canUse(world, pos, player)) return ActionResult.PASS;
ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand); ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
if (result.isAccepted()) return result; if (result.isAccepted()) return result;
return BlockHelper.useCamo(state, world, pos, player, hand, hit, 1); return BlockHelper.useCamo(state, world, pos, player, hand, hit, 1);
} }
// don't like this but might be useful
protected ActionResult superUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
return super.onUse(state, world, pos, player, hand, hit);
}
protected boolean canUse(World world, BlockPos pos, PlayerEntity player) { protected boolean canUse(World world, BlockPos pos, PlayerEntity player) {
return player.canModifyBlocks() && world.canPlayerModifyAt(player, pos); return player.canModifyBlocks() && world.canPlayerModifyAt(player, pos);
} }

View File

@ -0,0 +1,273 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.enums.DoorHinge;
import net.minecraft.block.enums.DoubleBlockHalf;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
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.MathHelper;
import net.minecraft.util.math.Vec3d;
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 ReFramedDoorBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] DOOR_VOXELS;
public ReFramedDoorBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(HORIZONTAL_FACING, Direction.NORTH)
.with(DOOR_HINGE, DoorHinge.LEFT)
.with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER)
.with(OPEN, false)
.with(POWERED, false)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(HORIZONTAL_FACING, DOOR_HINGE, DOUBLE_BLOCK_HALF, OPEN, POWERED));
}
@Override
public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
BlockPos pos_down = pos.down();
BlockState state_down = world.getBlockState(pos_down);
return state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER ? !state_down.isAir() : state_down.isOf(this);
}
@Override
public void neighborUpdate(BlockState state, World world, BlockPos pos, Block source, BlockPos sourcePos, boolean notify) {
if (world.isClient) return;
boolean powered = world.isReceivingRedstonePower(pos)
|| world.isReceivingRedstonePower(
state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER
? pos.up()
: pos.down()
);
if (!getDefaultState().isOf(source) && powered != state.get(POWERED)) {
if (state.get(OPEN) != powered)
playToggleSound(null, world, pos, powered);
world.setBlockState(pos, state.with(POWERED, powered).with(OPEN, powered), 2);
if (state.get(WATERLOGGED)) {
world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
}
}
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
Direction facing = ctx.getHorizontalPlayerFacing().getOpposite();
if (pos.getY() >= world.getTopY() - 1 || !world.getBlockState(pos.up()).canReplace(ctx)) return null;
BlockState state = super.getPlacementState(ctx)
.with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER)
.with(HORIZONTAL_FACING, facing);
if (world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.up()))
state = state.with(OPEN, true).with(POWERED, true);
return state.with(DOOR_HINGE, getHinge(facing, pos, world, BlockHelper.getRelativePos(ctx.getHitPos(), pos)));
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack, BlockState old_state, BlockEntity old_entity) {
world.setBlockState(
pos.up(),
state
.with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.UPPER)
.with(WATERLOGGED, world.getFluidState(pos.up()).isOf(Fluids.WATER)),
3
);
}
@Override
public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) {
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);
if (other_state.isOf(this) && other_state.get(DOUBLE_BLOCK_HALF) != half) {
world.setBlockState(other_pos, other_state.get(WATERLOGGED) ? Blocks.WATER.getDefaultState(): Blocks.AIR.getDefaultState(), 35);
world.syncWorldEvent(player, 2001, other_pos, Block.getRawIdFromState(other_state));
}
}
return super.onBreak(world, pos, state, player);
}
@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 BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState other, WorldAccess world, BlockPos pos, BlockPos moved) {
if (direction.getAxis() == Direction.Axis.Y
&& other.isOf(this)
&& other.get(DOUBLE_BLOCK_HALF) != state.get(DOUBLE_BLOCK_HALF)
&& other.get(OPEN) != state.get(OPEN)
) return state.cycle(OPEN);
Direction facing = state.get(HORIZONTAL_FACING);
if (direction == (
state.get(DOOR_HINGE) == DoorHinge.RIGHT
? facing.rotateYClockwise()
: facing.rotateYCounterclockwise())
&& other.isOf(this)
&& other.get(DOUBLE_BLOCK_HALF) == state.get(DOUBLE_BLOCK_HALF)
&& other.get(DOOR_HINGE) != state.get(DOOR_HINGE)
&& !state.get(POWERED)
) return state.with(OPEN, other.get(OPEN));
return super.getStateForNeighborUpdate(state, direction, other, world, pos, moved);
}
@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;
flip(state, world, pos, player);
return ActionResult.success(world.isClient);
}
@Override
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return switch (type) {
case LAND, AIR -> state.get(OPEN);
case WATER -> false;
};
}
@Override
public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stack_merger) {
if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK
&& !world.isClient()
&& !state.get(POWERED)
) flip(state, world, pos, null);
super.onExploded(state, world, pos, explosion, stack_merger);
}
private void flip(BlockState state, World world, BlockPos pos, @Nullable PlayerEntity player) {
state = state.cycle(OPEN);
world.setBlockState(pos, state, 10);
this.playToggleSound(player, world, pos, state.get(OPEN));
}
protected void playToggleSound(@Nullable PlayerEntity player, World world, BlockPos pos, boolean open) {
world.playSound(player, pos, open ? BlockSetType.OAK.doorOpen() : BlockSetType.OAK.doorClose(), SoundCategory.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F);
world.emitGameEvent(player, open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
Direction direction = state.get(HORIZONTAL_FACING);
if (state.get(OPEN)) direction = switch (state.get(DOOR_HINGE)) {
case RIGHT -> direction.rotateYCounterclockwise();
case LEFT -> direction.rotateYClockwise();
};
return DOOR_VOXELS[direction.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 mirror == BlockMirror.NONE ? state : state.with(HORIZONTAL_FACING, mirror.apply(state.get(HORIZONTAL_FACING))).cycle(DOOR_HINGE);
}
@Override
public long getRenderingSeed(BlockState state, BlockPos pos) {
return MathHelper.hashCode(pos.getX(), pos.down(state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER ? 0 : 1).getY(), pos.getZ());
}
private DoorHinge getHinge(Direction facing, BlockPos pos, World world, Vec3d hit_pos) {
Direction left = facing.rotateYClockwise();
BlockPos left_pos = pos.offset(left);
BlockState left_state = world.getBlockState(left_pos);
Direction right = facing.rotateYCounterclockwise();
BlockPos right_pos = pos.offset(right);
BlockState right_state = world.getBlockState(right_pos);
DoorHinge hinge = null;
if (left_state.isSideSolidFullSquare(world, left_pos, right))
hinge = DoorHinge.LEFT;
if (right_state.isSideSolidFullSquare(world, right_pos, left))
hinge = hinge == DoorHinge.LEFT ? null : DoorHinge.RIGHT;
if (hinge != null) return hinge;
if (left_state.isOf(this)
&& left_state.get(HORIZONTAL_FACING) == facing
&& left_state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER
&& left_state.get(DOOR_HINGE) == DoorHinge.LEFT
) hinge = DoorHinge.RIGHT;
if (right_state.isOf(this)
&& right_state.get(HORIZONTAL_FACING) == facing
&& right_state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER
&& right_state.get(DOOR_HINGE) == DoorHinge.RIGHT
) hinge = hinge == DoorHinge.RIGHT ? null : DoorHinge.LEFT;
if (hinge != null) return hinge;
return switch (facing.getAxis()) {
case Z -> {
if (left.getDirection() == Direction.AxisDirection.POSITIVE)
yield hit_pos.getX() < 0.5 ? DoorHinge.RIGHT : DoorHinge.LEFT;
else // left.getDirection() == Direction.AxisDirection.NEGATIVE
yield hit_pos.getX() < 0.5 ? DoorHinge.LEFT : DoorHinge.RIGHT;
}
case X -> {
if (left.getDirection() == Direction.AxisDirection.POSITIVE)
yield hit_pos.getZ() < 0.5 ? DoorHinge.RIGHT : DoorHinge.LEFT;
else // left.getDirection() == Direction.AxisDirection.NEGATIVE
yield hit_pos.getZ() < 0.5 ? DoorHinge.LEFT : DoorHinge.RIGHT;
}
default -> DoorHinge.LEFT;
};
}
static {
VoxelShape SHAPE = createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 3.0);
DOOR_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 4)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.build();
}
}

View File

@ -69,17 +69,17 @@ public abstract class ReFramedDoubleBlock extends ReFramedBlock {
@Override @Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return getCullingShape(state, view, pos);
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return isGhost(view, pos) ? empty() : fullCube(); return isGhost(view, pos) ? empty() : fullCube();
} }
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return getCollisionShape(state, view, pos, ShapeContext.absent());
}
@Override @Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit); if (!canUse(world, pos, player)) return ActionResult.PASS;
ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand); ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
if (result.isAccepted()) return result; if (result.isAccepted()) return result;
return BlockHelper.useCamo(state, world, pos, player, hand, hit, getHitShape(state, hit)); return BlockHelper.useCamo(state, world, pos, player, hand, hit, getHitShape(state, hit));

View File

@ -10,6 +10,8 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.function.BooleanBiFunction; import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
@ -29,18 +31,11 @@ public class ReFramedHalfStairBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] HALF_STAIR_VOXELS; public static final VoxelShape[] HALF_STAIR_VOXELS;
private record ModelCacheKey(Corner corner, int face) {}
public ReFramedHalfStairBlock(Settings settings) { public ReFramedHalfStairBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN).with(CORNER_FACE, 0)); setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN).with(CORNER_FACE, 0));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(CORNER), state.get(CORNER_FACE));
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER,CORNER_FACE)); super.appendProperties(builder.add(CORNER,CORNER_FACE));
@ -108,6 +103,20 @@ public class ReFramedHalfStairBlock extends WaterloggableReFramedBlock {
return HALF_STAIR_VOXELS[state.get(CORNER_FACE) + state.get(CORNER).getID() * 3]; return HALF_STAIR_VOXELS[state.get(CORNER_FACE) + state.get(CORNER).getID() * 3];
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
Corner corner = state.get(CORNER).rotate(rotation);
Direction face = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return state.with(CORNER, corner).with(CORNER_FACE, corner.getDirectionIndex(rotation.rotate(face)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
Corner corner = state.get(CORNER).mirror(mirror);
Direction face = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return state.with(CORNER, corner).with(CORNER_FACE, corner.getDirectionIndex(mirror.apply(face)));
}
@Override @Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) { public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
if (new_state.isOf(ReFramed.HALF_STAIRS_SLAB)) return Map.of(1, 1); if (new_state.isOf(ReFramed.HALF_STAIRS_SLAB)) return Map.of(1, 1);

View File

@ -7,7 +7,10 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -22,18 +25,11 @@ import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBlock { public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBlock {
private record ModelCacheKey(Corner corner, int face) {}
public ReFramedHalfStairsSlabBlock(Settings settings) { public ReFramedHalfStairsSlabBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN).with(CORNER_FACE, 0)); setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN).with(CORNER_FACE, 0));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(CORNER), state.get(CORNER_FACE));
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER,CORNER_FACE)); super.appendProperties(builder.add(CORNER,CORNER_FACE));
@ -48,7 +44,7 @@ public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBloc
} }
@Override @Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return isGhost(view, pos) ? empty(): getSlabShape(state.get(CORNER).getDirection(state.get(CORNER_FACE))); return isGhost(view, pos) ? empty(): getSlabShape(state.get(CORNER).getDirection(state.get(CORNER_FACE)));
} }
@ -57,6 +53,20 @@ public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBloc
return getSlabShape(state.get(CORNER).getDirection(state.get(CORNER_FACE))); return getSlabShape(state.get(CORNER).getDirection(state.get(CORNER_FACE)));
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
Corner corner = state.get(CORNER).rotate(rotation);
Direction face = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return state.with(CORNER, corner).with(CORNER_FACE, corner.getDirectionIndex(rotation.rotate(face)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
Corner corner = state.get(CORNER).mirror(mirror);
Direction face = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return state.with(CORNER, corner).with(CORNER_FACE, corner.getDirectionIndex(mirror.apply(face)));
}
@Override @Override
public VoxelShape getShape(BlockState state, int i) { public VoxelShape getShape(BlockState state, int i) {
Corner corner = state.get(CORNER); Corner corner = state.get(CORNER);

View File

@ -9,6 +9,8 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
@ -27,11 +29,6 @@ public class ReFramedHalfStairsStairBlock extends WaterloggableReFramedDoubleBlo
setDefaultState(getDefaultState().with(EDGE, NORTH_DOWN)); setDefaultState(getDefaultState().with(EDGE, NORTH_DOWN));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(EDGE);
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE)); super.appendProperties(builder.add(EDGE));
@ -44,7 +41,7 @@ public class ReFramedHalfStairsStairBlock extends WaterloggableReFramedDoubleBlo
} }
@Override @Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return isGhost(view, pos) ? empty(): getStairShape(state.get(EDGE), StairShape.STRAIGHT); return isGhost(view, pos) ? empty(): getStairShape(state.get(EDGE), StairShape.STRAIGHT);
} }
@ -53,6 +50,16 @@ public class ReFramedHalfStairsStairBlock extends WaterloggableReFramedDoubleBlo
return getStairShape(state.get(EDGE), StairShape.STRAIGHT); return getStairShape(state.get(EDGE), StairShape.STRAIGHT);
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(EDGE, state.get(EDGE).rotate(rotation));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(EDGE, state.get(EDGE).mirror(mirror));
}
@Override @Override
public VoxelShape getShape(BlockState state, int i) { public VoxelShape getShape(BlockState state, int i) {
Edge edge = state.get(EDGE); Edge edge = state.get(EDGE);

View File

@ -7,8 +7,9 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -20,18 +21,12 @@ import static net.minecraft.state.property.Properties.LAYERS;
public class ReFramedLayerBlock extends ReFramedSlabBlock { public class ReFramedLayerBlock extends ReFramedSlabBlock {
public static final VoxelShape[] LAYER_VOXELS; public static final VoxelShape[] LAYER_VOXELS;
private record ModelCacheKey(Direction face, int layer) {}
public ReFramedLayerBlock(Settings settings) { public ReFramedLayerBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(LAYERS, 1)); setDefaultState(getDefaultState().with(LAYERS, 1));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(FACING), state.get(LAYERS));
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(LAYERS)); super.appendProperties(builder.add(LAYERS));
@ -58,6 +53,16 @@ public class ReFramedLayerBlock extends ReFramedSlabBlock {
return previous.with(LAYERS, previous.get(LAYERS) + 1); return previous.with(LAYERS, previous.get(LAYERS) + 1);
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(FACING, mirror.apply(state.get(FACING)));
}
static { static {
VoxelListBuilder builder = VoxelListBuilder.create(createCuboidShape(0, 0, 0, 16, 2, 16), 48) VoxelListBuilder builder = VoxelListBuilder.create(createCuboidShape(0, 0, 0, 16, 2, 16), 48)
.add(createCuboidShape(0, 0, 0, 16, 4, 16)) .add(createCuboidShape(0, 0, 0, 16, 4, 16))

View File

@ -0,0 +1,130 @@
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 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)))
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) {
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);
PANE_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 5)
.add(SIDE)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.build();
}
}

View File

@ -4,17 +4,16 @@ import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Map;
import static net.minecraft.state.property.Properties.AXIS; import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedPillarBlock extends WaterloggableReFramedBlock { public class ReFramedPillarBlock extends WaterloggableReFramedBlock {
@ -26,30 +25,13 @@ public class ReFramedPillarBlock extends WaterloggableReFramedBlock {
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y)); setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(AXIS);
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(AXIS)); super.appendProperties(builder.add(AXIS));
} }
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
return !(context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
|| !(
block_item.getBlock() == this
&& state.get(AXIS) != context.getSide().getAxis()
)
);
}
@Override @Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
// TODO: PILLARS WALL
return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis()); return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis());
} }
@ -63,9 +45,13 @@ public class ReFramedPillarBlock extends WaterloggableReFramedBlock {
} }
@Override @Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) { public BlockState rotate(BlockState state, BlockRotation rotation) {
// if (new_state.getBlock() == ReFramed.PILLARS_WALL) return Map.of(1, 1); // TODO: PILLARS WALL return state.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
return super.getThemeMap(state, new_state); }
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(AXIS, mirror.apply(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
} }
static { static {

View File

@ -0,0 +1,133 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.enums.WallShape;
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.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.stream.Collectors;
import static fr.adrien1106.reframed.block.ReFramedWallBlock.*;
import static net.minecraft.state.property.Properties.*;
import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedPillarsWallBlock extends WaterloggableReFramedDoubleBlock {
public ReFramedPillarsWallBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(EAST_WALL_SHAPE, WallShape.NONE)
.with(NORTH_WALL_SHAPE, WallShape.NONE)
.with(WEST_WALL_SHAPE, WallShape.NONE)
.with(SOUTH_WALL_SHAPE, WallShape.NONE)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EAST_WALL_SHAPE, NORTH_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE));
}
@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;
BlockState top_state = dir == Direction.UP? other_state: world.getBlockState(pos.up());
boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN);
VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN);
Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
.collect(Collectors.toMap(d -> d, d -> {
if (d == dir) return other_state;
return world.getBlockState(pos.offset(d));
}));
return getWallState(new_state, top_state, neighbors, top_shape, fs, world, pos);
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
BlockState top_state = world.getBlockState(pos.up());
boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN);
VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN);
Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
.collect(Collectors.toMap(d -> d, d -> world.getBlockState(pos.offset(d))));
return getWallState(state, top_state, neighbors, top_shape, fs, world, pos);
}
@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 getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
if (isGhost(view, pos)) return empty();
VoxelShape shape = WALL_VOXELS[9];
for (Direction dir : Direction.Type.HORIZONTAL) {
if (state.get(getWallShape(dir)) != WallShape.NONE)
shape = VoxelShapes.union(shape, WALL_VOXELS[8 + dir.ordinal()]);
}
return shape;
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return isGhost(view, pos) ? empty(): getOutlineShape(state, view, pos, ShapeContext.absent());
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
VoxelShape shape = WALL_VOXELS[0];
for (Direction dir: Direction.Type.HORIZONTAL) {
WallShape wall_shape = state.get(getWallShape(dir));
if (wall_shape != WallShape.NONE)
shape = VoxelShapes.union(shape, WALL_VOXELS[1 + (wall_shape.ordinal()-1) * 4 + (dir.ordinal() - 2)]);
}
return shape;
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getWallShape(rotation.rotate(dir)), state.get(getWallShape(dir)))
, (prev, next) -> next);
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getWallShape(mirror.apply(dir)), state.get(getWallShape(dir)))
, (prev, next) -> next);
}
@Override
public VoxelShape getShape(BlockState state, int i) {
if (i == 1) return WALL_VOXELS[0];
VoxelShape shape = VoxelShapes.empty();
for (Direction dir: Direction.Type.HORIZONTAL) {
WallShape wall_shape = state.get(getWallShape(dir));
if (wall_shape != WallShape.NONE)
shape = VoxelShapes.union(shape, WALL_VOXELS[1 + (wall_shape.ordinal()-1) * 4 + (dir.ordinal() - 2)]);
}
return shape;
}
}

View File

@ -7,6 +7,8 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
@ -33,11 +35,6 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock {
setDefaultState(getDefaultState().with(FACING, Direction.DOWN)); setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(FACING);
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(FACING)); super.appendProperties(builder.add(FACING));
@ -77,6 +74,16 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock {
return getSlabShape(state.get(FACING)); return getSlabShape(state.get(FACING));
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(FACING, mirror.apply(state.get(FACING)));
}
public static VoxelShape getSlabShape(Direction side) { public static VoxelShape getSlabShape(Direction side) {
return switch (side) { return switch (side) {
case DOWN -> DOWN; case DOWN -> DOWN;

View File

@ -4,11 +4,14 @@ import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.*; import static fr.adrien1106.reframed.block.ReFramedSlabBlock.*;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
import static net.minecraft.state.property.Properties.AXIS; import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock { public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock {
@ -18,11 +21,6 @@ public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock {
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y)); setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(AXIS);
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(AXIS)); super.appendProperties(builder.add(AXIS));
@ -34,6 +32,16 @@ public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock {
return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis()); return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis());
} }
@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());
}
@Override @Override
public VoxelShape getShape(BlockState state, int i) { public VoxelShape getShape(BlockState state, int i) {
return switch (state.get(AXIS)) { return switch (state.get(AXIS)) {

View File

@ -10,6 +10,8 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -34,11 +36,6 @@ public class ReFramedSmallCubeBlock extends WaterloggableReFramedBlock {
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN)); setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(CORNER);
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER)); super.appendProperties(builder.add(CORNER));
@ -130,6 +127,16 @@ public class ReFramedSmallCubeBlock extends WaterloggableReFramedBlock {
return SMALL_CUBE_VOXELS[state.get(CORNER).getID()]; return SMALL_CUBE_VOXELS[state.get(CORNER).getID()];
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(CORNER, state.get(CORNER).rotate(rotation));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(CORNER, state.get(CORNER).mirror(mirror));
}
@Override @Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) { public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
if (new_state.isOf(ReFramed.HALF_STAIRS_SLAB)) return Map.of(1, 2); if (new_state.isOf(ReFramed.HALF_STAIRS_SLAB)) return Map.of(1, 2);

View File

@ -8,6 +8,8 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
@ -25,11 +27,6 @@ public class ReFramedSmallCubesStepBlock extends WaterloggableReFramedDoubleBloc
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN)); setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(EDGE);
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE)); super.appendProperties(builder.add(EDGE));
@ -42,7 +39,7 @@ public class ReFramedSmallCubesStepBlock extends WaterloggableReFramedDoubleBloc
} }
@Override @Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return isGhost(view, pos) ? empty(): getStepShape(state.get(EDGE)); return isGhost(view, pos) ? empty(): getStepShape(state.get(EDGE));
} }
@ -51,6 +48,16 @@ public class ReFramedSmallCubesStepBlock extends WaterloggableReFramedDoubleBloc
return getStepShape(state.get(EDGE)); return getStepShape(state.get(EDGE));
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(EDGE, state.get(EDGE).rotate(rotation));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(EDGE, state.get(EDGE).mirror(mirror));
}
@Override @Override
public VoxelShape getShape(BlockState state, int i) { public VoxelShape getShape(BlockState state, int i) {
Edge edge = state.get(EDGE); Edge edge = state.get(EDGE);

View File

@ -11,6 +11,8 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.function.BooleanBiFunction; import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
@ -31,18 +33,12 @@ import static fr.adrien1106.reframed.util.blocks.StairShape.*;
public class ReFramedStairBlock extends WaterloggableReFramedBlock { public class ReFramedStairBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] STAIR_VOXELS; public static final VoxelShape[] STAIR_VOXELS;
private record ModelCacheKey(Edge edge, StairShape shape) {}
public ReFramedStairBlock(Settings settings) { public ReFramedStairBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT)); setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(EDGE), state.get(STAIR_SHAPE));
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE, STAIR_SHAPE)); super.appendProperties(builder.add(EDGE, STAIR_SHAPE));
@ -101,6 +97,31 @@ public class ReFramedStairBlock extends WaterloggableReFramedBlock {
return getStairShape(state.get(EDGE), state.get(STAIR_SHAPE)); return getStairShape(state.get(EDGE), state.get(STAIR_SHAPE));
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
Edge prev_edge = state.get(EDGE);
Edge edge = prev_edge.rotate(rotation);
if (prev_edge.getAxis() == Direction.Axis.Y) return state.with(EDGE, edge);
if (prev_edge.getFace().getDirection() == edge.getFace().getDirection()) // 90° rotations
state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).mirror());
else state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).flip());
if (prev_edge.getAxis() == edge.getAxis()) // 180° rotation
state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).mirror());
return state.with(EDGE, edge);
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
Edge prev_edge = state.get(EDGE);
Edge edge = prev_edge.mirror(mirror);
return state
.with(STAIR_SHAPE, prev_edge == edge ? state.get(STAIR_SHAPE).mirror() : state.get(STAIR_SHAPE).flip())
.with(EDGE, edge);
}
public static VoxelShape getStairShape(Edge edge, StairShape shape) { public static VoxelShape getStairShape(Edge edge, StairShape shape) {
return STAIR_VOXELS[edge.getID() * 9 + shape.getID()]; return STAIR_VOXELS[edge.getID() * 9 + shape.getID()];
} }

View File

@ -7,6 +7,8 @@ import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
@ -22,18 +24,12 @@ import static fr.adrien1106.reframed.util.blocks.BlockProperties.STAIR_SHAPE;
public class ReFramedStairsCubeBlock extends ReFramedDoubleBlock { public class ReFramedStairsCubeBlock extends ReFramedDoubleBlock {
private static final VoxelShape[] STAIRS_CUBE_VOXELS = VoxelListBuilder.buildFrom(STAIR_VOXELS); private static final VoxelShape[] STAIRS_CUBE_VOXELS = VoxelListBuilder.buildFrom(STAIR_VOXELS);
private record ModelCacheKey(Edge edge, StairShape shape) {}
public ReFramedStairsCubeBlock(Settings settings) { public ReFramedStairsCubeBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(STAIR_SHAPE, StairShape.STRAIGHT)); setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(STAIR_SHAPE, StairShape.STRAIGHT));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(EDGE), state.get(STAIR_SHAPE));
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE, STAIR_SHAPE)); super.appendProperties(builder.add(EDGE, STAIR_SHAPE));
@ -54,6 +50,32 @@ public class ReFramedStairsCubeBlock extends ReFramedDoubleBlock {
return super.getPlacementState(ctx).with(EDGE, face).with(STAIR_SHAPE, shape); return super.getPlacementState(ctx).with(EDGE, face).with(STAIR_SHAPE, shape);
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
Edge prev_edge = state.get(EDGE);
Edge edge = prev_edge.rotate(rotation);
if (prev_edge.getAxis() == Direction.Axis.Y) return state.with(EDGE, edge);
if (prev_edge.getFace().getDirection() == edge.getFace().getDirection()) // 90° rotations
state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).mirror());
else state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).flip());
if (prev_edge.getAxis() == edge.getAxis()) // 180° rotation
state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).mirror());
return state.with(EDGE, edge);
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
Edge prev_edge = state.get(EDGE);
Edge edge = prev_edge.mirror(mirror);
return state
.with(STAIR_SHAPE, prev_edge == edge ? state.get(STAIR_SHAPE).mirror() : state.get(STAIR_SHAPE).flip())
.with(EDGE, edge);
}
@Override @Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved); super.onStateReplaced(state, world, pos, newState, moved);

View File

@ -10,6 +10,8 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -34,11 +36,6 @@ public class ReFramedStepBlock extends WaterloggableReFramedBlock {
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN)); setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(EDGE);
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE)); super.appendProperties(builder.add(EDGE));
@ -130,6 +127,16 @@ public class ReFramedStepBlock extends WaterloggableReFramedBlock {
return getStepShape(state.get(EDGE)); return getStepShape(state.get(EDGE));
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(EDGE, state.get(EDGE).rotate(rotation));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(EDGE, state.get(EDGE).mirror(mirror));
}
public static VoxelShape getStepShape(Edge edge) { public static VoxelShape getStepShape(Edge edge) {
return STEP_VOXELS[edge.getID()]; return STEP_VOXELS[edge.getID()];
} }

View File

@ -7,6 +7,8 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; 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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Direction.Axis; import net.minecraft.util.math.Direction.Axis;
@ -22,18 +24,12 @@ import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.util.shape.VoxelShapes.empty; import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock { public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock {
private record ModelCacheKey(Direction facing, Axis axis) {}
public ReFramedStepsSlabBlock(Settings settings) { public ReFramedStepsSlabBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(FACING, Direction.DOWN).with(AXIS, Axis.X)); setDefaultState(getDefaultState().with(FACING, Direction.DOWN).with(AXIS, Axis.X));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(FACING), state.get(AXIS));
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(FACING, AXIS)); super.appendProperties(builder.add(FACING, AXIS));
@ -48,7 +44,7 @@ public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock {
} }
@Override @Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return isGhost(view, pos) ? empty() : getSlabShape(state.get(FACING)); return isGhost(view, pos) ? empty() : getSlabShape(state.get(FACING));
} }
@ -57,6 +53,21 @@ public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock {
return getSlabShape(state.get(FACING)); return getSlabShape(state.get(FACING));
} }
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state
.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis())
.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
if (state.get(FACING).getAxis() != Axis.Y)
return state.with(FACING, mirror.apply(state.get(FACING)));
return state;
}
@Override @Override
public VoxelShape getShape(BlockState state, int i) { public VoxelShape getShape(BlockState state, int i) {
Axis axis = state.get(AXIS); Axis axis = state.get(AXIS);

View File

@ -0,0 +1,159 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.*;
import net.minecraft.block.enums.BlockHalf;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
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.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
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 ReFramedTrapdoorBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] TRAPDOOR_VOXELS;
public ReFramedTrapdoorBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(HORIZONTAL_FACING, Direction.NORTH)
.with(BLOCK_HALF, BlockHalf.BOTTOM)
.with(OPEN, false)
.with(POWERED, false)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(HORIZONTAL_FACING, BLOCK_HALF, OPEN, POWERED));
}
@Override
public void neighborUpdate(BlockState state, World world, BlockPos pos, Block source, BlockPos sourcePos, boolean notify) {
if (world.isClient) return;
boolean powered = world.isReceivingRedstonePower(pos);
if (powered != state.get(POWERED)) {
if (state.get(OPEN) != powered) {
state = state.with(OPEN, powered);
playToggleSound(null, world, pos, powered);
}
world.setBlockState(pos, state.with(POWERED, powered), 2);
if (state.get(WATERLOGGED)) {
world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
}
}
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
Direction side = ctx.getSide();
if (side.getAxis().isVertical()) state = state
.with(HORIZONTAL_FACING, ctx.getHorizontalPlayerFacing().getOpposite())
.with(BLOCK_HALF, side == Direction.UP ? BlockHalf.BOTTOM : BlockHalf.TOP);
else state = state
.with(HORIZONTAL_FACING, side)
.with(BLOCK_HALF, ctx.getHitPos().getY() - pos.getY() > 0.5 ? BlockHalf.TOP : BlockHalf.BOTTOM);
if (world.isReceivingRedstonePower(pos)) state = state.with(OPEN, true).with(POWERED, true);
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 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;
flip(state, world, pos, player);
return ActionResult.success(world.isClient);
}
@Override
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return switch (type) {
case LAND, AIR -> state.get(OPEN);
case WATER -> state.get(WATERLOGGED);
};
}
@Override
public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stack_merger) {
if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK
&& !world.isClient()
&& !state.get(POWERED)
) flip(state, world, pos, null);
super.onExploded(state, world, pos, explosion, stack_merger);
}
private void flip(BlockState state, World world, BlockPos pos, @Nullable PlayerEntity player) {
state = state.cycle(OPEN);
world.setBlockState(pos, state, 2);
this.playToggleSound(player, world, pos, state.get(OPEN));
}
protected void playToggleSound(@Nullable PlayerEntity player, World world, BlockPos pos, boolean open) {
world.playSound(player, pos, open ? BlockSetType.OAK.trapdoorOpen() : BlockSetType.OAK.trapdoorClose(), SoundCategory.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F);
world.emitGameEvent(player, open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
int index;
if (!state.get(OPEN)) index = state.get(BLOCK_HALF) == BlockHalf.BOTTOM ? 0 : 1;
else index = state.get(HORIZONTAL_FACING).ordinal();
return TRAPDOOR_VOXELS[index];
}
@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)));
}
static {
VoxelShape SHAPE = createCuboidShape(0, 0, 0, 16, 3, 16);
TRAPDOOR_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 6)
.add(VoxelHelper::mirrorY)
.add(VoxelHelper::rotateX)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.build();
}
}

View File

@ -7,6 +7,8 @@ import net.minecraft.item.ItemPlacementContext;
import net.minecraft.registry.tag.BlockTags; import net.minecraft.registry.tag.BlockTags;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.state.property.Property; import net.minecraft.state.property.Property;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.function.BooleanBiFunction; import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
@ -15,19 +17,20 @@ import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldAccess; import net.minecraft.world.WorldAccess;
import net.minecraft.world.WorldView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static net.minecraft.state.property.Properties.*; import static net.minecraft.state.property.Properties.*;
public class ReframedWallBlock extends WaterloggableReFramedBlock { public class ReFramedWallBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] WALL_VOXELS; public static final VoxelShape[] WALL_VOXELS;
private record ModelCacheKey(boolean up, WallShape east, WallShape north, WallShape west, WallShape south) {} public ReFramedWallBlock(Settings settings) {
public ReframedWallBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState() setDefaultState(getDefaultState()
.with(UP, true) .with(UP, true)
@ -38,17 +41,6 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock {
); );
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(
state.get(UP),
state.get(EAST_WALL_SHAPE),
state.get(NORTH_WALL_SHAPE),
state.get(WEST_WALL_SHAPE),
state.get(SOUTH_WALL_SHAPE)
);
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(UP, EAST_WALL_SHAPE, NORTH_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); super.appendProperties(builder.add(UP, EAST_WALL_SHAPE, NORTH_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE));
@ -61,34 +53,13 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock {
BlockState top_state = dir == Direction.UP? other_state: world.getBlockState(pos.up()); BlockState top_state = dir == Direction.UP? other_state: world.getBlockState(pos.up());
boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN); boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN);
VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN); VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN);
if (dir == Direction.UP) {
for (Direction d : Direction.Type.HORIZONTAL) {
Property<WallShape> wall_shape = getWallShape(d);
if (state.get(wall_shape) == WallShape.NONE) continue;
new_state = new_state.with(
wall_shape,
fs
|| (top_state.contains(wall_shape) && top_state.get(wall_shape) != WallShape.NONE)
|| shouldUseTall(WALL_VOXELS[dir.ordinal() + 3], top_shape)
? WallShape.TALL
: WallShape.LOW
);
}
return new_state.with(UP, shouldHavePost(new_state, top_state, top_shape));
}
boolean side_full = other_state.isSideSolidFullSquare(world, moved, dir.getOpposite()); Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
if (shouldConnectTo(other_state, side_full, dir.getOpposite())) { .collect(Collectors.toMap(d -> d, d -> {
Property<WallShape> wall_shape = getWallShape(dir); if (d == dir) return other_state;
new_state = new_state.with( return world.getBlockState(pos.offset(d));
wall_shape, }));
fs new_state = getWallState(new_state, top_state, neighbors, top_shape, fs, world, pos);
|| (top_state.contains(wall_shape) && top_state.get(wall_shape) != WallShape.NONE)
|| shouldUseTall(WALL_VOXELS[dir.ordinal() + 3], top_shape)
? WallShape.TALL
: WallShape.LOW
);
} else new_state = new_state.with(getWallShape(dir), WallShape.NONE);
return new_state.with(UP, shouldHavePost(new_state, top_state, top_shape)); return new_state.with(UP, shouldHavePost(new_state, top_state, top_shape));
} }
@ -97,25 +68,14 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock {
BlockState state = super.getPlacementState(ctx); BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld(); World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos(); BlockPos pos = ctx.getBlockPos();
BlockState top_state = world.getBlockState(pos.up()); BlockState top_state = world.getBlockState(pos.up());
boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN); boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN);
VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN); VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN);
for (Direction dir : Direction.Type.HORIZONTAL) {
BlockPos offset = pos.offset(dir); Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
BlockState neighbor = world.getBlockState(offset); .collect(Collectors.toMap(d -> d, d -> world.getBlockState(pos.offset(d))));
boolean side_full = neighbor.isSideSolidFullSquare(world, offset, dir.getOpposite()); state = getWallState(state, top_state, neighbors, top_shape, fs, world, pos);
if (shouldConnectTo(neighbor, side_full, dir.getOpposite())) {
Property<WallShape> wall_shape = getWallShape(dir);
state = state.with(
wall_shape,
fs
|| (top_state.contains(wall_shape) && top_state.get(wall_shape) != WallShape.NONE)
|| shouldUseTall(WALL_VOXELS[dir.ordinal() + 3], top_shape)
? WallShape.TALL
: WallShape.LOW
);
}
}
return state.with(UP, shouldHavePost(state, top_state, top_shape)); return state.with(UP, shouldHavePost(state, top_state, top_shape));
} }
@ -148,9 +108,43 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock {
return shape; return shape;
} }
private static boolean shouldHavePost(BlockState state, BlockState top_state, VoxelShape top_shape) { @Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getWallShape(rotation.rotate(dir)), state.get(getWallShape(dir)))
, (prev, next) -> next);
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getWallShape(mirror.apply(dir)), state.get(getWallShape(dir)))
, (prev, next) -> next);
}
public static BlockState getWallState(BlockState state, BlockState top_state, Map<Direction, BlockState> neighbors, VoxelShape top_shape, boolean fs, WorldView world, BlockPos pos) {
for (Direction dir : Direction.Type.HORIZONTAL) {
BlockPos offset = pos.offset(dir);
BlockState neighbor = neighbors.get(dir);
boolean side_full = neighbor.isSideSolidFullSquare(world, offset, dir.getOpposite());
Property<WallShape> wall_shape = getWallShape(dir);
if (shouldConnectTo(neighbor, side_full, dir.getOpposite())) {
state = state.with(
wall_shape,
fs
|| (top_state.contains(wall_shape) && top_state.get(wall_shape) != WallShape.NONE)
|| shouldUseTall(WALL_VOXELS[dir.ordinal() + 3], top_shape)
? WallShape.TALL
: WallShape.LOW
);
} else state = state.with(wall_shape, WallShape.NONE);
}
return state;
}
public static boolean shouldHavePost(BlockState state, BlockState top_state, VoxelShape top_shape) {
// above has post // above has post
if (top_state.contains(NORTH_WALL_SHAPE) && top_state.get(UP)) return true; if ((top_state.contains(UP) && top_state.get(UP)) || (!top_state.contains(UP) && top_state.contains(NORTH_WALL_SHAPE))) return true;
if (Stream.of(Direction.SOUTH, Direction.EAST) // Opposites are different if (Stream.of(Direction.SOUTH, Direction.EAST) // Opposites are different
.anyMatch(dir -> state.get(getWallShape(dir)) != state.get(getWallShape(dir.getOpposite()))) .anyMatch(dir -> state.get(getWallShape(dir)) != state.get(getWallShape(dir.getOpposite())))
@ -160,23 +154,22 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock {
if (Direction.Type.HORIZONTAL.stream().allMatch(dir -> state.get(getWallShape(dir)) == WallShape.NONE)) if (Direction.Type.HORIZONTAL.stream().allMatch(dir -> state.get(getWallShape(dir)) == WallShape.NONE))
return true; return true;
// tall Matching sides // Matching sides
if (Stream.of(Direction.SOUTH, Direction.EAST) if (Stream.of(Direction.SOUTH, Direction.EAST)
.anyMatch(dir -> .anyMatch(dir ->
state.get(getWallShape(dir)) == WallShape.TALL state.get(getWallShape(dir)) == state.get(getWallShape(dir.getOpposite()))
&& state.get(getWallShape(dir.getOpposite())) == WallShape.TALL
)) return false; )) return false;
return top_state.isIn(BlockTags.WALL_POST_OVERRIDE) || top_shape == null || shouldUseTall(WALL_VOXELS[0], top_shape); return top_state.isIn(BlockTags.WALL_POST_OVERRIDE) || top_shape == null || shouldUseTall(WALL_VOXELS[0], top_shape);
} }
private static boolean shouldConnectTo(BlockState state, boolean side_full, Direction side) { public static boolean shouldConnectTo(BlockState state, boolean side_full, Direction side) {
Block block = state.getBlock(); Block block = state.getBlock();
boolean bl = block instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, side); boolean bl = block instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, side);
return state.isIn(BlockTags.WALLS) || !WallBlock.cannotConnect(state) && side_full || block instanceof PaneBlock || bl; return state.isIn(BlockTags.WALLS) || !WallBlock.cannotConnect(state) && side_full || block instanceof PaneBlock || bl;
} }
private static boolean shouldUseTall(VoxelShape self_shape, VoxelShape other_shape) { public static boolean shouldUseTall(VoxelShape self_shape, VoxelShape other_shape) {
return !VoxelShapes.matchesAnywhere( return !VoxelShapes.matchesAnywhere(
self_shape, self_shape,
other_shape, other_shape,
@ -184,7 +177,7 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock {
); );
} }
private static Property<WallShape> getWallShape(Direction dir) { public static Property<WallShape> getWallShape(Direction dir) {
return switch (dir) { return switch (dir) {
case EAST -> EAST_WALL_SHAPE; case EAST -> EAST_WALL_SHAPE;
case NORTH -> NORTH_WALL_SHAPE; case NORTH -> NORTH_WALL_SHAPE;

View File

@ -14,10 +14,6 @@ import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.ChunkSectionPos; import net.minecraft.util.math.ChunkSectionPos;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.*;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.STAIR_SHAPE;
import static net.minecraft.state.property.Properties.*;
public class ReFramedClient implements ClientModInitializer { public class ReFramedClient implements ClientModInitializer {
public static final ReFramedModelProvider PROVIDER = new ReFramedModelProvider(); public static final ReFramedModelProvider PROVIDER = new ReFramedModelProvider();
@ -31,92 +27,109 @@ public class ReFramedClient implements ClientModInitializer {
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0])); BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0]));
// CUBE // CUBE
HELPER.addReFramedModel("cube" , HELPER.auto(new Identifier("block/cube"), 2)); HELPER.addReFramedModel("cube" , HELPER.auto(new Identifier("block/cube")));
// SMALL_CUBE // SMALL_CUBE
HELPER.addReFramedModel("small_cube" , HELPER.auto(ReFramed.id("block/small_cube/base"), 9, CORNER)); HELPER.addReFramedModel("small_cube" , HELPER.auto(ReFramed.id("block/small_cube/base")));
// SMALL_CUBES_STEP // SMALL_CUBES_STEP
HELPER.addReFramedModel("small_cubes_step" , HELPER.autoDouble(ReFramed.id("block/small_cube/base"), ReFramed.id("block/small_cube/step/base"), 9, EDGE)); HELPER.addReFramedModel("small_cubes_step" , HELPER.autoDouble(ReFramed.id("block/small_cube/base"), ReFramed.id("block/small_cube/step/base")));
HELPER.addReFramedModel("small_cubes_step_reverse" , HELPER.autoDouble(ReFramed.id("block/small_cube/step/base"), ReFramed.id("block/small_cube/base"), 4, EDGE)); HELPER.addReFramedModel("small_cubes_step_reverse" , HELPER.autoDouble(ReFramed.id("block/small_cube/step/base"), ReFramed.id("block/small_cube/base")));
// SLAB // SLAB
HELPER.addReFramedModel("slab" , HELPER.auto(new Identifier("block/slab"), 7, FACING)); HELPER.addReFramedModel("slab" , HELPER.auto(new Identifier("block/slab")));
// SLAB_CUBE // SLAB_CUBE
HELPER.addReFramedModel("double_slab" , HELPER.autoDouble(new Identifier("block/slab"), new Identifier("block/slab_top"), 4, AXIS)); HELPER.addReFramedModel("double_slab" , HELPER.autoDouble(new Identifier("block/slab"), new Identifier("block/slab_top")));
// STAIR // STAIR
HELPER.addReFramedModel("stair" , HELPER.auto(ReFramed.id("block/stair/straight"), 13, EDGE)); HELPER.addReFramedModel("stair" , HELPER.auto(ReFramed.id("block/stair/straight")));
HELPER.addReFramedModel("outers_stair" , HELPER.auto(ReFramed.id("block/stair/double_outer"), 24, EDGE, STAIR_SHAPE)); HELPER.addReFramedModel("outers_stair" , HELPER.auto(ReFramed.id("block/stair/double_outer")));
HELPER.addReFramedModel("inner_stair" , HELPER.auto(ReFramed.id("block/stair/inner"), 24, EDGE, STAIR_SHAPE)); HELPER.addReFramedModel("inner_stair" , HELPER.auto(ReFramed.id("block/stair/inner")));
HELPER.addReFramedModel("outer_stair" , HELPER.auto(ReFramed.id("block/stair/outer"), 16, EDGE, STAIR_SHAPE)); HELPER.addReFramedModel("outer_stair" , HELPER.auto(ReFramed.id("block/stair/outer")));
HELPER.addReFramedModel("outer_side_stair" , HELPER.auto(ReFramed.id("block/stair/outer_side"), 32, EDGE, STAIR_SHAPE)); HELPER.addReFramedModel("outer_side_stair" , HELPER.auto(ReFramed.id("block/stair/outer_side")));
// STAIRS_CUBE // STAIRS_CUBE
HELPER.addReFramedModel("stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/straight"), ReFramed.id("block/stair/cube/straight"), 13, EDGE)); HELPER.addReFramedModel("stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/straight"), ReFramed.id("block/stair/cube/straight")));
HELPER.addReFramedModel("outers_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/double_outer"), ReFramed.id("block/stair/cube/double_outer"), 24, EDGE, STAIR_SHAPE)); HELPER.addReFramedModel("outers_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/double_outer"), ReFramed.id("block/stair/cube/double_outer")));
HELPER.addReFramedModel("inner_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/inner"), ReFramed.id("block/stair/cube/inner"), 24, EDGE, STAIR_SHAPE)); HELPER.addReFramedModel("inner_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/inner"), ReFramed.id("block/stair/cube/inner")));
HELPER.addReFramedModel("outer_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/outer"), ReFramed.id("block/stair/cube/outer"), 16, EDGE, STAIR_SHAPE)); HELPER.addReFramedModel("outer_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/outer"), ReFramed.id("block/stair/cube/outer")));
HELPER.addReFramedModel("outer_side_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/outer_side"), ReFramed.id("block/stair/cube/outer_side"), 32, EDGE, STAIR_SHAPE)); HELPER.addReFramedModel("outer_side_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/outer_side"), ReFramed.id("block/stair/cube/outer_side")));
// HALF_STAIR // HALF_STAIR
HELPER.addReFramedModel("half_stair_down" , HELPER.auto(ReFramed.id("block/half_stair/down"), 9, CORNER, CORNER_FACE)); HELPER.addReFramedModel("half_stair_down" , HELPER.auto(ReFramed.id("block/half_stair/down")));
HELPER.addReFramedModel("half_stair_side" , HELPER.auto(ReFramed.id("block/half_stair/side"), 16, CORNER, CORNER_FACE)); HELPER.addReFramedModel("half_stair_side" , HELPER.auto(ReFramed.id("block/half_stair/side")));
// HALF_STAIRS_SLAB // HALF_STAIRS_SLAB
HELPER.addReFramedModel("half_stairs_slab_down" , HELPER.autoDouble(ReFramed.id("block/half_stair/down"), ReFramed.id("block/half_stair/slab/down"), 9, CORNER, CORNER_FACE)); HELPER.addReFramedModel("half_stairs_slab_down" , HELPER.autoDouble(ReFramed.id("block/half_stair/down"), ReFramed.id("block/half_stair/slab/down")));
HELPER.addReFramedModel("half_stairs_slab_side" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/slab/side"), 16, CORNER, CORNER_FACE)); HELPER.addReFramedModel("half_stairs_slab_side" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/slab/side")));
// HALF_STAIRS_STAIR // HALF_STAIRS_STAIR
HELPER.addReFramedModel("half_stairs_stair_down" , HELPER.autoDouble(ReFramed.id("block/half_stair/down"), ReFramed.id("block/half_stair/stair/down"), 5, EDGE)); HELPER.addReFramedModel("half_stairs_stair_down" , HELPER.autoDouble(ReFramed.id("block/half_stair/down"), ReFramed.id("block/half_stair/stair/down")));
HELPER.addReFramedModel("half_stairs_stair_side" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/stair/side"), 6, EDGE)); HELPER.addReFramedModel("half_stairs_stair_side" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/stair/side")));
HELPER.addReFramedModel("half_stairs_stair_reverse" , HELPER.autoDouble(ReFramed.id("block/half_stair/stair/side"), ReFramed.id("block/half_stair/side"), 2, EDGE)); HELPER.addReFramedModel("half_stairs_stair_reverse" , HELPER.autoDouble(ReFramed.id("block/half_stair/stair/side"), ReFramed.id("block/half_stair/side")));
// STEP // STEP
HELPER.addReFramedModel("step" , HELPER.auto(ReFramed.id("block/step/down"), 13, EDGE)); HELPER.addReFramedModel("step" , HELPER.auto(ReFramed.id("block/step/down")));
// STEPS_SLAB // STEPS_SLAB
HELPER.addReFramedModel("steps_slab" , HELPER.autoDouble(ReFramed.id("block/step/down"), ReFramed.id("block/step/slab/down"), 5, FACING, AXIS)); HELPER.addReFramedModel("steps_slab" , HELPER.autoDouble(ReFramed.id("block/step/down"), ReFramed.id("block/step/slab/down")));
HELPER.addReFramedModel("steps_slab_side" , HELPER.autoDouble(ReFramed.id("block/step/side"), ReFramed.id("block/step/slab/side"), 8, FACING, AXIS)); HELPER.addReFramedModel("steps_slab_side" , HELPER.autoDouble(ReFramed.id("block/step/side"), ReFramed.id("block/step/slab/side")));
// LAYER // LAYER
HELPER.addReFramedModel("layer_1" , HELPER.auto(new Identifier("block/snow_height2"), 7, FACING)); HELPER.addReFramedModel("layer_1" , HELPER.auto(new Identifier("block/snow_height2")));
HELPER.addReFramedModel("layer_2" , HELPER.auto(new Identifier("block/snow_height4"), 6, FACING)); HELPER.addReFramedModel("layer_2" , HELPER.auto(new Identifier("block/snow_height4")));
HELPER.addReFramedModel("layer_3" , HELPER.auto(new Identifier("block/snow_height6"), 6, FACING)); HELPER.addReFramedModel("layer_3" , HELPER.auto(new Identifier("block/snow_height6")));
HELPER.addReFramedModel("layer_4" , HELPER.auto(new Identifier("block/snow_height8"), 6, FACING)); HELPER.addReFramedModel("layer_4" , HELPER.auto(new Identifier("block/snow_height8")));
HELPER.addReFramedModel("layer_5" , HELPER.auto(new Identifier("block/snow_height10"), 6, FACING)); HELPER.addReFramedModel("layer_5" , HELPER.auto(new Identifier("block/snow_height10")));
HELPER.addReFramedModel("layer_6" , HELPER.auto(new Identifier("block/snow_height12"), 6, FACING)); HELPER.addReFramedModel("layer_6" , HELPER.auto(new Identifier("block/snow_height12")));
HELPER.addReFramedModel("layer_7" , HELPER.auto(new Identifier("block/snow_height14"), 6, FACING)); HELPER.addReFramedModel("layer_7" , HELPER.auto(new Identifier("block/snow_height14")));
HELPER.addReFramedModel("layer_8" , HELPER.auto(new Identifier("block/cube"), 1)); HELPER.addReFramedModel("layer_8" , HELPER.auto(new Identifier("block/cube")));
// PILLAR // PILLAR
HELPER.addReFramedModel("pillar" , HELPER.auto(ReFramed.id("block/pillar"), 4, AXIS)); HELPER.addReFramedModel("pillar" , HELPER.auto(ReFramed.id("block/pillar")));
// WALL // WALL
HELPER.addReFramedModel("wall_inventory" , HELPER.auto(ReFramed.id("block/wall/inventory/default"), 1)); HELPER.addReFramedModel("wall_inventory" , HELPER.auto(ReFramed.id("block/wall/inventory/default")));
// --------------------- pillar // --------------------- pillar
HELPER.addReFramedModel("wall_core" , HELPER.auto(ReFramed.id("block/wall/pillar/core"), 1, UP)); HELPER.addReFramedModel("wall_core" , HELPER.auto(ReFramed.id("block/wall/pillar/core")));
HELPER.addReFramedModel("wall_pillar_low" , HELPER.auto(ReFramed.id("block/wall/pillar/low"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_pillar_low" , HELPER.auto(ReFramed.id("block/wall/pillar/low")));
HELPER.addReFramedModel("wall_pillar_tall" , HELPER.auto(ReFramed.id("block/wall/pillar/tall"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_pillar_tall" , HELPER.auto(ReFramed.id("block/wall/pillar/tall")));
HELPER.addReFramedModel("wall_pillar_none" , HELPER.auto(ReFramed.id("block/wall/pillar/none"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_pillar_none" , HELPER.auto(ReFramed.id("block/wall/pillar/none")));
// --------------------- side // --------------------- side
HELPER.addReFramedModel("wall_side_low" , HELPER.auto(ReFramed.id("block/wall/side/low"), 92, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_side_low" , HELPER.auto(ReFramed.id("block/wall/side/low")));
HELPER.addReFramedModel("wall_side_tall" , HELPER.auto(ReFramed.id("block/wall/side/tall"), 92, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_side_tall" , HELPER.auto(ReFramed.id("block/wall/side/tall")));
// --------------------- junction // --------------------- junction
HELPER.addReFramedModel("wall_low_e" , HELPER.auto(ReFramed.id("block/wall/junction/low"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_e" , HELPER.auto(ReFramed.id("block/wall/junction/low")));
HELPER.addReFramedModel("wall_tall_e" , HELPER.auto(ReFramed.id("block/wall/junction/tall"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_e" , HELPER.auto(ReFramed.id("block/wall/junction/tall")));
// --------------------- junction_i // --------------------- junction_i
HELPER.addReFramedModel("wall_low_i" , HELPER.auto(ReFramed.id("block/wall/junction/low_i"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_i" , HELPER.auto(ReFramed.id("block/wall/junction/low_i")));
HELPER.addReFramedModel("wall_tall_i" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_i" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i")));
HELPER.addReFramedModel("wall_low_tall_i" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_i"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_tall_i" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_i")));
// --------------------- junction_c // --------------------- junction_c
HELPER.addReFramedModel("wall_low_c" , HELPER.auto(ReFramed.id("block/wall/junction/low_c"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_c" , HELPER.auto(ReFramed.id("block/wall/junction/low_c")));
HELPER.addReFramedModel("wall_tall_c" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_c" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c")));
HELPER.addReFramedModel("wall_low_tall_c" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_c"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_tall_c" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_c")));
HELPER.addReFramedModel("wall_tall_low_c" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_c"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_low_c" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_c")));
// --------------------- junction_t // --------------------- junction_t
HELPER.addReFramedModel("wall_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_t"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_t")));
HELPER.addReFramedModel("wall_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_t"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_t")));
HELPER.addReFramedModel("wall_tall_low_c_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_c_t"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_low_c_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_c_t")));
HELPER.addReFramedModel("wall_tall_i_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i_low_t"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_i_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i_low_t")));
HELPER.addReFramedModel("wall_low_i_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_i_tall_t"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_i_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_i_tall_t")));
HELPER.addReFramedModel("wall_low_tall_c_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_c_t"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_tall_c_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_c_t")));
HELPER.addReFramedModel("wall_low_c_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_c_tall_t"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_c_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_c_tall_t")));
HELPER.addReFramedModel("wall_tall_c_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c_low_t"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_c_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c_low_t")));
// --------------------- junction_x // --------------------- junction_x
HELPER.addReFramedModel("wall_low_x" , HELPER.auto(ReFramed.id("block/wall/junction/low_x"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_low_x" , HELPER.auto(ReFramed.id("block/wall/junction/low_x")));
HELPER.addReFramedModel("wall_tall_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_x"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_x")));
HELPER.addReFramedModel("wall_tall_i_low_i_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i_low_i_x"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_i_low_i_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i_low_i_x")));
HELPER.addReFramedModel("wall_tall_low_t_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_t_x"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_low_t_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_t_x")));
HELPER.addReFramedModel("wall_tall_c_low_c_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c_low_c_x"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_c_low_c_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c_low_c_x")));
HELPER.addReFramedModel("wall_tall_t_low_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_t_low_x"), 4, NORTH_WALL_SHAPE, EAST_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE)); HELPER.addReFramedModel("wall_tall_t_low_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_t_low_x")));
// PILLAR WALL
HELPER.addReFramedModel("pillars_wall_inventory" , HELPER.autoDouble(ReFramed.id("block/pillar"), ReFramed.id("block/wall/full/inventory/sides")));
HELPER.addReFramedModel("pillars_wall_low" , HELPER.autoDouble(ReFramed.id("block/wall/full/pillar/low"), ReFramed.id("block/wall/full/side/low")));
HELPER.addReFramedModel("pillars_wall_tall" , HELPER.autoDouble(ReFramed.id("block/wall/full/pillar/tall"), ReFramed.id("block/wall/full/side/tall")));
// PANE
HELPER.addReFramedModel("pane_inventory" , HELPER.auto(ReFramed.id("block/pane")));
HELPER.addReFramedModel("pane_post" , HELPER.auto(new Identifier("block/glass_pane_post")));
HELPER.addReFramedModel("pane_side" , HELPER.auto(new Identifier("block/glass_pane_side")));
HELPER.addReFramedModel("pane_side_alt" , HELPER.auto(new Identifier("block/glass_pane_side_alt")));
HELPER.addReFramedModel("pane_noside" , HELPER.auto(new Identifier("block/glass_pane_noside")));
HELPER.addReFramedModel("pane_noside_alt" , HELPER.auto(new Identifier("block/glass_pane_noside_alt")));
// TRAPDOOR
HELPER.addReFramedModel("trapdoor_open" , HELPER.auto(new Identifier("block/oak_trapdoor_open")));
HELPER.addReFramedModel("trapdoor_bottom" , HELPER.auto(new Identifier("block/oak_trapdoor_bottom")));
HELPER.addReFramedModel("trapdoor_top" , HELPER.auto(new Identifier("block/oak_trapdoor_top")));
// DOOR
HELPER.addReFramedModel("door_inventory" , HELPER.auto(ReFramed.id("block/door")));
//item model assignments (in lieu of models/item/___.json) //item model assignments (in lieu of models/item/___.json)
@ -134,11 +147,15 @@ public class ReFramedClient implements ClientModInitializer {
HELPER.assignItemModel("steps_slab" , ReFramed.STEPS_SLAB); HELPER.assignItemModel("steps_slab" , ReFramed.STEPS_SLAB);
HELPER.assignItemModel("layer_1" , ReFramed.LAYER); HELPER.assignItemModel("layer_1" , ReFramed.LAYER);
HELPER.assignItemModel("pillar" , ReFramed.PILLAR); HELPER.assignItemModel("pillar" , ReFramed.PILLAR);
HELPER.assignItemModel("pillars_wall_inventory", ReFramed.PILLARS_WALL);
HELPER.assignItemModel("wall_inventory" , ReFramed.WALL); HELPER.assignItemModel("wall_inventory" , ReFramed.WALL);
HELPER.assignItemModel("pane_inventory" , ReFramed.PANE);
HELPER.assignItemModel("trapdoor_bottom" , ReFramed.TRAPDOOR);
HELPER.assignItemModel("door_inventory" , ReFramed.DOOR);
} }
private void privateInit() { private void privateInit() {
//set up some magic to force chunk rerenders when you change a template (see TemplateEntity) //set up some magic to force chunk re-renders when you change a template (see TemplateEntity)
ReFramed.chunkRerenderProxy = (world, pos) -> { ReFramed.chunkRerenderProxy = (world, pos) -> {
if(world == MinecraftClient.getInstance().world) { if(world == MinecraftClient.getInstance().world) {
MinecraftClient.getInstance().worldRenderer.scheduleBlockRender( MinecraftClient.getInstance().worldRenderer.scheduleBlockRender(

View File

@ -13,7 +13,6 @@ import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.item.ItemConvertible; import net.minecraft.item.ItemConvertible;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -28,18 +27,18 @@ public class ReFramedClientHelper {
private final ReFramedModelProvider prov; private final ReFramedModelProvider prov;
public UnbakedRetexturedModel auto(Identifier parent, int model_count, Property<?>... properties) { public UnbakedRetexturedModel auto(Identifier parent) {
return new UnbakedAutoRetexturedModel(parent, model_count, properties); return new UnbakedAutoRetexturedModel(parent);
} }
public UnbakedRetexturedModel json(Identifier parent, int model_count, Property<?>... properties) { public UnbakedRetexturedModel json(Identifier parent) {
return new UnbakedJsonRetexturedModel(parent, model_count, properties); return new UnbakedJsonRetexturedModel(parent);
} }
public UnbakedModel autoDouble(Identifier first, Identifier second, int model_count, Property<?>... properties) { public UnbakedModel autoDouble(Identifier first, Identifier second) {
return new UnbakedDoubleRetexturedModel( return new UnbakedDoubleRetexturedModel(
auto(first, model_count, properties), auto(first),
auto(second, model_count, properties) auto(second)
); );
} }

View File

@ -17,8 +17,8 @@ import java.util.function.Supplier;
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements MultiRetexturableModel { public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements MultiRetexturableModel {
private final ForwardingBakedModel model_1, model_2; private final RetexturingBakedModel model_1, model_2;
public DoubleRetexturingBakedModel(ForwardingBakedModel model_1, ForwardingBakedModel model_2) { public DoubleRetexturingBakedModel(RetexturingBakedModel model_1, RetexturingBakedModel model_2) {
this.wrapped = model_1.getWrappedModel(); this.wrapped = model_1.getWrappedModel();
this.model_1 = model_1; this.model_1 = model_1;
this.model_2 = model_2; this.model_2 = model_2;
@ -44,7 +44,7 @@ public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements
} }
@Override @Override
public List<ForwardingBakedModel> models() { public List<RetexturingBakedModel> models() {
return List.of(model_1, model_2); return List.of(model_1, model_2);
} }
} }

View File

@ -1,10 +1,8 @@
package fr.adrien1106.reframed.client.model; package fr.adrien1106.reframed.client.model;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import java.util.List; import java.util.List;
public interface MultiRetexturableModel { public interface MultiRetexturableModel {
List<ForwardingBakedModel> models(); List<RetexturingBakedModel> models();
} }

View File

@ -20,38 +20,28 @@ import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.state.property.Property;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Stream;
public abstract class RetexturingBakedModel extends ForwardingBakedModel { public abstract class RetexturingBakedModel extends ForwardingBakedModel {
public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state, int model_count, Property<?>... properties) { public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state) {
this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this
this.appearance_manager = tam; this.appearance_manager = tam;
this.theme_index = theme_index; this.theme_index = theme_index;
this.uv_lock = settings.isUvLocked(); this.uv_lock = settings.isUvLocked();
this.item_state = item_state; this.item_state = item_state;
this.properties = properties;
BASE_MESH_CACHE = new Object2ObjectLinkedOpenHashMap<>(model_count, 0.25f) {
@Override
protected void rehash(int v) {}
};
} }
protected final CamoAppearanceManager appearance_manager; protected final CamoAppearanceManager appearance_manager;
protected final int theme_index; protected final int theme_index;
protected final boolean uv_lock; protected final boolean uv_lock;
protected final BlockState item_state; protected final BlockState item_state;
protected final Property<?>[] properties;
protected record MeshCacheKey(Object state_key, CamoAppearance appearance, int model_id) {} protected record MeshCacheKey(Object state_key, CamoAppearance appearance, int model_id) {}
/** cache that store retextured models */ /** cache that store retextured models */
@ -59,7 +49,11 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
protected final Cache<MeshCacheKey, Mesh> RETEXTURED_MESH_CACHE = CacheBuilder.newBuilder().maximumSize(256).build(); protected final Cache<MeshCacheKey, Mesh> RETEXTURED_MESH_CACHE = CacheBuilder.newBuilder().maximumSize(256).build();
/** cache that stores the base meshes which has the size of the amount of models */ /** cache that stores the base meshes which has the size of the amount of models */
protected final Object2ObjectLinkedOpenHashMap<Object, Mesh> BASE_MESH_CACHE; protected final Object2ObjectLinkedOpenHashMap<Object, Mesh> BASE_MESH_CACHE =
new Object2ObjectLinkedOpenHashMap<>(2, 0.25f) {
@Override
protected void rehash(int v) {}
};
protected static final Direction[] DIRECTIONS_AND_NULL; protected static final Direction[] DIRECTIONS_AND_NULL;
static { static {
@ -88,15 +82,18 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
return appearance_manager.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite(); return appearance_manager.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite();
} }
public int getThemeIndex() {
return theme_index;
}
@Override @Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) { 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; BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null;
QuadEmitter quad_emitter = context.getEmitter(); QuadEmitter quad_emitter = context.getEmitter();
List<?> model_key = Stream.of(properties).map(state::get).toList();
if(theme == null || theme.isAir()) { if(theme == null || theme.isAir()) {
getRetexturedMesh( getRetexturedMesh(
new MeshCacheKey( new MeshCacheKey(
model_key, hashCode(),
appearance_manager.getDefaultAppearance(theme_index), appearance_manager.getDefaultAppearance(theme_index),
0 0
), ),
@ -112,7 +109,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed); if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, world, pos, 0); int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, world, pos, 0);
MeshCacheKey key = new MeshCacheKey(model_key, camo, model_id); MeshCacheKey key = new MeshCacheKey(hashCode(), camo, model_id);
// do not clutter the cache with single-use meshes // do not clutter the cache with single-use meshes
Mesh untintedMesh = camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state); Mesh untintedMesh = camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state);
@ -154,6 +151,13 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
} }
} }
public boolean useAmbientOcclusion(BlockRenderView view, BlockPos pos) {
if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)) return false;
CamoAppearance appearance = appearance_manager
.getCamoAppearance(view, frame_entity.getTheme(theme_index), pos, theme_index, false);
return appearance.getAO(theme_index);
}
protected Mesh getRetexturedMesh(MeshCacheKey key, BlockState state) { protected Mesh getRetexturedMesh(MeshCacheKey key, BlockState state) {
if (RETEXTURED_MESH_CACHE.asMap().containsKey(key)) return RETEXTURED_MESH_CACHE.getIfPresent(key); if (RETEXTURED_MESH_CACHE.asMap().containsKey(key)) return RETEXTURED_MESH_CACHE.getIfPresent(key);
Mesh mesh = transformMesh(key, state); Mesh mesh = transformMesh(key, state);

View File

@ -14,7 +14,6 @@ import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
@ -24,8 +23,8 @@ import java.util.function.Function;
public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel { public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel {
public UnbakedAutoRetexturedModel(Identifier parent, int state_count, Property<?>... properties) { public UnbakedAutoRetexturedModel(Identifier parent) {
super(parent, state_count, properties); super(parent);
item_state = Blocks.AIR.getDefaultState(); item_state = Blocks.AIR.getDefaultState();
} }
@ -37,9 +36,7 @@ public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel {
ReFramedClient.HELPER.getCamoAppearanceManager(texture_getter), ReFramedClient.HELPER.getCamoAppearanceManager(texture_getter),
theme_index, theme_index,
bake_settings, bake_settings,
item_state, item_state
state_count,
properties
) { ) {
protected Mesh convertModel(BlockState state) { protected Mesh convertModel(BlockState state) {
Renderer r = ReFramedClient.HELPER.getFabricRenderer(); Renderer r = ReFramedClient.HELPER.getFabricRenderer();

View File

@ -1,6 +1,5 @@
package fr.adrien1106.reframed.client.model; package fr.adrien1106.reframed.client.model;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.Baker; import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.render.model.ModelBakeSettings;
@ -41,8 +40,8 @@ public class UnbakedDoubleRetexturedModel implements UnbakedModel {
@Override @Override
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings model_bake_settings, Identifier identifier) { public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings model_bake_settings, Identifier identifier) {
return new DoubleRetexturingBakedModel( return new DoubleRetexturingBakedModel(
(ForwardingBakedModel) model_1.bake(baker, texture_getter, model_bake_settings, identifier), (RetexturingBakedModel) model_1.bake(baker, texture_getter, model_bake_settings, identifier),
(ForwardingBakedModel) model_2.bake(baker, texture_getter, model_bake_settings, identifier) (RetexturingBakedModel) model_2.bake(baker, texture_getter, model_bake_settings, identifier)
); );
} }
} }

View File

@ -15,7 +15,6 @@ import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
@ -25,8 +24,8 @@ import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel { public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
public UnbakedJsonRetexturedModel(Identifier parent, int state_count, Property<?>... properties) { public UnbakedJsonRetexturedModel(Identifier parent) {
super(parent, state_count, properties); super(parent);
} }
@Nullable @Nullable
@ -45,9 +44,7 @@ public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
ReFramedClient.HELPER.getCamoAppearanceManager(spriteLookup), ReFramedClient.HELPER.getCamoAppearanceManager(spriteLookup),
theme_index, theme_index,
bake_settings, bake_settings,
item_state, item_state
state_count,
properties
) { ) {
protected Mesh convertModel(BlockState state) { protected Mesh convertModel(BlockState state) {
Renderer r = ReFramedClient.HELPER.getFabricRenderer(); Renderer r = ReFramedClient.HELPER.getFabricRenderer();

View File

@ -2,7 +2,6 @@ package fr.adrien1106.reframed.client.model;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.UnbakedModel; import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import java.util.Collection; import java.util.Collection;
@ -15,13 +14,9 @@ public abstract class UnbakedRetexturedModel implements UnbakedModel {
protected int theme_index = 1; protected int theme_index = 1;
protected BlockState item_state; protected BlockState item_state;
protected final int state_count;
protected final Property<?>[] properties;
public UnbakedRetexturedModel(Identifier parent, int state_count, Property<?>... properties) { public UnbakedRetexturedModel(Identifier parent) {
this.parent = parent; this.parent = parent;
this.state_count = state_count;
this.properties = properties;
} }
public UnbakedRetexturedModel setThemeIndex(int theme_index) { public UnbakedRetexturedModel setThemeIndex(int theme_index) {

View File

@ -5,6 +5,7 @@ import com.google.common.cache.CacheBuilder;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.client.model.QuadPosBounds; import fr.adrien1106.reframed.client.model.QuadPosBounds;
import fr.adrien1106.reframed.client.model.RetexturingBakedModel;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
@ -32,18 +33,18 @@ import static net.minecraft.util.shape.VoxelShapes.combine;
public class RenderHelper { public class RenderHelper {
// self culling cache of the models not made thread local so that it is only computed once // self culling cache of the models not made thread local so that it is only computed once
private static final Cache<CullElement, Integer[]> INNER_CULL_MAP = CacheBuilder.newBuilder().maximumSize(1024).build(); private static final Cache<CullElement, Integer[]> INNER_CULL_MAP = CacheBuilder.newBuilder().build();
private record CullElement(Block block, Object state_key, int model) { } private record CullElement(Block block, Object state_key, int model) { }
/** /**
* compute which quad might cull with another model quad * compute which quad might cull with another model quad
* @param state - the state of the model * @param state - the state of the model
* @param models - list of models on the same block * @param models - list of models on the same block
* @param hash - the hash of main model
*/ */
public static void computeInnerCull(BlockState state, List<ForwardingBakedModel> models) { public static void computeInnerCull(BlockState state, List<RetexturingBakedModel> models, int hash) {
if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return; if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return;
Object key = frame_block.getModelCacheKey(state); if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, hash, 1))) return;
if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, key, 1))) return;
Renderer r = ReFramedClient.HELPER.getFabricRenderer(); Renderer r = ReFramedClient.HELPER.getFabricRenderer();
QuadEmitter quad_emitter = r.meshBuilder().getEmitter(); QuadEmitter quad_emitter = r.meshBuilder().getEmitter();
@ -73,15 +74,15 @@ public class RenderHelper {
} }
} }
} }
INNER_CULL_MAP.put(new CullElement(frame_block, key, self_id), cull_array); INNER_CULL_MAP.put(new CullElement(frame_block, hash, self_id), cull_array);
} }
} }
public static boolean shouldDrawInnerFace(BlockState state, BlockRenderView view, BlockPos pos, int quad_index, int theme_index) { public static boolean shouldDrawInnerFace(BlockState state, BlockRenderView view, BlockPos pos, int quad_index, int theme_index, int hash) {
if (!(state.getBlock() instanceof ReFramedBlock frame_block) if (!(state.getBlock() instanceof ReFramedBlock frame_block)
|| !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity) || !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)
) return true; ) return true;
CullElement key = new CullElement(frame_block, frame_block.getModelCacheKey(state), theme_index); CullElement key = new CullElement(frame_block, hash, theme_index);
if (!INNER_CULL_MAP.asMap().containsKey(key)) return true; if (!INNER_CULL_MAP.asMap().containsKey(key)) return true;
// needs to be Integer object because array is initialized with null not 0 // needs to be Integer object because array is initialized with null not 0

View File

@ -14,9 +14,11 @@ import java.util.Map;
public class RebakedModel implements BakedModel { public class RebakedModel implements BakedModel {
protected final Map<Direction, List<BakedQuad>> face_quads; protected final Map<Direction, List<BakedQuad>> face_quads;
protected boolean ambient_occlusion;
public RebakedModel(Map<Direction, List<BakedQuad>> face_quads) { public RebakedModel(Map<Direction, List<BakedQuad>> face_quads, boolean ambient_occlusion) {
this.face_quads = face_quads; this.face_quads = face_quads;
this.ambient_occlusion = ambient_occlusion;
} }
@Override @Override
@ -26,7 +28,7 @@ public class RebakedModel implements BakedModel {
@Override @Override
public boolean useAmbientOcclusion() { public boolean useAmbientOcclusion() {
return true; return ambient_occlusion;
} }
@Override @Override

View File

@ -16,7 +16,9 @@ import java.util.concurrent.CompletableFuture;
public class GBlockTag extends BlockTagProvider { public class GBlockTag extends BlockTagProvider {
private static final Map<Class<? extends Block>, TagGetter> providers = new HashMap<>(); private static final Map<Class<? extends Block>, TagGetter> providers = new HashMap<>();
static { static {
providers.put(ReframedWallBlock.class, new Wall()); providers.put(ReFramedPillarsWallBlock.class, new PillarsWall());
providers.put(ReFramedWallBlock.class, new Wall());
providers.put(ReFramedPaneBlock.class, new Pane());
} }
public GBlockTag(FabricDataOutput output, CompletableFuture<WrapperLookup> registries) { public GBlockTag(FabricDataOutput output, CompletableFuture<WrapperLookup> registries) {

View File

@ -32,7 +32,11 @@ public class GBlockstate extends FabricModelProvider {
providers.put(ReFramedStairsCubeBlock.class, new StairsCube()); providers.put(ReFramedStairsCubeBlock.class, new StairsCube());
providers.put(ReFramedStepBlock.class, new Step()); providers.put(ReFramedStepBlock.class, new Step());
providers.put(ReFramedStepsSlabBlock.class, new StepsSlab()); providers.put(ReFramedStepsSlabBlock.class, new StepsSlab());
providers.put(ReframedWallBlock.class, new Wall()); providers.put(ReFramedPillarsWallBlock.class, new PillarsWall());
providers.put(ReFramedWallBlock.class, new Wall());
providers.put(ReFramedPaneBlock.class, new Pane());
providers.put(ReFramedTrapdoorBlock.class, new Trapdoor());
providers.put(ReFramedDoorBlock.class, new Door());
} }
public GBlockstate(FabricDataOutput output) { public GBlockstate(FabricDataOutput output) {

View File

@ -34,7 +34,11 @@ public class GRecipe extends FabricRecipeProvider {
providers.put(ReFramedStairsCubeBlock.class, new StairsCube()); providers.put(ReFramedStairsCubeBlock.class, new StairsCube());
providers.put(ReFramedStepBlock.class, new Step()); providers.put(ReFramedStepBlock.class, new Step());
providers.put(ReFramedStepsSlabBlock.class, new StepsSlab()); providers.put(ReFramedStepsSlabBlock.class, new StepsSlab());
providers.put(ReframedWallBlock.class, new Wall()); providers.put(ReFramedPillarsWallBlock.class, new PillarsWall());
providers.put(ReFramedWallBlock.class, new Wall());
providers.put(ReFramedPaneBlock.class, new Pane());
providers.put(ReFramedTrapdoorBlock.class, new Trapdoor());
providers.put(ReFramedDoorBlock.class, new Door());
providers.put(ReFramedBlueprintItem.class, new Blueprint()); providers.put(ReFramedBlueprintItem.class, new Blueprint());
providers.put(ReFramedHammerItem.class, new Hammer()); providers.put(ReFramedHammerItem.class, new Hammer());
providers.put(ReFramedScrewdriverItem.class, new Screwdriver()); providers.put(ReFramedScrewdriverItem.class, new Screwdriver());

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 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.client.When;
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.block.enums.DoorHinge.*;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.*;
public class Door implements RecipeSetter, BlockStateProvider {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 1);
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, convertible, 3)
.pattern("II")
.pattern("II")
.pattern("II")
.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 door = ReFramed.id("trapdoor_open_special");
return MultipartBlockStateSupplier.create(block)
// SOUTH
.with(When.anyOf(
GBlockstate.when(OPEN, false, HORIZONTAL_FACING, Direction.SOUTH),
GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.EAST, DOOR_HINGE, LEFT),
GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.WEST, DOOR_HINGE, RIGHT)
),
GBlockstate.variant(door, true, R0, R0))
// WEST
.with(When.anyOf(
GBlockstate.when(OPEN, false, HORIZONTAL_FACING, Direction.WEST),
GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.SOUTH, DOOR_HINGE, LEFT),
GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.NORTH, DOOR_HINGE, RIGHT)
),
GBlockstate.variant(door, true, R0, R90))
// NORTH
.with(When.anyOf(
GBlockstate.when(OPEN, false, HORIZONTAL_FACING, Direction.NORTH),
GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.WEST, DOOR_HINGE, LEFT),
GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.EAST, DOOR_HINGE, RIGHT)
),
GBlockstate.variant(door, true, R0, R180))
// EAST
.with(When.anyOf(
GBlockstate.when(OPEN, false, HORIZONTAL_FACING, Direction.EAST),
GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.NORTH, DOOR_HINGE, LEFT),
GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.SOUTH, DOOR_HINGE, RIGHT)
),
GBlockstate.variant(door, true, R0, R270));
}
}

View File

@ -0,0 +1,76 @@
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.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 Pane 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, 32)
.pattern("III")
.pattern("I I")
.pattern("III")
.input('I', ReFramed.CUBE)
.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.WALLS);
}
@Override
public BlockStateSupplier getMultipart(Block block) {
Identifier
pane_side = ReFramed.id("pane_side_special"),
pane_side_alt = ReFramed.id("pane_side_alt_special"),
pane_noside = ReFramed.id("pane_noside_special"),
pane_noside_alt = ReFramed.id("pane_noside_alt_special");
return MultipartBlockStateSupplier.create(block)
// PILLAR CORE
.with(GBlockstate.variant(ReFramed.id("pane_post_special"), true, R0, R0))
// SIDE
.with(GBlockstate.when(NORTH, true),
GBlockstate.variant(pane_side, true, R0, R0))
.with(GBlockstate.when(EAST, true),
GBlockstate.variant(pane_side, true, R0, R90))
.with(GBlockstate.when(SOUTH, true),
GBlockstate.variant(pane_side_alt, true, R0, R0))
.with(GBlockstate.when(WEST, true),
GBlockstate.variant(pane_side_alt, true, R0, R90))
// NOSIDE
.with(GBlockstate.when(NORTH, false),
GBlockstate.variant(pane_noside, true, R0, R0))
.with(GBlockstate.when(EAST, false),
GBlockstate.variant(pane_noside_alt, true, R0, R0))
.with(GBlockstate.when(SOUTH, false),
GBlockstate.variant(pane_noside_alt, true, R0, R90))
.with(GBlockstate.when(WEST, false),
GBlockstate.variant(pane_noside, true, R0, R270));
}
}

View File

@ -0,0 +1,82 @@
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.block.enums.WallShape.*;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.*;
public class PillarsWall implements RecipeSetter, TagGetter, BlockStateProvider {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 1);
ShapelessRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, convertible, 2)
.input(ReFramed.WALL, 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.WALLS);
}
@Override
public BlockStateSupplier getMultipart(Block block) {
Identifier
low = ReFramed.id("pillars_wall_low_special"),
tall = ReFramed.id("pillars_wall_tall_special"),
none = ReFramed.id("wall_pillar_none_special");
return MultipartBlockStateSupplier.create(block)
// PILLAR CORE
.with(GBlockstate.variant(ReFramed.id("wall_core_special"), true, R0, R0))
// LOW
.with(GBlockstate.when(NORTH_WALL_SHAPE, LOW),
GBlockstate.variant(low, true, R0, R0))
.with(GBlockstate.when(EAST_WALL_SHAPE, LOW),
GBlockstate.variant(low, true, R0, R90))
.with(GBlockstate.when(SOUTH_WALL_SHAPE, LOW),
GBlockstate.variant(low, true, R0, R180))
.with(GBlockstate.when(WEST_WALL_SHAPE, LOW),
GBlockstate.variant(low, true, R0, R270))
// TALL
.with(GBlockstate.when(NORTH_WALL_SHAPE, TALL),
GBlockstate.variant(tall, true, R0, R0))
.with(GBlockstate.when(EAST_WALL_SHAPE, TALL),
GBlockstate.variant(tall, true, R0, R90))
.with(GBlockstate.when(SOUTH_WALL_SHAPE, TALL),
GBlockstate.variant(tall, true, R0, R180))
.with(GBlockstate.when(WEST_WALL_SHAPE, TALL),
GBlockstate.variant(tall, true, R0, R270))
// PILLAR NONE
.with(GBlockstate.when(NORTH_WALL_SHAPE, NONE),
GBlockstate.variant(none, true, R0, R0))
.with(GBlockstate.when(EAST_WALL_SHAPE, NONE),
GBlockstate.variant(none, true, R0, R90))
.with(GBlockstate.when(SOUTH_WALL_SHAPE, NONE),
GBlockstate.variant(none, true, R0, R180))
.with(GBlockstate.when(WEST_WALL_SHAPE, NONE),
GBlockstate.variant(none, true, R0, R270));
}
}

View File

@ -0,0 +1,56 @@
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.BlockHalf;
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.*;
import static net.minecraft.state.property.Properties.*;
public class Trapdoor implements RecipeSetter, BlockStateProvider {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 2);
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, convertible, 2)
.pattern(" I")
.pattern("III")
.pattern("II ")
.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 open = ReFramed.id("trapdoor_open_special");
return MultipartBlockStateSupplier.create(block)
.with(GBlockstate.when(OPEN, false, BLOCK_HALF, BlockHalf.BOTTOM),
GBlockstate.variant(ReFramed.id("trapdoor_bottom_special"), true, R0, R0))
.with(GBlockstate.when(OPEN, false, BLOCK_HALF, BlockHalf.TOP),
GBlockstate.variant(ReFramed.id("trapdoor_top_special"), true, R0, R0))
.with(GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.NORTH),
GBlockstate.variant(open, true, R0, R0))
.with(GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.EAST),
GBlockstate.variant(open, true, R0, R90))
.with(GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.SOUTH),
GBlockstate.variant(open, true, R0, R180))
.with(GBlockstate.when(OPEN, true, HORIZONTAL_FACING, Direction.WEST),
GBlockstate.variant(open, true, R0, R270));
}
}

View File

@ -32,6 +32,8 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM
@Shadow(remap = false) @Final private Int2ObjectMap<Sprite> textures; @Shadow(remap = false) @Final private Int2ObjectMap<Sprite> textures;
@Shadow public abstract boolean useAmbientOcclusion();
/** /**
* Reuses the emitQuad method to compute the quads to be used by the frame * Reuses the emitQuad method to compute the quads to be used by the frame
* *
@ -77,6 +79,6 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM
})); }));
}); });
return new RebakedModel(face_quads); return new RebakedModel(face_quads, useAmbientOcclusion());
} }
} }

View File

@ -89,6 +89,6 @@ public abstract class ContinuityCTMBakedModelMixin extends ForwardingBakedModel
transform.getProcessingContext().reset(); // reset instead of outputting to emitter transform.getProcessingContext().reset(); // reset instead of outputting to emitter
transform.invokeReset(); transform.invokeReset();
return new RebakedModel(face_quads); return new RebakedModel(face_quads, useAmbientOcclusion());
} }
} }

View File

@ -24,6 +24,6 @@ public abstract class IndiumAbstractBlockRenderContextMixin {
private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) { private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) {
if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face); if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face);
return !RenderHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex()); return !RenderHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex(), info.getModelHash());
} }
} }

View File

@ -19,6 +19,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin { public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin {
@Unique private int theme_index = 0; @Unique private int theme_index = 0;
@Unique private int model_hash = 0;
@Redirect( @Redirect(
method = "shouldDrawFaceInner", method = "shouldDrawFaceInner",
@ -36,8 +37,9 @@ public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo
} }
@Override @Override
public void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo, int theme_index) { public void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo, int theme_index, int model_hash) {
this.theme_index = theme_index; this.theme_index = theme_index;
this.model_hash = model_hash;
prepareForBlock(blockState, blockPos, seed, modelAo); prepareForBlock(blockState, blockPos, seed, modelAo);
} }
@ -45,4 +47,9 @@ public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo
public int getThemeIndex() { public int getThemeIndex() {
return theme_index; return theme_index;
} }
@Override
public int getModelHash() {
return model_hash;
}
} }

View File

@ -1,15 +1,19 @@
package fr.adrien1106.reframed.mixin.compat; package fr.adrien1106.reframed.mixin.compat;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel; 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.client.util.RenderHelper;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; 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.AbstractBlockRenderContext;
import link.infra.indium.renderer.render.BlockRenderInfo;
import link.infra.indium.renderer.render.TerrainRenderContext; import link.infra.indium.renderer.render.TerrainRenderContext;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockRenderContext; import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockRenderContext;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -28,18 +32,34 @@ public abstract class IndiumTerrainRenderContextMixin extends AbstractBlockRende
), remap = false, ), remap = false,
cancellable = true) cancellable = true)
private void renderMultipleModels(BlockRenderContext ctx, CallbackInfo ci) { private void renderMultipleModels(BlockRenderContext ctx, CallbackInfo ci) {
if (!(ctx.model() instanceof IMultipartBakedModelMixin wrapped) if (!(ctx.model() instanceof IMultipartBakedModelMixin wrapped)) return;
|| !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return; List<BakedModel> models = wrapped.getModels(ctx.state());
if (models.stream().noneMatch(bakedModel ->
bakedModel instanceof MultiRetexturableModel
|| bakedModel instanceof RetexturingBakedModel
)) return;
List<ForwardingBakedModel> models = retexturing_model.models(); models.forEach(model -> {
RenderHelper.computeInnerCull(ctx.state(), models); if (model instanceof MultiRetexturableModel multi_model) {
int i = 0; RenderHelper.computeInnerCull(ctx.state(), multi_model.models(), model.hashCode());
for (BakedModel model : models) { multi_model.models().forEach(rexteruable_model ->
i++; renderModel(ctx, rexteruable_model, aoCalc, blockInfo, this, model.hashCode())
aoCalc.clear(); );
((IBlockRenderInfoMixin) blockInfo).prepareForBlock(ctx.state(), ctx.pos(), ctx.seed(), model.useAmbientOcclusion(), i); } else if (model instanceof RetexturingBakedModel rexteruable_model)
model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); renderModel(ctx, rexteruable_model, aoCalc, blockInfo, this, model.hashCode());
} else model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
});
ci.cancel(); ci.cancel();
} }
@Unique
private static void renderModel(BlockRenderContext ctx, RetexturingBakedModel model, AoCalculator aoCalc, BlockRenderInfo block_info, RenderContext context, int model_hash) {
aoCalc.clear();
((IBlockRenderInfoMixin) block_info).prepareForBlock(
ctx.state(), ctx.pos(), ctx.seed(),
model.useAmbientOcclusion(block_info.blockView, ctx.pos()),
model.getThemeIndex(), model_hash
);
model.emitBlockQuads(block_info.blockView, block_info.blockState, block_info.blockPos, block_info.randomSupplier, context);
}
} }

View File

@ -29,8 +29,12 @@ public abstract class AbstractBlockRenderContextMixin {
) )
) )
private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) { private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) {
if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face); if (face != null
|| quad.tag() == 0
|| !(blockInfo instanceof IBlockRenderInfoMixin info)
|| info.getThemeIndex() == 0
) return isFaceCulled(face);
return !RenderHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex()); return !RenderHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex(), info.getModelHash());
} }
} }

View File

@ -25,6 +25,7 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
@Shadow public BlockRenderView blockView; @Shadow public BlockRenderView blockView;
@Unique private int theme_index = 0; @Unique private int theme_index = 0;
@Unique private int model_hash = 0;
@ModifyArg(method = "prepareForBlock", @ModifyArg(method = "prepareForBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
@ -42,8 +43,9 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
@Override @Override
@Unique @Unique
public void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index) { public void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index, int model_hash) {
this.theme_index = theme_index; this.theme_index = theme_index;
this.model_hash = model_hash;
prepareForBlock(state, pos, ao); prepareForBlock(state, pos, ao);
} }
@ -51,4 +53,9 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
public int getThemeIndex() { public int getThemeIndex() {
return theme_index; return theme_index;
} }
@Override
public int getModelHash() {
return model_hash;
}
} }

View File

@ -19,7 +19,7 @@ public class MultipartBakedModelMixin implements IMultipartBakedModelMixin {
@Shadow @Final private List<Pair<Predicate<BlockState>, BakedModel>> components; @Shadow @Final private List<Pair<Predicate<BlockState>, BakedModel>> components;
@Override @Override
public BakedModel getModel(BlockState state) { public List<BakedModel> getModels(BlockState state) {
return components.stream().map(pair -> pair.getLeft().test(state) ? pair.getRight(): null).filter(Objects::nonNull).findAny().orElse(null); return components.stream().map(pair -> pair.getLeft().test(state) ? pair.getRight(): null).filter(Objects::nonNull).toList();
} }
} }

View File

@ -1,17 +1,21 @@
package fr.adrien1106.reframed.mixin.render; package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel; 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.client.util.RenderHelper;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; 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.AbstractBlockRenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext; import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -27,18 +31,34 @@ public abstract class TerrainRenderContextMixin extends AbstractBlockRenderConte
), remap = false, ), remap = false,
cancellable = true) cancellable = true)
private void renderMultipleModels(BlockState state, BlockPos pos, BakedModel wrapper, MatrixStack matrixStack, CallbackInfo ci) { private void renderMultipleModels(BlockState state, BlockPos pos, BakedModel wrapper, MatrixStack matrixStack, CallbackInfo ci) {
if (!(wrapper instanceof IMultipartBakedModelMixin wrapped) if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)) return;
|| !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return; List<BakedModel> models = wrapped.getModels(state);
if (models.stream().noneMatch(bakedModel ->
bakedModel instanceof MultiRetexturableModel
|| bakedModel instanceof RetexturingBakedModel
)) return;
List<ForwardingBakedModel> models = retexturing_model.models(); models.forEach(model -> {
RenderHelper.computeInnerCull(state, models); if (model instanceof MultiRetexturableModel multi_model) {
int i = 0; RenderHelper.computeInnerCull(state, multi_model.models(), model.hashCode());
for (BakedModel model : models) { multi_model.models().forEach(retexturable_model ->
i++; renderModel(state, retexturable_model, pos, aoCalc, blockInfo, this, model.hashCode())
aoCalc.clear(); );
((IBlockRenderInfoMixin) blockInfo).prepareForBlock(state, pos, model.useAmbientOcclusion(), i); } else if (model instanceof RetexturingBakedModel retexturable_model)
model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); renderModel(state, retexturable_model, pos, aoCalc, blockInfo, this, model.hashCode());
} else model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
});
ci.cancel(); ci.cancel();
} }
@Unique
private static void renderModel(BlockState state, RetexturingBakedModel model, BlockPos pos, 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

@ -1,5 +1,7 @@
package fr.adrien1106.reframed.util.blocks; package fr.adrien1106.reframed.util.blocks;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.StringIdentifiable; import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
@ -119,4 +121,20 @@ public enum Corner implements StringIdentifiable {
if (edge.getFirstDirection() != third_direction && edge.getSecondDirection() != third_direction) return third_direction; if (edge.getFirstDirection() != third_direction && edge.getSecondDirection() != third_direction) return third_direction;
return first_direction; return first_direction;
} }
public Corner rotate(BlockRotation rotation) {
return getByDirections(
rotation.rotate(first_direction),
rotation.rotate(second_direction),
rotation.rotate(third_direction)
);
}
public Corner mirror(BlockMirror mirror) {
return getByDirections(
mirror.apply(first_direction),
mirror.apply(second_direction),
mirror.apply(third_direction)
);
}
} }

View File

@ -1,5 +1,7 @@
package fr.adrien1106.reframed.util.blocks; package fr.adrien1106.reframed.util.blocks;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.StringIdentifiable; import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
@ -48,13 +50,19 @@ public enum Edge implements StringIdentifiable {
public Direction getSecondDirection() { public Direction getSecondDirection() {
return second_direction; return second_direction;
} }
public Direction getRightDirection() { public Direction getRightDirection() {
return Direction.from(axis, Direction.AxisDirection.NEGATIVE); return Direction.from(axis, Direction.AxisDirection.NEGATIVE);
} }
public Direction getLeftDirection() { public Direction getLeftDirection() {
return Direction.from(axis, Direction.AxisDirection.POSITIVE); return Direction.from(axis, Direction.AxisDirection.POSITIVE);
} }
public Direction getFace() {
return first_direction == Direction.UP || first_direction == Direction.DOWN ? second_direction : first_direction;
}
public boolean hasDirection(Direction direction) { public boolean hasDirection(Direction direction) {
return this.first_direction.equals(direction) return this.first_direction.equals(direction)
|| this.second_direction.equals(direction); || this.second_direction.equals(direction);
@ -97,4 +105,18 @@ public enum Edge implements StringIdentifiable {
.filter(value -> value.name().equals(name)) .filter(value -> value.name().equals(name))
.findFirst().orElse(Edge.NORTH_DOWN); .findFirst().orElse(Edge.NORTH_DOWN);
} }
public Edge rotate(BlockRotation rotation) {
return getByDirections(
rotation.rotate(first_direction),
rotation.rotate(second_direction)
);
}
public Edge mirror(BlockMirror mirror) {
return getByDirections(
mirror.apply(first_direction),
mirror.apply(second_direction)
);
}
} }

View File

@ -48,4 +48,31 @@ public enum StairShape implements StringIdentifiable {
.findFirst().orElse(StairShape.STRAIGHT); .findFirst().orElse(StairShape.STRAIGHT);
} }
public StairShape mirror() {
return switch (this) {
case STRAIGHT -> STRAIGHT;
case INNER_RIGHT -> INNER_LEFT;
case INNER_LEFT -> INNER_RIGHT;
case OUTER_RIGHT -> OUTER_LEFT;
case OUTER_LEFT -> OUTER_RIGHT;
case FIRST_OUTER_RIGHT -> FIRST_OUTER_LEFT;
case FIRST_OUTER_LEFT -> FIRST_OUTER_RIGHT;
case SECOND_OUTER_RIGHT -> SECOND_OUTER_LEFT;
case SECOND_OUTER_LEFT -> SECOND_OUTER_RIGHT;
};
}
public StairShape flip() {
return switch (this) {
case STRAIGHT -> STRAIGHT;
case INNER_RIGHT -> INNER_RIGHT;
case INNER_LEFT -> INNER_LEFT;
case OUTER_RIGHT -> OUTER_RIGHT;
case OUTER_LEFT -> OUTER_LEFT;
case FIRST_OUTER_RIGHT -> SECOND_OUTER_RIGHT;
case FIRST_OUTER_LEFT -> SECOND_OUTER_LEFT;
case SECOND_OUTER_RIGHT -> FIRST_OUTER_RIGHT;
case SECOND_OUTER_LEFT -> FIRST_OUTER_LEFT;
};
}
} }

View File

@ -5,9 +5,11 @@ import net.minecraft.util.math.BlockPos;
public interface IBlockRenderInfoMixin { public interface IBlockRenderInfoMixin {
void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index); void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index, int model_hash);
void prepareForBlock(BlockState state, BlockPos pos, long seed, boolean ao, int theme_index); void prepareForBlock(BlockState state, BlockPos pos, long seed, boolean ao, int theme_index, int model_hash);
int getThemeIndex(); int getThemeIndex();
int getModelHash();
} }

View File

@ -3,7 +3,9 @@ package fr.adrien1106.reframed.util.mixin;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import java.util.List;
public interface IMultipartBakedModelMixin { public interface IMultipartBakedModelMixin {
BakedModel getModel(BlockState state); List<BakedModel> getModels(BlockState state);
} }

View File

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

View File

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

View File

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