fixed small cube and caching for dynamic models items because items rerender often

This commit is contained in:
Adrien1106 2024-03-09 15:26:50 +01:00
parent bd412bfccd
commit 5fcb983592
6 changed files with 41 additions and 44 deletions

View File

@ -30,7 +30,7 @@ import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
* TODO add Hammer from framed ( removes theme ) for sure * TODO add Hammer from framed ( removes theme ) for sure
* TODO add screwdriver ( iterate over theme states ) ? * TODO add screwdriver ( iterate over theme states ) ?
* TODO add blueprint for survival friendly copy paste of a theme. * 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 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 put more coherence in the double theme orders / directions
* TODO better connected textures * TODO better connected textures

View File

@ -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.BlockProperties.EDGE;
import static fr.adrien1106.reframed.util.blocks.Edge.*; import static fr.adrien1106.reframed.util.blocks.Edge.*;
import static net.minecraft.data.client.VariantSettings.Rotation.*; import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedDoubleSmallBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider { 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)); 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 @Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getStepShape(state.get(EDGE)); return getStepShape(state.get(EDGE));
} }
@Override // TODO @Override
public VoxelShape getShape(BlockState state, int i) { public VoxelShape getShape(BlockState state, int i) {
return switch (state.get(EDGE)) { 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 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 UP_NORTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 4 : 7);
case WEST_DOWN -> SMALL_CUBE_VOXELS.get(i == 1 ? 2 : 3); case WEST_DOWN -> SMALL_CUBE_VOXELS.get(i == 1 ? 2 : 3);
case DOWN_EAST -> SMALL_CUBE_VOXELS.get(i == 1 ? 0 : 1); case DOWN_EAST -> SMALL_CUBE_VOXELS.get(i == 1 ? 0 : 1);
case EAST_UP -> SMALL_CUBE_VOXELS.get(i == 1 ? 4 : 5); case EAST_UP -> SMALL_CUBE_VOXELS.get(i == 1 ? 5 : 4);
case UP_WEST -> SMALL_CUBE_VOXELS.get(i == 1 ? 6 : 7); case UP_WEST -> SMALL_CUBE_VOXELS.get(i == 1 ? 7 : 6);
case WEST_NORTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 3 : 7); case WEST_NORTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 3 : 7);
case NORTH_EAST -> SMALL_CUBE_VOXELS.get(i == 1 ? 0 : 4); case NORTH_EAST -> SMALL_CUBE_VOXELS.get(i == 1 ? 0 : 4);
case EAST_SOUTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 1 : 5); case EAST_SOUTH -> SMALL_CUBE_VOXELS.get(i == 1 ? 1 : 5);
@ -82,10 +88,10 @@ public class ReFramedDoubleSmallBlock extends WaterloggableReFramedDoubleBlock i
@Override @Override
public int getTopThemeIndex(BlockState state) { public int getTopThemeIndex(BlockState state) {
return super.getTopThemeIndex(state); // TODO return 2;
} }
@Override // TODO @Override
public BlockStateSupplier getMultipart() { public BlockStateSupplier getMultipart() {
Identifier small_cube_id = ReFramed.id("double_small_cube_special"); Identifier small_cube_id = ReFramed.id("double_small_cube_special");
return MultipartBlockStateSupplier.create(this) return MultipartBlockStateSupplier.create(this)

View File

@ -108,7 +108,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
} }
if(theme.getBlock() == Blocks.BARRIER) return; 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); long seed = theme.getRenderingSeed(pos);
int model_id = 0; int model_id = 0;
if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed); if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
@ -135,7 +135,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
int tint; int tint;
BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index); BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index);
if(!theme.isAir()) { 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); tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
} else { } else {
appearance = appearance_manager.getDefaultAppearance(theme_index); appearance = appearance_manager.getDefaultAppearance(theme_index);

View File

@ -1,11 +1,12 @@
package fr.adrien1106.reframed.client.model.apperance; 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.ReFramed;
import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.client.model.DynamicBakedModel; import fr.adrien1106.reframed.client.model.DynamicBakedModel;
import fr.adrien1106.reframed.client.model.QuadPosBounds; import fr.adrien1106.reframed.client.model.QuadPosBounds;
import fr.adrien1106.reframed.mixin.model.WeightedBakedModelAccessor; import fr.adrien1106.reframed.mixin.model.WeightedBakedModelAccessor;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.Renderer;
@ -67,11 +68,8 @@ public class CamoAppearanceManager {
private final CamoAppearance accent_appearance; private final CamoAppearance accent_appearance;
private final CamoAppearance barrierItemAppearance; private final CamoAppearance barrierItemAppearance;
private static final Object2ObjectLinkedOpenHashMap<BlockState, CamoAppearance> APPEARANCE_CACHE = private static final Cache<BlockState, CamoAppearance> APPEARANCE_CACHE = CacheBuilder.newBuilder().maximumSize(2048).build();
new Object2ObjectLinkedOpenHashMap<>(2048, 0.25f) {
@Override
protected void rehash(int n) {}
};
private final AtomicInteger serial_number = new AtomicInteger(0); //Mutable private final AtomicInteger serial_number = new AtomicInteger(0); //Mutable
private final EnumMap<BlendMode, RenderMaterial> ao_materials = new EnumMap<>(BlendMode.class); private final EnumMap<BlendMode, RenderMaterial> ao_materials = new EnumMap<>(BlendMode.class);
@ -81,19 +79,24 @@ public class CamoAppearanceManager {
return appearance == 2 ? accent_appearance: default_appearance; 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); 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 // add support for connected textures and more generally any compatible models injected so that they return baked quads
if (model instanceof DynamicBakedModel dynamic_model) { 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 // 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); CamoAppearance appearance = computeAppearance(model, state);
APPEARANCE_CACHE.putAndMoveToFirst(state, appearance); APPEARANCE_CACHE.put(state, appearance);
return appearance; return appearance;
} }

View File

@ -56,8 +56,7 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM
level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity
? framed_entity.getTheme(theme_index) ? framed_entity.getTheme(theme_index)
: state, pos, direction) : 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()); Sprite texture = textures.get(sprite.sprite());
if (texture == null) return quads; if (texture == null) return quads;
emitter.square(direction, sprite.left(), sprite.bottom(), sprite.right(), sprite.top(), sprite.depth()); emitter.square(direction, sprite.left(), sprite.bottom(), sprite.right(), sprite.top(), sprite.depth());

