Skip to content

Commit

Permalink
Merge pull request #20 from der-fruhling-entertainment/dev/v0.2.2
Browse files Browse the repository at this point in the history
Version v0.2.2
  • Loading branch information
der-fruhling authored Apr 23, 2024
2 parents 0677e5a + 71fc1c5 commit b291c24
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 62 deletions.
28 changes: 25 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
- Fabric: This mod now correctly depends on Create Fabric. Sorry for the issues.
- Updated mod metadata to include info about the project.
- Player camera now leans while their train is on a slope. [View issue.](https://github.com/der-fruhling-entertainment/create-train-perspective/issues/11)
- Also, camera now rolls when facing sideways on a slope.
- (hopefully this feature doesn't break other mods)
- Player legs are now locked forward while seated on a train. [View issue.](https://github.com/der-fruhling-entertainment/create-train-perspective/issues/13)
- Players standing on a train will now be affected the same way as players sitting. [View issue.](https://github.com/der-fruhling-entertainment/create-train-perspective/issues/14)
- This does not fix the Create mod issue where standing players may be ejected from the train if it is traveling at high speeds.
- Players riding taller trains now lean correctly. [View issue.](https://github.com/der-fruhling-entertainment/create-train-perspective/issues/16)

Full Changelog: https://github.com/der-fruhling/create-train-perspective/compare/v0.2.0...v0.2.1
Known issues:
- The mod's leaning is a bit jittery. [View issue.](https://github.com/der-fruhling-entertainment/create-train-perspective/issues/22)
- This is probably more of a limitation of Minecraft itself. It might be fixable with a config option to emulate pitch, but that wouldn't make the player appear correctly to other players.
- The easing back to normal posture after jumping off a train on a slope could use some work. [View issue.](https://github.com/der-fruhling-entertainment/create-train-perspective/issues/23)
- The third-person camera is janky while on a train currently on a slope. [View issue.](https://github.com/der-fruhling-entertainment/create-train-perspective/issues/24)

[View full change log.](https://github.com/der-fruhling/create-train-perspective/compare/v0.2.1...v0.2.2)

---

Issues?
Feature Requests?
[View the issue tracker!](https://github.com/der-fruhling-entertainment/create-train-perspective/issues)

Questions?
[Join the Discord!](https://discord.gg/AyM66DhPKr)
Or,
[discuss on GitHub!](https://github.com/der-fruhling-entertainment/create-train-perspective/discussions)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package net.derfruhling.minecraft.create.trainperspective;

public interface Camera3D {
float getZRot();
void setRotation3D(float y, float x, float z);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,39 @@
import com.simibubi.create.content.trains.entity.CarriageContraptionEntity;
import dev.architectury.event.events.common.TickEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import org.apache.commons.lang3.mutable.MutableInt;
import org.slf4j.Logger;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

// The value here should match an entry in the META-INF/mods.toml file
public class CreateTrainPerspectiveMod {
// Define mod id in a common place for everything to reference
public static final String MODID = "create_train_perspective";
public static CreateTrainPerspectiveMod INSTANCE;
// Directly reference a slf4j logger
private static final Logger LOGGER = LogUtils.getLogger();

public CreateTrainPerspectiveMod() {
TickEvent.PLAYER_POST.register(this::onTickPlayer);
INSTANCE = this;
}

private static class RotationState {
public final CarriageContraptionEntity entity;
private float lastYaw;
public boolean standingState, isMounted, shouldTickState = true;

public RotationState(CarriageContraptionEntity entity) {
public RotationState(CarriageContraptionEntity entity, boolean standingState, boolean isMounted) {
this.entity = entity;
lastYaw = entity.yaw;
this.standingState = standingState;
this.isMounted = isMounted;
}

public float getYawDelta() {
Expand All @@ -47,26 +53,75 @@ public void onEntityMount(boolean isMounting, Entity entityMounting, Entity enti
entityMounting instanceof LocalPlayer player &&
entityBeingMounted instanceof CarriageContraptionEntity contraption
) {
var persp = (PlayerPerspectiveBehavior) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player);
var persp = (Perspective) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player);
if(isMounting) {
var state = new RotationState(contraption);
states.put(entityMounting.getUUID(), state);
persp.enable(state.entity.pitch, state.entity.yaw);
if(!states.containsKey(entityMounting.getUUID())) {
var state = new RotationState(contraption, false, true);
states.put(entityMounting.getUUID(), state);
persp.enable(state.entity.pitch, state.entity.yaw);
} else {
states.get(entityMounting.getUUID()).isMounted = true;
}
} else {
states.remove(entityMounting.getUUID());
persp.disable();
if(states.containsKey(entityMounting.getUUID())) {
if(states.get(entityMounting.getUUID()).standingState) {
states.get(entityMounting.getUUID()).isMounted = false;
} else {
states.remove(entityMounting.getUUID());
persp.disable();
}
}
}
}
}

public void tickStandingPlayers(final CarriageContraptionEntity contraption) {
for(Map.Entry<Entity, MutableInt> entry : contraption.collidingEntities.entrySet()) {
var entity = entry.getKey();
var ticks = entry.getValue();
if(entity instanceof LocalPlayer player) {
if(player.getVehicle() != null) continue;

var state = states.get(player.getUUID());
if (state == null) {
var persp = (Perspective) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player);
state = new RotationState(contraption, true, false);
states.put(player.getUUID(), state);
persp.enable(state.entity.pitch, state.entity.yaw);
} else if(ticks.getValue() >= 2) {
state.shouldTickState = false;
} else if(!state.shouldTickState) {
state.shouldTickState = true;
}
}
}
}

private void tickState(LocalPlayer player) {
var state = states.get(player.getUUID());
var persp = (Perspective) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player);
persp.setLean(state.entity.pitch);
persp.setYaw(state.entity.yaw);
player.setYRot(player.getYRot() + state.getYawDelta());
player.setYBodyRot(player.getYRot());
}

public void onTickPlayer(final Player player) {
if (!(player instanceof AbstractClientPlayer)) return;
if(states.containsKey(player.getUUID())) {
if(player instanceof LocalPlayer localPlayer && states.containsKey(player.getUUID())) {
var state = states.get(player.getUUID());
var persp = (PlayerPerspectiveBehavior) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer((AbstractClientPlayer) player);
persp.setLean(state.entity.pitch);
persp.setYaw(state.entity.yaw);
player.setYRot(player.getYRot() + state.getYawDelta());

if(state.shouldTickState) {
tickState(localPlayer);
} else {
var persp = (Perspective) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player);
persp.diminish();
}

if(localPlayer.onGround() && state.standingState) {
var persp = (Perspective) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player);
states.remove(player.getUUID());
persp.disable();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package net.derfruhling.minecraft.create.trainperspective;

import net.minecraft.client.Camera;

public class MixinUtil {
private MixinUtil() {}

public static Camera3D asCamera3D(Camera camera) {
return (Camera3D) camera;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package net.derfruhling.minecraft.create.trainperspective;

public interface PlayerPerspectiveBehavior {
public interface Perspective {
void enable(float initialLean, float initialYaw);
void disable();
void setLean(float lean);
void setYaw(float yaw);
float getLean();
float getYaw();

default void diminish() {
setLean(getLean() * 0.97f);
setYaw(getYaw() * 0.97f);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package net.derfruhling.minecraft.create.trainperspective.mixin;

import net.derfruhling.minecraft.create.trainperspective.Camera3D;
import net.derfruhling.minecraft.create.trainperspective.Perspective;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(Camera.class)
@Implements({@Interface(iface = Camera3D.class, prefix = "ctp$")})
public abstract class CameraMixin {
@Shadow private Entity entity;
@Shadow private float xRot;
@Shadow private float yRot;
@Unique private float ctp$zRot;

@Shadow @Final private Quaternionf rotation;
@Shadow @Final private Vector3f forwards;
@Shadow @Final private Vector3f up;
@Shadow @Final private Vector3f left;

@Shadow protected abstract void setRotation(float f, float g);

@Unique
public void ctp$setRotation3D(float y, float x, float z) {
this.xRot = x;
this.yRot = y;
this.ctp$zRot = z;
this.rotation.rotationYXZ(-y * 0.017453292F, x * 0.017453292F, z * 0.017453292F);
this.forwards.set(0.0F, 0.0F, 1.0F).rotate(this.rotation);
this.up.set(0.0F, 1.0F, 0.0F).rotate(this.rotation);
this.left.set(1.0F, 0.0F, 0.0F).rotate(this.rotation);
}

@Unique
public float ctp$getZRot() {
return this.ctp$zRot;
}

@Redirect(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setRotation(FF)V"))
public void modifyRotationsPrimary(Camera instance, float y, float x) {
if(entity instanceof LocalPlayer player) {
var persp = (Perspective) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player);
ctp$setRotation3D(y,
x - persp.getLean() * Mth.sin((persp.getYaw() - y) * Mth.DEG_TO_RAD),
persp.getLean() * Mth.cos((persp.getYaw() - y) * Mth.DEG_TO_RAD));
} else setRotation(y, x);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.derfruhling.minecraft.create.trainperspective.mixin;

import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.ContraptionCollider;
import com.simibubi.create.content.trains.entity.CarriageContraptionEntity;
import net.derfruhling.minecraft.create.trainperspective.CreateTrainPerspectiveMod;
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.CallbackInfo;

@Mixin(ContraptionCollider.class)
public class ContraptionColliderMixin {
@Inject(method = "collideEntities", at = @At("HEAD"), remap = false)
private static void saveClientPlayerFromClipping(
AbstractContraptionEntity contraptionEntity,
CallbackInfo ci
) {
if(contraptionEntity instanceof CarriageContraptionEntity carriage) {
CreateTrainPerspectiveMod.INSTANCE.tickStandingPlayers(carriage);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
package net.derfruhling.minecraft.create.trainperspective.fabric.mixin;
package net.derfruhling.minecraft.create.trainperspective.mixin;

import net.derfruhling.minecraft.create.trainperspective.fabric.ModFabricEntrypoint;
import net.derfruhling.minecraft.create.trainperspective.CreateTrainPerspectiveMod;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable;
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;

import javax.annotation.Nullable;

@Mixin(Entity.class)
public class EntityMixin {
@Shadow @Nullable private Entity vehicle;

@Inject(method = "startRiding(Lnet/minecraft/world/entity/Entity;Z)Z", at = @At(value = "RETURN", ordinal = 4))
public void onStartRiding(Entity entity, boolean bl, CallbackInfoReturnable<Boolean> cir) {
ModFabricEntrypoint.getInstance().common.onEntityMount(true, (Entity)(Object)this, entity);
CreateTrainPerspectiveMod.INSTANCE.onEntityMount(true, (Entity)(Object)this, entity);
}

@Inject(method = "removeVehicle", at = @At("HEAD"))
public void onRemoveVehicle(CallbackInfo ci) {
if(vehicle != null) {
ModFabricEntrypoint.getInstance().common.onEntityMount(false, (Entity)(Object)this, vehicle);
CreateTrainPerspectiveMod.INSTANCE.onEntityMount(false, (Entity)(Object)this, vehicle);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.derfruhling.minecraft.create.trainperspective.mixin;

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.derfruhling.minecraft.create.trainperspective.MixinUtil;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.GameRenderer;
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(GameRenderer.class)
public class GameRendererMixin {
@Shadow @Final private Camera mainCamera;

@Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V", ordinal = 3, shift = At.Shift.AFTER))
public void applyZRotation(float f, long l, PoseStack poseStack, CallbackInfo ci) {
poseStack.mulPose(Axis.ZP.rotationDegrees(MixinUtil.asCamera3D(mainCamera).getZRot()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.derfruhling.minecraft.create.trainperspective.PlayerPerspectiveBehavior;
import net.derfruhling.minecraft.create.trainperspective.Perspective;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.util.Mth;
Expand All @@ -15,7 +15,7 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(PlayerRenderer.class)
@Implements({@Interface(iface = PlayerPerspectiveBehavior.class, prefix = "ctp$")})
@Implements({@Interface(iface = Perspective.class, prefix = "ctp$")})
public class PlayerRendererMixin {
@Unique private boolean ctp$perspectiveActive = false;
@Unique private float ctp$lean = 0.0f, ctp$yaw = 0.0f;
Expand All @@ -39,18 +39,30 @@ public class PlayerRendererMixin {
ctp$yaw = yaw;
}

public float ctp$getLean() {
return ctp$lean;
}

public float ctp$getYaw() {
return ctp$yaw;
}

@Inject(
method = "setupRotations(Lnet/minecraft/client/player/AbstractClientPlayer;Lcom/mojang/blaze3d/vertex/PoseStack;FFF)V",
at = @At(
value = "INVOKE",
shift = At.Shift.BEFORE,
target = "Lnet/minecraft/client/renderer/entity/LivingEntityRenderer;setupRotations(Lnet/minecraft/world/entity/LivingEntity;Lcom/mojang/blaze3d/vertex/PoseStack;FFF)V"
value = "HEAD"
)
)
protected void setupRotations(AbstractClientPlayer p_117802_, PoseStack p_117803_, float p_117804_, float p_117805_, float p_117806_, CallbackInfo ci) {
if(ctp$perspectiveActive) {
p_117803_.rotateAround(Axis.ZP.rotationDegrees(Mth.cos(Mth.DEG_TO_RAD * ctp$yaw) * ctp$lean), 0, 1.3f, 0);
p_117803_.rotateAround(Axis.XP.rotationDegrees(Mth.sin(Mth.DEG_TO_RAD * ctp$yaw) * -ctp$lean), 0, 1.3f, 0);
float height = 0;

if(p_117802_.getVehicle() != null) {
height = 1.4f;
}

p_117803_.rotateAround(Axis.ZP.rotationDegrees(Mth.cos(Mth.DEG_TO_RAD * ctp$yaw) * ctp$lean), 0, height, 0);
p_117803_.rotateAround(Axis.XP.rotationDegrees(Mth.sin(Mth.DEG_TO_RAD * ctp$yaw) * -ctp$lean), 0, height, 0);
}
}
}
Loading

0 comments on commit b291c24

Please sign in to comment.