Compare commits

..

4 Commits

Author SHA1 Message Date
394c75f9ed fix error + cleanup 2024-03-08 20:45:11 +01:00
e6c9ee63cd self culling !! 2024-03-08 20:38:14 +01:00
239802ce7b cleanup 2024-03-06 22:16:43 +01:00
5f87489939 better caching for both appearances and meshed models 2024-03-06 22:10:11 +01:00
54 changed files with 535 additions and 304 deletions

View File

@ -1,7 +1,7 @@
package fr.adrien1106.reframed; package fr.adrien1106.reframed;
import fr.adrien1106.reframed.block.*; import fr.adrien1106.reframed.block.*;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
@ -26,7 +26,9 @@ import java.util.function.BiConsumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* TODO self culling, fix other models, better connected textures, preload items, better cache * TODO self culling
* TODO fix other models
* TODO better connected textures
*/ */
public class ReFramed implements ModInitializer { public class ReFramed implements ModInitializer {
public static final String MODID = "reframed"; public static final String MODID = "reframed";

View File

@ -2,7 +2,7 @@ package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.RecipeSetter; import fr.adrien1106.reframed.generator.RecipeSetter;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.*; import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
@ -33,7 +33,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import static fr.adrien1106.reframed.util.BlockProperties.LIGHT; import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
public class ReFramedBlock extends Block implements BlockEntityProvider, RecipeSetter { public class ReFramedBlock extends Block implements BlockEntityProvider, RecipeSetter {
@ -41,6 +41,23 @@ public class ReFramedBlock extends Block implements BlockEntityProvider, RecipeS
super(settings); super(settings);
setDefaultState(getDefaultState().with(LIGHT, false)); setDefaultState(getDefaultState().with(LIGHT, false));
} }
/**
* Generates a record for the key so that it replaces the blockstate
* which may have states that returns same models
* @param state - the state_key to generate the key from
* @return a cache key with only relevant properties
*/
public Object getModelCacheKey(BlockState state) {
return "";
}
/**
* @return the amount of models the block can have prevents allocating too much space for a model
*/
public int getModelStateCount() {
return 1;
}
//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 //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 //Just make your own BlockEntityType, it's fine, you can even use the same ReFramedEntity class
@ -98,7 +115,7 @@ public class ReFramedBlock extends Block implements BlockEntityProvider, RecipeS
&& themes.stream().noneMatch(theme -> theme.getLuminance() != 0)) && themes.stream().noneMatch(theme -> theme.getLuminance() != 0))
drops.add(new ItemStack(Items.GLOWSTONE_DUST)); drops.add(new ItemStack(Items.GLOWSTONE_DUST));
if(!frame_entity.isSolid() if(!frame_entity.isSolid()
&& themes.stream().anyMatch(theme -> theme.isSolid())) && themes.stream().anyMatch(AbstractBlockState::isSolid))
drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT)); drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
ItemScatterer.spawn(world, pos, drops); ItemScatterer.spawn(world, pos, drops);

View File

@ -1,8 +1,8 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;

View File

@ -44,7 +44,7 @@ public class ReFramedDoubleEntity extends ReFramedEntity {
public void readNbt(NbtCompound nbt) { public void readNbt(NbtCompound nbt) {
super.readNbt(nbt); super.readNbt(nbt);
BlockState rendered_state = second_state;// keep previous state to check if rerender is needed BlockState rendered_state = second_state;// keep previous state_key to check if rerender is needed
second_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 2)); second_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 2));
// Force a chunk remesh on the client if the displayed blockstate has changed // Force a chunk remesh on the client if the displayed blockstate has changed

View File

@ -13,7 +13,6 @@ import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.recipe.book.RecipeCategory; import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.state.property.Properties;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
@ -25,25 +24,36 @@ import static net.minecraft.data.client.VariantSettings.Rotation.R90;
import static net.minecraft.state.property.Properties.AXIS; import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedDoubleSlabBlock extends ReFramedDoubleBlock implements BlockStateProvider { public class ReFramedDoubleSlabBlock extends ReFramedDoubleBlock implements BlockStateProvider {
public ReFramedDoubleSlabBlock(Settings settings) { public ReFramedDoubleSlabBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(Properties.AXIS, Direction.Axis.Y)); setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
}
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(AXIS);
}
@Override
public int getModelStateCount() {
return 3;
} }
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(Properties.AXIS)); super.appendProperties(builder.add(AXIS));
} }
@Nullable @Nullable
@Override @Override
public BlockState getPlacementState(ItemPlacementContext ctx) { public BlockState getPlacementState(ItemPlacementContext ctx) {
return super.getPlacementState(ctx).with(Properties.AXIS, ctx.getSide().getAxis()); return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis());
} }
@Override @Override
public VoxelShape getShape(BlockState state, int i) { public VoxelShape getShape(BlockState state, int i) {
return switch (state.get(Properties.AXIS)) { return switch (state.get(AXIS)) {
case Y -> i == 2 ? UP : DOWN; case Y -> i == 2 ? UP : DOWN;
case Z -> i == 2 ? NORTH : SOUTH; case Z -> i == 2 ? NORTH : SOUTH;
case X -> i == 2 ? EAST : WEST; case X -> i == 2 ? EAST : WEST;

View File

@ -2,9 +2,9 @@ package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider; import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.property.Corner; import fr.adrien1106.reframed.util.blocks.Corner;
import fr.adrien1106.reframed.util.property.StairShape; import fr.adrien1106.reframed.util.blocks.StairShape;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -28,17 +28,27 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static fr.adrien1106.reframed.block.ReFramedStairsBlock.*; import static fr.adrien1106.reframed.block.ReFramedStairsBlock.*;
import static fr.adrien1106.reframed.util.BlockProperties.CORNER; import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.BlockProperties.STAIR_SHAPE; import static fr.adrien1106.reframed.util.blocks.BlockProperties.STAIR_SHAPE;
import static fr.adrien1106.reframed.util.property.StairShape.STRAIGHT;
public class ReFramedDoubleStairsBlock extends ReFramedDoubleBlock implements BlockStateProvider { public class ReFramedDoubleStairsBlock extends ReFramedDoubleBlock implements BlockStateProvider {
private static final List<VoxelShape> COMPLEMENT_LIST = new ArrayList<>(52); private static final List<VoxelShape> COMPLEMENT_LIST = new ArrayList<>(52);
private record ModelCacheKey(Corner corner, StairShape shape) {}
public ReFramedDoubleStairsBlock(Settings settings) { public ReFramedDoubleStairsBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT)); setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN).with(STAIR_SHAPE, StairShape.STRAIGHT));
}
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(CORNER), state.get(STAIR_SHAPE));
}
@Override
public int getModelStateCount() {
return 108; // Has 12 * 9 state combination and 52 models still reduces cache size
} }
@Override @Override

View File

