feat: Axiom now renders frames in selections properly

This commit is contained in:
Adrien1106 2024-06-12 00:13:38 +02:00
parent 52fb6840ad
commit 520430d5bc
13 changed files with 418 additions and 3 deletions

View File

@ -84,6 +84,9 @@ repositories {
includeGroup "maven.modrinth"
}
}
maven {
url = "https://jitpack.io"
}
mavenCentral()
// Add repositories to retrieve artifacts from in here.
@ -120,6 +123,7 @@ dependencies {
// Axiom for blueprint support
modCompileOnly "maven.modrinth:N6n5dqoA:nvx3oDkz"
modCompileOnly "com.github.moulberry:AxiomClientAPI:1.0.5.3"
// Fabric API.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

View File

@ -5,12 +5,15 @@ import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
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 java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
@ -34,6 +37,13 @@ public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements
return model_1.getParticleSprite();
}
@Override
public List<BakedQuad> getQuads(BlockState blockState, Direction face, Random rand) {
List<BakedQuad> quads = new ArrayList<>(model_1.getQuads(blockState, face, rand));
quads.addAll(model_2.getQuads(blockState, face, rand));
return quads;
}
@Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {}

View File

@ -12,11 +12,13 @@ import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import net.fabricmc.fabric.api.renderer.v1.mesh.*;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
@ -25,6 +27,7 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@ -70,6 +73,39 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
return mesh;
}
private List<BakedQuad>[] quads = null;
@Override
public List<BakedQuad> getQuads(BlockState state, Direction face, Random rand) {
if (quads == null) {
quads = ModelHelper.toQuadLists(
getRetexturedMesh(
new MeshCacheKey(
hashCode(),
appearance_manager.getDefaultAppearance(theme_index),
0
),
state
)
);
}
return quads[ModelHelper.toFaceIndex(face)];
}
public void setCamo(BlockRenderView world, BlockState state, BlockPos pos) {
if (state == null || state.isAir()) {
quads = null;
return;
}
CamoAppearance camo = appearance_manager.getCamoAppearance(world, state, pos, theme_index, false);
MeshCacheKey key = new MeshCacheKey(
hashCode(),
camo,
0
);
quads = ModelHelper.toQuadLists(camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state));
}
protected abstract Mesh convertModel(BlockState state);
@Override
@ -176,7 +212,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
emitter.copyFrom(quad);
i = key.appearance.transformQuad(emitter, i, quad_index.get(), key.model_id, uv_lock);
} while (i > 0);
// kinda weird to do it like that but other directions don't use the quad_index so it doesn't matter
// kinda weird to do it like that but other directions don't use the quad_index, so it doesn't matter
if (quad.cullFace() == null) quad_index.getAndIncrement();
});

View File

@ -33,7 +33,14 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
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)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomChunkedBlockRegionMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClientBlockEntitySerializerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomClipboardMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomCloneBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomPlacementMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomMoveBuilderToolMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomScale3xMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AxiomRotSpriteMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(5)));
}

View File

