diff --git a/src/main/java/fr/adrien1106/reframed/ReFramed.java b/src/main/java/fr/adrien1106/reframed/ReFramed.java index 80b942e..954c421 100644 --- a/src/main/java/fr/adrien1106/reframed/ReFramed.java +++ b/src/main/java/fr/adrien1106/reframed/ReFramed.java @@ -30,7 +30,7 @@ import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT; * TODO add Hammer from framed ( removes theme ) for sure * TODO add screwdriver ( iterate over theme states ) ? * TODO add blueprint for survival friendly copy paste of a theme. - * TODO fix other models ( + half stair ) + * TODO fix other models ( + half stair + layers ) * TODO get better naming for the shapes (will break a lot of already placed blocks) * TODO put more coherence in the double theme orders / directions * TODO better connected textures diff --git a/src/main/java/fr/adrien1106/reframed/ReFramedDoubleSmallBlock.java b/src/main/java/fr/adrien1106/reframed/ReFramedDoubleSmallBlock.java index 5951c90..90c145a 100644 --- a/src/main/java/fr/adrien1106/reframed/ReFramedDoubleSmallBlock.java +++ b/src/main/java/fr/adrien1106/reframed/ReFramedDoubleSmallBlock.java @@ -28,6 +28,7 @@ import static fr.adrien1106.reframed.block.ReFramedStepBlock.getStepShape; import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE; import static fr.adrien1106.reframed.util.blocks.Edge.*; import static net.minecraft.data.client.VariantSettings.Rotation.*; +import static net.minecraft.util.shape.VoxelShapes.empty; public class ReFramedDoubleSmallBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider { @@ -57,22 +58,27 @@ public class ReFramedDoubleSmallBlock extends WaterloggableReFramedDoubleBlock i return super.getPlacementState(ctx).with(EDGE, BlockHelper.getPlacementEdge(ctx)); } + @Override + public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) { + return isGhost(view, pos) ? empty(): getStepShape(state.get(EDGE)); + } + @Override public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { return getStepShape(state.get(EDGE)); } - @Override // TODO + @Override public VoxelShape getShape(BlockState state, int i) { return switch (state.get(EDGE)) { - case NORTH_DOWN -> SMALL_CUBE_VOXELS.get(i == 1 ? 0 : 3); + case NORTH_DOWN -> SMALL_CUBE_VOXELS.get(i == 1 ? 3 : 0); case DOWN_SOUTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 1 : 2); - case SOUTH_UP -> SMALL_CUBE_VOXELS.get(i == 1 ? 5 : 6); + case SOUTH_UP -> SMALL_CUBE_VOXELS.get(i == 1 ? 6 : 5); case UP_NORTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 4 : 7); case WEST_DOWN -> SMALL_CUBE_VOXELS.get(i == 1 ? 2 : 3); case DOWN_EAST -> SMALL_CUBE_VOXELS.get(i == 1 ? 0 : 1); - case EAST_UP -> SMALL_CUBE_VOXELS.get(i == 1 ? 4 : 5); - case UP_WEST -> SMALL_CUBE_VOXELS.get(i == 1 ? 6 : 7); + case EAST_UP -> SMALL_CUBE_VOXELS.get(i == 1 ? 5 : 4); + case UP_WEST -> SMALL_CUBE_VOXELS.get(i == 1 ? 7 : 6); case WEST_NORTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 3 : 7); case NORTH_EAST -> SMALL_CUBE_VOXELS.get(i == 1 ? 0 : 4); case EAST_SOUTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 1 : 5); @@ -82,10 +88,10 @@ public class ReFramedDoubleSmallBlock extends WaterloggableReFramedDoubleBlock i @Override public int getTopThemeIndex(BlockState state) { - return super.getTopThemeIndex(state); // TODO + return 2; } - @Override // TODO + @Override public BlockStateSupplier getMultipart() { Identifier small_cube_id = ReFramed.id("double_small_cube_special"); return MultipartBlockStateSupplier.create(this) 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 a9e1232..5ce4b48 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/RetexturingBakedModel.java @@ -108,7 +108,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { } if(theme.getBlock() == Blocks.BARRIER) return; - CamoAppearance camo = appearance_manager.getCamoAppearance(world, theme, pos, theme_index); + CamoAppearance camo = appearance_manager.getCamoAppearance(world, theme, pos, theme_index, false); long seed = theme.getRenderingSeed(pos); int model_id = 0; if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed); @@ -135,7 +135,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel { int tint; BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index); if(!theme.isAir()) { - appearance = appearance_manager.getCamoAppearance(null, theme, null, theme_index); + appearance = appearance_manager.getCamoAppearance(null, theme, null, theme_index, true); tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0); } else { appearance = appearance_manager.getDefaultAppearance(theme_index); 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 22cdd66..f6a5c64 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 @@ -1,11 +1,12 @@ package fr.adrien1106.reframed.client.model.apperance; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.model.DynamicBakedModel; import fr.adrien1106.reframed.client.model.QuadPosBounds; import fr.adrien1106.reframed.mixin.model.WeightedBakedModelAccessor; -import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.renderer.v1.Renderer; @@ -67,11 +68,8 @@ public class CamoAppearanceManager { private final CamoAppearance accent_appearance; private final CamoAppearance barrierItemAppearance; - private static final Object2ObjectLinkedOpenHashMap APPEARANCE_CACHE = - new Object2ObjectLinkedOpenHashMap<>(2048, 0.25f) { - @Override - protected void rehash(int n) {} - }; + private static final Cache APPEARANCE_CACHE = CacheBuilder.newBuilder().maximumSize(2048).build(); + private final AtomicInteger serial_number = new AtomicInteger(0); //Mutable private final EnumMap ao_materials = new EnumMap<>(BlendMode.class); @@ -81,19 +79,24 @@ public class CamoAppearanceManager { return appearance == 2 ? accent_appearance: default_appearance; } - public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index) { + public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index, boolean item) { BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state); // add support for connected textures and more generally any compatible models injected so that they return baked quads if (model instanceof DynamicBakedModel dynamic_model) { - return computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state); + // cache items as they get rendered more often + if (item && APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state); + + CamoAppearance appearance = computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state); + if (item) APPEARANCE_CACHE.put(state, appearance); + return appearance; } // refresh cache - if (APPEARANCE_CACHE.containsKey(state)) return APPEARANCE_CACHE.getAndMoveToFirst(state); + if (APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state); CamoAppearance appearance = computeAppearance(model, state); - APPEARANCE_CACHE.putAndMoveToFirst(state, appearance); + APPEARANCE_CACHE.put(state, appearance); return appearance; } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java index aac9f62..2bbcb90 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java @@ -56,8 +56,7 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity ? framed_entity.getTheme(theme_index) : state, pos, direction) - ) - .forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> { + ).forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> { Sprite texture = textures.get(sprite.sprite()); if (texture == null) return quads; emitter.square(direction, sprite.left(), sprite.bottom(), sprite.right(), sprite.top(), sprite.depth()); diff --git a/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java b/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java index 5ff1cc9..fd9cb55 100644 --- a/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java +++ b/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java @@ -48,7 +48,7 @@ public class BlockHelper { // self culling cache of the models not made thread local so that it is only computed once private static final Cache INNER_CULL_MAP = CacheBuilder.newBuilder().maximumSize(1024).build(); - private record CullElement(Object state_key, int model) {} + private record CullElement(Block block, Object state_key, int model) {} public static Corner getPlacementCorner(ItemPlacementContext ctx) { Direction side = ctx.getSide().getOpposite(); @@ -172,17 +172,18 @@ public class BlockHelper { // check for default light emission if (placement_state.getLuminance() > 0 - && themes.stream().noneMatch(theme -> theme.getLuminance() > 0)) - if (block_entity.emitsLight()) Block.dropStack(world, pos, new ItemStack(Items.GLOWSTONE_DUST)); - else block_entity.toggleLight(); + && themes.stream().noneMatch(theme -> theme.getLuminance() > 0) + && !block_entity.emitsLight() + ) + block_entity.toggleLight(); world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight())); // check for default redstone emission if (placement_state.getWeakRedstonePower(world, pos, Direction.NORTH) > 0 - && themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) > 0)) - if (block_entity.emitsRedstone()) Block.dropStack(world, pos, new ItemStack(Items.GLOWSTONE_DUST)); - else block_entity.toggleRedstone(); + && themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) > 0) + && !block_entity.emitsRedstone() + ) block_entity.toggleRedstone(); if(!player.isCreative()) held.decrement(1); world.playSound(player, pos, placement_state.getSoundGroup().getPlaceSound(), SoundCategory.BLOCKS, 1f, 1.1f); @@ -203,10 +204,6 @@ public class BlockHelper { if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST) { block_entity.toggleLight(); world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight())); - - if(!player.isCreative()) - if (block_entity.emitsLight()) held.decrement(1); - else held.increment(1); world.playSound(player, pos, SoundEvents.BLOCK_GLASS_HIT, SoundCategory.BLOCKS, 1f, 1f); return ActionResult.SUCCESS; } @@ -214,10 +211,6 @@ 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)) { block_entity.toggleRedstone(); - - if(!player.isCreative()) - if (block_entity.emitsRedstone()) held.decrement(1); - else held.increment(1); world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f); return ActionResult.SUCCESS; } @@ -225,10 +218,6 @@ public class BlockHelper { // Frame will lose its collision if applied with popped chorus fruit if(held.getItem() == Items.POPPED_CHORUS_FRUIT && ext.canRemoveCollision(state, world, pos)) { block_entity.toggleSolidity(); - - if(!player.isCreative()) - if (!block_entity.isSolid()) held.decrement(1); - else held.increment(1); world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f); return ActionResult.SUCCESS; } @@ -245,7 +234,7 @@ public class BlockHelper { public static void computeInnerCull(BlockState state, List models) { if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return; Object key = frame_block.getModelCacheKey(state); - if (INNER_CULL_MAP.asMap().containsKey(new CullElement(key, 1))) return; + if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, key, 1))) return; Renderer r = ReFramedClient.HELPER.getFabricRenderer(); QuadEmitter quad_emitter = r.meshBuilder().getEmitter(); @@ -275,7 +264,7 @@ public class BlockHelper { } } } - INNER_CULL_MAP.put(new CullElement(key, self_id), cull_array); + INNER_CULL_MAP.put(new CullElement(frame_block, key, self_id), cull_array); } } @@ -284,7 +273,7 @@ public class BlockHelper { if ( !(state.getBlock() instanceof ReFramedBlock frame_block) || !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity) ) return true; - CullElement key = new CullElement(frame_block.getModelCacheKey(state), theme_index); + CullElement key = new CullElement(frame_block, frame_block.getModelCacheKey(state), theme_index); if (!INNER_CULL_MAP.asMap().containsKey(key)) return true; // needs to be Integer object because array is initialized with null not 0