@ -1,9 +1,9 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.BlockStateProvider; import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -24,19 +24,31 @@ import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.*; import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
import static fr.adrien1106.reframed.block.ReFramedStepBlock.STEP_VOXELS; import static fr.adrien1106.reframed.block.ReFramedStepBlock.STEP_VOXELS;
import static net.minecraft.data.client.VariantSettings.Rotation.*; import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.state.property.Properties.AXIS; import static net.minecraft.state.property.Properties.AXIS;
import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.util.shape.VoxelShapes.empty; import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedDoubleStepBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider { public class ReFramedDoubleStepBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider {
private record ModelCacheKey(Direction facing, Axis axis) {}
public ReFramedDoubleStepBlock(Settings settings) { public ReFramedDoubleStepBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(FACING, Direction.DOWN).with(AXIS, Axis.X)); setDefaultState(getDefaultState().with(FACING, Direction.DOWN).with(AXIS, Axis.X));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(FACING), state.get(AXIS));
}
@Override
public int getModelStateCount() {
return 18;
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(FACING, AXIS)); super.appendProperties(builder.add(FACING, AXIS));

View File

@ -1,8 +1,8 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.BlockProperties; import fr.adrien1106.reframed.util.blocks.BlockProperties;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
@ -27,7 +27,7 @@ import java.util.Objects;
//Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic, //Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic,
//is pretty important since players might place a lot of them. There were tons and tons of these at Blanketcon. //is pretty important since players might place a lot of them. There were tons and tons of these at Blanketcon.
//To that end, most of the state has been crammed into a bitfield. //To that end, most of the state_key has been crammed into a bitfield.
public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity { public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity {
protected BlockState first_state = Blocks.AIR.getDefaultState(); protected BlockState first_state = Blocks.AIR.getDefaultState();
protected byte bit_field = SOLIDITY_MASK; protected byte bit_field = SOLIDITY_MASK;
@ -47,7 +47,7 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
public void readNbt(NbtCompound nbt) { public void readNbt(NbtCompound nbt) {
super.readNbt(nbt); super.readNbt(nbt);
BlockState rendered_state = first_state; // keep previous state to check if rerender is needed BlockState rendered_state = first_state; // keep previous state_key to check if rerender is needed
first_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 1)); first_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 1));
if (nbt.contains(BITFIELD_KEY)) bit_field = nbt.getByte(BITFIELD_KEY); if (nbt.contains(BITFIELD_KEY)) bit_field = nbt.getByte(BITFIELD_KEY);

View File

@ -38,6 +38,16 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements Blo
super(settings); super(settings);
setDefaultState(getDefaultState().with(FACING, Direction.DOWN)); setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(FACING);
}
@Override
public int getModelStateCount() {
return 6;
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {

View File

@ -3,10 +3,10 @@ package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.GBlockstate; import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.BlockStateProvider; import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.VoxelHelper; import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.property.Corner; import fr.adrien1106.reframed.util.blocks.Corner;
import fr.adrien1106.reframed.util.property.StairShape; import fr.adrien1106.reframed.util.blocks.StairShape;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -34,19 +34,30 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
import static fr.adrien1106.reframed.util.BlockProperties.*; import static fr.adrien1106.reframed.util.blocks.BlockProperties.*;
import static fr.adrien1106.reframed.util.property.StairShape.*; import static fr.adrien1106.reframed.util.blocks.StairShape.*;
import static net.minecraft.data.client.VariantSettings.Rotation.*; import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static fr.adrien1106.reframed.util.property.Corner.*; import static fr.adrien1106.reframed.util.blocks.Corner.*;
public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements BlockStateProvider { public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
public static final List<VoxelShape> VOXEL_LIST = new ArrayList<>(52); public static final List<VoxelShape> VOXEL_LIST = new ArrayList<>(52);
private record ModelCacheKey(Corner corner, StairShape shape) {}
public ReFramedStairsBlock(Settings settings) { public ReFramedStairsBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT)); setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return new ModelCacheKey(state.get(CORNER), state.get(STAIR_SHAPE));
}
@Override
public int getModelStateCount() {
return 108; // Has 12 * 9 state combination and 52 models still reduces cache size
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {

View File

@ -3,9 +3,9 @@ package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.GBlockstate; import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.BlockStateProvider; import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.VoxelHelper; import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.property.Corner; import fr.adrien1106.reframed.util.blocks.Corner;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -28,8 +28,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static fr.adrien1106.reframed.util.BlockProperties.CORNER; import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.property.Corner.*; import static fr.adrien1106.reframed.util.blocks.Corner.*;
import static net.minecraft.data.client.VariantSettings.Rotation.*; import static net.minecraft.data.client.VariantSettings.Rotation.*;
public class ReFramedStepBlock extends WaterloggableReFramedBlock implements BlockStateProvider { public class ReFramedStepBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
@ -41,6 +41,16 @@ public class ReFramedStepBlock extends WaterloggableReFramedBlock implements Blo
setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN)); setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN));
} }
@Override
public Object getModelCacheKey(BlockState state) {
return state.get(CORNER);
}
@Override
public int getModelStateCount() {
return 12;
}
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER)); super.appendProperties(builder.add(CORNER));

View File

@ -5,7 +5,6 @@ import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -45,7 +44,7 @@ public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements
} }
@Override @Override
public List<BakedModel> models() { public List<ForwardingBakedModel> models() {
return List.of(model_1, model_2); return List.of(model_1, model_2);
} }
} }

View File

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

View File

@ -76,4 +76,15 @@ public record QuadPosBounds(float min_x, float max_x, float min_y, float max_y,
quad.pos(i, pos); quad.pos(i, pos);
} }
} }
@Override
public boolean equals(Object obj) {
if (!(obj instanceof QuadPosBounds other)) return false;
return MathHelper.approximatelyEquals(min_x, other.min_x)
&& MathHelper.approximatelyEquals(min_y, other.min_y)
&& MathHelper.approximatelyEquals(min_z, other.min_z)
&& MathHelper.approximatelyEquals(max_x, other.max_x)
&& MathHelper.approximatelyEquals(max_y, other.max_y)
&& MathHelper.approximatelyEquals(max_z, other.max_z);
}
} }

View File

