From f222e4dc24bf89cd85408419c3473e9feb43ac51 Mon Sep 17 00:00:00 2001 From: Adrien1106 Date: Mon, 25 Mar 2024 15:22:28 +0100 Subject: [PATCH] fixed incompatibility with MoreCulling + fixed server crash with clientside classes import + continuity connected textures support --- .gitignore | 1 + build.gradle | 9 + gradle.properties | 1 + .../java/fr/adrien1106/reframed/ReFramed.java | 2 + .../client/model/DynamicBakedModel.java | 3 +- .../apperance/CamoAppearanceManager.java | 13 +- .../reframed/client/util/RenderHelper.java | 177 ++++++++++++++++++ .../reframed/compat/ICTMQuadTransform.java | 22 +++ ...akedAthenaModel.java => RebakedModel.java} | 10 +- .../reframed/mixin/CompatMixinPlugin.java | 42 ++--- .../mixin/compat/AthenaBakedModelMixin.java | 19 +- .../compat/AthenaWrappedGetterMixin.java | 11 +- .../compat/ContinuityCTMBakedModelMixin.java | 94 ++++++++++ .../ContinuityCTMQuadTransformMixin.java | 56 ++++++ .../ContinuityConnectionPredicateMixin.java | 32 ++++ .../ContinuityModelWrappingHandlerMixin.java | 29 +++ ...IndiumAbstractBlockRenderContextMixin.java | 4 +- .../IndiumTerrainBlockRenderInfoMixin.java | 4 +- .../IndiumTerrainRenderContextMixin.java | 4 +- .../SodiumBlockOcclusionCacheMixin.java | 8 +- .../AbstractBlockRenderContextMixin.java | 4 +- .../mixin/render/BlockRenderInfoMixin.java | 4 +- .../render/TerrainRenderContextMixin.java | 4 +- .../reframed/util/blocks/BlockHelper.java | 167 +---------------- src/main/resources/fabric.mod.json | 3 +- src/main/resources/reframed.mixins.json | 4 + 26 files changed, 503 insertions(+), 224 deletions(-) create mode 100644 src/main/java/fr/adrien1106/reframed/client/util/RenderHelper.java create mode 100644 src/main/java/fr/adrien1106/reframed/compat/ICTMQuadTransform.java rename src/main/java/fr/adrien1106/reframed/compat/{RebakedAthenaModel.java => RebakedModel.java} (85%) create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityCTMBakedModelMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityCTMQuadTransformMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityConnectionPredicateMixin.java create mode 100644 src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityModelWrappingHandlerMixin.java diff --git a/.gitignore b/.gitignore index 54a7985..1d52742 100755 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ local.properties *-autosave.kra *.kra~ /src/generated/ +/libs/ diff --git a/build.gradle b/build.gradle index 5799aee..a778b0e 100755 --- a/build.gradle +++ b/build.gradle @@ -110,10 +110,18 @@ dependencies { modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}" modRuntimeOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}" + // Continuity for connectedTextures + modCompileOnly "maven.modrinth:continuity:${project.continuity_version}" + modRuntimeOnly "maven.modrinth:continuity:${project.continuity_version}" + // Chipped to test athena implementation modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7" modRuntimeOnly "earth.terrarium.chipped:Chipped-fabric-${project.minecraft_version}:3.1.2" + // Special Model Loader for Cocricot compatibility + modCompileOnly "maven.modrinth:ajFw7VkX:dYbsug6P" // Special Model Loader +// modRuntimeOnly "maven.modrinth:ajFw7VkX:dYbsug6P" + // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" } @@ -126,6 +134,7 @@ processResources { inputs.property "athena_version", project.athena_version inputs.property "indium_version", project.indium_version inputs.property "sodium_version", project.sodium_version + inputs.property "continuity_version", project.continuity_version filteringCharset "UTF-8" filesMatching("fabric.mod.json") { diff --git a/gradle.properties b/gradle.properties index 6a153bd..c0ef6c2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,3 +24,4 @@ git_repo=ReFramed athena_version=3.3.0 sodium_version=0.5.8 indium_version=1.0.30 +continuity_version=3.0.0-beta.4+1.20.2 diff --git a/src/main/java/fr/adrien1106/reframed/ReFramed.java b/src/main/java/fr/adrien1106/reframed/ReFramed.java index ee970b2..691fe87 100644 --- a/src/main/java/fr/adrien1106/reframed/ReFramed.java +++ b/src/main/java/fr/adrien1106/reframed/ReFramed.java @@ -29,8 +29,10 @@ import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT; /** * TODO make block pairable by right click -> for v1.6 + * TODO Dynamic Ambient Occlusion -> for v1.6 * TODO add minecraft models like wall fence etc -> for v1.6 * TODO better connected textures -> maybe v1.6 ? + * TODO support continuity overlays -> not scheduled */ public class ReFramed implements ModInitializer { public static final String MODID = "reframed"; diff --git a/src/main/java/fr/adrien1106/reframed/client/model/DynamicBakedModel.java b/src/main/java/fr/adrien1106/reframed/client/model/DynamicBakedModel.java index 2f4d109..cb81daf 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/DynamicBakedModel.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/DynamicBakedModel.java @@ -4,7 +4,8 @@ import net.minecraft.block.BlockState; import net.minecraft.client.render.model.BakedModel; import net.minecraft.util.math.BlockPos; import net.minecraft.world.BlockRenderView; +import org.jetbrains.annotations.Nullable; public interface DynamicBakedModel { - BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos, int theme_index); + BakedModel computeQuads(@Nullable BlockRenderView level, BlockState origin_state, @Nullable BlockPos pos, int theme_index); } diff --git a/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java b/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java index 63ab5b2..e30fe39 100644 --- a/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java +++ b/src/main/java/fr/adrien1106/reframed/client/model/apperance/CamoAppearanceManager.java @@ -6,6 +6,7 @@ import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.model.DynamicBakedModel; import fr.adrien1106.reframed.client.model.QuadPosBounds; +import fr.adrien1106.reframed.compat.RebakedModel; import fr.adrien1106.reframed.mixin.model.WeightedBakedModelAccessor; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -86,14 +87,18 @@ public class CamoAppearanceManager { public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index, boolean item) { BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state); - // add support for connected textures and more generally any compatible models injected so that they return baked quads + // add support for connected textures that uses dynamic baking if (model instanceof DynamicBakedModel dynamic_model) { // cache items as they get rendered more often if (item && APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state); - CamoAppearance appearance = computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state); - if (item) APPEARANCE_CACHE.put(state, appearance); - return appearance; + model = dynamic_model.computeQuads(world, state, pos, theme_index); + // if model isn't rebaked its just wrapped (i.e. not dynamic and may be cached) + if (model instanceof RebakedModel) { + CamoAppearance appearance = computeAppearance(model, state); + if (item) APPEARANCE_CACHE.put(state, appearance); + return appearance; + } } // refresh cache diff --git a/src/main/java/fr/adrien1106/reframed/client/util/RenderHelper.java b/src/main/java/fr/adrien1106/reframed/client/util/RenderHelper.java new file mode 100644 index 0000000..4df304b --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/client/util/RenderHelper.java @@ -0,0 +1,177 @@ +package fr.adrien1106.reframed.client.util; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import fr.adrien1106.reframed.block.ReFramedBlock; +import fr.adrien1106.reframed.client.ReFramedClient; +import fr.adrien1106.reframed.client.model.QuadPosBounds; +import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.renderer.v1.Renderer; +import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; +import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.function.BooleanBiFunction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.random.Random; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.BlockView; + +import java.util.List; +import java.util.Objects; + +import static net.minecraft.util.shape.VoxelShapes.combine; + +@Environment(EnvType.CLIENT) +public class RenderHelper { + + + // self culling cache of the models not made thread local so that it is only computed once + private static final Cache INNER_CULL_MAP = CacheBuilder.newBuilder().maximumSize(1024).build(); + private record CullElement(Block block, Object state_key, int model) {} + + /** + * 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 models) { + if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return; + Object key = frame_block.getModelCacheKey(state); + if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, key, 1))) return; + + Renderer r = ReFramedClient.HELPER.getFabricRenderer(); + QuadEmitter quad_emitter = r.meshBuilder().getEmitter(); + RenderMaterial material = r.materialFinder().clear().find(); + Random random = Random.create(); + + List> 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 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(frame_block, 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, 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 + public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) { + ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null; + ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e ? e : null; + BlockState other_state = world.getBlockState(other_pos); + + // normal behaviour + if (self == null && other == null) return Block.shouldDrawSide(self_state, world, pos, side, other_pos); + + // self is a normal Block + if (self == null && other_state.getBlock() instanceof ReFramedBlock other_block) { + VoxelShape self_shape = self_state.getCullingShape(world, pos); + if (self_shape.isEmpty()) return true; + + int i = 0; + VoxelShape other_shape = VoxelShapes.empty(); + for (BlockState s: other.getThemes()) { + i++; + if (self_state.isSideInvisible(s, side) || s.isOpaque()) + other_shape = combine( + other_shape, + other_block + .getShape(other_state, i) + .getFace(side.getOpposite()), + BooleanBiFunction.OR + ); + } + + // determine if side needs to be rendered + return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); + } + + BlockState self_theme = self.getTheme(theme_index); + // other is normal Block + if (other == null && self_state.getBlock() instanceof ReFramedBlock self_block) { + // Transparent is simple if self and the neighbor are invisible don't render side (like default) + if (self_theme.isSideInvisible(other_state, side)) return false; + + // Opaque is also simple as each model are rendered one by one + if (other_state.isOpaque()) { + // no cache section :( because it differs between each instance of the frame + VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side); + if (self_shape.isEmpty()) return true; + VoxelShape other_shape = other_state.getCullingFace(world, other_pos, side.getOpposite()); + + // determine if side needs to be rendered + return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); + } + + return true; + } + + // Both are frames + // here both are computed in the same zone as there will necessarily a shape comparison + if (self_state.getBlock() instanceof ReFramedBlock self_block && other_state.getBlock() instanceof ReFramedBlock other_block) { + VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side); + if (self_shape.isEmpty()) return true; + + int i = 0; + VoxelShape other_shape = VoxelShapes.empty(); + for (BlockState s: other.getThemes()) { + i++; + if (self_theme.isSideInvisible(s, side) || s.isOpaque()) + other_shape = combine( + other_shape, + other_block + .getShape(other_state, i) + .getFace(side.getOpposite()), + BooleanBiFunction.OR + ); + } + + // determine if side needs to be rendered + return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); + } + + return true; + } +} diff --git a/src/main/java/fr/adrien1106/reframed/compat/ICTMQuadTransform.java b/src/main/java/fr/adrien1106/reframed/compat/ICTMQuadTransform.java new file mode 100644 index 0000000..9a0e24d --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/compat/ICTMQuadTransform.java @@ -0,0 +1,22 @@ +package fr.adrien1106.reframed.compat; + +import me.pepperbell.continuity.client.model.QuadProcessors; +import me.pepperbell.continuity.impl.client.ProcessingContextImpl; +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; +import net.minecraft.block.BlockState; +import net.minecraft.client.texture.Sprite; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; + +import java.util.function.Function; +import java.util.function.Supplier; + +public interface ICTMQuadTransform extends RenderContext.QuadTransform { + + void invokePrepare(BlockRenderView view, BlockState state, BlockPos pos, Supplier random, boolean manual_culling, Function slice); + + ProcessingContextImpl getProcessingContext(); + + void invokeReset(); +} diff --git a/src/main/java/fr/adrien1106/reframed/compat/RebakedAthenaModel.java b/src/main/java/fr/adrien1106/reframed/compat/RebakedModel.java similarity index 85% rename from src/main/java/fr/adrien1106/reframed/compat/RebakedAthenaModel.java rename to src/main/java/fr/adrien1106/reframed/compat/RebakedModel.java index f0741ac..cb5c54c 100644 --- a/src/main/java/fr/adrien1106/reframed/compat/RebakedAthenaModel.java +++ b/src/main/java/fr/adrien1106/reframed/compat/RebakedModel.java @@ -12,10 +12,10 @@ import net.minecraft.util.math.random.Random; import java.util.List; import java.util.Map; -public class RebakedAthenaModel implements BakedModel { +public class RebakedModel implements BakedModel { protected final Map> face_quads; - public RebakedAthenaModel(Map> face_quads) { + public RebakedModel(Map> face_quads) { this.face_quads = face_quads; } @@ -26,7 +26,7 @@ public class RebakedAthenaModel implements BakedModel { @Override public boolean useAmbientOcclusion() { - return false; + return true; } @Override @@ -51,11 +51,11 @@ public class RebakedAthenaModel implements BakedModel { @Override public ModelTransformation getTransformation() { - return null; + return ModelTransformation.NONE; } @Override public ModelOverrideList getOverrides() { - return null; + return ModelOverrideList.EMPTY; } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java b/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java index a0a36a5..2530838 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/CompatMixinPlugin.java @@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -16,24 +17,27 @@ public class CompatMixinPlugin implements IMixinConfigPlugin { private static final FabricLoader LOADER = FabricLoader.getInstance(); private static final Logger LOGGER = LoggerFactory.getLogger("ReFramed MIXIN"); - private static final List COMPAT_MOD = List.of("athena", "indium", "sodium"); - private static final Map> CONDITIONS = Map.of( - "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.render.TerrainRenderContextMixin", () -> !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.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)), - "fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2)) - ); + private static final List COMPAT_MOD = List.of("athena", "indium", "sodium", "special-model-loader", "continuity"); + private static final Map> CONDITIONS = new HashMap<>(); + static { + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityConnectionPredicateMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMQuadTransformMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); + CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityModelWrappingHandlerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4))); + } @Override - public void onLoad(String mixin_package) { - - } + public void onLoad(String mixin_package) {} @Override public String getRefMapperConfig() { @@ -46,9 +50,7 @@ public class CompatMixinPlugin implements IMixinConfigPlugin { } @Override - public void acceptTargets(Set mine, Set others) { - - } + public void acceptTargets(Set mine, Set others) {} @Override public List getMixins() { @@ -56,9 +58,7 @@ public class CompatMixinPlugin implements IMixinConfigPlugin { } @Override - public void preApply(String target_class_name, ClassNode target_class, String mixin_class_name, IMixinInfo mixin_info) { - - } + public void preApply(String target_class_name, ClassNode target_class, String mixin_class_name, IMixinInfo mixin_info) {} @Override public void postApply(String target_class, ClassNode target, String mixin_class, IMixinInfo mixin_info) { diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java index 2bbcb90..20bd2f4 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaBakedModelMixin.java @@ -5,7 +5,7 @@ import earth.terrarium.athena.api.client.fabric.WrappedGetter; import earth.terrarium.athena.api.client.models.AthenaBlockModel; import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.model.DynamicBakedModel; -import fr.adrien1106.reframed.compat.RebakedAthenaModel; +import fr.adrien1106.reframed.compat.RebakedModel; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.fabricmc.fabric.api.renderer.v1.Renderer; @@ -18,6 +18,7 @@ import net.minecraft.client.texture.Sprite; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.world.BlockRenderView; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -35,27 +36,27 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM * Reuses the emitQuad method to compute the quads to be used by the frame * * @param level - the world - * @param state - the current block camo + * @param origin_state - the current block camo * @param pos - the block position * @return - the rebakedmodel containing the computed quads */ @Override - public BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos, int theme_index) { + public BakedModel computeQuads(@Nullable BlockRenderView level, BlockState origin_state, @Nullable BlockPos pos, int theme_index) { Map> face_quads = new HashMap<>(); Renderer r = ReFramedClient.HELPER.getFabricRenderer(); QuadEmitter emitter = r.meshBuilder().getEmitter(); WrappedGetter getter = new WrappedGetter(level); + BlockState state = level != null && pos != null + && level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity + ? framed_entity.getTheme(theme_index) + : origin_state; Arrays.stream(Direction.values()).forEach(direction -> { face_quads.put(direction, new ArrayList<>()); (level == null || pos == null ? model.getDefaultQuads(direction).get(direction) - : model.getQuads( - getter, - level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity - ? framed_entity.getTheme(theme_index) - : state, pos, direction) + : model.getQuads(getter, state, pos, direction) ).forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> { Sprite texture = textures.get(sprite.sprite()); if (texture == null) return quads; @@ -76,6 +77,6 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM })); }); - return new RebakedAthenaModel(face_quads); + return new RebakedModel(face_quads); } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java index bb31415..3ae0979 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/AthenaWrappedGetterMixin.java @@ -13,10 +13,13 @@ import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(WrappedGetter.class) public class AthenaWrappedGetterMixin { - @Redirect(method = "query", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/BlockRenderView;" + - "getBlockState(Lnet/minecraft/util/math/BlockPos;)" + - "Lnet/minecraft/block/BlockState;")) + @Redirect( + method = "query", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/BlockRenderView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;" + ) + ) // TODO better connected textures private BlockState queryCamoState(BlockRenderView world, BlockPos pos, @Local(argsOnly = true) BlockState reference_state) { // get Any that will connect or return any other (/!\ isOf is an uncertain check) if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityCTMBakedModelMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityCTMBakedModelMixin.java new file mode 100644 index 0000000..4add74d --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityCTMBakedModelMixin.java @@ -0,0 +1,94 @@ +package fr.adrien1106.reframed.mixin.compat; + +import fr.adrien1106.reframed.client.ReFramedClient; +import fr.adrien1106.reframed.client.model.DynamicBakedModel; +import fr.adrien1106.reframed.compat.ICTMQuadTransform; +import fr.adrien1106.reframed.compat.RebakedModel; +import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; +import me.pepperbell.continuity.client.config.ContinuityConfig; +import me.pepperbell.continuity.client.model.CTMBakedModel; +import me.pepperbell.continuity.client.model.ModelObjectsContainer; +import me.pepperbell.continuity.client.model.QuadProcessors; +import me.pepperbell.continuity.client.util.RenderUtil; +import net.fabricmc.fabric.api.renderer.v1.Renderer; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; +import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedQuad; +import net.minecraft.client.texture.Sprite; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.*; +import java.util.function.Function; +import java.util.function.Supplier; + +@Mixin(CTMBakedModel.class) +public abstract class ContinuityCTMBakedModelMixin extends ForwardingBakedModel implements DynamicBakedModel { + + @Shadow protected abstract Function getSliceFunc(BlockState state); + + @Override + public BakedModel computeQuads(@Nullable BlockRenderView level, BlockState origin_state, @Nullable BlockPos pos, int theme_index) { + if (wrapped instanceof DynamicBakedModel wrapped_dynamic) // support wrap of dynamic models + return wrapped_dynamic.computeQuads(level, origin_state, pos, theme_index); + + + ModelObjectsContainer container = ModelObjectsContainer.get(); + // normally baked model / wrapped or feature disabled or item (i.e. no need to compute quads) + if (level == null || pos == null + || !ContinuityConfig.INSTANCE.connectedTextures.get() + || !container.featureStates.getConnectedTexturesState().isEnabled() + ) return wrapped; + + Map> face_quads = new HashMap<>(); + + Renderer r = ReFramedClient.HELPER.getFabricRenderer(); + QuadEmitter emitter = r.meshBuilder().getEmitter(); + + // get applicable state + BlockState state = level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity + ? framed_entity.getTheme(theme_index) + : origin_state; + + // get random supplier + Random random = Random.create(); + Supplier random_supplier = () -> { + random.setSeed(state.getRenderingSeed(pos)); + return random; + }; + + // get quad transform and prepare + ICTMQuadTransform transform = ((ICTMQuadTransform) container.ctmQuadTransform); + transform.invokePrepare( + level, + state, + pos, + random_supplier, + ContinuityConfig.INSTANCE.useManualCulling.get(), + getSliceFunc(state) + ); + Arrays.stream(Direction.values()).forEach(direction -> { + face_quads.put(direction, new ArrayList<>()); + + wrapped.getQuads(state, direction, random_supplier.get()).forEach(quad -> face_quads.computeIfPresent(direction, (d, quads) -> { + emitter.fromVanilla(quad, emitter.material(), direction); + transform.transform(emitter); + quads.add(emitter.toBakedQuad(RenderUtil.getSpriteFinder().find(emitter))); + return quads; + })); +// transform.getProcessingContext().getExtraQuadEmitter(); // TODO start here for overlay support + }); + + transform.getProcessingContext().reset(); // reset instead of outputting to emitter + transform.invokeReset(); + + return new RebakedModel(face_quads); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityCTMQuadTransformMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityCTMQuadTransformMixin.java new file mode 100644 index 0000000..02ba637 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityCTMQuadTransformMixin.java @@ -0,0 +1,56 @@ +package fr.adrien1106.reframed.mixin.compat; + +import fr.adrien1106.reframed.compat.ICTMQuadTransform; +import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; +import me.pepperbell.continuity.client.model.CullingCache; +import me.pepperbell.continuity.client.model.QuadProcessors; +import me.pepperbell.continuity.impl.client.ProcessingContextImpl; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView; +import net.minecraft.block.BlockState; +import net.minecraft.client.texture.Sprite; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.function.Function; +import java.util.function.Supplier; + +@Mixin(targets = "me.pepperbell.continuity.client.model.CTMBakedModel$CTMQuadTransform") +public abstract class ContinuityCTMQuadTransformMixin implements ICTMQuadTransform { + @Shadow(remap = false) @Final protected ProcessingContextImpl processingContext; + + @Shadow public abstract void prepare(BlockRenderView view, BlockState state, BlockPos pos, Supplier random, boolean manual_culling, Function slice); + @Shadow(remap = false) public abstract void reset(); + + @Redirect( + method = "transform", + at = @At( + value = "INVOKE", + target = "Lme/pepperbell/continuity/client/model/CullingCache;shouldCull(Lnet/fabricmc/fabric/api/renderer/v1/mesh/QuadView;Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Z" + ) + ) + private boolean camo_replacement(CullingCache cache, QuadView quad, BlockRenderView view, BlockPos pos, BlockState state) { + if (view.getBlockEntity(pos) instanceof ThemeableBlockEntity) return false; + return cache.shouldCull(quad, view, pos, state); + } + + // uses this because invoker did not want to work for some reason + public void invokePrepare(BlockRenderView view, BlockState state, BlockPos pos, Supplier random, boolean manual_culling, Function slice) { + prepare(view, state, pos, random, manual_culling, slice); + } + + @Override + public ProcessingContextImpl getProcessingContext() { + return processingContext; + } + + @Override + public void invokeReset() { + reset(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityConnectionPredicateMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityConnectionPredicateMixin.java new file mode 100644 index 0000000..364b885 --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityConnectionPredicateMixin.java @@ -0,0 +1,32 @@ +package fr.adrien1106.reframed.mixin.compat; + +import com.llamalad7.mixinextras.sugar.Local; +import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; +import me.pepperbell.continuity.client.processor.ConnectionPredicate; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockRenderView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ConnectionPredicate.class) +public interface ContinuityConnectionPredicateMixin { + + @Redirect( + method = "shouldConnect(Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/client/texture/Sprite;)Z", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/BlockRenderView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;" + ) + ) // TODO better connected textures + private BlockState getBlockState(BlockRenderView view, BlockPos pos, @Local(argsOnly = true) BlockState state) { + if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)) return view.getBlockState(pos); + return frame_entity.getThemes() + .stream() + .filter(theme -> theme.getBlock() == state.getBlock()) + .findFirst() + .orElse(frame_entity.getTheme(0)); + } + +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityModelWrappingHandlerMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityModelWrappingHandlerMixin.java new file mode 100644 index 0000000..8e7791b --- /dev/null +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/ContinuityModelWrappingHandlerMixin.java @@ -0,0 +1,29 @@ +package fr.adrien1106.reframed.mixin.compat; + +import fr.adrien1106.reframed.block.ReFramedBlock; +import me.pepperbell.continuity.client.resource.ModelWrappingHandler; +import net.minecraft.block.Block; +import net.minecraft.registry.DefaultedRegistry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.Iterator; + +@Mixin(ModelWrappingHandler.class) +public class ContinuityModelWrappingHandlerMixin { + + @Redirect( + method = "createBlockStateModelIdMap", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/registry/DefaultedRegistry;iterator()Ljava/util/Iterator;" + ) + ) + private static Iterator filterFrames(DefaultedRegistry registry) { + return registry + .stream() + .filter(block -> !(block instanceof ReFramedBlock)) + .iterator(); + } +} diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumAbstractBlockRenderContextMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumAbstractBlockRenderContextMixin.java index e9d2f43..4490579 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumAbstractBlockRenderContextMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumAbstractBlockRenderContextMixin.java @@ -1,7 +1,7 @@ package fr.adrien1106.reframed.mixin.compat; import com.llamalad7.mixinextras.sugar.Local; -import fr.adrien1106.reframed.util.blocks.BlockHelper; +import fr.adrien1106.reframed.client.util.RenderHelper; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; import link.infra.indium.renderer.mesh.MutableQuadViewImpl; import link.infra.indium.renderer.render.AbstractBlockRenderContext; @@ -24,6 +24,6 @@ public abstract class IndiumAbstractBlockRenderContextMixin { private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) { if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face); - return !BlockHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex()); + return !RenderHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex()); } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainBlockRenderInfoMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainBlockRenderInfoMixin.java index 1fef1c3..2e95fde 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainBlockRenderInfoMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainBlockRenderInfoMixin.java @@ -1,6 +1,6 @@ package fr.adrien1106.reframed.mixin.compat; -import fr.adrien1106.reframed.util.blocks.BlockHelper; +import fr.adrien1106.reframed.client.util.RenderHelper; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; import link.infra.indium.renderer.render.BlockRenderInfo; @@ -32,7 +32,7 @@ public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity || view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity)) return instance.shouldDrawSide(state, view, pos, face); - return BlockHelper.shouldDrawSide(state, view, pos, face, other_pos, theme_index); + return RenderHelper.shouldDrawSide(state, view, pos, face, other_pos, theme_index); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainRenderContextMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainRenderContextMixin.java index 42c5534..5855735 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainRenderContextMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/IndiumTerrainRenderContextMixin.java @@ -1,7 +1,7 @@ package fr.adrien1106.reframed.mixin.compat; import fr.adrien1106.reframed.client.model.MultiRetexturableModel; -import fr.adrien1106.reframed.util.blocks.BlockHelper; +import fr.adrien1106.reframed.client.util.RenderHelper; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; import link.infra.indium.renderer.render.AbstractBlockRenderContext; @@ -32,7 +32,7 @@ public abstract class IndiumTerrainRenderContextMixin extends AbstractBlockRende || !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return; List models = retexturing_model.models(); - BlockHelper.computeInnerCull(ctx.state(), models); + RenderHelper.computeInnerCull(ctx.state(), models); int i = 0; for (BakedModel model : models) { i++; diff --git a/src/main/java/fr/adrien1106/reframed/mixin/compat/SodiumBlockOcclusionCacheMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/compat/SodiumBlockOcclusionCacheMixin.java index 3e6c28d..9d622a1 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/compat/SodiumBlockOcclusionCacheMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/compat/SodiumBlockOcclusionCacheMixin.java @@ -1,7 +1,7 @@ package fr.adrien1106.reframed.mixin.compat; import com.llamalad7.mixinextras.sugar.Local; -import fr.adrien1106.reframed.util.blocks.BlockHelper; +import fr.adrien1106.reframed.client.util.RenderHelper; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache; import net.minecraft.block.BlockState; @@ -19,12 +19,12 @@ public class SodiumBlockOcclusionCacheMixin { @Inject( method = "shouldDrawSide", at = @At( - value = "INVOKE_ASSIGN", - target = "Lnet/minecraft/world/BlockView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;", + value = "INVOKE", + target = "Lnet/minecraft/util/math/BlockPos$Mutable;set(III)Lnet/minecraft/util/math/BlockPos$Mutable;", shift = At.Shift.AFTER ), cancellable = true) private void shouldDrawFrameNeighborSide(BlockState self_state, BlockView view, BlockPos self_pos, Direction face, CallbackInfoReturnable cir, @Local BlockPos.Mutable other_pos) { if (!(view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity)) return; - cir.setReturnValue(BlockHelper.shouldDrawSide(self_state, view, self_pos, face, other_pos, 0)); + cir.setReturnValue(RenderHelper.shouldDrawSide(self_state, view, self_pos, face, other_pos, 0)); } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/render/AbstractBlockRenderContextMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/AbstractBlockRenderContextMixin.java index 6d02c28..388ea4a 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/render/AbstractBlockRenderContextMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/AbstractBlockRenderContextMixin.java @@ -1,7 +1,7 @@ package fr.adrien1106.reframed.mixin.render; import com.llamalad7.mixinextras.sugar.Local; -import fr.adrien1106.reframed.util.blocks.BlockHelper; +import fr.adrien1106.reframed.client.util.RenderHelper; 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; @@ -31,6 +31,6 @@ public abstract class AbstractBlockRenderContextMixin { private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) { if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face); - return !BlockHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex()); + return !RenderHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex()); } } diff --git a/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderInfoMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderInfoMixin.java index 9a5485c..122e62a 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderInfoMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/BlockRenderInfoMixin.java @@ -1,7 +1,7 @@ package fr.adrien1106.reframed.mixin.render; import com.llamalad7.mixinextras.sugar.Local; -import fr.adrien1106.reframed.util.blocks.BlockHelper; +import fr.adrien1106.reframed.client.util.RenderHelper; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity; import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo; @@ -37,7 +37,7 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin { @Redirect(method = "shouldDrawFace", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/BlockPos;)Z")) private boolean shouldDrawAdjacentCamoSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos) { - return BlockHelper.shouldDrawSide(state, world, pos, side, other_pos, theme_index); + return RenderHelper.shouldDrawSide(state, world, pos, side, other_pos, theme_index); } @Override diff --git a/src/main/java/fr/adrien1106/reframed/mixin/render/TerrainRenderContextMixin.java b/src/main/java/fr/adrien1106/reframed/mixin/render/TerrainRenderContextMixin.java index ddb93ef..41e1813 100644 --- a/src/main/java/fr/adrien1106/reframed/mixin/render/TerrainRenderContextMixin.java +++ b/src/main/java/fr/adrien1106/reframed/mixin/render/TerrainRenderContextMixin.java @@ -1,7 +1,7 @@ package fr.adrien1106.reframed.mixin.render; import fr.adrien1106.reframed.client.model.MultiRetexturableModel; -import fr.adrien1106.reframed.util.blocks.BlockHelper; +import fr.adrien1106.reframed.client.util.RenderHelper; import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin; import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; @@ -31,7 +31,7 @@ public abstract class TerrainRenderContextMixin extends AbstractBlockRenderConte || !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return; List models = retexturing_model.models(); - BlockHelper.computeInnerCull(state, models); + RenderHelper.computeInnerCull(state, models); int i = 0; for (BakedModel model : models) { i++; diff --git a/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java b/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java index daaf5ad..cef30ab 100644 --- a/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java +++ b/src/main/java/fr/adrien1106/reframed/util/blocks/BlockHelper.java @@ -1,19 +1,8 @@ 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.ReFramedEntity; import fr.adrien1106.reframed.block.ReFramedStairBlock; import fr.adrien1106.reframed.block.ReFramedStairsCubeBlock; -import fr.adrien1106.reframed.client.ReFramedClient; -import fr.adrien1106.reframed.client.model.QuadPosBounds; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.renderer.v1.Renderer; -import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; -import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; -import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.minecraft.block.Block; import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockState; @@ -25,33 +14,27 @@ import net.minecraft.sound.SoundEvents; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Pair; -import net.minecraft.util.function.BooleanBiFunction; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.random.Random; import net.minecraft.util.shape.VoxelShape; -import net.minecraft.util.shape.VoxelShapes; -import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockView; import net.minecraft.world.World; -import java.util.*; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE; import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT; import static fr.adrien1106.reframed.util.blocks.StairShape.*; -import static net.minecraft.util.shape.VoxelShapes.combine; public class BlockHelper { - // self culling cache of the models not made thread local so that it is only computed once - private static final Cache INNER_CULL_MAP = CacheBuilder.newBuilder().maximumSize(1024).build(); - private record CullElement(Block block, Object state_key, int model) {} - public static Corner getPlacementCorner(ItemPlacementContext ctx) { Direction side = ctx.getSide().getOpposite(); Vec3d pos = getHitPos(ctx.getHitPos(), ctx.getBlockPos()); @@ -232,148 +215,6 @@ public class BlockHelper { 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 - */ - @Environment(EnvType.CLIENT) - public static void computeInnerCull(BlockState state, List models) { - if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return; - Object key = frame_block.getModelCacheKey(state); - if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, 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> 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 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(frame_block, key, self_id), cull_array); - } - } - - @Environment(EnvType.CLIENT) - 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, 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 - @Environment(EnvType.CLIENT) - public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) { - ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null; - ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e ? e : null; - BlockState other_state = world.getBlockState(other_pos); - - // normal behaviour - if (self == null && other == null) return Block.shouldDrawSide(self_state, world, pos, side, other_pos); - - // self is a normal Block - if (self == null && other_state.getBlock() instanceof ReFramedBlock other_block) { - VoxelShape self_shape = self_state.getCullingShape(world, pos); - if (self_shape.isEmpty()) return true; - - int i = 0; - VoxelShape other_shape = VoxelShapes.empty(); - for (BlockState s: other.getThemes()) { - i++; - if (self_state.isSideInvisible(s, side) || s.isOpaque()) - other_shape = combine( - other_shape, - other_block - .getShape(other_state, i) - .getFace(side.getOpposite()), - BooleanBiFunction.OR - ); - } - - // determine if side needs to be rendered - return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); - } - - BlockState self_theme = self.getTheme(theme_index); - // other is normal Block - if (other == null && self_state.getBlock() instanceof ReFramedBlock self_block) { - // Transparent is simple if self and the neighbor are invisible don't render side (like default) - if (self_theme.isSideInvisible(other_state, side)) return false; - - // Opaque is also simple as each model are rendered one by one - if (other_state.isOpaque()) { - // no cache section :( because it differs between each instance of the frame - VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side); - if (self_shape.isEmpty()) return true; - VoxelShape other_shape = other_state.getCullingFace(world, other_pos, side.getOpposite()); - - // determine if side needs to be rendered - return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); - } - - return true; - } - - // Both are frames - // here both are computed in the same zone as there will necessarily a shape comparison - if (self_state.getBlock() instanceof ReFramedBlock self_block && other_state.getBlock() instanceof ReFramedBlock other_block) { - VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side); - if (self_shape.isEmpty()) return true; - - int i = 0; - VoxelShape other_shape = VoxelShapes.empty(); - for (BlockState s: other.getThemes()) { - i++; - if (self_theme.isSideInvisible(s, side) || s.isOpaque()) - other_shape = combine( - other_shape, - other_block - .getShape(other_state, i) - .getFace(side.getOpposite()), - BooleanBiFunction.OR - ); - } - - // determine if side needs to be rendered - return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST); - } - - return true; - } - public static boolean cursorMatchesFace(VoxelShape shape, Vec3d pos) { Map axes = Arrays.stream(Direction.Axis.values()) .collect(Collectors.toMap( diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 57b230c..a7e1264 100755 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -33,6 +33,7 @@ "suggests": { "athena": "^${athena_version}", "sodium": "^${sodium_version}", - "indium": "^${indium_version}" + "indium": "^${indium_version}", + "continuity": "^${continuity_version}" } } \ No newline at end of file diff --git a/src/main/resources/reframed.mixins.json b/src/main/resources/reframed.mixins.json index 059e59b..de74135 100644 --- a/src/main/resources/reframed.mixins.json +++ b/src/main/resources/reframed.mixins.json @@ -14,6 +14,10 @@ "compat.AthenaBakedModelMixin", "compat.AthenaConnectedBlockModelMixin", "compat.AthenaWrappedGetterMixin", + "compat.ContinuityConnectionPredicateMixin", + "compat.ContinuityCTMBakedModelMixin", + "compat.ContinuityCTMQuadTransformMixin", + "compat.ContinuityModelWrappingHandlerMixin", "compat.IndiumAbstractBlockRenderContextMixin", "compat.IndiumTerrainBlockRenderInfoMixin", "compat.IndiumTerrainRenderContextMixin",