Skip to content

Commit c55604d

Browse files
committed
Implement fabric outline rendering
1 parent d274295 commit c55604d

3 files changed

Lines changed: 85 additions & 34 deletions

File tree

common/src/main/java/dev/ryanhcode/sable/mixinhelpers/block_outline_render/SubLevelCamera.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import net.minecraft.world.phys.Vec3;
99
import org.jetbrains.annotations.ApiStatus;
1010
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
1112
import org.joml.Quaterniond;
1213
import org.joml.Quaternionf;
1314
import org.joml.Vector3f;
@@ -23,22 +24,37 @@ public class SubLevelCamera extends Camera {
2324
private final BlockPos.MutableBlockPos blockPosition = new BlockPos.MutableBlockPos();
2425
private Vec3 pos = Vec3.ZERO;
2526

26-
public void transform(final Camera renderCamera, final Pose3dc pose) {
27+
public void setCamera(final Camera renderCamera) {
2728
this.renderCamera = renderCamera;
29+
}
30+
31+
public void setPose(@Nullable final Pose3dc pose) {
32+
if (pose != null) {
33+
final Vec3 pos = pose.transformPositionInverse(this.renderCamera.getPosition());
34+
35+
final Quaternionf rotation = this.rotation();
36+
this.renderCamera.rotation().mul(this.inverseOrientationf.set(pose.orientation().invert(this.inverseOrientation)), rotation);
2837

29-
final Vec3 pos = pose.transformPositionInverse(renderCamera.getPosition());
38+
this.blockPosition.set(pos.x, pos.y, pos.z);
39+
this.pos = pos;
3040

31-
final Quaternionf rotation = this.rotation();
32-
renderCamera.rotation().mul(this.inverseOrientationf.set(pose.orientation().invert(this.inverseOrientation)), rotation);
41+
rotation.getEulerAnglesYXZ(this.rotationYXZ);
3342

34-
this.blockPosition.set(pos.x, pos.y, pos.z);
35-
this.pos = pos;
43+
this.getLookVector().set(0.0F, 0.0F, -1.0F).rotate(rotation);
44+
this.getUpVector().set(0.0F, 1.0F, 0.0F).rotate(rotation);
45+
this.getLeftVector().set(-1.0F, 0.0F, 0.0F).rotate(rotation);
46+
} else {
47+
this.pos = this.renderCamera.getPosition();
48+
this.blockPosition.set(this.pos.x, this.pos.y, this.pos.z);
49+
this.rotationYXZ.set(this.renderCamera.getXRot(), this.renderCamera.getYRot(), 0);
3650

37-
rotation.getEulerAnglesYXZ(this.rotationYXZ);
51+
final Quaternionf rotation = this.rotation();
52+
rotation.set(this.renderCamera.rotation());
3853

39-
this.getLookVector().set(0.0F, 0.0F, -1.0F).rotate(rotation);
40-
this.getUpVector().set(0.0F, 1.0F, 0.0F).rotate(rotation);
41-
this.getLeftVector().set(-1.0F, 0.0F, 0.0F).rotate(rotation);
54+
this.getLookVector().set(0.0F, 0.0F, -1.0F).rotate(rotation);
55+
this.getUpVector().set(0.0F, 1.0F, 0.0F).rotate(rotation);
56+
this.getLeftVector().set(-1.0F, 0.0F, 0.0F).rotate(rotation);
57+
}
4258
}
4359

4460
public void clear() {
@@ -100,4 +116,8 @@ public void reset() {
100116
public float getPartialTickTime() {
101117
return this.renderCamera.getPartialTickTime();
102118
}
119+
120+
public Camera getRenderCamera() {
121+
return this.renderCamera;
122+
}
103123
}
Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
package dev.ryanhcode.sable.fabric.mixin.block_outline_render;
22

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;
37
import com.mojang.blaze3d.vertex.PoseStack;
48
import com.mojang.blaze3d.vertex.VertexConsumer;
59
import dev.ryanhcode.sable.Sable;
610
import dev.ryanhcode.sable.companion.math.Pose3dc;
11+
import dev.ryanhcode.sable.mixinhelpers.block_outline_render.SubLevelCamera;
712
import dev.ryanhcode.sable.sublevel.ClientSubLevel;
8-
import net.minecraft.client.Minecraft;
13+
import net.minecraft.client.Camera;
914
import net.minecraft.client.multiplayer.ClientLevel;
1015
import net.minecraft.client.renderer.LevelRenderer;
1116
import net.minecraft.core.BlockPos;
1217
import net.minecraft.world.entity.Entity;
1318
import net.minecraft.world.level.block.state.BlockState;
1419
import net.minecraft.world.phys.Vec3;
15-
import net.minecraft.world.phys.shapes.CollisionContext;
16-
import net.minecraft.world.phys.shapes.VoxelShape;
1720
import org.jetbrains.annotations.Nullable;
21+
import org.joml.Quaterniondc;
1822
import org.joml.Quaternionf;
19-
import org.joml.Vector3d;
23+
import org.joml.Vector3dc;
24+
import org.spongepowered.asm.mixin.Debug;
2025
import org.spongepowered.asm.mixin.Mixin;
2126
import org.spongepowered.asm.mixin.Shadow;
2227
import org.spongepowered.asm.mixin.Unique;
@@ -27,46 +32,71 @@
2732
/**
2833
* Transforms block hover outlines for sublevels.
2934
*/
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
3138
public abstract class LevelRendererMixin {
39+
3240
// Storage vectors to avoid repeated allocation
33-
private final @Unique Vector3d sable$localTranslationStorage = new Vector3d();
34-
private final @Unique Vector3d sable$globalTranslationStorage = new Vector3d();
3541
private final @Unique Quaternionf sable$orientationStorage = new Quaternionf();
42+
private final @Unique SubLevelCamera sable$sublevelCamera = new SubLevelCamera();
3643

3744
@Shadow
3845
@Nullable
3946
private ClientLevel level;
4047

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);
4353
}
4454

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);
4865

4966
if (subLevel == null) {
67+
original.call(instance, poseStack, consumer, entity, camX, camY, camZ, pos, state);
5068
return;
5169
}
5270

53-
ps.pushPose();
71+
poseStack.pushPose();
5472

5573
final Pose3dc pose = subLevel.renderPose();
5674

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();
5882

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());
6195

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);
6697

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();
6899

69-
ps.popPose();
70-
ci.cancel();
100+
this.sable$sublevelCamera.setPose(null);
71101
}
72102
}

neoforge/src/main/java/dev/ryanhcode/sable/neoforge/mixin/block_outline_render/LevelRendererMixin.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ public abstract class LevelRendererMixin {
6262

6363
final Pose3dc pose = subLevel.renderPose();
6464

65-
this.sable$sublevelCamera.transform(camera, pose);
65+
this.sable$sublevelCamera.setCamera(camera);
66+
this.sable$sublevelCamera.setPose(pose);
6667
final Vec3 cameraPosition = this.sable$sublevelCamera.getPosition();
6768
final Vec3 realCameraPosition = camera.getPosition();
6869

0 commit comments

Comments
 (0)