@ -1,17 +1,15 @@
package fr.adrien1106.reframed.client.model; package fr.adrien1106.reframed.client.model;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.block.ReFramedEntity; import fr.adrien1106.reframed.block.ReFramedEntity;
import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.client.model.apperance.SpriteProperties;
import fr.adrien1106.reframed.mixin.MinecraftAccessor; import fr.adrien1106.reframed.mixin.MinecraftAccessor;
import fr.adrien1106.reframed.client.model.apperance.CamoAppearance; import fr.adrien1106.reframed.client.model.apperance.CamoAppearance;
import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager; import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance; import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.*;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -26,34 +24,43 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import java.util.List; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier; import java.util.function.Supplier;
public abstract class RetexturingBakedModel extends ForwardingBakedModel { public abstract class RetexturingBakedModel extends ForwardingBakedModel {
public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state, boolean ao) { public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state, boolean ao) {
this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this
this.tam = tam; this.appearance_manager = tam;
this.theme_index = theme_index; this.theme_index = theme_index;
this.uv_lock = settings.isUvLocked(); this.uv_lock = settings.isUvLocked();
this.item_state = item_state; this.item_state = item_state;
this.ao = ao; this.ao = ao;
int cache_size = 64; // default is 64 why don't ask me and it should get overwritten
if (item_state.getBlock() instanceof ReFramedBlock frame_block) cache_size = frame_block.getModelStateCount() + 1;
BASE_MESH_CACHE = new Object2ObjectLinkedOpenHashMap<>(cache_size, 0.25f) {
@Override
protected void rehash(int v) {}
};
} }
protected final CamoAppearanceManager tam; protected final CamoAppearanceManager appearance_manager;
protected final int theme_index; protected final int theme_index;
protected final boolean uv_lock; protected final boolean uv_lock;
protected final BlockState item_state;
protected final boolean ao; protected final boolean ao;
protected final BlockState item_state;
/* ----------------------------------------------- CACHE ELEMENT ------------------------------------------------ */ protected record MeshCacheKey(Object state_key, CamoAppearance appearance, int model_id) {}
// TODO make static ? for connected textures ? /** cache that store retextured models */
protected record MeshCacheKey(BlockState state, TransformCacheKey transform) {} protected final Object2ObjectLinkedOpenHashMap<MeshCacheKey, Mesh> RETEXTURED_MESH_CACHE =
protected record TransformCacheKey(CamoAppearance appearance, int model_id) {} new Object2ObjectLinkedOpenHashMap<>(128, 0.25f) {
protected final ConcurrentMap<TransformCacheKey, RetexturingTransformer> retextured_transforms = new ConcurrentHashMap<>(); @Override
protected final ConcurrentMap<MeshCacheKey, Mesh> retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache protected void rehash(int v) {}
};
/** cache that stores the base meshes which has the size of the amount of models */
protected final Object2ObjectLinkedOpenHashMap<Object, Mesh> BASE_MESH_CACHE;
protected static final Direction[] DIRECTIONS_AND_NULL; protected static final Direction[] DIRECTIONS_AND_NULL;
static { static {
@ -62,11 +69,12 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
System.arraycopy(values, 0, DIRECTIONS_AND_NULL, 0, values.length); System.arraycopy(values, 0, DIRECTIONS_AND_NULL, 0, values.length);
} }
protected final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>(); protected Mesh getBaseMesh(Object key, BlockState state) {
protected Mesh getBaseMesh(BlockState state) {
//Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate //Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate
return jsonToMesh.computeIfAbsent(state, this::convertModel); if (BASE_MESH_CACHE.containsKey(key)) return BASE_MESH_CACHE.getAndMoveToFirst(key);
Mesh mesh = convertModel(state);
BASE_MESH_CACHE.putAndMoveToFirst(key, mesh);
return mesh;
} }
protected abstract Mesh convertModel(BlockState state); protected abstract Mesh convertModel(BlockState state);
@ -78,39 +86,42 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
@Override @Override
public Sprite getParticleSprite() { public Sprite getParticleSprite() {
return tam.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite(); return appearance_manager.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite();
} }
@Override @Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) { public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
// skip render if block not a frame (which should always be the case
if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return;
BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null; BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null;
QuadEmitter quad_emitter = context.getEmitter(); QuadEmitter quad_emitter = context.getEmitter();
if(theme == null || theme.isAir()) { if(theme == null || theme.isAir()) {
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(theme_index), 0)), 0).outputTo(quad_emitter); getRetexturedMesh(
new MeshCacheKey(
frame_block.getModelCacheKey(state),
appearance_manager.getDefaultAppearance(theme_index),
0
),
state
).outputTo(quad_emitter);
return; return;
} }
if(theme.getBlock() == Blocks.BARRIER) return; if(theme.getBlock() == Blocks.BARRIER) return;
CamoAppearance camo = tam.getCamoAppearance(world, theme, pos, theme_index); CamoAppearance camo = appearance_manager.getCamoAppearance(world, theme, pos, theme_index);
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);
int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, world, pos, 0); int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, world, pos, 0);
Mesh untintedMesh = getUntintedRetexturedMesh( Mesh untintedMesh = getRetexturedMesh(new MeshCacheKey(frame_block.getModelCacheKey(state), camo, model_id), state);
new MeshCacheKey(
state,
new TransformCacheKey(camo, model_id)
),
seed
);
//The specific tint might vary a lot; imagine grass color smoothly changing. Trying to bake the tint into //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. //the cached mesh will pollute it with a ton of single-use meshes with only slightly different colors.
if(tint == 0xFFFFFFFF) { if(tint == 0xFFFFFFFF) {
untintedMesh.outputTo(quad_emitter); untintedMesh.outputTo(quad_emitter);
} else { } else {
context.pushTransform(new TintingTransformer(camo, tint, seed)); context.pushTransform(new TintingTransformer(camo, model_id, tint));
untintedMesh.outputTo(quad_emitter); untintedMesh.outputTo(quad_emitter);
context.popTransform(); context.popTransform();
} }
@ -120,121 +131,72 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) { public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
//cheeky: if the item has NBT data, pluck out the blockstate from it & look up the item color provider //cheeky: if the item has NBT data, pluck out the blockstate from it & look up the item color provider
//none of this is accessible unless you're in creative mode doing ctrl-pick btw //none of this is accessible unless you're in creative mode doing ctrl-pick btw
CamoAppearance nbtAppearance; CamoAppearance appearance;
int tint; int tint;
BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index); BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index);
if(!theme.isAir()) { if(!theme.isAir()) {
nbtAppearance = tam.getCamoAppearance(null, theme, null, theme_index); appearance = appearance_manager.getCamoAppearance(null, theme, null, theme_index);
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 {
nbtAppearance = tam.getDefaultAppearance(theme_index); appearance = appearance_manager.getDefaultAppearance(theme_index);
tint = 0xFFFFFFFF; tint = 0xFFFFFFFF;
} }
Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(item_state, new TransformCacheKey(nbtAppearance, 0)), 0); Mesh untintedMesh = getRetexturedMesh(new MeshCacheKey("I", appearance, 0), item_state);
QuadEmitter quad_emitter = context.getEmitter(); QuadEmitter quad_emitter = context.getEmitter();
if(tint == 0xFFFFFFFF) { if(tint == 0xFFFFFFFF) {
untintedMesh.outputTo(quad_emitter); untintedMesh.outputTo(quad_emitter);
} else { } else {
context.pushTransform(new TintingTransformer(nbtAppearance, tint, 0)); context.pushTransform(new TintingTransformer(appearance, 0, tint));
untintedMesh.outputTo(quad_emitter); untintedMesh.outputTo(quad_emitter);
context.popTransform(); context.popTransform();
} }
} }
protected Mesh getUntintedRetexturedMesh(MeshCacheKey key, long seed) { protected Mesh getRetexturedMesh(MeshCacheKey key, BlockState state) {
return retextured_meshes.computeIfAbsent(key, (k) -> createUntintedRetexturedMesh(k, seed)); if (RETEXTURED_MESH_CACHE.containsKey(key)) return RETEXTURED_MESH_CACHE.getAndMoveToFirst(key);
Mesh mesh = transformMesh(key, state);
RETEXTURED_MESH_CACHE.putAndMoveToFirst(key, mesh);
return mesh;
} }
protected Mesh createUntintedRetexturedMesh(MeshCacheKey key, long seed) { protected Mesh transformMesh(MeshCacheKey key, BlockState state) {
RetexturingTransformer transformer = retextured_transforms.computeIfAbsent(key.transform, (k) -> new RetexturingTransformer(k.appearance, seed));
return pretransformMesh(getBaseMesh(key.state), transformer);
}
private static Mesh pretransformMesh(Mesh mesh, RetexturingTransformer transform) {
MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder(); MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();
mesh.forEach(quad -> { AtomicInteger quad_index = new AtomicInteger();
getBaseMesh(key.state_key, state).forEach(quad -> {
int i = -1; int i = -1;
do { do {
emitter.copyFrom(quad); emitter.copyFrom(quad);
i = transform.transform(emitter, i); i = key.appearance.transformQuad(emitter, i, quad_index.get(), key.model_id, ao, uv_lock);
} while (i > 0); } while (i > 0);
// 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();
}); });
return builder.build(); return builder.build();
} }
public class RetexturingTransformer {
private final long seed;
protected RetexturingTransformer(CamoAppearance ta, long seed) {
this.ta = ta;
this.seed = seed;
}
protected final CamoAppearance ta;
public int transform(QuadEmitter quad, int i) {
if(quad.tag() == 0) return 0; //Pass the quad through unmodified.
Direction direction = quad.nominalFace();
List<SpriteProperties> sprites = ta.getSprites(direction, seed);
if (i == -1) i = sprites.size();
SpriteProperties properties = sprites.get(sprites.size() - i);
i--;
QuadPosBounds bounds = properties.bounds();
if (bounds == null) { // sprite applies anywhere e.g. default behaviour
quad.material(ta.getRenderMaterial(ao));
quad.spriteBake(
properties.sprite(),
MutableQuadView.BAKE_NORMALIZED
| properties.flags()
| (uv_lock ? MutableQuadView.BAKE_LOCK_UV : 0)
);
quad.tag(i+1);
quad.emit();
return i;
}
// verify if sprite covers the current quad and apply the new size
QuadPosBounds origin_bounds = QuadPosBounds.read(quad, false);
if (!bounds.matches(origin_bounds)) return i;
// apply new quad shape
quad.material(ta.getRenderMaterial(ao));
bounds.intersection(origin_bounds, direction.getAxis()).apply(quad, origin_bounds);
quad.spriteBake( // seems to work without the flags and break with it
properties.sprite(),
MutableQuadView.BAKE_NORMALIZED
| MutableQuadView.BAKE_LOCK_UV
);
quad.tag(i+1);
quad.emit();
return i;
}
}
protected static class TintingTransformer implements RenderContext.QuadTransform { protected static class TintingTransformer implements RenderContext.QuadTransform {
private final long seed; private final CamoAppearance appearance;
protected TintingTransformer(CamoAppearance ta, int tint, long seed) { private final int model_id;
this.ta = ta; private final int tint;
protected TintingTransformer(CamoAppearance appearance, int model_id, int tint) {
this.appearance = appearance;
this.model_id = model_id;
this.tint = tint; this.tint = tint;
this.seed = seed;
} }
protected final CamoAppearance ta;
protected final int tint;
@Override @Override
public boolean transform(MutableQuadView quad) { public boolean transform(MutableQuadView quad) {
if(quad.tag() == 0) return true; int camo_quad_index = quad.tag() - ((quad.tag() >>> 8) << 8);
if(camo_quad_index == 0) return true;
if(appearance.hasColor(quad.nominalFace(), model_id, camo_quad_index)) quad.color(tint, tint, tint, tint);
if(ta.hasColor(quad.nominalFace(), seed, quad.tag())) quad.color(tint, tint, tint, tint);
return true; return true;
} }
} }

