001/* 002 * This file is part of Baritone. 003 * 004 * Baritone is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU Lesser General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * Baritone is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public License 015 * along with Baritone. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package baritone.api.utils; 019 020import baritone.api.BaritoneAPI; 021import baritone.api.IBaritone; 022import net.minecraft.client.player.LocalPlayer; 023import net.minecraft.core.BlockPos; 024import net.minecraft.core.Direction; 025import net.minecraft.util.Mth; 026import net.minecraft.world.entity.Entity; 027import net.minecraft.world.level.block.BaseFireBlock; 028import net.minecraft.world.level.block.state.BlockState; 029import net.minecraft.world.phys.BlockHitResult; 030import net.minecraft.world.phys.HitResult; 031import net.minecraft.world.phys.Vec3; 032import net.minecraft.world.phys.shapes.Shapes; 033import net.minecraft.world.phys.shapes.VoxelShape; 034 035import java.util.Optional; 036 037/** 038 * @author Brady 039 * @since 9/25/2018 040 */ 041public final class RotationUtils { 042 043 /** 044 * Constant that a degree value is multiplied by to get the equivalent radian value 045 */ 046 public static final double DEG_TO_RAD = Math.PI / 180.0; 047 public static final float DEG_TO_RAD_F = (float) DEG_TO_RAD; 048 049 /** 050 * Constant that a radian value is multiplied by to get the equivalent degree value 051 */ 052 public static final double RAD_TO_DEG = 180.0 / Math.PI; 053 public static final float RAD_TO_DEG_F = (float) RAD_TO_DEG; 054 055 /** 056 * Offsets from the root block position to the center of each side. 057 */ 058 private static final Vec3[] BLOCK_SIDE_MULTIPLIERS = new Vec3[]{ 059 new Vec3(0.5, 0, 0.5), // Down 060 new Vec3(0.5, 1, 0.5), // Up 061 new Vec3(0.5, 0.5, 0), // North 062 new Vec3(0.5, 0.5, 1), // South 063 new Vec3(0, 0.5, 0.5), // West 064 new Vec3(1, 0.5, 0.5) // East 065 }; 066 067 private RotationUtils() {} 068 069 /** 070 * Calculates the rotation from BlockPos<sub>dest</sub> to BlockPos<sub>orig</sub> 071 * 072 * @param orig The origin position 073 * @param dest The destination position 074 * @return The rotation from the origin to the destination 075 */ 076 public static Rotation calcRotationFromCoords(BlockPos orig, BlockPos dest) { 077 return calcRotationFromVec3d(new Vec3(orig.getX(), orig.getY(), orig.getZ()), new Vec3(dest.getX(), dest.getY(), dest.getZ())); 078 } 079 080 /** 081 * Wraps the target angles to a relative value from the current angles. This is done by 082 * subtracting the current from the target, normalizing it, and then adding the current 083 * angles back to it. 084 * 085 * @param current The current angles 086 * @param target The target angles 087 * @return The wrapped angles 088 */ 089 public static Rotation wrapAnglesToRelative(Rotation current, Rotation target) { 090 if (current.yawIsReallyClose(target)) { 091 return new Rotation(current.getYaw(), target.getPitch()); 092 } 093 return target.subtract(current).normalize().add(current); 094 } 095 096 /** 097 * Calculates the rotation from Vec<sub>dest</sub> to Vec<sub>orig</sub> and makes the 098 * return value relative to the specified current rotations. 099 * 100 * @param orig The origin position 101 * @param dest The destination position 102 * @param current The current rotations 103 * @return The rotation from the origin to the destination 104 * @see #wrapAnglesToRelative(Rotation, Rotation) 105 */ 106 public static Rotation calcRotationFromVec3d(Vec3 orig, Vec3 dest, Rotation current) { 107 return wrapAnglesToRelative(current, calcRotationFromVec3d(orig, dest)); 108 } 109 110 /** 111 * Calculates the rotation from Vec<sub>dest</sub> to Vec<sub>orig</sub> 112 * 113 * @param orig The origin position 114 * @param dest The destination position 115 * @return The rotation from the origin to the destination 116 */ 117 private static Rotation calcRotationFromVec3d(Vec3 orig, Vec3 dest) { 118 double[] delta = {orig.x - dest.x, orig.y - dest.y, orig.z - dest.z}; 119 double yaw = Mth.atan2(delta[0], -delta[2]); 120 double dist = Math.sqrt(delta[0] * delta[0] + delta[2] * delta[2]); 121 double pitch = Mth.atan2(delta[1], dist); 122 return new Rotation( 123 (float) (yaw * RAD_TO_DEG), 124 (float) (pitch * RAD_TO_DEG) 125 ); 126 } 127 128 /** 129 * Calculates the look vector for the specified yaw/pitch rotations. 130 * 131 * @param rotation The input rotation 132 * @return Look vector for the rotation 133 */ 134 public static Vec3 calcLookDirectionFromRotation(Rotation rotation) { 135 float flatZ = Mth.cos((-rotation.getYaw() * DEG_TO_RAD_F) - (float) Math.PI); 136 float flatX = Mth.sin((-rotation.getYaw() * DEG_TO_RAD_F) - (float) Math.PI); 137 float pitchBase = -Mth.cos(-rotation.getPitch() * DEG_TO_RAD_F); 138 float pitchHeight = Mth.sin(-rotation.getPitch() * DEG_TO_RAD_F); 139 return new Vec3(flatX * pitchBase, pitchHeight, flatZ * pitchBase); 140 } 141 142 @Deprecated 143 public static Vec3 calcVec3dFromRotation(Rotation rotation) { 144 return calcLookDirectionFromRotation(rotation); 145 } 146 147 /** 148 * @param ctx Context for the viewing entity 149 * @param pos The target block position 150 * @return The optional rotation 151 * @see #reachable(IPlayerContext, BlockPos, double) 152 */ 153 public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos) { 154 return reachable(ctx, pos, false); 155 } 156 157 public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos, boolean wouldSneak) { 158 return reachable(ctx, pos, ctx.playerController().getBlockReachDistance(), wouldSneak); 159 } 160 161 /** 162 * Determines if the specified entity is able to reach the center of any of the sides 163 * of the specified block. It first checks if the block center is reachable, and if so, 164 * that rotation will be returned. If not, it will return the first center of a given 165 * side that is reachable. The return type will be {@link Optional#empty()} if the entity is 166 * unable to reach any of the sides of the block. 167 * 168 * @param ctx Context for the viewing entity 169 * @param pos The target block position 170 * @param blockReachDistance The block reach distance of the entity 171 * @return The optional rotation 172 */ 173 public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos, double blockReachDistance) { 174 return reachable(ctx, pos, blockReachDistance, false); 175 } 176 177 public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos, double blockReachDistance, boolean wouldSneak) { 178 if (BaritoneAPI.getSettings().remainWithExistingLookDirection.value && ctx.isLookingAt(pos)) { 179 /* 180 * why add 0.0001? 181 * to indicate that we actually have a desired pitch 182 * the way we indicate that the pitch can be whatever and we only care about the yaw 183 * is by setting the desired pitch to the current pitch 184 * setting the desired pitch to the current pitch + 0.0001 means that we do have a desired pitch, it's 185 * just what it currently is 186 * 187 * or if you're a normal person literally all this does it ensure that we don't nudge the pitch to a normal level 188 */ 189 Rotation hypothetical = ctx.playerRotations().add(new Rotation(0, 0.0001F)); 190 if (wouldSneak) { 191 // the concern here is: what if we're looking at it now, but as soon as we start sneaking we no longer are 192 HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), hypothetical, blockReachDistance, true); 193 if (result != null && result.getType() == HitResult.Type.BLOCK && ((BlockHitResult) result).getBlockPos().equals(pos)) { 194 return Optional.of(hypothetical); // yes, if we sneaked we would still be looking at the block 195 } 196 } else { 197 return Optional.of(hypothetical); 198 } 199 } 200 Optional<Rotation> possibleRotation = reachableCenter(ctx, pos, blockReachDistance, wouldSneak); 201 //System.out.println("center: " + possibleRotation); 202 if (possibleRotation.isPresent()) { 203 return possibleRotation; 204 } 205 206 BlockState state = ctx.world().getBlockState(pos); 207 VoxelShape shape = state.getShape(ctx.world(), pos); 208 if (shape.isEmpty()) { 209 shape = Shapes.block(); 210 } 211 for (Vec3 sideOffset : BLOCK_SIDE_MULTIPLIERS) { 212 double xDiff = shape.min(Direction.Axis.X) * sideOffset.x + shape.max(Direction.Axis.X) * (1 - sideOffset.x); 213 double yDiff = shape.min(Direction.Axis.Y) * sideOffset.y + shape.max(Direction.Axis.Y) * (1 - sideOffset.y); 214 double zDiff = shape.min(Direction.Axis.Z) * sideOffset.z + shape.max(Direction.Axis.Z) * (1 - sideOffset.z); 215 possibleRotation = reachableOffset(ctx, pos, new Vec3(pos.getX(), pos.getY(), pos.getZ()).add(xDiff, yDiff, zDiff), blockReachDistance, wouldSneak); 216 if (possibleRotation.isPresent()) { 217 return possibleRotation; 218 } 219 } 220 return Optional.empty(); 221 } 222 223 /** 224 * Determines if the specified entity is able to reach the specified block with 225 * the given offsetted position. The return type will be {@link Optional#empty()} if 226 * the entity is unable to reach the block with the offset applied. 227 * 228 * @param ctx Context for the viewing entity 229 * @param pos The target block position 230 * @param offsetPos The position of the block with the offset applied. 231 * @param blockReachDistance The block reach distance of the entity 232 * @return The optional rotation 233 */ 234 public static Optional<Rotation> reachableOffset(IPlayerContext ctx, BlockPos pos, Vec3 offsetPos, double blockReachDistance, boolean wouldSneak) { 235 Vec3 eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(ctx.player()) : ctx.player().getEyePosition(1.0F); 236 Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, ctx.playerRotations()); 237 Rotation actualRotation = BaritoneAPI.getProvider().getBaritoneForPlayer(ctx.player()).getLookBehavior().getAimProcessor().peekRotation(rotation); 238 HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), actualRotation, blockReachDistance, wouldSneak); 239 //System.out.println(result); 240 if (result != null && result.getType() == HitResult.Type.BLOCK) { 241 if (((BlockHitResult) result).getBlockPos().equals(pos)) { 242 return Optional.of(rotation); 243 } 244 if (ctx.world().getBlockState(pos).getBlock() instanceof BaseFireBlock && ((BlockHitResult) result).getBlockPos().equals(pos.below())) { 245 return Optional.of(rotation); 246 } 247 } 248 return Optional.empty(); 249 } 250 251 /** 252 * Determines if the specified entity is able to reach the specified block where it is 253 * looking at the direct center of it's hitbox. 254 * 255 * @param ctx Context for the viewing entity 256 * @param pos The target block position 257 * @param blockReachDistance The block reach distance of the entity 258 * @return The optional rotation 259 */ 260 public static Optional<Rotation> reachableCenter(IPlayerContext ctx, BlockPos pos, double blockReachDistance, boolean wouldSneak) { 261 return reachableOffset(ctx, pos, VecUtils.calculateBlockCenter(ctx.world(), pos), blockReachDistance, wouldSneak); 262 } 263 264 @Deprecated 265 public static Optional<Rotation> reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance) { 266 return reachable(entity, pos, blockReachDistance, false); 267 } 268 269 @Deprecated 270 public static Optional<Rotation> reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { 271 IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(entity); 272 IPlayerContext ctx = baritone.getPlayerContext(); 273 return reachable(ctx, pos, blockReachDistance, wouldSneak); 274 } 275 276 @Deprecated 277 public static Optional<Rotation> reachableOffset(Entity entity, BlockPos pos, Vec3 offsetPos, double blockReachDistance, boolean wouldSneak) { 278 Vec3 eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(entity) : entity.getEyePosition(1.0F); 279 Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, new Rotation(entity.getYRot(), entity.getXRot())); 280 HitResult result = RayTraceUtils.rayTraceTowards(entity, rotation, blockReachDistance, wouldSneak); 281 //System.out.println(result); 282 if (result != null && result.getType() == HitResult.Type.BLOCK) { 283 if (((BlockHitResult) result).getBlockPos().equals(pos)) { 284 return Optional.of(rotation); 285 } 286 if (entity.level().getBlockState(pos).getBlock() instanceof BaseFireBlock && ((BlockHitResult) result).getBlockPos().equals(pos.below())) { 287 return Optional.of(rotation); 288 } 289 } 290 return Optional.empty(); 291 } 292 293 @Deprecated 294 public static Optional<Rotation> reachableCenter(Entity entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { 295 return reachableOffset(entity, pos, VecUtils.calculateBlockCenter(entity.level(), pos), blockReachDistance, wouldSneak); 296 } 297}