Anchor crouch collision to player feet
This commit is contained in:
1 parent
c186a35c72
commit
fceabe9cad
8 files changed
+65
-152
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
# Sable Gravity 0.7.4
|
# Sable Gravity 0.7.5
|
||||||
|
|
||||||
Sable Gravity is a multidimensional gravity-frame kernel for moving and arbitrarily rotated
|
Sable Gravity is a multidimensional gravity-frame kernel for moving and arbitrarily rotated
|
||||||
Sable ships. A frame owns a dimension-qualified identity, a local coordinate system, a rigid
|
Sable ships. A frame owns a dimension-qualified identity, a local coordinate system, a rigid
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx2G
|
|||||||
org.gradle.daemon=false
|
org.gradle.daemon=false
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
mod_id=sablegravity
|
mod_id=sablegravity
|
||||||
mod_version=0.7.4-player-recovery
|
mod_version=0.7.5-foot-anchor
|
||||||
minecraft_version=1.21.1
|
minecraft_version=1.21.1
|
||||||
neo_version=21.1.228
|
neo_version=21.1.228
|
||||||
sable_version=2.0.3+mc1.21.1
|
sable_version=2.0.3+mc1.21.1
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
package dev.nightly.sablegravity.mixin;
|
package dev.nightly.sablegravity.mixin;
|
||||||
|
|
||||||
import dev.nightly.sablegravity.SableGravityState;
|
import dev.nightly.sablegravity.SableGravityState;
|
||||||
import dev.nightly.sablegravity.physics.SableFrameOrientation;
|
|
||||||
import dev.nightly.sablegravity.physics.SableLocalPlayerPhysics;
|
import dev.nightly.sablegravity.physics.SableLocalPlayerPhysics;
|
||||||
import dev.nightly.sablegravity.physics.SableOrientedBounds;
|
|
||||||
import dev.nightly.sablegravity.physics.SableParentWorldCollision;
|
import dev.nightly.sablegravity.physics.SableParentWorldCollision;
|
||||||
import dev.ryanhcode.sable.sublevel.SubLevel;
|
import dev.ryanhcode.sable.sublevel.SubLevel;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.joml.Quaterniond;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
@@ -17,8 +14,6 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps Entity.bb as metadata/broadphase only while arbitrary gravity is active. This mixin has
|
* Keeps Entity.bb as metadata/broadphase only while arbitrary gravity is active. This mixin has
|
||||||
* higher priority than Sable's Entity mixin so the semantic-flag restoration point is inserted
|
* higher priority than Sable's Entity mixin so the semantic-flag restoration point is inserted
|
||||||
@@ -26,8 +21,6 @@ import java.util.UUID;
|
|||||||
*/
|
*/
|
||||||
@Mixin(value = Entity.class, priority = 3000)
|
@Mixin(value = Entity.class, priority = 3000)
|
||||||
public abstract class EntityAabbIsolationMixin {
|
public abstract class EntityAabbIsolationMixin {
|
||||||
@Unique private Vec3 sablegravity$localFeetBeforeResize;
|
|
||||||
@Unique private UUID sablegravity$frameBeforeResize;
|
|
||||||
@Unique private boolean sablegravity$insideMove;
|
@Unique private boolean sablegravity$insideMove;
|
||||||
|
|
||||||
@Inject(method = "collide", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "collide", at = @At("HEAD"), cancellable = true)
|
||||||
@@ -82,90 +75,8 @@ public abstract class EntityAabbIsolationMixin {
|
|||||||
sablegravity$insideMove = false;
|
sablegravity$insideMove = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "refreshDimensions", at = @At("HEAD"))
|
|
||||||
private void sablegravity$captureFeetBeforeResize(CallbackInfo ci) {
|
|
||||||
Entity self = (Entity) (Object) this;
|
|
||||||
if (!(self instanceof Player player) || !usesOrientedBody(player)) {
|
|
||||||
sablegravity$clearResizeAnchor();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SubLevel frame = currentFrame(player);
|
|
||||||
if (!validFrame(player, frame)) {
|
|
||||||
sablegravity$clearResizeAnchor();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A pose transition belongs to the Sable frame, not to parent-world space. Anchoring the
|
|
||||||
// transient resize to a world point lets a simultaneously moving/rotating frame turn
|
|
||||||
// crouch into a visible teleport or tangent slide. Store the invariant local feet point.
|
|
||||||
Quaterniond orientation = SableFrameOrientation.getFrameOrientation(frame, 1.0F);
|
|
||||||
Vec3 worldFeet = SableOrientedBounds.worldFeet(player, orientation);
|
|
||||||
sablegravity$localFeetBeforeResize = frame.logicalPose()
|
|
||||||
.transformPositionInverse(worldFeet);
|
|
||||||
sablegravity$frameBeforeResize = frame.getUniqueId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "refreshDimensions", at = @At("TAIL"))
|
|
||||||
private void sablegravity$restoreFeetAfterResize(CallbackInfo ci) {
|
|
||||||
Entity self = (Entity) (Object) this;
|
|
||||||
if (!(self instanceof Player player)
|
|
||||||
|| sablegravity$localFeetBeforeResize == null) {
|
|
||||||
sablegravity$clearResizeAnchor();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SubLevel frame = currentFrame(player);
|
|
||||||
if (!validFrame(player, frame)
|
|
||||||
|| !frame.getUniqueId().equals(sablegravity$frameBeforeResize)) {
|
|
||||||
sablegravity$clearResizeAnchor();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Quaterniond orientation = SableFrameOrientation.getFrameOrientation(frame, 1.0F);
|
|
||||||
Vec3 physicalFeet = frame.logicalPose().transformPosition(
|
|
||||||
sablegravity$localFeetBeforeResize
|
|
||||||
);
|
|
||||||
Vec3 rawPosition = SableOrientedBounds.rawPositionForFeet(
|
|
||||||
player,
|
|
||||||
physicalFeet,
|
|
||||||
orientation
|
|
||||||
);
|
|
||||||
if (Double.isFinite(rawPosition.x)
|
|
||||||
&& Double.isFinite(rawPosition.y)
|
|
||||||
&& Double.isFinite(rawPosition.z)) {
|
|
||||||
player.setPos(rawPosition);
|
|
||||||
}
|
|
||||||
sablegravity$clearResizeAnchor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private static boolean usesOrientedBody(Player player) {
|
|
||||||
if (!((SableGravityState) player).sablegravity$isEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return player.isLocalPlayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private static SubLevel currentFrame(Player player) {
|
|
||||||
if (player.isLocalPlayer()) {
|
|
||||||
SubLevel attached = SableLocalPlayerPhysics.getAttachedFrameIfPresent(player);
|
|
||||||
if (validFrame(player, attached)) {
|
|
||||||
return attached;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static boolean validFrame(Player player, SubLevel frame) {
|
private static boolean validFrame(Player player, SubLevel frame) {
|
||||||
return frame != null && !frame.isRemoved() && frame.getLevel() == player.level();
|
return frame != null && !frame.isRemoved() && frame.getLevel() == player.level();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
|
||||||
private void sablegravity$clearResizeAnchor() {
|
|
||||||
sablegravity$localFeetBeforeResize = null;
|
|
||||||
sablegravity$frameBeforeResize = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import dev.nightly.sablegravity.physics.SableLocalCollisionProbe;
|
|||||||
import dev.nightly.sablegravity.physics.SableLocalPlayerPhysics;
|
import dev.nightly.sablegravity.physics.SableLocalPlayerPhysics;
|
||||||
import dev.nightly.sablegravity.physics.SableOrientedBounds;
|
import dev.nightly.sablegravity.physics.SableOrientedBounds;
|
||||||
import dev.nightly.sablegravity.physics.SableParentWorldCollision;
|
import dev.nightly.sablegravity.physics.SableParentWorldCollision;
|
||||||
|
import dev.ryanhcode.sable.Sable;
|
||||||
import dev.ryanhcode.sable.sublevel.SubLevel;
|
import dev.ryanhcode.sable.sublevel.SubLevel;
|
||||||
import net.minecraft.world.entity.EntityDimensions;
|
import net.minecraft.world.entity.EntityDimensions;
|
||||||
import net.minecraft.world.entity.Pose;
|
import net.minecraft.world.entity.Pose;
|
||||||
@@ -27,11 +28,19 @@ public abstract class PlayerPoseFitMixin {
|
|||||||
) {
|
) {
|
||||||
Player player = (Player) (Object) this;
|
Player player = (Player) (Object) this;
|
||||||
SableGravityState state = (SableGravityState) player;
|
SableGravityState state = (SableGravityState) player;
|
||||||
if (!state.sablegravity$isEnabled() || !player.isLocalPlayer()) {
|
if (!state.sablegravity$isEnabled()
|
||||||
|
|| (player.level().isClientSide() && !player.isLocalPlayer())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubLevel frame = SableLocalPlayerPhysics.getAttachedFrameIfPresent(player);
|
// The local client owns movement, but the server still owns pose/entity-data
|
||||||
|
// synchronization. Its upright parent-world fit test sees the rotated ship as unrelated
|
||||||
|
// terrain and rejects CROUCHING even though the exact ship-local OBB fits. Use the same
|
||||||
|
// frame-aware fit contract on both authoritative endpoints; remote client replicas only
|
||||||
|
// consume the synchronized pose and do not run this branch.
|
||||||
|
SubLevel frame = player.isLocalPlayer()
|
||||||
|
? SableLocalPlayerPhysics.getAttachedFrameIfPresent(player)
|
||||||
|
: Sable.HELPER.getTrackingSubLevel(player);
|
||||||
if (frame == null || frame.isRemoved() || frame.getLevel() != player.level()) {
|
if (frame == null || frame.isRemoved() || frame.getLevel() != player.level()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import net.minecraft.world.phys.Vec3;
|
|||||||
import org.joml.Quaterniondc;
|
import org.joml.Quaterniondc;
|
||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
@@ -66,7 +67,7 @@ public abstract class SableCollisionYawMixin {
|
|||||||
return SubLevelEntityCollision.getHitBoxYaw(pose);
|
return SubLevelEntityCollision.getHitBoxYaw(pose);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rotates non-player bodies around their invariant raw feet position, never around the eye. */
|
/** Rotates gravity bodies around their invariant raw/network feet position, never the eye. */
|
||||||
@Inject(
|
@Inject(
|
||||||
method = "transformEntityBoundsCenter",
|
method = "transformEntityBoundsCenter",
|
||||||
at = @At("HEAD"),
|
at = @At("HEAD"),
|
||||||
@@ -80,12 +81,7 @@ public abstract class SableCollisionYawMixin {
|
|||||||
Vector3d center,
|
Vector3d center,
|
||||||
CallbackInfo ci
|
CallbackInfo ci
|
||||||
) {
|
) {
|
||||||
if (entity instanceof Player || orientation == null) {
|
if (!sablegravity$usesFeetAnchoredBody(entity, orientation)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
GravityEntityState state = (GravityEntityState) entity;
|
|
||||||
if (state.sablegravity$isInLocalSimulation()
|
|
||||||
|| state.sablegravity$getFrameId() == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,17 +113,39 @@ public abstract class SableCollisionYawMixin {
|
|||||||
float partialTicks,
|
float partialTicks,
|
||||||
Quaterniondc orientation
|
Quaterniondc orientation
|
||||||
) {
|
) {
|
||||||
if (!(entity instanceof Player) && orientation != null) {
|
if (sablegravity$usesFeetAnchoredBody(entity, orientation)) {
|
||||||
GravityEntityState state = (GravityEntityState) entity;
|
return new Vector3d(
|
||||||
if (!state.sablegravity$isInLocalSimulation()
|
entity.getX(),
|
||||||
&& state.sablegravity$getFrameId() != null) {
|
entity.getY() - partialTicks,
|
||||||
return new Vector3d(
|
entity.getZ()
|
||||||
entity.getX(),
|
);
|
||||||
entity.getY() - partialTicks,
|
|
||||||
entity.getZ()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return companion.getFeetPos(entity, partialTicks, orientation);
|
return companion.getFeetPos(entity, partialTicks, orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private static boolean sablegravity$usesFeetAnchoredBody(
|
||||||
|
Entity entity,
|
||||||
|
Quaterniondc orientation
|
||||||
|
) {
|
||||||
|
if (orientation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GravityEntityState state = (GravityEntityState) entity;
|
||||||
|
if (state.sablegravity$isInLocalSimulation()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity instanceof Player player) {
|
||||||
|
if (!player.isLocalPlayer()
|
||||||
|
|| !((SableGravityState) player).sablegravity$isEnabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SubLevel frame = SableLocalPlayerPhysics.getAttachedFrameIfPresent(player);
|
||||||
|
return frame != null && !frame.isRemoved() && frame.getLevel() == player.level();
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.sablegravity$getFrameId() != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -130,11 +130,9 @@ public final class SableLocalPlayerPhysics {
|
|||||||
) {
|
) {
|
||||||
SubLevel previousFrame = validFrame(player, ATTACHED_FRAMES.get(player));
|
SubLevel previousFrame = validFrame(player, ATTACHED_FRAMES.get(player));
|
||||||
|
|
||||||
// Sable's custom-orientation OBB is rotated around the eye, while vanilla stores the raw
|
// Raw/network position is the invariant physical feet point. The collision mixin rotates
|
||||||
// entity position at the unrotated feet. Switching the quaternion without rebasing the raw
|
// the OBB center around it, so attaching or changing pose never needs a packet-coordinate
|
||||||
// position moves the physical OBB bottom by up to twice the eye height. That exact offset
|
// rebase.
|
||||||
// was then reported as inheritedMotion (2.32 blocks in the supplied log) and launched the
|
|
||||||
// player. Preserve the physical feet point and change only the raw eye-anchor position.
|
|
||||||
Vec3 physicalFeet;
|
Vec3 physicalFeet;
|
||||||
if (previousFrame != null) {
|
if (previousFrame != null) {
|
||||||
physicalFeet = SableOrientedBounds.worldFeet(
|
physicalFeet = SableOrientedBounds.worldFeet(
|
||||||
@@ -238,8 +236,7 @@ public final class SableLocalPlayerPhysics {
|
|||||||
if (attached != null
|
if (attached != null
|
||||||
&& !attached.isRemoved()
|
&& !attached.isRemoved()
|
||||||
&& attached.getLevel() == player.level()) {
|
&& attached.getLevel() == player.level()) {
|
||||||
// Undo the eye-anchor raw-position rebase before vanilla resumes treating position()
|
// Keep the invariant physical feet when vanilla resumes upright collision.
|
||||||
// as the physical feet. Otherwise toggling gravity off visibly teleports the player.
|
|
||||||
Vec3 physicalFeet = SableOrientedBounds.worldFeet(
|
Vec3 physicalFeet = SableOrientedBounds.worldFeet(
|
||||||
player,
|
player,
|
||||||
SableFrameOrientation.getFrameOrientation(attached, 1.0F)
|
SableFrameOrientation.getFrameOrientation(attached, 1.0F)
|
||||||
@@ -324,7 +321,7 @@ public final class SableLocalPlayerPhysics {
|
|||||||
// Incrementally adding lastPose -> logicalPose deltas accumulated interpolation and
|
// Incrementally adding lastPose -> logicalPose deltas accumulated interpolation and
|
||||||
// client/server round-off as a small tangent displacement every tick; the player had zero
|
// client/server round-off as a small tangent displacement every tick; the player had zero
|
||||||
// local X/Z velocity but still slid off the deck. The local feet coordinate is the source
|
// local X/Z velocity but still slid off the deck. The local feet coordinate is the source
|
||||||
// of truth, so converge the raw eye-anchor position to its current logical-pose image.
|
// of truth, so converge raw/network feet to its current logical-pose image.
|
||||||
Vec3 actualFrameMotion = applyCorrectedFrameMotion(player, frameMotion);
|
Vec3 actualFrameMotion = applyCorrectedFrameMotion(player, frameMotion);
|
||||||
LAST_FRAME_MOTION.put(player, actualFrameMotion);
|
LAST_FRAME_MOTION.put(player, actualFrameMotion);
|
||||||
Vector3d inheritedVelocity =
|
Vector3d inheritedVelocity =
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import org.joml.Quaterniondc;
|
|||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Canonical dimensions and the exact eye-anchored OBB used by Sable gravity entities.
|
* Canonical dimensions for the feet-anchored OBB used by Sable gravity entities.
|
||||||
*
|
*
|
||||||
* Minecraft's AABB type cannot encode an arbitrary orientation. The exact body is therefore
|
* Minecraft's AABB type cannot encode an arbitrary orientation. The exact body is therefore
|
||||||
* represented by a center, half extents and quaternion; {@link #worldEnvelope(Entity, Quaterniondc)}
|
* represented by a center, half extents and quaternion; {@link #worldEnvelope(Entity, Quaterniondc)}
|
||||||
@@ -32,58 +32,36 @@ public final class SableOrientedBounds {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Exact world-space OBB center matching Sable's transformEntityBoundsCenter contract. */
|
/** Exact world-space OBB center: raw entity position is always the physical feet point. */
|
||||||
public static Vec3 worldCenter(Entity entity, Quaterniondc orientation) {
|
public static Vec3 worldCenter(Entity entity, Quaterniondc orientation) {
|
||||||
double halfHeight = entity.getBbHeight() * 0.5D;
|
double halfHeight = entity.getBbHeight() * 0.5D;
|
||||||
double anchorOffset = entity.getEyeHeight() - halfHeight;
|
|
||||||
Vector3d rotatedOffset = orientation.transform(
|
Vector3d rotatedOffset = orientation.transform(
|
||||||
new Vector3d(0.0D, anchorOffset, 0.0D),
|
new Vector3d(0.0D, halfHeight, 0.0D),
|
||||||
new Vector3d()
|
new Vector3d()
|
||||||
);
|
);
|
||||||
Vec3 position = entity.position();
|
Vec3 position = entity.position();
|
||||||
return new Vec3(
|
return new Vec3(
|
||||||
position.x - rotatedOffset.x,
|
position.x + rotatedOffset.x,
|
||||||
position.y + entity.getEyeHeight() - rotatedOffset.y,
|
position.y + rotatedOffset.y,
|
||||||
position.z - rotatedOffset.z
|
position.z + rotatedOffset.z
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Physical feet point after rotating the canonical body around the eye anchor. */
|
/** Raw/network position and physical feet are the same invariant point. */
|
||||||
public static Vec3 worldFeet(Entity entity, Quaterniondc orientation) {
|
public static Vec3 worldFeet(Entity entity, Quaterniondc orientation) {
|
||||||
Vector3d rotatedEyeOffset = orientation.transform(
|
return entity.position();
|
||||||
new Vector3d(0.0D, entity.getEyeHeight(), 0.0D),
|
|
||||||
new Vector3d()
|
|
||||||
);
|
|
||||||
Vec3 position = entity.position();
|
|
||||||
return new Vec3(
|
|
||||||
position.x - rotatedEyeOffset.x,
|
|
||||||
position.y + entity.getEyeHeight() - rotatedEyeOffset.y,
|
|
||||||
position.z - rotatedEyeOffset.z
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a physical world-space feet point back to Minecraft's raw entity position for
|
* Converts physical feet to raw/network position. The orientation is intentionally irrelevant:
|
||||||
* Sable's eye-anchored custom orientation contract.
|
* pose and eye-height changes must never move the packet coordinate.
|
||||||
*
|
|
||||||
* <p>Sable rotates a custom entity OBB around the eye. Therefore {@code Entity.position()}
|
|
||||||
* is no longer the physical bottom-center of the OBB. Keeping the feet fixed while changing
|
|
||||||
* orientation requires moving the raw position by {@code Q * eyeOffset - worldEyeOffset}.</p>
|
|
||||||
*/
|
*/
|
||||||
public static Vec3 rawPositionForFeet(
|
public static Vec3 rawPositionForFeet(
|
||||||
Entity entity,
|
Entity entity,
|
||||||
Vec3 physicalFeet,
|
Vec3 physicalFeet,
|
||||||
Quaterniondc orientation
|
Quaterniondc orientation
|
||||||
) {
|
) {
|
||||||
Vector3d rotatedEyeOffset = orientation.transform(
|
return physicalFeet;
|
||||||
new Vector3d(0.0D, entity.getEyeHeight(), 0.0D),
|
|
||||||
new Vector3d()
|
|
||||||
);
|
|
||||||
return new Vec3(
|
|
||||||
physicalFeet.x + rotatedEyeOffset.x,
|
|
||||||
physicalFeet.y + rotatedEyeOffset.y - entity.getEyeHeight(),
|
|
||||||
physicalFeet.z + rotatedEyeOffset.z
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vec3 localFeet(
|
public static Vec3 localFeet(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ license = "All Rights Reserved"
|
|||||||
|
|
||||||
[[mods]]
|
[[mods]]
|
||||||
modId = "sablegravity"
|
modId = "sablegravity"
|
||||||
version = "0.7.4-player-recovery"
|
version = "0.7.5-foot-anchor"
|
||||||
displayName = "Sable Gravity Local Physics"
|
displayName = "Sable Gravity Local Physics"
|
||||||
authors = "Nightly"
|
authors = "Nightly"
|
||||||
description = '''Sable ship-local player gravity and collision. Non-player entities remain on Sable's native lifecycle while the universal frame layer is rebuilt safely.'''
|
description = '''Sable ship-local player gravity and collision. Non-player entities remain on Sable's native lifecycle while the universal frame layer is rebuilt safely.'''
|
||||||
|
|||||||
Reference in new issue
Block a user