View File

@ -43,7 +43,7 @@ public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel {
Renderer r = ReFramedClient.HELPER.getFabricRenderer(); Renderer r = ReFramedClient.HELPER.getFabricRenderer();
MeshBuilder builder = r.meshBuilder(); MeshBuilder builder = r.meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();
RenderMaterial mat = tam.getCachedMaterial(state, false); RenderMaterial mat = appearance_manager.getCachedMaterial(state, false);
Random rand = Random.create(42); Random rand = Random.create(42);

View File

@ -51,7 +51,7 @@ public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
Renderer r = ReFramedClient.HELPER.getFabricRenderer(); Renderer r = ReFramedClient.HELPER.getFabricRenderer();
MeshBuilder builder = r.meshBuilder(); MeshBuilder builder = r.meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();
RenderMaterial mat = tam.getCachedMaterial(state, false); RenderMaterial mat = appearance_manager.getCachedMaterial(state, false);
Random rand = Random.create(42); Random rand = Random.create(42);

View File

@ -14,7 +14,7 @@ public abstract class UnbakedRetexturedModel implements UnbakedModel {
protected int theme_index = 1; protected int theme_index = 1;
protected BlockState item_state; protected BlockState item_state;
protected boolean ao = true; protected final boolean ao = true;
public UnbakedRetexturedModel(Identifier parent) { public UnbakedRetexturedModel(Identifier parent) {
this.parent = parent; this.parent = parent;

View File

@ -1,13 +1,77 @@
package fr.adrien1106.reframed.client.model.apperance; package fr.adrien1106.reframed.client.model.apperance;
import fr.adrien1106.reframed.client.model.QuadPosBounds;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public interface CamoAppearance { public abstract class CamoAppearance {
@NotNull RenderMaterial getRenderMaterial(boolean ao); protected final int id;
@NotNull List<SpriteProperties> getSprites(Direction dir, long seed); protected final RenderMaterial ao_material;
boolean hasColor(Direction dir, long seed, int index); protected final RenderMaterial material;
protected CamoAppearance(RenderMaterial ao_material, RenderMaterial material, int id) {
this.id = id;
this.ao_material = ao_material;
this.material = material;
}
public abstract @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id);
public abstract boolean hasColor(Direction dir, int model_id, int index);
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
return ao && ao_material != null? ao_material : material;
}
public int transformQuad(QuadEmitter quad, int i, int quad_index, int model_id, boolean ao, boolean uv_lock) {
if(quad.tag() == 0) return 0; // Pass the quad through unmodified.
Direction direction = quad.nominalFace();
List<SpriteProperties> sprites = getSprites(direction, model_id);
if (i == -1) i = sprites.size();
SpriteProperties properties = sprites.get(sprites.size() - i);
int tag = i + (quad_index << 8);
i--;
QuadPosBounds bounds = properties.bounds();
if (bounds == null) { // sprite applies anywhere e.g. default behaviour
quad.material(getRenderMaterial(ao));
quad.spriteBake(
properties.sprite(),
MutableQuadView.BAKE_NORMALIZED
| properties.flags()
| (uv_lock ? MutableQuadView.BAKE_LOCK_UV : 0)
);
quad.tag(tag);
quad.emit();
return i;
}
// verify if sprite covers the current quad and apply the new size
QuadPosBounds origin_bounds = QuadPosBounds.read(quad, false);
if (!bounds.matches(origin_bounds)) return i;
// apply new quad shape
quad.material(getRenderMaterial(ao));
bounds.intersection(origin_bounds, direction.getAxis()).apply(quad, origin_bounds);
quad.spriteBake( // seems to work without the flags and break with it
properties.sprite(),
MutableQuadView.BAKE_NORMALIZED
| MutableQuadView.BAKE_LOCK_UV
);
quad.tag(tag);
quad.emit();
return i;
}
@Override
public int hashCode() {
return id;
}
} }

View File

@ -5,6 +5,7 @@ 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.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
@ -29,7 +30,6 @@ import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
@ -40,20 +40,20 @@ public class CamoAppearanceManager {
for(BlendMode blend : BlendMode.values()) { for(BlendMode blend : BlendMode.values()) {
finder.clear().disableDiffuse(false).blendMode(blend); finder.clear().disableDiffuse(false).blendMode(blend);
materialsWithoutAo.put(blend, finder.ambientOcclusion(TriState.FALSE).find()); materials.put(blend, finder.ambientOcclusion(TriState.FALSE).find());
materialsWithAo.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO ao_materials.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO
} }
Sprite sprite = spriteLookup.apply(DEFAULT_SPRITE_MAIN); Sprite sprite = spriteLookup.apply(DEFAULT_SPRITE_MAIN);
if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !"); if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
this.default_appearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement()); this.default_appearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
sprite = spriteLookup.apply(DEFAULT_SPRITE_SECONDARY); sprite = spriteLookup.apply(DEFAULT_SPRITE_SECONDARY);
if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !"); if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
this.accent_appearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement()); this.accent_appearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
sprite = spriteLookup.apply(BARRIER_SPRITE_ID); sprite = spriteLookup.apply(BARRIER_SPRITE_ID);
this.barrierItemAppearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement()); this.barrierItemAppearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
} }
protected static final SpriteIdentifier DEFAULT_SPRITE_MAIN = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block")); protected static final SpriteIdentifier DEFAULT_SPRITE_MAIN = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block"));
@ -63,12 +63,16 @@ public class CamoAppearanceManager {
private final CamoAppearance default_appearance; private final CamoAppearance default_appearance;
private final CamoAppearance accent_appearance; private final CamoAppearance accent_appearance;
private final CamoAppearance barrierItemAppearance; private final CamoAppearance barrierItemAppearance;
private final ConcurrentHashMap<BlockState, CamoAppearance> appearanceCache = new ConcurrentHashMap<>(); //Mutable, append-only cache private static final Object2ObjectLinkedOpenHashMap<BlockState, CamoAppearance> APPEARANCE_CACHE =
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> materialsWithAo = new EnumMap<>(BlendMode.class); private final EnumMap<BlendMode, RenderMaterial> ao_materials = new EnumMap<>(BlendMode.class);
private final EnumMap<BlendMode, RenderMaterial> materialsWithoutAo = new EnumMap<>(BlendMode.class); //Immutable contents private final EnumMap<BlendMode, RenderMaterial> materials = new EnumMap<>(BlendMode.class); //Immutable contents
public CamoAppearance getDefaultAppearance(int appearance) { public CamoAppearance getDefaultAppearance(int appearance) {
return appearance == 2 ? accent_appearance: default_appearance; return appearance == 2 ? accent_appearance: default_appearance;
@ -81,11 +85,17 @@ public class CamoAppearanceManager {
if (model instanceof DynamicBakedModel dynamic_model) { if (model instanceof DynamicBakedModel dynamic_model) {
return computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state); return computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state);
} }
return appearanceCache.computeIfAbsent(state, block_state -> computeAppearance(model, block_state));
// refresh cache
if (APPEARANCE_CACHE.containsKey(state)) return APPEARANCE_CACHE.getAndMoveToFirst(state);
CamoAppearance appearance = computeAppearance(model, state);
APPEARANCE_CACHE.putAndMoveToFirst(state, appearance);
return appearance;
} }
public RenderMaterial getCachedMaterial(BlockState state, boolean ao) { public RenderMaterial getCachedMaterial(BlockState state, boolean ao) {
Map<BlendMode, RenderMaterial> m = ao ? materialsWithAo : materialsWithoutAo; Map<BlendMode, RenderMaterial> m = ao ? ao_materials : materials;
return m.get(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state))); return m.get(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state)));
} }

