1 Commits

Author SHA1 Message Date
e0b04f0efd feat: added complex shape raycasting 2024-07-22 18:54:46 +02:00
25 changed files with 317 additions and 225 deletions

View File

@@ -44,6 +44,7 @@ sourceSets {
}
loom {
accessWidenerPath = file("src/main/resources/reframed.accesswidener")
runs {
// This adds a new gradle task that runs the datagen API: "gradlew runDatagen"
datagen {

View File

@@ -9,7 +9,7 @@ loader_version=0.15.11
# Mod Properties
modrinth_id = jCpoCBpn
mod_version = 1.6.7
mod_version = 1.6.6
maven_group = fr.adrien1106
archives_base_name = ReFramed
mod_id = reframed

View File

@@ -103,6 +103,7 @@ public class ReFramed implements ModInitializer {
BLUEPRINT = registerItem("blueprint" , new ReFramedBlueprintItem(new Item.Settings()));
BLUEPRINT_WRITTEN = registerItem("blueprint_written" , new ReFramedBlueprintWrittenItem(new Item.Settings().maxCount(1)));
registerBlock("test", new TestBlock(cp(Blocks.OAK_PLANKS).nonOpaque())); // TODO remove
REFRAMED_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("camo"),
BlockEntityType.Builder.create(
@@ -135,11 +136,11 @@ public class ReFramed implements ModInitializer {
private static AbstractBlock.Settings cp(Block base) {
return AbstractBlock.Settings.copy(base)
.luminance(state -> state.contains(LIGHT) && state.get(LIGHT) ? 15 : 0)
.nonOpaque()
.sounds(BlockSoundGroup.WOOD)
.hardness(0.2f)
.suffocates(Blocks::never)
.solidBlock(Blocks::always)
.blockVision(Blocks::always);
.suffocates((a,b,c) -> false)
.blockVision((a,b,c) -> false);
}
private static <I extends Item> I registerItem(String path, I item) {

View File

@@ -2,11 +2,9 @@ package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
@@ -19,7 +17,6 @@ import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.ItemScatterer;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
@@ -34,7 +31,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
@@ -43,26 +39,10 @@ import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
public class ReFramedBlock extends Block implements BlockEntityProvider {
public ReFramedBlock(Settings settings) {
super(settings.dynamicBounds());
super(settings);
setDefaultState(getDefaultState().with(LIGHT, false));
}
@Override
@SuppressWarnings("deprecation")
public int getOpacity(BlockState state, BlockView world, BlockPos pos) {
if (state.get(LIGHT)) return 0;
if (!(world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity)
|| frame_entity.getTheme(0).isOpaque())
return world.getMaxLightLevel();
return 0;
}
@Override
@SuppressWarnings("deprecation")
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return false;
}
@Override
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
@@ -228,17 +208,4 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
return Map.of();
}
public VoxelShape getShadingShape(BlockState state, BlockView world, BlockPos pos) {
if (!(world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)) return this.getCollisionShape(state, world, pos, ShapeContext.absent());
AtomicInteger i = new AtomicInteger(1);
return framed_entity.getThemes().stream().map((theme) -> {
int index = i.getAndIncrement();
return theme.isTransparent(world, pos) ? VoxelShapes.empty() : this.getShape(state, index);
}).reduce(
VoxelShapes.empty(),
(prev, current) -> VoxelShapes.combine(prev, current, BooleanBiFunction.OR)
);
}
}

View File

@@ -32,12 +32,12 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
protected BlockState first_state = Blocks.AIR.getDefaultState();
protected byte bit_field = SOLIDITY_MASK;
public static final byte LIGHT_MASK = 0b001;
public static final byte REDSTONE_MASK = 0b010;
public static final byte SOLIDITY_MASK = 0b100;
protected static final byte LIGHT_MASK = 0b001;
protected static final byte REDSTONE_MASK = 0b010;
protected static final byte SOLIDITY_MASK = 0b100;
public static final String BLOCKSTATE_KEY = "s";
public static final String BITFIELD_KEY = "b";
protected static final String BITFIELD_KEY = "b";
public ReFramedEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
@@ -145,10 +145,7 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
if (isSolid()) bit_field &= ~SOLIDITY_MASK;
else bit_field |= SOLIDITY_MASK;
if(world != null) {
world.setBlockState(pos, getCachedState());
ReFramed.chunkRerenderProxy.accept(world, pos);
}
if(world != null) world.setBlockState(pos, getCachedState());
markDirtyAndDispatch();
}

View File

@@ -0,0 +1,36 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.client.voxel.MorphVoxel;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
public class TestBlock extends Block {
public static MorphVoxel voxel = new MorphVoxel(createCuboidShape(1, 1, 1, 15, 15, 15),
new Vec3d[] {
new Vec3d(0, 0, 0),
new Vec3d(1, 0, 0),
new Vec3d(1, 1, 1),
new Vec3d(0, 1, 1),
new Vec3d(0, 1, 1),
new Vec3d(1, 1, 1),
new Vec3d(1, 0, 1),
new Vec3d(0, 0, 1)
});
public TestBlock(Settings settings) {
super(settings);
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return voxel;
}
}

View File

@@ -23,13 +23,7 @@ public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterlo
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(Properties.WATERLOGGED));
}
@Override
@SuppressWarnings("deprecation")
public boolean hasSidedTransparency(BlockState state) {
return true;
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {

View File

@@ -51,7 +51,7 @@ public class CamoAppearanceManager {
for(BlendMode blend : BlendMode.values()) {
finder.clear().disableDiffuse(false).blendMode(blend);
materials.put(blend, finder.ambientOcclusion(TriState.TRUE).find());
materials.put(blend, finder.ambientOcclusion(TriState.FALSE).find());
ao_materials.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO
}

View File

@@ -0,0 +1,7 @@
package fr.adrien1106.reframed.client.util;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
public record RaycastResult(Vec3d pos, Direction face, boolean inside) {
}

View File

@@ -89,11 +89,7 @@ public class RenderHelper {
BlockState self_theme = frame_entity.getTheme(theme_index);
BlockState other_theme = frame_entity.getTheme(cull_theme);
try {
if (self_theme.isSideInvisible(other_theme, null)) return false;
} catch (NullPointerException e) { // this can happen if mod haven't thought about inner faces
return true;
}
if (self_theme.isSideInvisible(other_theme, null)) return false;
return !self_theme.isOpaque() || !other_theme.isOpaque();
}
@@ -108,7 +104,7 @@ public class RenderHelper {
? e : null;
// normal behaviour
if ((theme_index == 0 && self != null) || (self == null && other == null))
if (theme_index == 0 || (self == null && other == null))
return Block.shouldDrawSide(self_state, world, pos, side, other_pos);
// self is a normal Block
@@ -120,7 +116,7 @@ public class RenderHelper {
VoxelShape other_shape = VoxelShapes.empty();
for (BlockState s: other.getThemes()) {
i++;
if (self_state.isSideInvisible(s, side) || (s.isOpaque() && (other.isSolid() || self_state.isTransparent(world ,pos))))
if (self_state.isSideInvisible(s, side) || s.isOpaque())
other_shape = combine(
other_shape,
other_block
@@ -141,7 +137,7 @@ public class RenderHelper {
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() && self.isSolid()) {
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;
@@ -164,7 +160,7 @@ public class RenderHelper {
VoxelShape other_shape = VoxelShapes.empty();
for (BlockState s: other.getThemes()) {
i++;
if (self_theme.isSideInvisible(s, side) || (s.isOpaque() && (!self.isSolid() || (other.isSolid() == self.isSolid()))))
if (self_theme.isSideInvisible(s, side) || s.isOpaque())
other_shape = combine(
other_shape,
other_block

View File

@@ -0,0 +1,42 @@
package fr.adrien1106.reframed.client.voxel;
import fr.adrien1106.reframed.client.util.RaycastResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import java.util.List;
import java.util.Objects;
public class MorphBox extends Box {
protected List<Triangle> triangles;
public MorphBox(List<Triangle> triangles) {
super(new BlockPos(0, 0, 0));
this.triangles = triangles;
}
public RaycastResult triangleRaycast(Vec3d min, Vec3d max) {
return triangles.stream()
.map(triangle -> {
Vec3d inter = triangle.intersection(min, max);
if (inter == null) return null;
return new RaycastResult(
inter,
triangle.face(),
false
);
})
.filter(Objects::nonNull)
.findFirst()
.orElseGet(() -> isInside(min) ? new RaycastResult(
min,
triangles.get(0).face(),
true
) : null);
}
public boolean isInside(Vec3d point) {
return triangles.stream().allMatch(triangle -> triangle.after(point));
}
}

View File

@@ -0,0 +1,100 @@
package fr.adrien1106.reframed.client.voxel;
import fr.adrien1106.reframed.client.util.RaycastResult;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.*;
import net.minecraft.util.shape.SimpleVoxelShape;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* Represents a VoxelShape that can be morphed using its 8 vertices.
* the vertices are distributed as follows:
* <pre><code>
* E--------F ABCD -> NORTH
* ^ /| /| EFGH -> SOUTH
* |/ | / | AHED -> EAST
* D--------C | BCGF -> WEST
* | |/ | | ABGH -> DOWN
* | H-----|--G CDFE -> UP
* | / | /
* |/ |/
* A--------B->
* </code></pre>
*/
public class MorphVoxel extends SimpleVoxelShape {
private static final int[][] EDGE_INDICES = {
{0, 1},
{1, 2},
{2, 3},
{3, 0},
{0, 7},
{1, 6},
{2, 5},
{3, 4},
{4, 5},
{5, 6},
{6, 7},
{7, 4}
};
private static final int[][] FACE_INDICES = {
{7, 6, 1, 0},
{5, 4, 3, 2},
{0, 1, 2, 3},
{4, 5, 6, 7},
{0, 3, 4, 7},
{6, 5, 2, 1}
};
protected final Vec3d[] v;
MorphBox box;
public MorphVoxel(VoxelShape wrapper, Vec3d[] vertices) {
super(wrapper.voxels);
List<Triangle> triangles = new ArrayList<>();
Stream.of(Direction.values()).forEach(dir -> {
int i = dir.ordinal();
List<Vec3d> v = IntStream.range(0, 4).mapToObj(j -> { // remove matching vertices
if (vertices[FACE_INDICES[i][j]].equals(vertices[FACE_INDICES[i][(j+1)%4]])) return null;
return vertices[FACE_INDICES[i][j]];
}).filter(Objects::nonNull).toList();
if (v.size() < 3) return; // skip if there are less than 3 vertices (e.g. a line/dot)
triangles.add(Triangle.of(dir,v));
});
box = new MorphBox(triangles);
v = vertices;
}
@Nullable
@Override
public BlockHitResult raycast(Vec3d start, Vec3d end, BlockPos pos) {
RaycastResult result = box.triangleRaycast(start.subtract(pos.getX(), pos.getY(), pos.getZ()), end.subtract(pos.getX(), pos.getY(), pos.getZ()));
return result == null
? null
: new BlockHitResult(
result.pos().add(pos.getX(), pos.getY(), pos.getZ()),
result.face(),
pos,
result.inside()
);
}
@Override
public void forEachEdge(VoxelShapes.BoxConsumer consumer) {
IntStream.range(0, 12).forEach(i -> {
Vec3d a = v[EDGE_INDICES[i][0]];
Vec3d b = v[EDGE_INDICES[i][1]];
consumer.consume(a.x, a.y, a.z, b.x, b.y, b.z);
});
}
}

View File

@@ -0,0 +1,78 @@
package fr.adrien1106.reframed.client.voxel;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import java.util.List;
import java.util.stream.IntStream;
/**
* Represents a Face of a MorphVoxel.
* @param v - list of vertices on the same plane
* @param e - list of the outer edges
* @param n - normal of the face
* @param face - the direction of the face
*/
public record Triangle(Vec3d[] v, Vec3d[] e, Vec3d n, Direction face) {
/**
* Creates a Triangle from a list of vertices.
* @param face - the direction of the face
* @param vertices - list of vertices on the same plane
* @return the Triangle
*/
public static Triangle of(Direction face, List<Vec3d> vertices) {
assert vertices.size() >= 3;
Vec3d[] v = vertices.toArray(new Vec3d[0]);
// compute the edges
Vec3d[] e = new Vec3d[v.length];
IntStream.range(0, v.length).forEach(i -> e[i] = v[(i + 1) % v.length].subtract(v[i]));
// compute the normal
Vec3d n = v[1].subtract(v[0]).crossProduct(v[2].subtract(v[0])).normalize();
return new Triangle(v, e, n, face);
}
/**
* Computes the intersection of a ray with the triangle.
* @param start - the start of the ray
* @param end - the end of the ray
* @return the intersection point or null if there is no intersection
*/
public Vec3d intersection(Vec3d start, Vec3d end) {
Vec3d start_v0 = start.subtract(v[0]);
Vec3d end_v1 = end.subtract(v[1]);
// check if the ray intersects the plane
if (n.dotProduct(start_v0) * n.dotProduct(end_v1) >= 0) return null;
Vec3d ray = end.subtract(start);
double direction = n.dotProduct(ray);
// plane normal is facing away from the ray
if (direction < 0) return null;
// get Intersection point
double t = -n.dotProduct(start_v0) / direction;
Vec3d intersection = start.add(ray.multiply(t));
// check if the intersection is inside the triangle
for (int i = 0; i < v.length; i++) {
Vec3d edge = e[i];
Vec3d edge_intersection = intersection.subtract(v[i]);
if (edge_intersection.length() < 1e-6) break; // intersection is on the vertex
double a = n.dotProduct(edge.crossProduct(edge_intersection));
if (a <= 0) return null; // intersection is outside the triangle
if (a <= edge.length() * 1e-6) break; // intersection is on the edge
}
return intersection;
}
public boolean after(Vec3d point) {
return n.dotProduct(point.subtract(v[0])) > 0;
}
}

View File

@@ -53,6 +53,8 @@ public class GBlockstate extends FabricModelProvider {
providers.put(ReFramedPostBlock.class, new Post());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
providers.put(TestBlock.class, new Test());
}
public GBlockstate(FabricDataOutput output) {

View File

@@ -0,0 +1,26 @@
package fr.adrien1106.reframed.generator.block;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.generator.GBlockstate;
import net.minecraft.block.Block;
import net.minecraft.data.client.BlockStateSupplier;
import net.minecraft.data.client.VariantsBlockStateSupplier;
import net.minecraft.util.Identifier;
import static net.minecraft.data.client.VariantSettings.Rotation.R0;
public class Test implements BlockStateProvider {
@Override
public BlockStateSupplier getMultipart(Block block) {
return VariantsBlockStateSupplier.create(
block,
GBlockstate.variant(
new Identifier("block/barrier"),
true,
R0, R0
)
);
}
}

View File

@@ -32,8 +32,7 @@ public class BlockItemMixin {
)
)
private static void placeBlockWithOffHandCamo(World world, PlayerEntity player, BlockPos pos, ItemStack stack, CallbackInfoReturnable<Boolean> cir, @Local LocalRef<NbtCompound> compound) {
if (player == null
|| compound.get() != null
if (compound.get() != null
|| player.getOffHandStack().isEmpty()
|| player.getMainHandStack().isEmpty()
|| !(player.getMainHandStack().getItem() instanceof BlockItem frame)

View File

@@ -31,8 +31,6 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumNonTerrainBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderContextMixin", () -> !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.render.FluidRendererMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(2)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.SodiumFluidRendererMixin", () -> 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)));

View File

@@ -1,62 +0,0 @@
package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.FluidRenderer;
import me.jellysquid.mods.sodium.client.world.WorldSlice;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
import net.minecraft.block.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(FluidRenderer.class)
public abstract class SodiumFluidRendererMixin {
@Redirect(
method = "isSideExposed",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/BlockState;isOpaque()Z"
)
)
private boolean isSideOpaqueExposed(BlockState state) {
if (!(state.getBlock() instanceof ReFramedBlock)) return state.isOpaque();
return true; // forces to compute correct shape
}
@Redirect(
method = "isSideExposed",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/BlockState;getCullingShape(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/util/shape/VoxelShape;"
)
)
private VoxelShape isSideShapeExposed(BlockState state, BlockView world, BlockPos pos) {
if (!(state.getBlock() instanceof ReFramedBlock block)) return state.getCullingShape(world, pos);
return block.getShadingShape(state, world, pos);
}
@Redirect(
method = "render",
at = @At(
value = "INVOKE",
target = "Lnet/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandlerRegistry;isBlockTransparent(Lnet/minecraft/block/Block;)Z"
)
)
private boolean getThemeState(FluidRenderHandlerRegistry fluid_handler, Block block, @Local(argsOnly = true) WorldSlice world, @Local(ordinal = 2) BlockPos pos, @Local BlockState state, @Local Direction dir) {
if (!(block instanceof ReFramedBlock rfblock && world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)) return fluid_handler.isBlockTransparent(block);
return !VoxelShapes.isSideCovered(VoxelShapes.fullCube(), rfblock.getShadingShape(state, world, pos), dir)
&& framed_entity.getThemes().stream()
.anyMatch(s -> s.getBlock() instanceof LeavesBlock
|| s.getBlock() instanceof TranslucentBlock
|| s.getBlock() instanceof AirBlock
);
}
}

View File

@@ -1,27 +0,0 @@
package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.client.util.RenderHelper;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Block.class)
public abstract class BlockMixin {
@Inject(
method = "shouldDrawSide",
at = @At("HEAD"),
cancellable = true
) // serves as a safety sometimes mods implements culling cache and hence will break some injections...
private static void shouldDrawSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, CallbackInfoReturnable<Boolean> cir) {
if (!(world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity)) return;
cir.setReturnValue(RenderHelper.shouldDrawSide(state, world, pos, side, other_pos, 0));
}
}

View File

@@ -1,56 +0,0 @@
package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.*;
import net.minecraft.client.render.block.FluidRenderer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(FluidRenderer.class)
public abstract class FluidRendererMixin {
@Inject(
method = "isSideCovered(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/Direction;FLnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Z",
at = @At("HEAD"),
cancellable = true
) // force dynamic water side rendering
private static void isSameSideCovered(BlockView world, Direction direction, float height, BlockPos pos, BlockState state, CallbackInfoReturnable<Boolean> cir) {
if (!(state.getBlock() instanceof ReFramedBlock block)
) return;
boolean is_covered = VoxelShapes.isSideCovered(
VoxelShapes.cuboid(0.0, 0.0, 0.0, 1.0, height, 1.0),
block.getShadingShape(state, world, pos),
direction
);
cir.setReturnValue(is_covered);
}
@Redirect(
method = "render",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/BlockRenderView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;",
ordinal = 7
)
)
private BlockState getThemeState(BlockRenderView world, BlockPos pos) {
if (!(world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)) return world.getBlockState(pos);
return framed_entity.getThemes().stream()
.anyMatch(state -> state.getBlock() instanceof LeavesBlock
|| state.getBlock() instanceof TranslucentBlock
|| state.getBlock() instanceof AirBlock
)
? Blocks.GLASS.getDefaultState()
: world.getBlockState(pos) ;
}
}

View File

@@ -10,6 +10,4 @@ public interface ThemeableBlockEntity {
void setTheme(BlockState state, int i);
List<BlockState> getThemes();
boolean isSolid();
}

View File

@@ -12,11 +12,10 @@ import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
import java.util.List;
import static fr.adrien1106.reframed.block.ReFramedEntity.*;
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
public class ThemedBlockEntity extends BlockEntity implements ThemeableBlockEntity {
private final List<BlockState> themes;
private final boolean isSolid;
public ThemedBlockEntity(NbtCompound compound, BlockPos pos, BlockState state) {
super(null, pos, state);
@@ -27,7 +26,6 @@ public class ThemedBlockEntity extends BlockEntity implements ThemeableBlockEnti
compound.getCompound(BLOCKSTATE_KEY + i)
));
}
isSolid = !compound.contains(BITFIELD_KEY) || (compound.getByte(BITFIELD_KEY) & SOLIDITY_MASK) != 0;
}
@Override
@@ -49,9 +47,4 @@ public class ThemedBlockEntity extends BlockEntity implements ThemeableBlockEnti
public List<BlockState> getThemes() {
return themes;
}
@Override
public boolean isSolid() {
return isSolid;
}
}

View File

@@ -25,6 +25,7 @@
"mixins": [
"reframed.mixins.json"
],
"accessWidener": "reframed.accesswidener",
"depends": {
"minecraft": "${minecraft_version}",
"fabricloader": "^${loader_version}",

View File

@@ -0,0 +1,4 @@
accessWidener v2 named
accessible field net/minecraft/util/shape/VoxelShape voxels Lnet/minecraft/util/shape/VoxelSet;
extendable class net/minecraft/util/shape/SimpleVoxelShape

View File

@@ -33,17 +33,14 @@
"compat.IndiumNonTerrainBlockRenderContextMixin",
"compat.IndiumTerrainRenderContextMixin",
"compat.SodiumBlockOcclusionCacheMixin",
"compat.SodiumFluidRendererMixin",
"model.WeightedBakedModelAccessor",
"particles.AccessorParticle",
"particles.AccessorSpriteBillboardParticle",
"particles.MixinBlockDustParticle",
"render.AbstractBlockRenderContextMixin",
"render.BlockMixin",
"render.BlockModelRendererMixin",
"render.BlockRenderContextMixin",
"render.BlockRenderInfoMixin",
"render.FluidRendererMixin",
"render.MultipartBakedModelMixin",
"render.TerrainRenderContextMixin",
"render.WorldRendererMixin",