@ -0,0 +1,141 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.client.model.RetexturingBakedModel;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.registry.Registries;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
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.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
import java.util.stream.Stream;
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
@Mixin(ChunkedBlockRegion.class)
public class AxiomChunkedBlockRegionMixin implements IAxiomChunkedBlockRegionMixin {
@Shadow
private static void renderBlock(BufferBuilder blockBuilder, BlockRenderManager renderManager, BlockPos.Mutable blockPos, Random rand, MatrixStack matrices, BlockRenderView blockAndTintGetter, Matrix4f currentPoseMatrix, Matrix4f basePoseMatrix, int x, int y, int z, BlockState dataState, boolean useAmbientOcclusion) {}
@Unique
private IntMatrix transform;
@Unique
private IntMatrix inverse_transform;
@Unique
private Long2ObjectMap<CompressedBlockEntity> block_entities;
@Redirect(
method = "uploadDirty",
at = @At(
value = "INVOKE",
target = "Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;renderBlock(Lnet/minecraft/client/render/BufferBuilder;Lnet/minecraft/client/render/block/BlockRenderManager;Lnet/minecraft/util/math/BlockPos$Mutable;Lnet/minecraft/util/math/random/Random;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/world/BlockRenderView;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;IIILnet/minecraft/block/BlockState;Z)V"
)
)
private void onRenderBlock(BufferBuilder buffer, BlockRenderManager renderer, BlockPos.Mutable pos, Random rand, MatrixStack matrices, BlockRenderView world, Matrix4f current_pos, Matrix4f base_pos, int x, int y, int z, BlockState state, boolean use_ao) {
BakedModel model;
List<BakedModel> models;
if (block_entities != null
&& state.getRenderType() == BlockRenderType.MODEL
&& (model = renderer.getModel(state)) != null
&& model instanceof IMultipartBakedModelMixin mpm
&& !(models = mpm.getModels(state)
.stream()
.filter(m -> m instanceof RetexturingBakedModel || m instanceof MultiRetexturableModel)
.toList()).isEmpty()
) {
long key = BlockPos.asLong(
inverse_transform.transformX(pos.getX(), pos.getY(), pos.getZ()),
inverse_transform.transformY(pos.getX(), pos.getY(), pos.getZ()),
inverse_transform.transformZ(pos.getX(), pos.getY(), pos.getZ())
);
if (block_entities.containsKey(key)) {
NbtCompound compound = block_entities.get(key).decompress();
models.stream()
.flatMap(m -> m instanceof MultiRetexturableModel mm
? mm.models().stream()
: Stream.of((RetexturingBakedModel)m)
)
.forEach(m -> m.setCamo(
world,
compound.contains(BLOCKSTATE_KEY + m.getThemeIndex())
? NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), compound.getCompound(BLOCKSTATE_KEY + m.getThemeIndex()))
: null,
pos
));
}
}
renderBlock(buffer, renderer, pos, rand, matrices, world, current_pos, base_pos, x, y, z, state, use_ao);
}
@Inject(
method = "uploadDirty",
at = @At("HEAD")
)
private void onUploadDirty(Camera camera, Vec3d translation, boolean canResort, boolean canUseAmbientOcclusion, CallbackInfo ci) {
if (transform == null) inverse_transform = new IntMatrix();
else inverse_transform = transform.copy();
inverse_transform.invert();
}
@Inject(
method = "flip",
at = @At("RETURN")
)
private void onFlip(Direction.Axis axis, CallbackInfoReturnable<ChunkedBlockRegion> cir) {
((IAxiomChunkedBlockRegionMixin) cir.getReturnValue()).setTransform(transform, block_entities);
}
@Inject(
method = "rotate",
at = @At("RETURN")
)
private void onRotate(Direction.Axis axis, int count, CallbackInfoReturnable<ChunkedBlockRegion> cir) {
((IAxiomChunkedBlockRegionMixin) cir.getReturnValue()).setTransform(transform, block_entities);
}
@Override
public void setTransform(IntMatrix transform, Long2ObjectMap<CompressedBlockEntity> block_entities) {
this.transform = transform;
this.block_entities = block_entities;
}
@Override
public IntMatrix getTransform() {
return transform;
}
@Override
public Long2ObjectMap<CompressedBlockEntity> getBlockEntities() {
return block_entities;
}
}

View File

@ -0,0 +1,25 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.clipboard.Clipboard;
import com.moulberry.axiom.clipboard.ClipboardObject;
import com.moulberry.axiom.utils.IntMatrix;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
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(Clipboard.class)
public class AxiomClipboardMixin {
@Inject(
method = "setClipboard(Lcom/moulberry/axiom/clipboard/ClipboardObject;)I",
at = @At(
value = "TAIL"
),
remap = false
)
private void onInit(ClipboardObject object, CallbackInfoReturnable<Integer> cir) {
((IAxiomChunkedBlockRegionMixin) object.blockRegion()).setTransform(new IntMatrix(), object.blockEntities());
}
}

View File

@ -0,0 +1,38 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.buildertools.CloneBuilderTool;
import com.moulberry.axiom.clipboard.SelectionBuffer;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(CloneBuilderTool.class)
public class AxiomCloneBuilderToolMixin {
@Shadow(remap = false) private ChunkedBlockRegion blockRegion;
@Shadow(remap = false) @Final private IntMatrix transformMatrix;
@Shadow(remap = false) private Long2ObjectMap<CompressedBlockEntity> blockEntities;
@Inject(
method = "lambda$initiateClone$0",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lcom/moulberry/axiom/clipboard/SelectionBuffer$CopyResult;blockEntities()Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;",
shift = At.Shift.AFTER
),
remap = false
)
private void onInitiateClone(int copyId, int offsetX, int offsetY, int offsetZ, SelectionBuffer.CopyResult copyResult, CallbackInfo ci) {
((IAxiomChunkedBlockRegionMixin) blockRegion).setTransform(transformMatrix, blockEntities);
}
}

View File