View File

@ -6,33 +6,22 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class ComputedAppearance implements CamoAppearance { public class ComputedAppearance extends CamoAppearance {
private final Appearance appearance; private final Appearance appearance;
private final int id;
private final RenderMaterial matWithAo;
private final RenderMaterial matWithoutAo;
public ComputedAppearance(@NotNull Appearance appearance, RenderMaterial withAo, RenderMaterial withoutAo, int id) { public ComputedAppearance(@NotNull Appearance appearance, RenderMaterial ao_material, RenderMaterial material, int id) {
super(ao_material, material, id);
this.appearance = appearance; this.appearance = appearance;
this.id = id;
this.matWithAo = withAo;
this.matWithoutAo = withoutAo;
} }
@Override @Override
public @NotNull RenderMaterial getRenderMaterial(boolean ao) { public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
return ao ? matWithAo : matWithoutAo;
}
@Override
public @NotNull List<SpriteProperties> getSprites(Direction dir, long seed) {
return appearance.sprites().get(dir); return appearance.sprites().get(dir);
} }
@Override @Override
public boolean hasColor(Direction dir, long seed, int index) { public boolean hasColor(Direction dir, int model_id, int index) {
List<SpriteProperties> properties = getSprites(dir, seed); List<SpriteProperties> properties = getSprites(dir, model_id);
if (index != 0) index = properties.size() - index; if (index != 0) index = properties.size() - index;
return properties.get(index).has_colors(); return properties.get(index).has_colors();
} }
@ -40,13 +29,7 @@ public class ComputedAppearance implements CamoAppearance {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if(this == o) return true; if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false; if(!(o instanceof ComputedAppearance that)) return false;
ComputedAppearance that = (ComputedAppearance) o;
return id == that.id; return id == that.id;
} }
@Override
public int hashCode() {
return id;
}
} }

View File

