Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,20 @@

import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import dev.ryanhcode.sable.Sable;
import dev.ryanhcode.sable.companion.math.Pose3dc;
import dev.ryanhcode.sable.sublevel.ClientSubLevel;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3d;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
Expand All @@ -33,51 +26,18 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

/**
* Changes the distance block damage is rendered from, transforms block damage rendering for sublevels, and renders block hover outlines for sublevels.
* Changes the distance block damage is rendered from, and transforms block damage rendering for sublevels.
*/
@Mixin(LevelRenderer.class)
public abstract class LevelRendererMixin {

// Storage vectors to avoid repeated allocation
private final @Unique Vector3d sable$localTranslationStorage = new Vector3d();
private final @Unique Vector3d sable$globalTranslationStorage = new Vector3d();
private final @Unique Quaternionf sable$orientationStorage = new Quaternionf();

@Shadow
@Nullable
private ClientLevel level;

@Shadow
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) {
}

@Inject(method = "renderHitOutline", at = @At("HEAD"), cancellable = true)
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) {
final ClientSubLevel subLevel = (ClientSubLevel) Sable.HELPER.getContaining(this.level, blockPos);

if (subLevel == null) {
return;
}

ps.pushPose();

final Pose3dc pose = subLevel.renderPose();

final Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition();

final Vector3d globalTranslation = pose.position().sub(cameraPos.x, cameraPos.y, cameraPos.z, this.sable$globalTranslationStorage);
final Vector3d localTranslation = this.sable$localTranslationStorage.set(blockPos.getX(), blockPos.getY(), blockPos.getZ()).sub(pose.rotationPoint());

// apply transforms
ps.translate(globalTranslation.x, globalTranslation.y, globalTranslation.z);
ps.mulPose(this.sable$orientationStorage.set(pose.orientation()));
ps.translate(localTranslation.x, localTranslation.y, localTranslation.z);

renderShape(ps, pConsumer, blockState.getShape(this.level, blockPos, CollisionContext.of(pEntity)), 0, 0, 0, 0.0F, 0.0F, 0.0F, 0.4F);

ps.popPose();
ci.cancel();
}

@Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;last()Lcom/mojang/blaze3d/vertex/PoseStack$Pose;", shift = At.Shift.BEFORE))
private void sable$preRenderBlockDamage(final DeltaTracker deltaTracker, final boolean bl, final Camera camera, final GameRenderer gameRenderer, final LightTexture lightTexture, final Matrix4f matrix4f, final Matrix4f matrix4f2, final CallbackInfo ci, @Local(ordinal = 0) final PoseStack ps, @Local(ordinal = 0) final BlockPos pos) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ public class CameraMixin {

@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
private void sable$setPosition(final Camera instance,
final double d,
final double e,
final double f,
final double x,
final double y,
final double z,
final Operation<Void> original,
@Local(argsOnly = true) final Entity entity,
@Local(argsOnly = true) final float partialTicks) {
Expand All @@ -58,7 +58,7 @@ public class CameraMixin {
return;
}

original.call(instance, d, e, f);
original.call(instance, x, y, z);

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package dev.ryanhcode.sable.mixinhelpers.block_outline_render;

import dev.ryanhcode.sable.companion.math.Pose3dc;
import net.minecraft.client.Camera;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.material.FogType;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaterniond;
import org.joml.Quaternionf;
import org.joml.Vector3f;

@ApiStatus.Internal
public class SubLevelCamera extends Camera {

private Camera renderCamera;
private final Quaterniond inverseOrientation = new Quaterniond();
private final Quaternionf inverseOrientationf = new Quaternionf();
private final Vector3f rotationYXZ = new Vector3f();

private final BlockPos.MutableBlockPos blockPosition = new BlockPos.MutableBlockPos();
private Vec3 pos = Vec3.ZERO;

public void setCamera(final Camera renderCamera) {
this.renderCamera = renderCamera;
}

public void setPose(@Nullable final Pose3dc pose) {
if (pose != null) {
final Vec3 pos = pose.transformPositionInverse(this.renderCamera.getPosition());

final Quaternionf rotation = this.rotation();
this.renderCamera.rotation().mul(this.inverseOrientationf.set(pose.orientation().invert(this.inverseOrientation)), rotation);

this.blockPosition.set(pos.x, pos.y, pos.z);
this.pos = pos;

rotation.getEulerAnglesYXZ(this.rotationYXZ);

this.getLookVector().set(0.0F, 0.0F, -1.0F).rotate(rotation);
this.getUpVector().set(0.0F, 1.0F, 0.0F).rotate(rotation);
this.getLeftVector().set(-1.0F, 0.0F, 0.0F).rotate(rotation);
} else {
this.pos = this.renderCamera.getPosition();
this.blockPosition.set(this.pos.x, this.pos.y, this.pos.z);
this.rotationYXZ.set(this.renderCamera.getXRot(), this.renderCamera.getYRot(), 0);

final Quaternionf rotation = this.rotation();
rotation.set(this.renderCamera.rotation());

this.getLookVector().set(0.0F, 0.0F, -1.0F).rotate(rotation);
this.getUpVector().set(0.0F, 1.0F, 0.0F).rotate(rotation);
this.getLeftVector().set(-1.0F, 0.0F, 0.0F).rotate(rotation);
}
}

public void clear() {
this.renderCamera = null;
this.pos = Vec3.ZERO;
}

@Override
public @NotNull Vec3 getPosition() {
return this.pos;
}

@Override
public @NotNull BlockPos getBlockPosition() {
return this.blockPosition;
}

@Override
public float getXRot() {
return (float) (180.0 / Math.PI * -this.rotationYXZ.x);
}

@Override
public float getYRot() {
return (float) (180.0 / Math.PI * -this.rotationYXZ.y + 180.0);
}

@Override
public @NotNull Entity getEntity() {
return this.renderCamera.getEntity();
}

@Override
public boolean isInitialized() {
return this.renderCamera.isInitialized();
}

@Override
public boolean isDetached() {
return this.renderCamera.isDetached();
}

@Override
public @NotNull NearPlane getNearPlane() {
return this.renderCamera.getNearPlane();
}

@Override
public @NotNull FogType getFluidInCamera() {
return this.renderCamera.getFluidInCamera();
}

@Override
public void reset() {
this.renderCamera.reset();
}

@Override
public float getPartialTickTime() {
return this.renderCamera.getPartialTickTime();
}

public Camera getRenderCamera() {
return this.renderCamera;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package dev.ryanhcode.sable.fabric.mixin.block_outline_render;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import dev.ryanhcode.sable.Sable;
import dev.ryanhcode.sable.companion.math.Pose3dc;
import dev.ryanhcode.sable.mixinhelpers.block_outline_render.SubLevelCamera;
import dev.ryanhcode.sable.sublevel.ClientSubLevel;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaterniondc;
import org.joml.Quaternionf;
import org.joml.Vector3dc;
import org.spongepowered.asm.mixin.Debug;
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.callback.CallbackInfo;

/**
* Transforms block hover outlines for sublevels.
*/
@Debug(export = true)
@Mixin(value = LevelRenderer.class, priority = 400)
// Make sure this applies first so the camera can be modified
public abstract class LevelRendererMixin {

// Storage vectors to avoid repeated allocation
private final @Unique Quaternionf sable$orientationStorage = new Quaternionf();
private final @Unique SubLevelCamera sable$sublevelCamera = new SubLevelCamera();

@Shadow
@Nullable
private ClientLevel level;

@Inject(method = "renderLevel", at = @At("HEAD"))
public void modifyCamera(final CallbackInfo ci, @Local(argsOnly = true) final LocalRef<Camera> cameraRef) {
this.sable$sublevelCamera.setCamera(cameraRef.get());
this.sable$sublevelCamera.setPose(null);
cameraRef.set(this.sable$sublevelCamera);
}

@Inject(method = "renderLevel", at = @At("TAIL"))
public void clearCamera(final CallbackInfo ci, @Local(argsOnly = true) final LocalRef<Camera> cameraRef) {
// This is important to make sure events fired after this mixin still have access to the camera
cameraRef.set(this.sable$sublevelCamera.getRenderCamera());
this.sable$sublevelCamera.clear();
}

@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"))
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) {
final ClientSubLevel subLevel = (ClientSubLevel) Sable.HELPER.getContaining(this.level, pos);

if (subLevel == null) {
original.call(instance, poseStack, consumer, entity, camX, camY, camZ, pos, state);
return;
}

poseStack.pushPose();

final Pose3dc pose = subLevel.renderPose();

this.sable$sublevelCamera.setPose(pose);
final Vec3 cameraPosition = this.sable$sublevelCamera.getPosition();

final Vector3dc position = pose.position();
final Vector3dc rotationPoint = pose.rotationPoint();
final Quaterniondc orientation = pose.orientation();
final Vector3dc scale = pose.scale();

poseStack.translate(
(float) (position.x() - camX),
(float) (position.y() - camY),
(float) (position.z() - camZ)
);
poseStack.mulPose(this.sable$orientationStorage.set(orientation));
poseStack.translate(
(float) -(rotationPoint.x() - cameraPosition.x),
(float) -(rotationPoint.y() - cameraPosition.y),
(float) -(rotationPoint.z() - cameraPosition.z)
);
poseStack.scale((float) scale.x(), (float) scale.y(), (float) scale.z());

original.call(instance, poseStack, consumer, entity, cameraPosition.x, cameraPosition.y, cameraPosition.z, pos, state);

poseStack.popPose();

this.sable$sublevelCamera.setPose(null);
}
}
1 change: 1 addition & 0 deletions fabric/src/main/resources/sable-fabric.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"minVersion": "0.8",
"plugin": "dev.ryanhcode.sable.plugin.SableMixinPlugin",
"client": [
"block_outline_render.LevelRendererMixin",
"camera_rotation.CameraMixin",
"compatibility.sodiumextras.EmbyToolsMixin",
"dynamic_directional_shading.SectionCompilerMixin",
Expand Down
Loading
Loading