version 1.5.6 fix for server imports + support for continuity and MoreCulling compat #11
1
.gitignore
vendored
1
.gitignore
vendored
@ -33,3 +33,4 @@ local.properties
|
|||||||
*-autosave.kra
|
*-autosave.kra
|
||||||
*.kra~
|
*.kra~
|
||||||
/src/generated/
|
/src/generated/
|
||||||
|
/libs/
|
||||||
|
@ -110,10 +110,18 @@ dependencies {
|
|||||||
modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
|
modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
|
||||||
modRuntimeOnly "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
|
// Chipped to test athena implementation
|
||||||
modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7"
|
modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7"
|
||||||
modRuntimeOnly "earth.terrarium.chipped:Chipped-fabric-${project.minecraft_version}:3.1.2"
|
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.
|
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||||
}
|
}
|
||||||
@ -126,6 +134,7 @@ processResources {
|
|||||||
inputs.property "athena_version", project.athena_version
|
inputs.property "athena_version", project.athena_version
|
||||||
inputs.property "indium_version", project.indium_version
|
inputs.property "indium_version", project.indium_version
|
||||||
inputs.property "sodium_version", project.sodium_version
|
inputs.property "sodium_version", project.sodium_version
|
||||||
|
inputs.property "continuity_version", project.continuity_version
|
||||||
filteringCharset "UTF-8"
|
filteringCharset "UTF-8"
|
||||||
|
|
||||||
filesMatching("fabric.mod.json") {
|
filesMatching("fabric.mod.json") {
|
||||||
|
@ -24,3 +24,4 @@ git_repo=ReFramed
|
|||||||
athena_version=3.3.0
|
athena_version=3.3.0
|
||||||
sodium_version=0.5.8
|
sodium_version=0.5.8
|
||||||
indium_version=1.0.30
|
indium_version=1.0.30
|
||||||
|
continuity_version=3.0.0-beta.4+1.20.2
|
||||||
|
@ -29,8 +29,10 @@ import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO make block pairable by right click -> for v1.6
|
* 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 add minecraft models like wall fence etc -> for v1.6
|
||||||
* TODO better connected textures -> maybe v1.6 ?
|
* TODO better connected textures -> maybe v1.6 ?
|
||||||
|
* TODO support continuity overlays -> not scheduled
|
||||||
*/
|
*/
|
||||||
public class ReFramed implements ModInitializer {
|
public class ReFramed implements ModInitializer {
|
||||||
public static final String MODID = "reframed";
|
public static final String MODID = "reframed";
|
||||||
|
@ -4,7 +4,8 @@ import net.minecraft.block.BlockState;
|
|||||||
import net.minecraft.client.render.model.BakedModel;
|
import net.minecraft.client.render.model.BakedModel;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public interface DynamicBakedModel {
|
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);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import fr.adrien1106.reframed.ReFramed;
|
|||||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||||
import fr.adrien1106.reframed.client.model.DynamicBakedModel;
|
import fr.adrien1106.reframed.client.model.DynamicBakedModel;
|
||||||
import fr.adrien1106.reframed.client.model.QuadPosBounds;
|
import fr.adrien1106.reframed.client.model.QuadPosBounds;
|
||||||
|
import fr.adrien1106.reframed.compat.RebakedModel;
|
||||||
import fr.adrien1106.reframed.mixin.model.WeightedBakedModelAccessor;
|
import fr.adrien1106.reframed.mixin.model.WeightedBakedModelAccessor;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
@ -86,15 +87,19 @@ public class CamoAppearanceManager {
|
|||||||
public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index, boolean item) {
|
public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index, boolean item) {
|
||||||
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
|
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
|
||||||
|
|
||||||
// add support for connected textures and more generally any compatible models injected so that they return baked quads
|
// add support for connected textures that uses dynamic baking
|
||||||
if (model instanceof DynamicBakedModel dynamic_model) {
|
if (model instanceof DynamicBakedModel dynamic_model) {
|
||||||
// cache items as they get rendered more often
|
// cache items as they get rendered more often
|
||||||
if (item && APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state);
|
if (item && APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state);
|
||||||
|
|
||||||
CamoAppearance appearance = computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state);
|
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);
|
if (item) APPEARANCE_CACHE.put(state, appearance);
|
||||||
return appearance;
|
return appearance;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// refresh cache
|
// refresh cache
|
||||||
if (APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state);
|
if (APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state);
|
||||||
|
@ -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<CullElement, Integer[]> 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<ForwardingBakedModel> 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<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(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;
|
||||||
|
}
|
||||||
|
}
|
@ -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> random, boolean manual_culling, Function<Sprite, QuadProcessors.Slice> slice);
|
||||||
|
|
||||||
|
ProcessingContextImpl getProcessingContext();
|
||||||
|
|
||||||
|
void invokeReset();
|
||||||
|
}
|
@ -12,10 +12,10 @@ import net.minecraft.util.math.random.Random;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class RebakedAthenaModel implements BakedModel {
|
public class RebakedModel implements BakedModel {
|
||||||
protected final Map<Direction, List<BakedQuad>> face_quads;
|
protected final Map<Direction, List<BakedQuad>> face_quads;
|
||||||
|
|
||||||
public RebakedAthenaModel(Map<Direction, List<BakedQuad>> face_quads) {
|
public RebakedModel(Map<Direction, List<BakedQuad>> face_quads) {
|
||||||
this.face_quads = face_quads;
|
this.face_quads = face_quads;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ public class RebakedAthenaModel implements BakedModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean useAmbientOcclusion() {
|
public boolean useAmbientOcclusion() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,11 +51,11 @@ public class RebakedAthenaModel implements BakedModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModelTransformation getTransformation() {
|
public ModelTransformation getTransformation() {
|
||||||
return null;
|
return ModelTransformation.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModelOverrideList getOverrides() {
|
public ModelOverrideList getOverrides() {
|
||||||
return null;
|
return ModelOverrideList.EMPTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -16,24 +17,27 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
|
|||||||
|
|
||||||
private static final FabricLoader LOADER = FabricLoader.getInstance();
|
private static final FabricLoader LOADER = FabricLoader.getInstance();
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("ReFramed MIXIN");
|
private static final Logger LOGGER = LoggerFactory.getLogger("ReFramed MIXIN");
|
||||||
private static final List<String> COMPAT_MOD = List.of("athena", "indium", "sodium");
|
private static final List<String> COMPAT_MOD = List.of("athena", "indium", "sodium", "special-model-loader", "continuity");
|
||||||
private static final Map<String, Supplier<Boolean>> CONDITIONS = Map.of(
|
private static final Map<String, Supplier<Boolean>> CONDITIONS = new HashMap<>();
|
||||||
"fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
|
static {
|
||||||
"fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
|
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)));
|
||||||
"fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)));
|
||||||
"fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||||
"fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||||
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||||
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||||
"fr.adrien1106.reframed.mixin.compat.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||||
"fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2))
|
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
|
@Override
|
||||||
public void onLoad(String mixin_package) {
|
public void onLoad(String mixin_package) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRefMapperConfig() {
|
public String getRefMapperConfig() {
|
||||||
@ -46,9 +50,7 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void acceptTargets(Set<String> mine, Set<String> others) {
|
public void acceptTargets(Set<String> mine, Set<String> others) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getMixins() {
|
public List<String> getMixins() {
|
||||||
@ -56,9 +58,7 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
public void postApply(String target_class, ClassNode target, String mixin_class, IMixinInfo mixin_info) {
|
public void postApply(String target_class, ClassNode target, String mixin_class, IMixinInfo mixin_info) {
|
||||||
|
@ -5,7 +5,7 @@ import earth.terrarium.athena.api.client.fabric.WrappedGetter;
|
|||||||
import earth.terrarium.athena.api.client.models.AthenaBlockModel;
|
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.RebakedModel;
|
||||||
import fr.adrien1106.reframed.util.blocks.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;
|
||||||
@ -18,6 +18,7 @@ import net.minecraft.client.texture.Sprite;
|
|||||||
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.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
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.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
|
* Reuses the emitQuad method to compute the quads to be used by the frame
|
||||||
*
|
*
|
||||||
* @param level - the world
|
* @param level - the world
|
||||||
* @param state - the current block camo
|
* @param origin_state - the current block camo
|
||||||
* @param pos - the block position
|
* @param pos - the block position
|
||||||
* @return - the rebakedmodel containing the computed quads
|
* @return - the rebakedmodel containing the computed quads
|
||||||
*/
|
*/
|
||||||
@Override
|
@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<Direction, List<BakedQuad>> face_quads = new HashMap<>();
|
Map<Direction, List<BakedQuad>> face_quads = new HashMap<>();
|
||||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||||
QuadEmitter emitter = r.meshBuilder().getEmitter();
|
QuadEmitter emitter = r.meshBuilder().getEmitter();
|
||||||
|
|
||||||
WrappedGetter getter = new WrappedGetter(level);
|
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 -> {
|
Arrays.stream(Direction.values()).forEach(direction -> {
|
||||||
face_quads.put(direction, new ArrayList<>());
|
face_quads.put(direction, new ArrayList<>());
|
||||||
|
|
||||||
(level == null || pos == null
|
(level == null || pos == null
|
||||||
? model.getDefaultQuads(direction).get(direction)
|
? model.getDefaultQuads(direction).get(direction)
|
||||||
: model.getQuads(
|
: model.getQuads(getter, state, pos, direction)
|
||||||
getter,
|
|
||||||
level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity
|
|
||||||
? framed_entity.getTheme(theme_index)
|
|
||||||
: state, pos, direction)
|
|
||||||
).forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> {
|
).forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> {
|
||||||
Sprite texture = textures.get(sprite.sprite());
|
Sprite texture = textures.get(sprite.sprite());
|
||||||
if (texture == null) return quads;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,13 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
|||||||
@Mixin(WrappedGetter.class)
|
@Mixin(WrappedGetter.class)
|
||||||
public class AthenaWrappedGetterMixin {
|
public class AthenaWrappedGetterMixin {
|
||||||
|
|
||||||
@Redirect(method = "query",
|
@Redirect(
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/BlockRenderView;" +
|
method = "query",
|
||||||
"getBlockState(Lnet/minecraft/util/math/BlockPos;)" +
|
at = @At(
|
||||||
"Lnet/minecraft/block/BlockState;"))
|
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) {
|
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)
|
// get Any that will connect or return any other (/!\ isOf is an uncertain check)
|
||||||
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)
|
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)
|
||||||
|
@ -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<Sprite, QuadProcessors.Slice> 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<Direction, List<BakedQuad>> 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> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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> random, boolean manual_culling, Function<Sprite, QuadProcessors.Slice> 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> random, boolean manual_culling, Function<Sprite, QuadProcessors.Slice> slice) {
|
||||||
|
prepare(view, state, pos, random, manual_culling, slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProcessingContextImpl getProcessingContext() {
|
||||||
|
return processingContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invokeReset() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<Block> filterFrames(DefaultedRegistry<Block> registry) {
|
||||||
|
return registry
|
||||||
|
.stream()
|
||||||
|
.filter(block -> !(block instanceof ReFramedBlock))
|
||||||
|
.iterator();
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
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.blocks.BlockHelper;
|
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||||
import link.infra.indium.renderer.mesh.MutableQuadViewImpl;
|
import link.infra.indium.renderer.mesh.MutableQuadViewImpl;
|
||||||
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
|
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) {
|
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);
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package fr.adrien1106.reframed.mixin.compat;
|
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.mixin.IBlockRenderInfoMixin;
|
||||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||||
import link.infra.indium.renderer.render.BlockRenderInfo;
|
import link.infra.indium.renderer.render.BlockRenderInfo;
|
||||||
@ -32,7 +32,7 @@ public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo
|
|||||||
if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity
|
if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity
|
||||||
|| view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity))
|
|| view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity))
|
||||||
return instance.shouldDrawSide(state, view, pos, face);
|
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
|
@Override
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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.blocks.BlockHelper;
|
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||||
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
||||||
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
|
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;
|
|| !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return;
|
||||||
|
|
||||||
List<ForwardingBakedModel> models = retexturing_model.models();
|
List<ForwardingBakedModel> models = retexturing_model.models();
|
||||||
BlockHelper.computeInnerCull(ctx.state(), models);
|
RenderHelper.computeInnerCull(ctx.state(), models);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (BakedModel model : models) {
|
for (BakedModel model : models) {
|
||||||
i++;
|
i++;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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.blocks.BlockHelper;
|
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||||
import fr.adrien1106.reframed.util.blocks.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;
|
||||||
@ -19,12 +19,12 @@ public class SodiumBlockOcclusionCacheMixin {
|
|||||||
@Inject(
|
@Inject(
|
||||||
method = "shouldDrawSide",
|
method = "shouldDrawSide",
|
||||||
at = @At(
|
at = @At(
|
||||||
value = "INVOKE_ASSIGN",
|
value = "INVOKE",
|
||||||
target = "Lnet/minecraft/world/BlockView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;",
|
target = "Lnet/minecraft/util/math/BlockPos$Mutable;set(III)Lnet/minecraft/util/math/BlockPos$Mutable;",
|
||||||
shift = At.Shift.AFTER
|
shift = At.Shift.AFTER
|
||||||
), cancellable = true)
|
), cancellable = true)
|
||||||
private void shouldDrawFrameNeighborSide(BlockState self_state, BlockView view, BlockPos self_pos, Direction face, CallbackInfoReturnable<Boolean> cir, @Local BlockPos.Mutable other_pos) {
|
private void shouldDrawFrameNeighborSide(BlockState self_state, BlockView view, BlockPos self_pos, Direction face, CallbackInfoReturnable<Boolean> cir, @Local BlockPos.Mutable other_pos) {
|
||||||
if (!(view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity)) return;
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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.blocks.BlockHelper;
|
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
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.mesh.MutableQuadViewImpl;
|
||||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext;
|
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) {
|
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);
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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.blocks.BlockHelper;
|
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||||
import fr.adrien1106.reframed.util.blocks.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;
|
||||||
@ -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"))
|
@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) {
|
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
|
@Override
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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.blocks.BlockHelper;
|
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||||
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
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;
|
|| !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return;
|
||||||
|
|
||||||
List<ForwardingBakedModel> models = retexturing_model.models();
|
List<ForwardingBakedModel> models = retexturing_model.models();
|
||||||
BlockHelper.computeInnerCull(state, models);
|
RenderHelper.computeInnerCull(state, models);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (BakedModel model : models) {
|
for (BakedModel model : models) {
|
||||||
i++;
|
i++;
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
package fr.adrien1106.reframed.util.blocks;
|
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.ReFramedEntity;
|
||||||
import fr.adrien1106.reframed.block.ReFramedStairBlock;
|
import fr.adrien1106.reframed.block.ReFramedStairBlock;
|
||||||
import fr.adrien1106.reframed.block.ReFramedStairsCubeBlock;
|
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.Block;
|
||||||
import net.minecraft.block.BlockEntityProvider;
|
import net.minecraft.block.BlockEntityProvider;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
@ -25,33 +14,27 @@ import net.minecraft.sound.SoundEvents;
|
|||||||
import net.minecraft.util.ActionResult;
|
import net.minecraft.util.ActionResult;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.Pair;
|
import net.minecraft.util.Pair;
|
||||||
import net.minecraft.util.function.BooleanBiFunction;
|
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
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.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.*;
|
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.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
|
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
|
||||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
|
import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
|
||||||
import static fr.adrien1106.reframed.util.blocks.StairShape.*;
|
import static fr.adrien1106.reframed.util.blocks.StairShape.*;
|
||||||
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(Block block, Object state_key, int model) {}
|
|
||||||
|
|
||||||
public static Corner getPlacementCorner(ItemPlacementContext ctx) {
|
public static Corner getPlacementCorner(ItemPlacementContext ctx) {
|
||||||
Direction side = ctx.getSide().getOpposite();
|
Direction side = ctx.getSide().getOpposite();
|
||||||
Vec3d pos = getHitPos(ctx.getHitPos(), ctx.getBlockPos());
|
Vec3d pos = getHitPos(ctx.getHitPos(), ctx.getBlockPos());
|
||||||
@ -232,148 +215,6 @@ 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
|
|
||||||
*/
|
|
||||||
@Environment(EnvType.CLIENT)
|
|
||||||
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(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<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(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) {
|
public static boolean cursorMatchesFace(VoxelShape shape, Vec3d pos) {
|
||||||
Map<Direction.Axis, Double> axes = Arrays.stream(Direction.Axis.values())
|
Map<Direction.Axis, Double> axes = Arrays.stream(Direction.Axis.values())
|
||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"suggests": {
|
"suggests": {
|
||||||
"athena": "^${athena_version}",
|
"athena": "^${athena_version}",
|
||||||
"sodium": "^${sodium_version}",
|
"sodium": "^${sodium_version}",
|
||||||
"indium": "^${indium_version}"
|
"indium": "^${indium_version}",
|
||||||
|
"continuity": "^${continuity_version}"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,6 +14,10 @@
|
|||||||
"compat.AthenaBakedModelMixin",
|
"compat.AthenaBakedModelMixin",
|
||||||
"compat.AthenaConnectedBlockModelMixin",
|
"compat.AthenaConnectedBlockModelMixin",
|
||||||
"compat.AthenaWrappedGetterMixin",
|
"compat.AthenaWrappedGetterMixin",
|
||||||
|
"compat.ContinuityConnectionPredicateMixin",
|
||||||
|
"compat.ContinuityCTMBakedModelMixin",
|
||||||
|
"compat.ContinuityCTMQuadTransformMixin",
|
||||||
|
"compat.ContinuityModelWrappingHandlerMixin",
|
||||||
"compat.IndiumAbstractBlockRenderContextMixin",
|
"compat.IndiumAbstractBlockRenderContextMixin",
|
||||||
"compat.IndiumTerrainBlockRenderInfoMixin",
|
"compat.IndiumTerrainBlockRenderInfoMixin",
|
||||||
"compat.IndiumTerrainRenderContextMixin",
|
"compat.IndiumTerrainRenderContextMixin",
|
||||||
|
Loading…
Reference in New Issue
Block a user