@ -7,47 +7,33 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class SingleSpriteAppearance implements CamoAppearance { public class SingleSpriteAppearance extends CamoAppearance {
private final @NotNull Sprite defaultSprite; private final @NotNull Sprite defaultSprite;
private final RenderMaterial mat;
private final int id;
public SingleSpriteAppearance(@NotNull Sprite defaultSprite, RenderMaterial mat, int id) { public SingleSpriteAppearance(@NotNull Sprite defaultSprite, RenderMaterial mat, int id) {
super(null, mat, id);
this.defaultSprite = defaultSprite; this.defaultSprite = defaultSprite;
this.mat = mat;
this.id = id;
} }
@Override @Override
public @NotNull RenderMaterial getRenderMaterial(boolean ao) { public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
return mat;
}
@Override
public @NotNull List<SpriteProperties> getSprites(Direction dir, long seed) {
return List.of(new SpriteProperties(defaultSprite, 0, null, false)); return List.of(new SpriteProperties(defaultSprite, 0, null, false));
} }
@Override @Override
public boolean hasColor(Direction dir, long seed, int index) { public boolean hasColor(Direction dir, int model_id, int index) {
return false; return false;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if(this == o) return true; if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false; if(!(o instanceof SingleSpriteAppearance that)) return false;
SingleSpriteAppearance that = (SingleSpriteAppearance) o;
return id == that.id; return id == that.id;
} }
@Override
public int hashCode() {
return id;
}
@Override @Override
public String toString() { public String toString() {
return "SingleSpriteAppearance[defaultSprite=%s, mat=%s, id=%d]".formatted(defaultSprite, mat, id); return "SingleSpriteAppearance[defaultSprite=%s, mat=%s, id=%d]".formatted(defaultSprite, material, id);
} }
} }

View File

@ -9,25 +9,14 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class WeightedComputedAppearance implements CamoAppearance { public class WeightedComputedAppearance extends CamoAppearance {
private final List<Weighted.Present<Appearance>> appearances; private final List<Weighted.Present<Appearance>> appearances;
private final int total_weight; private final int total_weight;
private final int id;
private final RenderMaterial matWithAo;
private final RenderMaterial matWithoutAo;
public WeightedComputedAppearance(@NotNull List<Weighted.Present<Appearance>> appearances, RenderMaterial withAo, RenderMaterial withoutAo, int id) { public WeightedComputedAppearance(@NotNull List<Weighted.Present<Appearance>> appearances, RenderMaterial ao_material, RenderMaterial material, int id) {
super(ao_material, material, id);
this.appearances = appearances; this.appearances = appearances;
this.total_weight = Weighting.getWeightSum(appearances); this.total_weight = Weighting.getWeightSum(appearances);
this.id = id;
this.matWithAo = withAo;
this.matWithoutAo = withoutAo;
}
@Override
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
return ao ? matWithAo : matWithoutAo;
} }
@ -37,21 +26,19 @@ public class WeightedComputedAppearance implements CamoAppearance {
.map(appearances::indexOf).orElse(0); .map(appearances::indexOf).orElse(0);
} }
private Appearance getAppearance(long seed) { private Appearance getAppearance(int model_id) {
Random random = Random.create(seed); return appearances.get(model_id).getData();
return Weighting.getAt(appearances, Math.abs((int)random.nextLong()) % total_weight)
.map(Weighted.Present::getData).get();
} }
@Override @Override
public @NotNull List<SpriteProperties> getSprites(Direction dir, long seed) { public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
return getAppearance(seed).sprites().get(dir); return getAppearance(model_id).sprites().get(dir);
} }
@Override @Override
public boolean hasColor(Direction dir, long seed, int index) { public boolean hasColor(Direction dir, int model_id, int index) {
List<SpriteProperties> properties = getSprites(dir, seed); List<SpriteProperties> properties = getSprites(dir, model_id);
if (index != 0) index = properties.size() - index; if (index != 0) index = properties.size() - index;
return properties.get(index).has_colors(); return properties.get(index).has_colors();
} }
@ -59,13 +46,7 @@ public class WeightedComputedAppearance implements CamoAppearance {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if(this == o) return true; if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false; if(!(o instanceof WeightedComputedAppearance that)) return false;
WeightedComputedAppearance that = (WeightedComputedAppearance) o;
return id == that.id; return id == that.id;
} }
@Override
public int hashCode() {
return id;
}
} }

View File

@ -17,9 +17,7 @@ public class GLanguage extends FabricLanguageProvider {
public void generateTranslations(TranslationBuilder builder) { public void generateTranslations(TranslationBuilder builder) {
builder.add(Registries.ITEM_GROUP.getKey(ReFramed.ITEM_GROUP).get(), "Frames"); builder.add(Registries.ITEM_GROUP.getKey(ReFramed.ITEM_GROUP).get(), "Frames");
builder.add("advancements.reframed.description", "Get all the frame types."); builder.add("advancements.reframed.description", "Get all the frame types.");
ReFramed.BLOCKS.forEach(block -> { ReFramed.BLOCKS.forEach(block -> builder.add(block, beautify(Registries.BLOCK.getId(block).getPath()) + " Frame"));
builder.add(block, beautify(Registries.BLOCK.getId(block).getPath()) + " Frame");
});
} }
private static String beautify(String name) { private static String beautify(String name) {

View File

@ -11,9 +11,7 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper; import net.minecraft.nbt.NbtHelper;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@ -23,8 +21,6 @@ import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
@Mixin(BlockItem.class) @Mixin(BlockItem.class)
public class BlockItemMixin { public class BlockItemMixin {
@Shadow @Final @Deprecated private Block block;
@Inject( @Inject(
method = "writeNbtToBlockEntity", method = "writeNbtToBlockEntity",
at = @At( at = @At(
@ -41,6 +37,7 @@ public class BlockItemMixin {
|| !Block.isShapeFullCube(block.getBlock().getDefaultState().getCollisionShape(world, pos)) || !Block.isShapeFullCube(block.getBlock().getDefaultState().getCollisionShape(world, pos))
) return; ) return;
NbtCompound new_comp = new NbtCompound(); NbtCompound new_comp = new NbtCompound();
player.getOffHandStack().decrement(1);
new_comp.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(block.getBlock().getDefaultState())); new_comp.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(block.getBlock().getDefaultState()));
compound.set(new_comp); compound.set(new_comp);
} }

View File

@ -21,9 +21,11 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
"fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)), "fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
"fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)), "fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
"fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)), "fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)), "fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)), "fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.compat.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2)) "fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2))
); );

View File

@ -6,7 +6,7 @@ import earth.terrarium.athena.api.client.models.AthenaBlockModel;
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.compat.RebakedAthenaModel; import fr.adrien1106.reframed.compat.RebakedAthenaModel;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;

View File

@ -3,7 +3,7 @@ package fr.adrien1106.reframed.mixin.compat;
import earth.terrarium.athena.api.client.utils.AppearanceAndTintGetter; import earth.terrarium.athena.api.client.utils.AppearanceAndTintGetter;
import earth.terrarium.athena.api.client.utils.CtmUtils; import earth.terrarium.athena.api.client.utils.CtmUtils;
import earth.terrarium.athena.impl.client.models.ConnectedBlockModel; import earth.terrarium.athena.impl.client.models.ConnectedBlockModel;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;

View File

