|
1 | 1 | package dev.ryanhcode.sable.fabric.mixin.block_outline_render; |
2 | 2 |
|
| 3 | +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; |
| 4 | +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; |
| 5 | +import com.llamalad7.mixinextras.sugar.Local; |
| 6 | +import com.llamalad7.mixinextras.sugar.ref.LocalRef; |
3 | 7 | import com.mojang.blaze3d.vertex.PoseStack; |
4 | 8 | import com.mojang.blaze3d.vertex.VertexConsumer; |
5 | 9 | import dev.ryanhcode.sable.Sable; |
6 | 10 | import dev.ryanhcode.sable.companion.math.Pose3dc; |
| 11 | +import dev.ryanhcode.sable.mixinhelpers.block_outline_render.SubLevelCamera; |
7 | 12 | import dev.ryanhcode.sable.sublevel.ClientSubLevel; |
8 | | -import net.minecraft.client.Minecraft; |
| 13 | +import net.minecraft.client.Camera; |
9 | 14 | import net.minecraft.client.multiplayer.ClientLevel; |
10 | 15 | import net.minecraft.client.renderer.LevelRenderer; |
11 | 16 | import net.minecraft.core.BlockPos; |
12 | 17 | import net.minecraft.world.entity.Entity; |
13 | 18 | import net.minecraft.world.level.block.state.BlockState; |
14 | 19 | import net.minecraft.world.phys.Vec3; |
15 | | -import net.minecraft.world.phys.shapes.CollisionContext; |
16 | | -import net.minecraft.world.phys.shapes.VoxelShape; |
17 | 20 | import org.jetbrains.annotations.Nullable; |
| 21 | +import org.joml.Quaterniondc; |
18 | 22 | import org.joml.Quaternionf; |
19 | | -import org.joml.Vector3d; |
| 23 | +import org.joml.Vector3dc; |
| 24 | +import org.spongepowered.asm.mixin.Debug; |
20 | 25 | import org.spongepowered.asm.mixin.Mixin; |
21 | 26 | import org.spongepowered.asm.mixin.Shadow; |
22 | 27 | import org.spongepowered.asm.mixin.Unique; |
|
27 | 32 | /** |
28 | 33 | * Transforms block hover outlines for sublevels. |
29 | 34 | */ |
30 | | -@Mixin(LevelRenderer.class) |
| 35 | +@Debug(export = true) |
| 36 | +@Mixin(value = LevelRenderer.class, priority = 400) |
| 37 | +// Make sure this applies first so the camera can be modified |
31 | 38 | public abstract class LevelRendererMixin { |
| 39 | + |
32 | 40 | // Storage vectors to avoid repeated allocation |
33 | | - private final @Unique Vector3d sable$localTranslationStorage = new Vector3d(); |
34 | | - private final @Unique Vector3d sable$globalTranslationStorage = new Vector3d(); |
35 | 41 | private final @Unique Quaternionf sable$orientationStorage = new Quaternionf(); |
| 42 | + private final @Unique SubLevelCamera sable$sublevelCamera = new SubLevelCamera(); |
36 | 43 |
|
37 | 44 | @Shadow |
38 | 45 | @Nullable |
39 | 46 | private ClientLevel level; |
40 | 47 |
|
41 | | - @Shadow |
42 | | - protected static void renderShape(final PoseStack arg, final VertexConsumer arg2, final VoxelShape arg3, final double d, final double e, final double f, final float g, final float h, final float i, final float j) { |
| 48 | + @Inject(method = "renderLevel", at = @At("HEAD")) |
| 49 | + public void modifyCamera(final CallbackInfo ci, @Local(argsOnly = true) final LocalRef<Camera> cameraRef) { |
| 50 | + this.sable$sublevelCamera.setCamera(cameraRef.get()); |
| 51 | + this.sable$sublevelCamera.setPose(null); |
| 52 | + cameraRef.set(this.sable$sublevelCamera); |
43 | 53 | } |
44 | 54 |
|
45 | | - @Inject(method = "renderHitOutline", at = @At("HEAD"), cancellable = true) |
46 | | - private void sable$preRenderHitOutline(final PoseStack ps, final VertexConsumer pConsumer, final Entity pEntity, final double pCamX, final double pCamY, final double pCamZ, final BlockPos blockPos, final BlockState blockState, final CallbackInfo ci) { |
47 | | - final ClientSubLevel subLevel = (ClientSubLevel) Sable.HELPER.getContaining(this.level, blockPos); |
| 55 | + @Inject(method = "renderLevel", at = @At("TAIL")) |
| 56 | + public void clearCamera(final CallbackInfo ci, @Local(argsOnly = true) final LocalRef<Camera> cameraRef) { |
| 57 | + // This is important to make sure events fired after this mixin still have access to the camera |
| 58 | + cameraRef.set(this.sable$sublevelCamera.getRenderCamera()); |
| 59 | + this.sable$sublevelCamera.clear(); |
| 60 | + } |
| 61 | + |
| 62 | + @WrapOperation(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;renderHitOutline(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;Lnet/minecraft/world/entity/Entity;DDDLnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V")) |
| 63 | + private void sable$preRenderHitOutline(final LevelRenderer instance, final PoseStack poseStack, final VertexConsumer consumer, final Entity entity, final double camX, final double camY, final double camZ, final BlockPos pos, final BlockState state, final Operation<Void> original, @Local(argsOnly = true) final Camera camera) { |
| 64 | + final ClientSubLevel subLevel = (ClientSubLevel) Sable.HELPER.getContaining(this.level, pos); |
48 | 65 |
|
49 | 66 | if (subLevel == null) { |
| 67 | + original.call(instance, poseStack, consumer, entity, camX, camY, camZ, pos, state); |
50 | 68 | return; |
51 | 69 | } |
52 | 70 |
|
53 | | - ps.pushPose(); |
| 71 | + poseStack.pushPose(); |
54 | 72 |
|
55 | 73 | final Pose3dc pose = subLevel.renderPose(); |
56 | 74 |
|
57 | | - final Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); |
| 75 | + this.sable$sublevelCamera.setPose(pose); |
| 76 | + final Vec3 cameraPosition = this.sable$sublevelCamera.getPosition(); |
| 77 | + |
| 78 | + final Vector3dc position = pose.position(); |
| 79 | + final Vector3dc rotationPoint = pose.rotationPoint(); |
| 80 | + final Quaterniondc orientation = pose.orientation(); |
| 81 | + final Vector3dc scale = pose.scale(); |
58 | 82 |
|
59 | | - final Vector3d globalTranslation = pose.position().sub(cameraPos.x, cameraPos.y, cameraPos.z, this.sable$globalTranslationStorage); |
60 | | - final Vector3d localTranslation = this.sable$localTranslationStorage.set(blockPos.getX(), blockPos.getY(), blockPos.getZ()).sub(pose.rotationPoint()); |
| 83 | + poseStack.translate( |
| 84 | + (float) (position.x() - camX), |
| 85 | + (float) (position.y() - camY), |
| 86 | + (float) (position.z() - camZ) |
| 87 | + ); |
| 88 | + poseStack.mulPose(this.sable$orientationStorage.set(orientation)); |
| 89 | + poseStack.translate( |
| 90 | + (float) -(rotationPoint.x() - cameraPosition.x), |
| 91 | + (float) -(rotationPoint.y() - cameraPosition.y), |
| 92 | + (float) -(rotationPoint.z() - cameraPosition.z) |
| 93 | + ); |
| 94 | + poseStack.scale((float) scale.x(), (float) scale.y(), (float) scale.z()); |
61 | 95 |
|
62 | | - // apply transforms |
63 | | - ps.translate(globalTranslation.x, globalTranslation.y, globalTranslation.z); |
64 | | - ps.mulPose(this.sable$orientationStorage.set(pose.orientation())); |
65 | | - ps.translate(localTranslation.x, localTranslation.y, localTranslation.z); |
| 96 | + original.call(instance, poseStack, consumer, entity, cameraPosition.x, cameraPosition.y, cameraPosition.z, pos, state); |
66 | 97 |
|
67 | | - renderShape(ps, pConsumer, blockState.getShape(this.level, blockPos, CollisionContext.of(pEntity)), 0, 0, 0, 0.0F, 0.0F, 0.0F, 0.4F); |
| 98 | + poseStack.popPose(); |
68 | 99 |
|
69 | | - ps.popPose(); |
70 | | - ci.cancel(); |
| 100 | + this.sable$sublevelCamera.setPose(null); |
71 | 101 | } |
72 | 102 | } |
0 commit comments