From c3e0ab16b74ae6a24e3531ec71e899119f95fd38 Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Mon, 15 Apr 2024 20:57:03 +0200 Subject: [PATCH] fixed cache issue with dynamic models from continuity and athena. Finished wall logic + updated to minor version 1.5.7 --- gradle.properties | 2 +- .../reframed/block/ReframedWallBlock.java | 78 ++++++++++++++++--- .../client/model/RetexturingBakedModel.java | 4 +- .../apperance/CamoAppearanceManager.java | 10 +-- 4 files changed, 78 insertions(+), 16 deletions(-) diff --git a/gradle.properties b/gradle.properties index d530df0..f8ef931 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ loader_version=0.15.6 # Mod Properties modrinth_id = jCpoCBpn -mod_version = 1.5.6 +mod_version = 1.5.7 maven_group = fr.adrien1106 archives_base_name = ReFramed mod_id = reframed diff --git a/src/main/java/fr/adrien1106/reframed/block/ReframedWallBlock.java b/src/main/java/fr/adrien1106/reframed/block/ReframedWallBlock.java index 9191776..561dbd3 100644 --- a/src/main/java/fr/adrien1106/reframed/block/ReframedWallBlock.java +++ b/src/main/java/fr/adrien1106/reframed/block/ReframedWallBlock.java @@ -14,6 +14,7 @@ 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.stream.Stream; @@ -58,6 +59,41 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock { super.appendProperties(builder.add(UP, 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); + if (dir == Direction.UP) { + for (Direction d : Direction.Type.HORIZONTAL) { + Property 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())) { + new_state = new_state.with( + getWallShape(dir), + fs || shouldUseTall(WALL_VOXELS[dir.ordinal() + 3], top_shape) + ? WallShape.TALL + : WallShape.LOW + ); + } else new_state = new_state.with(getWallShape(dir), WallShape.NONE); + return new_state.with(UP, shouldHavePost(new_state, top_state, top_shape)); + } + @Override public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { BlockState state = super.getPlacementState(ctx); @@ -67,9 +103,11 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock { 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) { - BlockState neighbor = world.getBlockState(pos.offset(dir)); - if (shouldConnectTo(neighbor, fs, dir.getOpposite())) { - state.with( + 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())) { + state = state.with( getWallShape(dir), fs || shouldUseTall(WALL_VOXELS[dir.ordinal() + 3], top_shape) ? WallShape.TALL @@ -80,15 +118,30 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock { return state.with(UP, shouldHavePost(state, top_state, top_shape)); } + @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 getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { VoxelShape shape = state.get(UP) ? WALL_VOXELS[0]: VoxelShapes.empty(); for (Direction dir : Direction.Type.HORIZONTAL) { WallShape wall_shape = state.get(getWallShape(dir)); - if (wall_shape != WallShape.NONE) { -// System.out.println("wall_shape: " + wall_shape + " wall_shape.ordinal-1: " + (wall_shape.ordinal()-1) + " dir.ordinal() - 2: " + (dir.ordinal() - 2)); + if (wall_shape != WallShape.NONE) shape = VoxelShapes.union(shape, WALL_VOXELS[1 + (wall_shape.ordinal()-1) * 4 + (dir.ordinal() - 2)]); - } + } + return shape; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { + VoxelShape shape = state.get(UP) ? WALL_VOXELS[9]: VoxelShapes.empty(); + for (Direction dir : Direction.Type.HORIZONTAL) { + if (state.get(getWallShape(dir)) != WallShape.NONE) + shape = VoxelShapes.union(shape, WALL_VOXELS[8 + dir.ordinal()]); } return shape; } @@ -115,10 +168,10 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock { return top_state.isIn(BlockTags.WALL_POST_OVERRIDE) || top_shape == null || shouldUseTall(WALL_VOXELS[0], top_shape); } - private static boolean shouldConnectTo(BlockState state, boolean faceFullSquare, Direction side) { + private 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) && faceFullSquare || block instanceof PaneBlock || bl; + return state.isIn(BlockTags.WALLS) || !WallBlock.cannotConnect(state) && side_full || block instanceof PaneBlock || bl; } private static boolean shouldUseTall(VoxelShape self_shape, VoxelShape other_shape) { @@ -141,9 +194,11 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock { static { VoxelShape POST = createCuboidShape(4, 0, 4, 12, 16, 12); + VoxelShape POST_COLLISION = createCuboidShape(4, 0, 4, 12, 24, 12); VoxelShape LOW = createCuboidShape(5, 0, 0, 11, 14, 8); VoxelShape TALL = createCuboidShape(5, 0, 0, 11, 16, 8); - WALL_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 9) + VoxelShape SIDE_COLLISION = createCuboidShape(5, 0, 0, 11, 24, 8); + WALL_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 14) .add(LOW) .add(VoxelHelper::mirrorZ) .add(VoxelHelper::rotateY) @@ -152,6 +207,11 @@ public class ReframedWallBlock extends WaterloggableReFramedBlock { .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(); } } diff --git a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java index 5ce4b48..0b211fc 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java @@ -114,7 +114,9 @@ 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); - Mesh untintedMesh = getRetexturedMesh(new MeshCacheKey(frame_block.getModelCacheKey(state), camo, model_id), state); + MeshCacheKey key = new MeshCacheKey(frame_block.getModelCacheKey(state), camo, model_id); + // do not clutter the cache with single-use meshes + Mesh untintedMesh = camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state); //The specific tint might vary a lot; imagine grass color smoothly changing. Trying to bake the tint into //the cached mesh will pollute it with a ton of single-use meshes with only slightly different colors. diff --git a/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java b/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java index e30fe39..665a5a2 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java @@ -95,7 +95,7 @@ public class CamoAppearanceManager { model = dynamic_model.computeQuads(world, state, pos, theme_index); // if model isn't rebaked its just wrapped (i.e. not dynamic and may be cached) if (model instanceof RebakedModel) { - CamoAppearance appearance = computeAppearance(model, state); + CamoAppearance appearance = computeAppearance(model, state, !item); if (item) APPEARANCE_CACHE.put(state, appearance); return appearance; } @@ -104,7 +104,7 @@ public class CamoAppearanceManager { // refresh cache if (APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state); - CamoAppearance appearance = computeAppearance(model, state); + CamoAppearance appearance = computeAppearance(model, state, false); APPEARANCE_CACHE.put(state, appearance); return appearance; } @@ -118,7 +118,7 @@ public class CamoAppearanceManager { // The computeIfAbsent map update will work without corrupting the map, but there will be some "wasted effort" computing the value twice. // The results are going to be the same, apart from their serialNumbers differing (= their equals & hashCode differing). // Tiny amount of wasted space in some caches if CamoAppearances are used as a map key, then. IMO it's not a critical issue. - private CamoAppearance computeAppearance(BakedModel model, BlockState state) { + private CamoAppearance computeAppearance(BakedModel model, BlockState state, boolean is_dynamic) { if(state.getBlock() == Blocks.BARRIER) return barrierItemAppearance; if (!(model instanceof WeightedBakedModelAccessor weighted_model)) { @@ -126,7 +126,7 @@ public class CamoAppearanceManager { getAppearance(model), getCachedMaterial(state, true), getCachedMaterial(state, false), - serial_number.getAndIncrement() + is_dynamic ? -1 : serial_number.getAndIncrement() ); } List> appearances = weighted_model.getModels().stream() @@ -137,7 +137,7 @@ public class CamoAppearanceManager { appearances, getCachedMaterial(state, true), getCachedMaterial(state, false), - serial_number.getAndIncrement() + is_dynamic ? -1 : serial_number.getAndIncrement() ); }