@ -2,7 +2,7 @@ package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import earth.terrarium.athena.api.client.fabric.WrappedGetter; import earth.terrarium.athena.api.client.fabric.WrappedGetter;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;

View File

@ -0,0 +1,29 @@
package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import link.infra.indium.renderer.mesh.MutableQuadViewImpl;
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
import link.infra.indium.renderer.render.BlockRenderInfo;
import net.minecraft.util.math.Direction;
import org.jetbrains.annotations.Nullable;
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.Redirect;
@Mixin(AbstractBlockRenderContext.class)
public abstract class IndiumAbstractBlockRenderContextMixin {
@Shadow(remap = false) protected BlockRenderInfo blockInfo;
@Shadow public abstract boolean isFaceCulled(@Nullable Direction face);
@Redirect(method = "renderQuad", at = @At(value = "INVOKE", target = "Llink/infra/indium/renderer/render/AbstractBlockRenderContext;isFaceCulled(Lnet/minecraft/util/math/Direction;)Z"))
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 !BlockHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex());
}
}

View File

@ -1,8 +1,8 @@
package fr.adrien1106.reframed.mixin.compat; package fr.adrien1106.reframed.mixin.compat;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import link.infra.indium.renderer.render.BlockRenderInfo; import link.infra.indium.renderer.render.BlockRenderInfo;
import link.infra.indium.renderer.render.TerrainBlockRenderInfo; import link.infra.indium.renderer.render.TerrainBlockRenderInfo;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache; import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache;
@ -18,7 +18,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TerrainBlockRenderInfo.class) @Mixin(TerrainBlockRenderInfo.class)
public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin { public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin {
@Unique private int theme_index = 1; @Unique private int theme_index = 0;
@Redirect( @Redirect(
method = "shouldDrawFaceInner", method = "shouldDrawFaceInner",
@ -40,4 +40,9 @@ public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo
this.theme_index = theme_index; this.theme_index = theme_index;
prepareForBlock(blockState, blockPos, seed, modelAo); prepareForBlock(blockState, blockPos, seed, modelAo);
} }
@Override
public int getThemeIndex() {
return theme_index;
}
} }

View File

@ -1,11 +1,13 @@
package fr.adrien1106.reframed.mixin.compat; package fr.adrien1106.reframed.mixin.compat;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel; import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.IMultipartBakedModelMixin; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
import link.infra.indium.renderer.render.AbstractBlockRenderContext; import link.infra.indium.renderer.render.AbstractBlockRenderContext;
import link.infra.indium.renderer.render.TerrainRenderContext; import link.infra.indium.renderer.render.TerrainRenderContext;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockRenderContext; import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockRenderContext;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@ -29,13 +31,14 @@ public abstract class IndiumTerrainRenderContextMixin extends AbstractBlockRende
if (!(ctx.model() instanceof IMultipartBakedModelMixin wrapped) if (!(ctx.model() instanceof IMultipartBakedModelMixin wrapped)
|| !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return; || !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return;
List<BakedModel> models = retexturing_model.models(); List<ForwardingBakedModel> models = retexturing_model.models();
BlockHelper.computeInnerCull(ctx.state(), models);
int i = 0; int i = 0;
for (BakedModel bakedModel : models) { for (BakedModel model : models) {
i++; i++;
aoCalc.clear(); aoCalc.clear();
((IBlockRenderInfoMixin) blockInfo).prepareForBlock(ctx.state(), ctx.pos(), ctx.seed(), bakedModel.useAmbientOcclusion(), i); ((IBlockRenderInfoMixin) blockInfo).prepareForBlock(ctx.state(), ctx.pos(), ctx.seed(), model.useAmbientOcclusion(), i);
bakedModel.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
} }
ci.cancel(); ci.cancel();
} }

View File

@ -1,8 +1,8 @@
package fr.adrien1106.reframed.mixin.compat; package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache; import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;

View File

@ -1,7 +1,7 @@
package fr.adrien1106.reframed.mixin.particles; package fr.adrien1106.reframed.mixin.particles;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.particle.BlockDustParticle; import net.minecraft.client.particle.BlockDustParticle;

View File

@ -2,7 +2,7 @@ package fr.adrien1106.reframed.mixin.particles;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;

View File

@ -2,7 +2,7 @@ package fr.adrien1106.reframed.mixin.particles;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;

View File

@ -0,0 +1,36 @@
package fr.adrien1106.reframed.mixin.render;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
import net.minecraft.util.math.Direction;
import org.jetbrains.annotations.Nullable;
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.Redirect;
@Mixin(AbstractBlockRenderContext.class)
public abstract class AbstractBlockRenderContextMixin {
@Shadow public abstract boolean isFaceCulled(@Nullable Direction face);
@Shadow(remap = false) @Final protected BlockRenderInfo blockInfo;
@Redirect(
method = "renderQuad",
at = @At(
value = "INVOKE",
target = "Lnet/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractBlockRenderContext;isFaceCulled(Lnet/minecraft/util/math/Direction;)Z"
)
)
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 !BlockHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex());
}
}

View File

@ -1,9 +1,9 @@
package fr.adrien1106.reframed.mixin.render; package fr.adrien1106.reframed.mixin.render;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.util.BlockHelper; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo; import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
@ -24,8 +24,7 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
@Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo); @Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo);
@Shadow public BlockRenderView blockView; @Shadow public BlockRenderView blockView;
@Unique @Unique private int theme_index = 0;
private int theme_index = 1;
@ModifyArg(method = "prepareForBlock", @ModifyArg(method = "prepareForBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
@ -47,4 +46,9 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
this.theme_index = theme_index; this.theme_index = theme_index;
prepareForBlock(state, pos, ao); prepareForBlock(state, pos, ao);
} }
@Override
public int getThemeIndex() {
return theme_index;
}
} }

View File

@ -1,6 +1,6 @@
package fr.adrien1106.reframed.mixin.render; package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.util.IMultipartBakedModelMixin; import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.MultipartBakedModel; import net.minecraft.client.render.model.MultipartBakedModel;

View File

@ -1,8 +1,10 @@
package fr.adrien1106.reframed.mixin.render; package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel; import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.IMultipartBakedModelMixin; 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.impl.client.indigo.renderer.render.AbstractBlockRenderContext; import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext; import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -28,13 +30,14 @@ public abstract class TerrainRenderContextMixin extends AbstractBlockRenderConte
if (!(wrapper instanceof IMultipartBakedModelMixin wrapped) if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)
|| !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return; || !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return;
List<BakedModel> models = retexturing_model.models(); List<ForwardingBakedModel> models = retexturing_model.models();
BlockHelper.computeInnerCull(state, models);
int i = 0; int i = 0;
for (BakedModel bakedModel : models) { for (BakedModel model : models) {
i++; i++;
aoCalc.clear(); aoCalc.clear();
((IBlockRenderInfoMixin) blockInfo).prepareForBlock(state, pos, bakedModel.useAmbientOcclusion(), i); ((IBlockRenderInfoMixin) blockInfo).prepareForBlock(state, pos, model.useAmbientOcclusion(), i);
bakedModel.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
} }
ci.cancel(); ci.cancel();
} }

