26 Commits

Author SHA1 Message Date
73bf27bdda Merge pull request 'fix: more checks on ThemedEntity Class (Always more to fix)' (#21) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m8s
Reviewed-on: #21
2024-06-14 17:17:37 +02:00
7df46a4b76 fix: more checks on ThemedEntity Class 2024-06-14 17:16:53 +02:00
402caaf549 Merge pull request 'fix: more injection fix + bad culling' (#20) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m5s
Reviewed-on: #20
2024-06-14 16:53:17 +02:00
42049047f7 fix: more injection fix + bad culling 2024-06-14 16:51:25 +02:00
4eddb32190 Merge pull request 'fix: injection remapping issues fixed + additional check' (#19) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m11s
Reviewed-on: #19
2024-06-14 00:37:13 +02:00
ddffd45a44 fix: injection remapping issues fixed + additional check 2024-06-14 00:35:13 +02:00
53f962da1b Merge pull request 'changed version' (#18) from dev into master
All checks were successful
deploy / deploy (push) Successful in 6m17s
Reviewed-on: #18
2024-06-13 23:10:51 +02:00
fcf02b68e6 feat: version was missing in previous commit 2024-06-13 22:55:51 +02:00
5517d813e7 Merge pull request 'added new blocks and further axiom support' (#17) from dev into master
Some checks failed
deploy / deploy (push) Has been cancelled
Reviewed-on: #17
2024-06-13 22:55:09 +02:00
b88a4abfe5 feat: more axiom support (connected textures and culling in selections) + newer version 2024-06-13 22:51:05 +02:00
ce650abc76 feat: added connected texture support on Axiom selection (includes blueprint previews etc) 2024-06-13 02:50:16 +02:00
535fd6151e feat: added post fence and postfence frames 2024-06-13 01:28:28 +02:00
1061431af7 feat: added button frame 2024-06-12 18:59:08 +02:00
520430d5bc feat: Axiom now renders frames in selections properly 2024-06-12 00:13:38 +02:00
52fb6840ad fix: added check in injections and fixed modifiers interactions and Layer drops 2024-06-12 00:12:07 +02:00
f6b403d852 Merge pull request 'feat: added support for ReFramed block entities with axiom' (#16) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m16s
Reviewed-on: #16
2024-05-23 12:34:22 +02:00
5430016be4 feat: added support for ReFramed block entities with axiom 2024-05-23 12:33:54 +02:00
aa3cf6d511 Merge pull request 'fix: falling block entities calling can replace crashing logic side' (#15) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m59s
Reviewed-on: #15
2024-05-23 00:47:20 +02:00
15a8f80210 fix: falling block entities calling can replace crashing logic side 2024-05-23 00:45:52 +02:00
cfdbfd3a6a 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
2024-05-14 15:53:28 +02:00
c49a978aa9 updated minor version for publishing 2024-05-14 15:27:34 +02:00
6b2ee1dc83 added added Door, Trapdoor and Pane 2024-05-14 15:21:18 +02:00
752ee956eb added support for rotation and mirroring 2024-05-13 12:58:42 +02:00
d5369823d9 added pillars wall and fixed issues related to multimodel self culling when using multiple models for the same state + unseless caching removed + still no dyn ao :( 2024-04-28 23:19:00 +02:00
c6f2244826 w.i.p. Dynamic AO 2024-04-28 15:27:00 +02:00
72c9c3511b fabric version update 2024-04-25 13:23:06 +02:00
98 changed files with 3958 additions and 498 deletions

View File

@@ -21,15 +21,8 @@ or based on preferences add the person(s) to the project
### What Shapes are planed to be added
Currently, the list of shapes to be added is pretty simple as the mod is still under development:
- Wall
- Fence
- Pane
- Button
- Pressure Plate
- Trapdoor
- Door
- Carpet
- Post
- Carpet (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)

View File

@@ -4,7 +4,7 @@ import fr.altarik.CreateTag
plugins {
id "com.modrinth.minotaur" version "2.+"
id 'fabric-loom' version '1.5-SNAPSHOT'
id 'fabric-loom' version '1.6-SNAPSHOT'
id 'maven-publish'
}
@@ -84,6 +84,9 @@ repositories {
includeGroup "maven.modrinth"
}
}
maven {
url = "https://jitpack.io"
}
mavenCentral()
// Add repositories to retrieve artifacts from in here.
@@ -106,11 +109,11 @@ dependencies {
// modRuntimeOnly "maven.modrinth:indium:${project.indium_version}+mc${project.minecraft_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}"
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}"
modRuntimeOnly "maven.modrinth:continuity:${project.continuity_version}"
@@ -118,6 +121,10 @@ dependencies {
modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7"
modRuntimeOnly "earth.terrarium.chipped:Chipped-fabric-${project.minecraft_version}:3.1.2"
// Axiom for blueprint support
modCompileOnly "maven.modrinth:N6n5dqoA:YxeYxQyz"
modCompileOnly "com.github.moulberry:AxiomClientAPI:1.0.5.3"
// Fabric API.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
}
@@ -200,8 +207,8 @@ publishing {
modrinth {
token = getEnv("MODRINTH_TOKEN", local.getProperty("modrinth_token"))
projectId = project.modrinth_id
versionNumber = project.mod_version
versionName = "${project.archives_base_name} ${project.mod_version}"
versionNumber = "${project.mod_version}-${project.minecraft_version}"
versionName = "${project.archives_base_name} ${project.mod_version}-${project.minecraft_version}"
versionType = project.mod_version.endsWith('SNAPSHOT') ? 'beta' : 'release'
uploadFile = remapJar
gameVersions = [project.minecraft_version]

View File

@@ -5,18 +5,18 @@ org.gradle.jvmargs=-Xmx1G
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.20.4
yarn_mappings=1.20.4+build.3
loader_version=0.15.6
loader_version=0.15.11
# Mod Properties
modrinth_id = jCpoCBpn
mod_version = 1.5.8
mod_version = 1.6.3
maven_group = fr.adrien1106
archives_base_name = ReFramed
mod_id = reframed
# Dependencies
# 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_repo=ReFramed

View File

@@ -37,7 +37,18 @@ public class ReFramed implements ModInitializer {
public static final String MODID = "reframed";
public static final ArrayList<Block> BLOCKS = new ArrayList<>();
public static Block CUBE, SMALL_CUBE, SMALL_CUBES_STEP, STAIR, HALF_STAIR, STAIRS_CUBE, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, SLAB, SLABS_CUBE, STEP, STEPS_SLAB, LAYER, PILLAR, WALL;
public static Block
CUBE,
SMALL_CUBE, SMALL_CUBES_STEP,
STAIR, STAIRS_CUBE,
HALF_STAIR, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR,
SLAB, SLABS_CUBE,
STEP, STEPS_SLAB,
LAYER,
PILLAR, PILLARS_WALL, WALL,
PANE, TRAPDOOR, DOOR,
BUTTON,
POST, POST_FENCE, FENCE;
public static final ArrayList<Item> ITEMS = new ArrayList<>();
public static Item HAMMER, SCREWDRIVER, BLUEPRINT, BLUEPRINT_WRITTEN;
@@ -65,7 +76,15 @@ public class ReFramed implements ModInitializer {
STEP = registerBlock("step" , new ReFramedStepBlock(cp(Blocks.OAK_SLAB)));
STEPS_SLAB = registerBlock("steps_slab" , new ReFramedStepsSlabBlock(cp(Blocks.OAK_SLAB)));
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)));
BUTTON = registerBlock("button" , new ReFramedButtonBlock(cp(Blocks.OAK_BUTTON)));
POST = registerBlock("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE)));
FENCE = registerBlock("fence" , new ReFramedFenceBlock(cp(Blocks.OAK_FENCE)));
POST_FENCE = registerBlock("post_fence" , new ReFramedPostFenceBlock(cp(Blocks.OAK_FENCE)));
HAMMER = registerItem("hammer" , new ReFramedHammerItem(new Item.Settings().maxCount(1)));
SCREWDRIVER = registerItem("screwdriver" , new ReFramedScrewdriverItem(new Item.Settings().maxCount(1)));

View File

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

View File

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

View File

@@ -9,7 +9,6 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.registry.Registries;
@@ -43,18 +42,6 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
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
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
@@ -73,25 +60,20 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit);
if (!canUse(world, pos, player)) return ActionResult.PASS;
ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
if (result.isAccepted()) return result;
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) {
return player.canModifyBlocks() && world.canPlayerModifyAt(player, pos);
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
if(!(newState.getBlock() instanceof ReFramedBlock) &&
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
if(!(new_state.getBlock() instanceof ReFramedBlock) &&
world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity &&
world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
) {
@@ -102,19 +84,9 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock()));
});
if(frame_entity.emitsRedstone()
&& themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) != 0))
drops.add(new ItemStack(Items.REDSTONE_TORCH));
if(frame_entity.emitsLight()
&& themes.stream().noneMatch(theme -> theme.getLuminance() != 0))
drops.add(new ItemStack(Items.GLOWSTONE_DUST));
if(!frame_entity.isSolid()
&& themes.stream().anyMatch(AbstractBlockState::isSolid))
drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
ItemScatterer.spawn(world, pos, drops);
}
super.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, new_state, moved);
}
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack, BlockState old_state, BlockEntity old_entity) {

View File

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

View File

@@ -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 new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction 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
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();
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return getCollisionShape(state, view, pos, ShapeContext.absent());
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit);
if (!canUse(world, pos, player)) return ActionResult.PASS;
ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
if (result.isAccepted()) return result;
return BlockHelper.useCamo(state, world, pos, player, hand, hit, getHitShape(state, hit));