View File

@ -48,7 +48,7 @@ public class BlockHelper {
// self culling cache of the models not made thread local so that it is only computed once // self culling cache of the models not made thread local so that it is only computed once
private static final Cache<CullElement, Integer[]> INNER_CULL_MAP = CacheBuilder.newBuilder().maximumSize(1024).build(); private static final Cache<CullElement, Integer[]> INNER_CULL_MAP = CacheBuilder.newBuilder().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) { public static Corner getPlacementCorner(ItemPlacementContext ctx) {
Direction side = ctx.getSide().getOpposite(); Direction side = ctx.getSide().getOpposite();
@ -172,17 +172,18 @@ public class BlockHelper {
// check for default light emission // check for default light emission
if (placement_state.getLuminance() > 0 if (placement_state.getLuminance() > 0
&& themes.stream().noneMatch(theme -> theme.getLuminance() > 0)) && themes.stream().noneMatch(theme -> theme.getLuminance() > 0)
if (block_entity.emitsLight()) Block.dropStack(world, pos, new ItemStack(Items.GLOWSTONE_DUST)); && !block_entity.emitsLight()
else block_entity.toggleLight(); )
block_entity.toggleLight();
world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight())); world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight()));
// check for default redstone emission // check for default redstone emission
if (placement_state.getWeakRedstonePower(world, pos, Direction.NORTH) > 0 if (placement_state.getWeakRedstonePower(world, pos, Direction.NORTH) > 0
&& themes.stream().noneMatch(theme -> theme.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)); && !block_entity.emitsRedstone()
else block_entity.toggleRedstone(); ) block_entity.toggleRedstone();
if(!player.isCreative()) held.decrement(1); if(!player.isCreative()) held.decrement(1);
world.playSound(player, pos, placement_state.getSoundGroup().getPlaceSound(), SoundCategory.BLOCKS, 1f, 1.1f); 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) { if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST) {
block_entity.toggleLight(); block_entity.toggleLight();
world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight())); 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); world.playSound(player, pos, SoundEvents.BLOCK_GLASS_HIT, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS; 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 // 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 && ext.canAddRedstoneEmission(state, world, pos)) {
block_entity.toggleRedstone(); 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); world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS; return ActionResult.SUCCESS;
} }
@ -225,10 +218,6 @@ public class BlockHelper {
// Frame will lose its collision if applied with popped chorus fruit // 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 && ext.canRemoveCollision(state, world, pos)) {
block_entity.toggleSolidity(); 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); world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS; return ActionResult.SUCCESS;
} }
@ -245,7 +234,7 @@ public class BlockHelper {
public static void computeInnerCull(BlockState state, List<ForwardingBakedModel> models) { public static void computeInnerCull(BlockState state, List<ForwardingBakedModel> models) {
if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return; if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return;
Object key = frame_block.getModelCacheKey(state); 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(); Renderer r = ReFramedClient.HELPER.getFabricRenderer();
QuadEmitter quad_emitter = r.meshBuilder().getEmitter(); 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) if ( !(state.getBlock() instanceof ReFramedBlock frame_block)
|| !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity) || !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)
) return true; ) return true;
CullElement key = new CullElement(frame_block.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; if (!INNER_CULL_MAP.asMap().containsKey(key)) return true;
// needs to be Integer object because array is initialized with null not 0 // needs to be Integer object because array is initialized with null not 0