View File

@ -2,7 +2,7 @@ package fr.adrien1106.reframed.mixin.sound;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;

View File

@ -2,7 +2,7 @@ package fr.adrien1106.reframed.mixin.sound;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;

View File

@ -1,7 +1,7 @@
package fr.adrien1106.reframed.mixin.sound; package fr.adrien1106.reframed.mixin.sound;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;

View File

@ -2,7 +2,7 @@ package fr.adrien1106.reframed.mixin.sound;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.client.render.WorldRenderer; import net.minecraft.client.render.WorldRenderer;

View File

@ -1,9 +1,15 @@
package fr.adrien1106.reframed.util; package fr.adrien1106.reframed.util.blocks;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import fr.adrien1106.reframed.block.ReFramedBlock; import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.block.ReFramedEntity; import fr.adrien1106.reframed.block.ReFramedEntity;
import fr.adrien1106.reframed.util.property.Corner; import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.util.property.StairShape; import fr.adrien1106.reframed.client.model.QuadPosBounds;
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.Block;
import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -19,24 +25,32 @@ import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes; import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static fr.adrien1106.reframed.util.BlockProperties.CORNER; import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.BlockProperties.LIGHT; import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
import static fr.adrien1106.reframed.util.property.StairShape.*; import static fr.adrien1106.reframed.util.blocks.StairShape.*;
import static net.minecraft.util.shape.VoxelShapes.combine; import static net.minecraft.util.shape.VoxelShapes.combine;
public class BlockHelper { public class BlockHelper {
// 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(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();
Vec3d pos = getHitPos(ctx.getHitPos(), ctx.getBlockPos()); Vec3d pos = getHitPos(ctx.getHitPos(), ctx.getBlockPos());
@ -58,7 +72,7 @@ public class BlockHelper {
Math.abs(axis_1.choose(pos.x, pos.y, pos.z)) > Math.abs(axis_2.choose(pos.x, pos.y, pos.z)) Math.abs(axis_1.choose(pos.x, pos.y, pos.z)) > Math.abs(axis_2.choose(pos.x, pos.y, pos.z))
? axis_1 ? axis_1
: axis_2 : axis_2
).get(); ).orElse(null);
} }
public static Vec3d getRelativePos(Vec3d pos, BlockPos block_pos) { public static Vec3d getRelativePos(Vec3d pos, BlockPos block_pos) {
@ -205,6 +219,66 @@ public class BlockHelper {
return ActionResult.PASS; return ActionResult.PASS;
} }
/**
* compute which quad might cull with another model quad
* @param state - the state of the model
* @param models - list of models on the same block
*/
public static void computeInnerCull(BlockState state, List<ForwardingBakedModel> 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;
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
QuadEmitter quad_emitter = r.meshBuilder().getEmitter();
RenderMaterial material = r.materialFinder().clear().find();
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);
return QuadPosBounds.read(quad_emitter, false);
}).toList()).toList();
Integer[] cull_array;
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++) {
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;
break;
}
}
}
INNER_CULL_MAP.put(new CullElement(key, 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)
|| !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)
) return true;
CullElement key = new CullElement(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
Integer cull_theme = Objects.requireNonNull(INNER_CULL_MAP.getIfPresent(key))[quad_index];
if (cull_theme == null) return true; // no culling possible
BlockState self_theme = frame_entity.getTheme(theme_index);
BlockState other_theme = frame_entity.getTheme(cull_theme);
if (self_theme.isSideInvisible(other_theme, null)) return false;
return !self_theme.isOpaque() || !other_theme.isOpaque();
}
// Doing this method from scratch as it is simpler to do than injecting everywhere // 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) { 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 self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null;

View File

@ -1,7 +1,5 @@
package fr.adrien1106.reframed.util; package fr.adrien1106.reframed.util.blocks;
import fr.adrien1106.reframed.util.property.Corner;
import fr.adrien1106.reframed.util.property.StairShape;
import net.minecraft.state.property.BooleanProperty; import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.EnumProperty; import net.minecraft.state.property.EnumProperty;

View File

@ -1,4 +1,4 @@
package fr.adrien1106.reframed.util.property; package fr.adrien1106.reframed.util.blocks;
import net.minecraft.util.StringIdentifiable; import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;

View File

@ -1,4 +1,4 @@
package fr.adrien1106.reframed.util; package fr.adrien1106.reframed.util.blocks;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;

View File

@ -1,4 +1,4 @@
package fr.adrien1106.reframed.util.property; package fr.adrien1106.reframed.util.blocks;
import net.minecraft.util.StringIdentifiable; import net.minecraft.util.StringIdentifiable;

View File

@ -1,4 +1,4 @@
package fr.adrien1106.reframed.util; package fr.adrien1106.reframed.util.blocks;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;

View File

@ -1,4 +1,4 @@
package fr.adrien1106.reframed.util; package fr.adrien1106.reframed.util.mixin;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -8,4 +8,6 @@ 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);
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 getThemeIndex();
} }

View File

@ -1,4 +1,4 @@
package fr.adrien1106.reframed.util; package fr.adrien1106.reframed.util.mixin;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;

View File

@ -30,7 +30,7 @@
"fabricloader": "^${loader_version}", "fabricloader": "^${loader_version}",
"fabric-api": "*" "fabric-api": "*"
}, },
"suggest": { "suggests": {
"athena": "^${athena_version}", "athena": "^${athena_version}",
"sodium": "^${sodium_version}", "sodium": "^${sodium_version}",
"indium": "^${indium_version}" "indium": "^${indium_version}"

View File

@ -14,6 +14,7 @@
"compat.AthenaBakedModelMixin", "compat.AthenaBakedModelMixin",
"compat.AthenaConnectedBlockModelMixin", "compat.AthenaConnectedBlockModelMixin",
"compat.AthenaWrappedGetterMixin", "compat.AthenaWrappedGetterMixin",
"compat.IndiumAbstractBlockRenderContextMixin",
"compat.IndiumTerrainBlockRenderInfoMixin", "compat.IndiumTerrainBlockRenderInfoMixin",
"compat.IndiumTerrainRenderContextMixin", "compat.IndiumTerrainRenderContextMixin",
"compat.SodiumBlockOcclusionCacheMixin", "compat.SodiumBlockOcclusionCacheMixin",
@ -21,6 +22,7 @@
"particles.AccessorParticle", "particles.AccessorParticle",
"particles.AccessorSpriteBillboardParticle", "particles.AccessorSpriteBillboardParticle",
"particles.MixinBlockDustParticle", "particles.MixinBlockDustParticle",
"render.AbstractBlockRenderContextMixin",
"render.BlockRenderInfoMixin", "render.BlockRenderInfoMixin",
"render.MultipartBakedModelMixin", "render.MultipartBakedModelMixin",
"render.TerrainRenderContextMixin", "render.TerrainRenderContextMixin",