View File

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

View File

@@ -10,6 +10,8 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
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.math.BlockPos;
import net.minecraft.util.math.Direction;
@@ -29,18 +31,11 @@ public class ReFramedHalfStairBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] HALF_STAIR_VOXELS;
private record ModelCacheKey(Corner corner, int face) {}
public ReFramedHalfStairBlock(Settings settings) {
super(settings);
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
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER,CORNER_FACE));
@@ -48,6 +43,7 @@ public class ReFramedHalfStairBlock extends WaterloggableReFramedBlock {
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null) return false;
Direction dir = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return !(
context.getPlayer().isSneaking()
@@ -108,6 +104,20 @@ public class ReFramedHalfStairBlock extends WaterloggableReFramedBlock {
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
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
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.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
@@ -22,18 +25,11 @@ import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBlock {
private record ModelCacheKey(Corner corner, int face) {}
public ReFramedHalfStairsSlabBlock(Settings settings) {
super(settings);
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
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER,CORNER_FACE));
@@ -48,7 +44,7 @@ public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBloc
}
@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)));
}
@@ -57,6 +53,20 @@ public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBloc
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
public VoxelShape getShape(BlockState state, int i) {
Corner corner = state.get(CORNER);

View File

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

View File

@@ -6,13 +6,18 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.context.LootContextParameterSet;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.state.property.Properties.LAYERS;
@@ -20,7 +25,6 @@ import static net.minecraft.state.property.Properties.LAYERS;
public class ReFramedLayerBlock extends ReFramedSlabBlock {
public static final VoxelShape[] LAYER_VOXELS;
private record ModelCacheKey(Direction face, int layer) {}
public ReFramedLayerBlock(Settings settings) {
super(settings);
@@ -28,8 +32,13 @@ public class ReFramedLayerBlock extends ReFramedSlabBlock {
}
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(FACING), state.get(LAYERS));
public List<ItemStack> getDroppedStacks(BlockState state, LootContextParameterSet.Builder builder) {
List<ItemStack> drops = super.getDroppedStacks(state, builder);
drops.forEach((stack) -> {
if (stack.getItem() instanceof BlockItem bi && bi.getBlock() instanceof ReFramedLayerBlock)
stack.setCount(state.get(LAYERS));
});
return drops;
}
@Override
@@ -44,6 +53,7 @@ public class ReFramedLayerBlock extends ReFramedSlabBlock {
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null) return false;
return !(
context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
@@ -58,6 +68,16 @@ public class ReFramedLayerBlock extends ReFramedSlabBlock {
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 {
VoxelListBuilder builder = VoxelListBuilder.create(createCuboidShape(0, 0, 0, 16, 2, 16), 48)
.add(createCuboidShape(0, 0, 0, 16, 4, 16))

View File

@@ -0,0 +1,47 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.PaneBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.registry.tag.BlockTags;
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;
public class ReFramedPaneBlock extends ConnectingReFramedBlock {
public static final VoxelShape[] PANE_VOXELS;
public ReFramedPaneBlock(Settings settings) {
super(settings);
}
@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(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, PANE_VOXELS[dir.ordinal() - 1]);
}
return shape;
}
@Override
protected boolean connectsTo(BlockState state, boolean fs, Direction dir) {
return !cannotConnect(state) && fs || state.getBlock() instanceof PaneBlock || state.isIn(BlockTags.WALLS);
}
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

@@ -1,56 +1,21 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedPillarBlock extends WaterloggableReFramedBlock {
public class ReFramedPillarBlock extends PillarReFramedBlock {
public static final VoxelShape[] PILLAR_VOXELS;
public ReFramedPillarBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
}
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(AXIS);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
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
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
// TODO: PILLARS WALL
return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis());
}
@Override
@@ -62,12 +27,6 @@ public class ReFramedPillarBlock extends WaterloggableReFramedBlock {
return PILLAR_VOXELS[axis.ordinal()];
}
@Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
// if (new_state.getBlock() == ReFramed.PILLARS_WALL) return Map.of(1, 1); // TODO: PILLARS WALL
return super.getThemeMap(state, new_state);
}
static {
final VoxelShape PILLAR = createCuboidShape(0, 4, 4, 16, 12, 12);
PILLAR_VOXELS = VoxelHelper.VoxelListBuilder.create(PILLAR, 3)

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 new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(new_state.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

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

View File

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

View File

@@ -7,6 +7,8 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
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;
@@ -32,11 +34,6 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock {
super(settings);
setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
}
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(FACING);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
@@ -45,6 +42,7 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock {
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null) return false;
return !(
context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
@@ -77,6 +75,16 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock {
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) {
return switch (side) {
case DOWN -> DOWN;

View File

@@ -4,11 +4,14 @@ import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
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.Direction;
import net.minecraft.util.shape.VoxelShape;
import org.jetbrains.annotations.Nullable;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.*;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock {
@@ -18,11 +21,6 @@ public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock {
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
}
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(AXIS);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(AXIS));
@@ -34,6 +32,16 @@ public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock {
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
public VoxelShape getShape(BlockState state, int i) {
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.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.math.Vec3d;
@@ -34,11 +36,6 @@ public class ReFramedSmallCubeBlock extends WaterloggableReFramedBlock {
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN));
}
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(CORNER);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER));
@@ -46,6 +43,7 @@ public class ReFramedSmallCubeBlock extends WaterloggableReFramedBlock {
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null) return false;
Corner corner = state.get(CORNER);
return !(
context.getPlayer().isSneaking()
@@ -130,6 +128,16 @@ public class ReFramedSmallCubeBlock extends WaterloggableReFramedBlock {
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
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
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.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.shape.VoxelShape;
import net.minecraft.world.BlockView;
@@ -25,11 +27,6 @@ public class ReFramedSmallCubesStepBlock extends WaterloggableReFramedDoubleBloc
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
}
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(EDGE);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE));
@@ -42,7 +39,7 @@ public class ReFramedSmallCubesStepBlock extends WaterloggableReFramedDoubleBloc
}
@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));
}
@@ -51,6 +48,16 @@ public class ReFramedSmallCubesStepBlock extends WaterloggableReFramedDoubleBloc
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
public VoxelShape getShape(BlockState state, int i) {
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.ItemPlacementContext;
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.math.BlockPos;
import net.minecraft.util.math.Direction;
@@ -31,17 +33,11 @@ import static fr.adrien1106.reframed.util.blocks.StairShape.*;
public class ReFramedStairBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] STAIR_VOXELS;
private record ModelCacheKey(Edge edge, StairShape shape) {}
public ReFramedStairBlock(Settings settings) {
super(settings);
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
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
@@ -50,6 +46,7 @@ public class ReFramedStairBlock extends WaterloggableReFramedBlock {
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null) return false;
return !(
context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
@@ -90,10 +87,10 @@ public class ReFramedStairBlock extends WaterloggableReFramedBlock {
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override
@@ -101,6 +98,31 @@ public class ReFramedStairBlock extends WaterloggableReFramedBlock {
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) {
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.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;
@@ -22,18 +24,12 @@ import static fr.adrien1106.reframed.util.blocks.BlockProperties.STAIR_SHAPE;
public class ReFramedStairsCubeBlock extends ReFramedDoubleBlock {
private static final VoxelShape[] STAIRS_CUBE_VOXELS = VoxelListBuilder.buildFrom(STAIR_VOXELS);
private record ModelCacheKey(Edge edge, StairShape shape) {}
public ReFramedStairsCubeBlock(Settings settings) {
super(settings);
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
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE, STAIR_SHAPE));
@@ -54,11 +50,37 @@ public class ReFramedStairsCubeBlock extends ReFramedDoubleBlock {
return super.getPlacementState(ctx).with(EDGE, face).with(STAIR_SHAPE, shape);
}
@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 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
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(new_state.getBlock())) world.removeBlockEntity(pos);
}
@Override

View File

@@ -10,6 +10,8 @@ import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
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.math.Vec3d;
@@ -34,11 +36,6 @@ public class ReFramedStepBlock extends WaterloggableReFramedBlock {
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
}
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(EDGE);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE));
@@ -46,6 +43,7 @@ public class ReFramedStepBlock extends WaterloggableReFramedBlock {
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null) return false;
Edge edge = state.get(EDGE);
return !(
context.getPlayer().isSneaking()
@@ -130,6 +128,16 @@ public class ReFramedStepBlock extends WaterloggableReFramedBlock {
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) {
return STEP_VOXELS[edge.getID()];
}

View File

@@ -7,6 +7,8 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Direction.Axis;
@@ -22,18 +24,12 @@ import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock {
private record ModelCacheKey(Direction facing, Axis axis) {}
public ReFramedStepsSlabBlock(Settings settings) {
super(settings);
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
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(FACING, AXIS));
@@ -48,7 +44,7 @@ public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock {
}
@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));
}
@@ -57,6 +53,21 @@ public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock {
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
public VoxelShape getShape(BlockState state, int i) {
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 new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, moved);
if(!state.isOf(new_state.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.state.StateManager;
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.math.BlockPos;
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.World;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.WorldView;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static net.minecraft.state.property.Properties.*;
public class ReframedWallBlock extends WaterloggableReFramedBlock {
public class ReFramedWallBlock extends WaterloggableReFramedBlock {
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);
setDefaultState(getDefaultState()
.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
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));
@@ -61,34 +53,13 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock {
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);
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());
if (shouldConnectTo(other_state, side_full, dir.getOpposite())) {
Property<WallShape> wall_shape = getWallShape(dir);
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
);
} else new_state = new_state.with(getWallShape(dir), WallShape.NONE);
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));
}));
new_state = getWallState(new_state, top_state, neighbors, top_shape, fs, world, pos);
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);
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);
for (Direction dir : Direction.Type.HORIZONTAL) {
BlockPos offset = pos.offset(dir);
BlockState neighbor = world.getBlockState(offset);
boolean side_full = neighbor.isSideSolidFullSquare(world, offset, dir.getOpposite());
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
);
}
}
Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
.collect(Collectors.toMap(d -> d, d -> world.getBlockState(pos.offset(d))));
state = getWallState(state, top_state, neighbors, top_shape, fs, world, pos);
return state.with(UP, shouldHavePost(state, top_state, top_shape));
}
@@ -148,9 +108,43 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock {
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
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
.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))
return true;
// tall Matching sides
// Matching sides
if (Stream.of(Direction.SOUTH, Direction.EAST)
.anyMatch(dir ->
state.get(getWallShape(dir)) == WallShape.TALL
&& state.get(getWallShape(dir.getOpposite())) == WallShape.TALL
state.get(getWallShape(dir)) == state.get(getWallShape(dir.getOpposite()))
)) return false;
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();
boolean bl = block instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, side);
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(
self_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) {
case EAST -> EAST_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.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 static final ReFramedModelProvider PROVIDER = new ReFramedModelProvider();
@@ -31,92 +27,123 @@ public class ReFramedClient implements ClientModInitializer {
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0]));
// CUBE
HELPER.addReFramedModel("cube" , HELPER.auto(new Identifier("block/cube"), 2));
HELPER.addReFramedModel("cube" , HELPER.auto(new Identifier("block/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
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_reverse" , HELPER.autoDouble(ReFramed.id("block/small_cube/step/base"), ReFramed.id("block/small_cube/base"), 4, 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")));
// SLAB
HELPER.addReFramedModel("slab" , HELPER.auto(new Identifier("block/slab"), 7, FACING));
HELPER.addReFramedModel("slab" , HELPER.auto(new Identifier("block/slab")));
// 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
HELPER.addReFramedModel("stair" , HELPER.auto(ReFramed.id("block/stair/straight"), 13, EDGE));
HELPER.addReFramedModel("outers_stair" , HELPER.auto(ReFramed.id("block/stair/double_outer"), 24, EDGE, STAIR_SHAPE));
HELPER.addReFramedModel("inner_stair" , HELPER.auto(ReFramed.id("block/stair/inner"), 24, EDGE, STAIR_SHAPE));
HELPER.addReFramedModel("outer_stair" , HELPER.auto(ReFramed.id("block/stair/outer"), 16, EDGE, STAIR_SHAPE));
HELPER.addReFramedModel("outer_side_stair" , HELPER.auto(ReFramed.id("block/stair/outer_side"), 32, EDGE, STAIR_SHAPE));
HELPER.addReFramedModel("stair" , HELPER.auto(ReFramed.id("block/stair/straight")));
HELPER.addReFramedModel("outers_stair" , HELPER.auto(ReFramed.id("block/stair/double_outer")));
HELPER.addReFramedModel("inner_stair" , HELPER.auto(ReFramed.id("block/stair/inner")));
HELPER.addReFramedModel("outer_stair" , HELPER.auto(ReFramed.id("block/stair/outer")));
HELPER.addReFramedModel("outer_side_stair" , HELPER.auto(ReFramed.id("block/stair/outer_side")));
// STAIRS_CUBE
HELPER.addReFramedModel("stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/straight"), ReFramed.id("block/stair/cube/straight"), 13, EDGE));
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("inner_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/inner"), ReFramed.id("block/stair/cube/inner"), 24, EDGE, STAIR_SHAPE));
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_side_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/outer_side"), ReFramed.id("block/stair/cube/outer_side"), 32, EDGE, STAIR_SHAPE));
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")));
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")));
HELPER.addReFramedModel("outer_side_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/outer_side"), ReFramed.id("block/stair/cube/outer_side")));
// HALF_STAIR
HELPER.addReFramedModel("half_stair_down" , HELPER.auto(ReFramed.id("block/half_stair/down"), 9, CORNER, CORNER_FACE));
HELPER.addReFramedModel("half_stair_side" , HELPER.auto(ReFramed.id("block/half_stair/side"), 16, 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")));
// 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_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_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")));
// 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_side" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/stair/side"), 6, EDGE));
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_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")));
HELPER.addReFramedModel("half_stairs_stair_reverse" , HELPER.autoDouble(ReFramed.id("block/half_stair/stair/side"), ReFramed.id("block/half_stair/side")));
// 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
HELPER.addReFramedModel("steps_slab" , HELPER.autoDouble(ReFramed.id("block/step/down"), ReFramed.id("block/step/slab/down"), 5, FACING, AXIS));
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" , 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")));
// LAYER
HELPER.addReFramedModel("layer_1" , HELPER.auto(new Identifier("block/snow_height2"), 7, FACING));
HELPER.addReFramedModel("layer_2" , HELPER.auto(new Identifier("block/snow_height4"), 6, FACING));
HELPER.addReFramedModel("layer_3" , HELPER.auto(new Identifier("block/snow_height6"), 6, FACING));
HELPER.addReFramedModel("layer_4" , HELPER.auto(new Identifier("block/snow_height8"), 6, FACING));
HELPER.addReFramedModel("layer_5" , HELPER.auto(new Identifier("block/snow_height10"), 6, FACING));
HELPER.addReFramedModel("layer_6" , HELPER.auto(new Identifier("block/snow_height12"), 6, FACING));
HELPER.addReFramedModel("layer_7" , HELPER.auto(new Identifier("block/snow_height14"), 6, FACING));
HELPER.addReFramedModel("layer_8" , HELPER.auto(new Identifier("block/cube"), 1));
HELPER.addReFramedModel("layer_1" , HELPER.auto(new Identifier("block/snow_height2")));
HELPER.addReFramedModel("layer_2" , HELPER.auto(new Identifier("block/snow_height4")));
HELPER.addReFramedModel("layer_3" , HELPER.auto(new Identifier("block/snow_height6")));
HELPER.addReFramedModel("layer_4" , HELPER.auto(new Identifier("block/snow_height8")));
HELPER.addReFramedModel("layer_5" , HELPER.auto(new Identifier("block/snow_height10")));
HELPER.addReFramedModel("layer_6" , HELPER.auto(new Identifier("block/snow_height12")));
HELPER.addReFramedModel("layer_7" , HELPER.auto(new Identifier("block/snow_height14")));
HELPER.addReFramedModel("layer_8" , HELPER.auto(new Identifier("block/cube")));
// PILLAR
HELPER.addReFramedModel("pillar" , HELPER.auto(ReFramed.id("block/pillar"), 4, AXIS));
HELPER.addReFramedModel("pillar" , HELPER.auto(ReFramed.id("block/pillar")));
// 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
HELPER.addReFramedModel("wall_core" , HELPER.auto(ReFramed.id("block/wall/pillar/core"), 1, UP));
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_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_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_core" , HELPER.auto(ReFramed.id("block/wall/pillar/core")));
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")));
HELPER.addReFramedModel("wall_pillar_none" , HELPER.auto(ReFramed.id("block/wall/pillar/none")));
// --------------------- 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_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_low" , HELPER.auto(ReFramed.id("block/wall/side/low")));
HELPER.addReFramedModel("wall_side_tall" , HELPER.auto(ReFramed.id("block/wall/side/tall")));
// --------------------- 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_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_low_e" , HELPER.auto(ReFramed.id("block/wall/junction/low")));
HELPER.addReFramedModel("wall_tall_e" , HELPER.auto(ReFramed.id("block/wall/junction/tall")));
// --------------------- 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_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_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_i" , HELPER.auto(ReFramed.id("block/wall/junction/low_i")));
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")));
// --------------------- 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_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_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_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_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")));
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")));
// --------------------- 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_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_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_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_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_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_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_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_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")));
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")));
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")));
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")));
// --------------------- 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_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_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_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_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_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_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")));
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")));
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")));
// 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")));
// BUTTON
HELPER.addReFramedModel("button_inventory" , HELPER.auto(new Identifier("block/button_inventory")));
HELPER.addReFramedModel("button" , HELPER.auto(new Identifier("block/button")));
HELPER.addReFramedModel("button_pressed" , HELPER.auto(new Identifier("block/button_pressed")));
// POST
HELPER.addReFramedModel("post" , HELPER.auto(ReFramed.id("block/post")));
// FENCE
HELPER.addReFramedModel("fence_inventory" , HELPER.auto(ReFramed.id("block/fence/inventory")));
HELPER.addReFramedModel("fence_core" , HELPER.auto(ReFramed.id("block/fence/core")));
HELPER.addReFramedModel("fence_side_off" , HELPER.auto(ReFramed.id("block/fence/side_off")));
HELPER.addReFramedModel("fence_side_on" , HELPER.auto(ReFramed.id("block/fence/side_on")));
// POST FENCE
HELPER.addReFramedModel("post_fence_inventory" , HELPER.autoDouble(ReFramed.id("block/post"), ReFramed.id("block/fence/full/inventory")));
HELPER.addReFramedModel("post_fence_side" , HELPER.autoDouble(ReFramed.id("block/fence/full/side_core"), ReFramed.id("block/fence/full/side_bars")));
//item model assignments (in lieu of models/item/___.json)
@@ -134,11 +161,19 @@ public class ReFramedClient implements ClientModInitializer {
HELPER.assignItemModel("steps_slab" , ReFramed.STEPS_SLAB);
HELPER.assignItemModel("layer_1" , ReFramed.LAYER);
HELPER.assignItemModel("pillar" , ReFramed.PILLAR);
HELPER.assignItemModel("pillars_wall_inventory", ReFramed.PILLARS_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);
HELPER.assignItemModel("button_inventory" , ReFramed.BUTTON);
HELPER.assignItemModel("post" , ReFramed.POST);
HELPER.assignItemModel("fence_inventory" , ReFramed.FENCE);
HELPER.assignItemModel("post_fence_inventory" , ReFramed.POST_FENCE);
}
private void privateInit() {
//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) -> {
if(world == MinecraftClient.getInstance().world) {
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.util.SpriteIdentifier;
import net.minecraft.item.ItemConvertible;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
@@ -28,18 +27,18 @@ public class ReFramedClientHelper {
private final ReFramedModelProvider prov;
public UnbakedRetexturedModel auto(Identifier parent, int model_count, Property<?>... properties) {
return new UnbakedAutoRetexturedModel(parent, model_count, properties);
public UnbakedRetexturedModel auto(Identifier parent) {
return new UnbakedAutoRetexturedModel(parent);
}
public UnbakedRetexturedModel json(Identifier parent, int model_count, Property<?>... properties) {
return new UnbakedJsonRetexturedModel(parent, model_count, properties);
public UnbakedRetexturedModel json(Identifier parent) {
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(
auto(first, model_count, properties),
auto(second, model_count, properties)
auto(first),
auto(second)
);
}

View File

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

View File

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

View File

@@ -12,15 +12,16 @@ import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import net.fabricmc.fabric.api.renderer.v1.mesh.*;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
import net.minecraft.state.property.Property;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
@@ -29,29 +30,21 @@ import net.minecraft.world.BlockRenderView;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Stream;
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.appearance_manager = tam;
this.theme_index = theme_index;
this.uv_lock = settings.isUvLocked();
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 int theme_index;
protected final boolean uv_lock;
protected final BlockState item_state;
protected final Property<?>[] properties;
protected record MeshCacheKey(Object state_key, CamoAppearance appearance, int model_id) {}
/** cache that store retextured models */
@@ -59,7 +52,11 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
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 */
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;
static {
@@ -68,7 +65,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
System.arraycopy(values, 0, DIRECTIONS_AND_NULL, 0, values.length);
}
protected Mesh getBaseMesh(Object key, BlockState state) {
protected Mesh getBaseMesh(Object key, BlockState state) {
//Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate
if (BASE_MESH_CACHE.containsKey(key)) return BASE_MESH_CACHE.getAndMoveToFirst(key);
Mesh mesh = convertModel(state);
@@ -76,7 +73,40 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
return mesh;
}
protected abstract Mesh convertModel(BlockState state);
private List<BakedQuad>[] quads = null;
@Override
public List<BakedQuad> getQuads(BlockState state, Direction face, Random rand) {
if (quads == null) {
quads = ModelHelper.toQuadLists(
getRetexturedMesh(
new MeshCacheKey(
hashCode(),
appearance_manager.getDefaultAppearance(theme_index),
0
),
state
)
);
}
return quads[ModelHelper.toFaceIndex(face)];
}
public void setCamo(BlockRenderView world, BlockState state, BlockPos pos) {
if (state == null || state.isAir()) {
quads = null;
return;
}
CamoAppearance camo = appearance_manager.getCamoAppearance(world, state, pos, theme_index, false);
MeshCacheKey key = new MeshCacheKey(
hashCode(),
camo,
0
);
quads = ModelHelper.toQuadLists(camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state));
}
protected abstract Mesh convertModel(BlockState state);
@Override
public boolean isVanillaAdapter() {
@@ -87,16 +117,20 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
public Sprite getParticleSprite() {
return appearance_manager.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite();
}
public int getThemeIndex() {
return theme_index;
}
@Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null;
QuadEmitter quad_emitter = context.getEmitter();
List<?> model_key = Stream.of(properties).map(state::get).toList();
if(theme == null || theme.isAir()) {
getRetexturedMesh(
new MeshCacheKey(
model_key,
hashCode(),
appearance_manager.getDefaultAppearance(theme_index),
0
),
@@ -112,7 +146,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
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
Mesh untintedMesh = camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state);
@@ -153,7 +187,14 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
context.popTransform();
}
}
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) {
if (RETEXTURED_MESH_CACHE.asMap().containsKey(key)) return RETEXTURED_MESH_CACHE.getIfPresent(key);
Mesh mesh = transformMesh(key, state);
@@ -172,7 +213,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
emitter.copyFrom(quad);
i = key.appearance.transformQuad(emitter, i, quad_index.get(), key.model_id, uv_lock);
} while (i > 0);
// kinda weird to do it like that but other directions don't use the quad_index so it doesn't matter
// kinda weird to do it like that but other directions don't use the quad_index, so it doesn't matter
if (quad.cullFace() == null) quad_index.getAndIncrement();
});

View File

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

View File

@@ -1,6 +1,5 @@
package fr.adrien1106.reframed.client.model;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.ModelBakeSettings;
@@ -41,8 +40,8 @@ public class UnbakedDoubleRetexturedModel implements UnbakedModel {
@Override
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings model_bake_settings, Identifier identifier) {
return new DoubleRetexturingBakedModel(
(ForwardingBakedModel) model_1.bake(baker, texture_getter, model_bake_settings, identifier),
(ForwardingBakedModel) model_2.bake(baker, texture_getter, model_bake_settings, identifier)
(RetexturingBakedModel) model_1.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.util.SpriteIdentifier;
import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
@@ -25,8 +24,8 @@ import java.util.Objects;
import java.util.function.Function;
public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
public UnbakedJsonRetexturedModel(Identifier parent, int state_count, Property<?>... properties) {
super(parent, state_count, properties);
public UnbakedJsonRetexturedModel(Identifier parent) {
super(parent);
}
@Nullable
@@ -45,9 +44,7 @@ public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
ReFramedClient.HELPER.getCamoAppearanceManager(spriteLookup),
theme_index,
bake_settings,
item_state,
state_count,
properties
item_state
) {
protected Mesh convertModel(BlockState state) {
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.client.render.model.UnbakedModel;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier;
import java.util.Collection;
@@ -15,13 +14,9 @@ public abstract class UnbakedRetexturedModel implements UnbakedModel {
protected int theme_index = 1;
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.state_count = state_count;
this.properties = properties;
}
public UnbakedRetexturedModel setThemeIndex(int theme_index) {

View File

@@ -5,13 +5,13 @@ import com.google.common.cache.CacheBuilder;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.client.model.QuadPosBounds;
import fr.adrien1106.reframed.client.model.RetexturingBakedModel;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.function.BooleanBiFunction;
@@ -32,18 +32,18 @@ import static net.minecraft.util.shape.VoxelShapes.combine;
public class RenderHelper {
// 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 record CullElement(Block block, Object state_key, int model) {}
private static final Cache<CullElement, Integer[]> INNER_CULL_MAP = CacheBuilder.newBuilder().build();
private record CullElement(Block block, Object state_key, int model) { }
/**
* 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 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;
Object key = frame_block.getModelCacheKey(state);
if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, key, 1))) return;
if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, hash, 1))) return;
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
QuadEmitter quad_emitter = r.meshBuilder().getEmitter();
@@ -51,8 +51,6 @@ public class RenderHelper {
Random random = Random.create();
List<List<QuadPosBounds>> model_bounds = models.stream()
.map(ForwardingBakedModel::getWrappedModel)
.filter(Objects::nonNull)
.map(wrapped -> wrapped.getQuads(state, null, random))
.map(quads -> quads.stream().map(quad -> {
quad_emitter.fromVanilla(quad, material, null);
@@ -60,12 +58,12 @@ public class RenderHelper {
}).toList()).toList();
Integer[] cull_array;
for(int self_id = 1; self_id <= model_bounds.size(); self_id++) {
for (int self_id = 1; self_id <= model_bounds.size(); self_id++) {
List<QuadPosBounds> self_bounds = model_bounds.get(self_id - 1);
cull_array = new Integer[self_bounds.size()];
for (int self_quad = 0; self_quad < cull_array.length; self_quad++) {
QuadPosBounds self_bound = self_bounds.get(self_quad);
for(int other_id = 1; other_id <= model_bounds.size(); other_id++) {
for (int other_id = 1; other_id <= model_bounds.size(); other_id++) {
if (other_id == self_id) continue;
if (model_bounds.get(other_id - 1).stream().anyMatch(other_bound -> other_bound.equals(self_bound))) {
cull_array[self_quad] = other_id;
@@ -73,15 +71,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) {
if ( !(state.getBlock() instanceof ReFramedBlock frame_block)
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)
|| !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)
) 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;
// needs to be Integer object because array is initialized with null not 0
@@ -97,12 +95,17 @@ public class RenderHelper {
// Doing this method from scratch as it is simpler to do than injecting everywhere
public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) {
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null;
ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e ? e : null;
BlockState other_state = world.getBlockState(other_pos);
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e
&& self_state.getBlock() instanceof ReFramedBlock
? e : null;
ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e
&& other_state.getBlock() instanceof ReFramedBlock
? e : null;
// normal behaviour
if (self == null && other == null) return Block.shouldDrawSide(self_state, world, pos, side, other_pos);
if (theme_index == 0 || (self == null && other == null))
return Block.shouldDrawSide(self_state, world, pos, side, other_pos);
// self is a normal Block
if (self == null && other_state.getBlock() instanceof ReFramedBlock other_block) {

View File

@@ -14,9 +14,11 @@ import java.util.Map;
public class RebakedModel implements BakedModel {
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.ambient_occlusion = ambient_occlusion;
}
@Override
@@ -26,7 +28,7 @@ public class RebakedModel implements BakedModel {
@Override
public boolean useAmbientOcclusion() {
return true;
return ambient_occlusion;
}
@Override

View File

@@ -16,7 +16,11 @@ import java.util.concurrent.CompletableFuture;
public class GBlockTag extends BlockTagProvider {
private static final Map<Class<? extends Block>, TagGetter> providers = new HashMap<>();
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());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
}
public GBlockTag(FabricDataOutput output, CompletableFuture<WrapperLookup> registries) {

View File

@@ -32,7 +32,15 @@ public class GBlockstate extends FabricModelProvider {
providers.put(ReFramedStairsCubeBlock.class, new StairsCube());
providers.put(ReFramedStepBlock.class, new Step());
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(ReFramedButtonBlock.class, new Button());
providers.put(ReFramedPostBlock.class, new Post());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
}
public GBlockstate(FabricDataOutput output) {

View File

@@ -34,7 +34,15 @@ public class GRecipe extends FabricRecipeProvider {
providers.put(ReFramedStairsCubeBlock.class, new StairsCube());
providers.put(ReFramedStepBlock.class, new Step());
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(ReFramedButtonBlock.class, new Button());
providers.put(ReFramedPostBlock.class, new Post());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
providers.put(ReFramedBlueprintItem.class, new Blueprint());
providers.put(ReFramedHammerItem.class, new Hammer());
providers.put(ReFramedScrewdriverItem.class, new Screwdriver());

View File

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

View File

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

View File

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

View File

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

View File

@@ -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

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

View File

@@ -17,7 +17,7 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
private static final FabricLoader LOADER = FabricLoader.getInstance();
private static final Logger LOGGER = LoggerFactory.getLogger("ReFramed MIXIN");
private static final List<String> COMPAT_MOD = List.of("athena", "indium", "sodium", "special-model-loader", "continuity");
private static final List<String> COMPAT_MOD = List.of("athena", "indium", "sodium", "special-model-loader", "continuity", "axiom");
private static final Map<String, Supplier<Boolean>> CONDITIONS = new HashMap<>();
static {
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)));
@@ -26,13 +26,24 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumNonTerrainBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityConnectionPredicateMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMQuadTransformMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityModelWrappingHandlerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomChunkedBlockRegionMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClientBlockEntitySerializerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClipboardMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomCloneBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomPlacementMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomMappedBlockAndTintGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomMoveBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomScale3xMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomRotSpriteMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
}

View File

@@ -32,6 +32,8 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM
@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
*
@@ -77,6 +79,6 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM
}));
});
return new RebakedModel(face_quads);
return new RebakedModel(face_quads, useAmbientOcclusion());
}
}

View File

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

View File

@@ -0,0 +1,26 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.world_modification.ClientBlockEntitySerializer;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.RegistryWrapper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ClientBlockEntitySerializer.class)
public class AxiomClientBlockEntitySerializerMixin {
@Inject(
method = "serialize",
at = @At("HEAD"),
remap = false,
cancellable = true
)
private static void serialize(BlockEntity blockEntity, RegistryWrapper.WrapperLookup provider, CallbackInfoReturnable<NbtCompound> cir) {
if (!(blockEntity instanceof ThemeableBlockEntity)) return;
cir.setReturnValue(blockEntity.createNbt());
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -89,6 +89,6 @@ public abstract class ContinuityCTMBakedModelMixin extends ForwardingBakedModel
transform.getProcessingContext().reset(); // reset instead of outputting to emitter
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) {
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

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

View File

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

View File

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

View File

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

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

View File

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

View File

@@ -25,6 +25,7 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
@Shadow public BlockRenderView blockView;
@Unique private int theme_index = 0;
@Unique private int model_hash = 0;
@ModifyArg(method = "prepareForBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
@@ -42,8 +43,9 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
@Override
@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.model_hash = model_hash;
prepareForBlock(state, pos, ao);
}
@@ -51,4 +53,9 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
public int getThemeIndex() {
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;
@Override
public BakedModel getModel(BlockState state) {
return components.stream().map(pair -> pair.getLeft().test(state) ? pair.getRight(): null).filter(Objects::nonNull).findAny().orElse(null);
public List<BakedModel> getModels(BlockState state) {
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;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.client.model.RetexturingBakedModel;
import fr.adrien1106.reframed.client.util.RenderHelper;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
import net.fabricmc.fabric.api.renderer.v1.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.BlockRenderInfo;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -27,18 +31,34 @@ public abstract class TerrainRenderContextMixin extends AbstractBlockRenderConte
), remap = false,
cancellable = true)
private void renderMultipleModels(BlockState state, BlockPos pos, BakedModel wrapper, MatrixStack matrixStack, CallbackInfo ci) {
if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)
|| !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return;
if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)) 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();
RenderHelper.computeInnerCull(state, models);
int i = 0;
for (BakedModel model : models) {
i++;
aoCalc.clear();
((IBlockRenderInfoMixin) blockInfo).prepareForBlock(state, pos, model.useAmbientOcclusion(), i);
model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
}
models.forEach(model -> {
if (model instanceof MultiRetexturableModel multi_model) {
RenderHelper.computeInnerCull(state, multi_model.models(), model.hashCode());
multi_model.models().forEach(retexturable_model ->
renderModel(state, retexturable_model, pos, aoCalc, blockInfo, this, model.hashCode())
);
} else if (model instanceof RetexturingBakedModel retexturable_model)
renderModel(state, retexturable_model, pos, aoCalc, blockInfo, this, model.hashCode());
else model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
});
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

@@ -0,0 +1,140 @@
package fr.adrien1106.reframed.util;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class DefaultList<T> implements List<T> {
protected T default_value;
protected List<T> elements;
public DefaultList(T default_value, List<T> elements) {
this.default_value = default_value;
this.elements = elements;
}
@Override
public int size() {
return elements.size();
}
@Override
public boolean isEmpty() {
return elements.isEmpty();
}
@Override
public boolean contains(Object o) {
return elements.contains(o) || default_value.equals(o);
}
@NotNull
@Override
public Iterator<T> iterator() {
return elements.iterator();
}
@NotNull
@Override
public Object[] toArray() {
return elements.toArray();
}
@NotNull
@Override
public <T1> T1[] toArray(@NotNull T1[] a) {
return elements.toArray(a);
}
@Override
public boolean add(T t) {
return elements.add(t);
}
@Override
public boolean remove(Object o) {
return elements.remove(o);
}
@Override
public boolean containsAll(@NotNull Collection<?> c) {
return elements.containsAll(c);
}
@Override
public boolean addAll(@NotNull Collection<? extends T> c) {
return elements.addAll(c);
}
@Override
public boolean addAll(int index, @NotNull Collection<? extends T> c) {
return elements.addAll(index, c);
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
return elements.removeAll(c);
}
@Override
public boolean retainAll(@NotNull Collection<?> c) {
return elements.retainAll(c);
}
@Override
public void clear() {
elements.clear();
}
@Override
public T get(int index) {
return index < elements.size() && index >= 0 ? elements.get(index) : default_value;
}
@Override
public T set(int index, T element) {
return elements.set(index, element);
}
@Override
public void add(int index, T element) {
elements.add(index, element);
}
@Override
public T remove(int index) {
return elements.remove(index);
}
@Override
public int indexOf(Object o) {
return elements.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return elements.lastIndexOf(o);
}
@NotNull
@Override
public ListIterator<T> listIterator() {
return elements.listIterator();
}
@NotNull
@Override
public ListIterator<T> listIterator(int index) {
return elements.listIterator();
}
@NotNull
@Override
public List<T> subList(int fromIndex, int toIndex) {
return elements.subList(fromIndex, toIndex);
}
}

View File

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

View File

@@ -1,5 +1,7 @@
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.math.Direction;
@@ -119,4 +121,20 @@ public enum Corner implements StringIdentifiable {
if (edge.getFirstDirection() != third_direction && edge.getSecondDirection() != third_direction) return third_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;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.math.Direction;
@@ -48,13 +50,19 @@ public enum Edge implements StringIdentifiable {
public Direction getSecondDirection() {
return second_direction;
}
public Direction getRightDirection() {
return Direction.from(axis, Direction.AxisDirection.NEGATIVE);
}
public Direction getLeftDirection() {
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) {
return this.first_direction.equals(direction)
|| this.second_direction.equals(direction);
@@ -97,4 +105,18 @@ public enum Edge implements StringIdentifiable {
.filter(value -> value.name().equals(name))
.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

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

View File

@@ -48,4 +48,31 @@ public enum StairShape implements StringIdentifiable {
.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

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

View File

@@ -5,9 +5,11 @@ import net.minecraft.util.math.BlockPos;
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 getModelHash();
}

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"}
}
}
]
}

View File

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