@ -0,0 +1,38 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.buildertools.MoveBuilderTool;
import com.moulberry.axiom.clipboard.SelectionBuffer;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MoveBuilderTool.class)
public class AxiomMoveBuilderToolMixin {
@Shadow(remap = false) private ChunkedBlockRegion blockRegion;
@Shadow(remap = false) @Final private IntMatrix transformMatrix;
@Shadow(remap = false) private Long2ObjectMap<CompressedBlockEntity> blockEntities;
@Inject(
method = "lambda$initiateMovement$1",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lcom/moulberry/axiom/clipboard/SelectionBuffer$CopyResult;blockEntities()Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;",
shift = At.Shift.AFTER
),
remap = false
)
private void onInitiateClone(int copyId, int offsetX, int offsetY, int offsetZ, SelectionBuffer.CopyResult copyResult, CallbackInfo ci) {
((IAxiomChunkedBlockRegionMixin) blockRegion).setTransform(transformMatrix, blockEntities);
}
}

View File

@ -0,0 +1,39 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.clipboard.Placement;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.util.math.BlockPos;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Placement.class)
public class AxiomPlacementMixin {
@Shadow(remap = false) private Long2ObjectMap<CompressedBlockEntity> blockEntities;
@Inject(
method = "replacePlacement(Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;Ljava/lang/String;)V",
at = @At("HEAD"),
remap = false
)
private void onReplacePlacement(ChunkedBlockRegion region, String description, CallbackInfo ci) {
((IAxiomChunkedBlockRegionMixin) region).setTransform(new IntMatrix(), blockEntities);
}
@Inject(
method = "startPlacement(Lnet/minecraft/util/math/BlockPos;Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;Ljava/lang/String;)I",
at = @At("HEAD"),
remap = false
)
private void onStartPlacement(BlockPos target, ChunkedBlockRegion region, Long2ObjectMap<CompressedBlockEntity> entities, String description, CallbackInfoReturnable<Integer> cir) {
((IAxiomChunkedBlockRegionMixin) region).setTransform(new IntMatrix(), entities);
}
}

View File

@ -0,0 +1,27 @@
package fr.adrien1106.reframed.mixin.compat;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.scaling.RotSprite;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
import org.joml.Matrix4f;
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(RotSprite.class)
public class AxiomRotSpriteMixin {
@Inject(
method = "rotateCachedWithOutput",
at = @At(
value = "HEAD"
),
remap = false
)
private static void onRotateCachedWithOutput(ChunkedBlockRegion in, Matrix4f matrix4f, ChunkedBlockRegion out, int x, int y, int z, CallbackInfoReturnable<ChunkedBlockRegion> cir) {
IAxiomChunkedBlockRegionMixin iin = (IAxiomChunkedBlockRegionMixin) in;
((IAxiomChunkedBlockRegionMixin) out).setTransform(iin.getTransform(), iin.getBlockEntities());
}
}

View File

@ -0,0 +1,29 @@
package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local;
import com.moulberry.axiom.render.regions.ChunkedBlockRegion;
import com.moulberry.axiom.scaling.Scale3x;
import fr.adrien1106.reframed.util.mixin.IAxiomChunkedBlockRegionMixin;
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(Scale3x.class)
public class AxiomScale3xMixin {
@Inject(
method = "scale3x",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lcom/moulberry/axiom/render/regions/ChunkedBlockRegion;<init>()V",
shift = At.Shift.AFTER
),
remap = false
)
private static void onInit(ChunkedBlockRegion in, boolean postProcessing, CallbackInfoReturnable<ChunkedBlockRegion> cir, @Local(ordinal = 1) ChunkedBlockRegion out) {
IAxiomChunkedBlockRegionMixin iin = (IAxiomChunkedBlockRegionMixin) in;
((IAxiomChunkedBlockRegionMixin) out).setTransform(iin.getTransform(), iin.getBlockEntities());
}
}

View File

@ -0,0 +1,14 @@
package fr.adrien1106.reframed.util.mixin;
import com.moulberry.axiom.utils.IntMatrix;
import com.moulberry.axiom.world_modification.CompressedBlockEntity;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
public interface IAxiomChunkedBlockRegionMixin {
void setTransform(IntMatrix transform, Long2ObjectMap<CompressedBlockEntity> block_entities);
IntMatrix getTransform();
Long2ObjectMap<CompressedBlockEntity> getBlockEntities();
}

View File

@ -15,7 +15,14 @@
"compat.AthenaBakedModelMixin",
"compat.AthenaConnectedBlockModelMixin",
"compat.AthenaWrappedGetterMixin",
"compat.AxiomChunkedBlockRegionMixin",
"compat.AxiomClientBlockEntitySerializerMixin",
"compat.AxiomClipboardMixin",
"compat.AxiomCloneBuilderToolMixin",
"compat.AxiomMoveBuilderToolMixin",
"compat.AxiomPlacementMixin",
"compat.AxiomRotSpriteMixin",
"compat.AxiomScale3xMixin",
"compat.ContinuityConnectionPredicateMixin",
"compat.ContinuityCTMBakedModelMixin",
"compat.ContinuityCTMQuadTransformMixin",